MongoDB 排序

在 MongoDB 中使用 sort() 方法对数据进行排序,sort() 方法可以通过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,而 -1 是用于降序排列。

sort() 方法基本语法如下所示:

db.COLLECTION_NAME.find().sort({ KEY:1 })

注意:sort() 方法最多可以对 32 个键进行排序。

sort() 示例

该示例插入几条数据到 col 集合,然后分别按照 age 属性递增、递减排序。如下:

# 查看 col 集合所有数据
> db.col.find()
{ "_id" : ObjectId("64e71af810366fa87109a12f"), "name" : "张三", "age" : 18, "email" : "zhangsan@outlook.com" }
{ "_id" : ObjectId("64e71af810366fa87109a130"), "name" : "李四", "age" : 22, "email" : "lisi@qq.com" }
{ "_id" : ObjectId("64e71af810366fa87109a131"), "name" : "王五", "age" : 26, "email" : "wangwu@sina.com.cn" }
{ "_id" : ObjectId("64e71af810366fa87109a132"), "name" : "赵六", "age" : 27, "email" : "zhaoliu@gmail.com" }
{ "_id" : ObjectId("64e71af810366fa87109a133"), "name" : "顾七", "age" : 30, "email" : "guqi@qq.com" }
{ "_id" : ObjectId("64e71af810366fa87109a134"), "name" : "何八", "age" : 42, "email" : "heba@outlook.com" }

# 根据 age 属性对 find() 查询的结果进行递增排序
> db.col.find().sort({age:1})
{ "_id" : ObjectId("64e71af810366fa87109a12f"), "name" : "张三", "age" : 18, "email" : "zhangsan@outlook.com" }
{ "_id" : ObjectId("64e71af810366fa87109a130"), "name" : "李四", "age" : 22, "email" : "lisi@qq.com" }
{ "_id" : ObjectId("64e71af810366fa87109a131"), "name" : "王五", "age" : 26, "email" : "wangwu@sina.com.cn" }
{ "_id" : ObjectId("64e71af810366fa87109a132"), "name" : "赵六", "age" : 27, "email" : "zhaoliu@gmail.com" }
{ "_id" : ObjectId("64e71af810366fa87109a133"), "name" : "顾七", "age" : 30, "email" : "guqi@qq.com" }
{ "_id" : ObjectId("64e71af810366fa87109a134"), "name" : "何八", "age" : 42, "email" : "heba@outlook.com" }

# 根据 age 属性对 find() 查询的结果进行递减排序
> db.col.find().sort({age:-1})
{ "_id" : ObjectId("64e71af810366fa87109a134"), "name" : "何八", "age" : 42, "email" : "heba@outlook.com" }
{ "_id" : ObjectId("64e71af810366fa87109a133"), "name" : "顾七", "age" : 30, "email" : "guqi@qq.com" }
{ "_id" : ObjectId("64e71af810366fa87109a132"), "name" : "赵六", "age" : 27, "email" : "zhaoliu@gmail.com" }
{ "_id" : ObjectId("64e71af810366fa87109a131"), "name" : "王五", "age" : 26, "email" : "wangwu@sina.com.cn" }
{ "_id" : ObjectId("64e71af810366fa87109a130"), "name" : "李四", "age" : 22, "email" : "lisi@qq.com" }
{ "_id" : ObjectId("64e71af810366fa87109a12f"), "name" : "张三", "age" : 18, "email" : "zhangsan@outlook.com" }

排序一致性

已在 4.4 版中更改。

MongoDB 不会按特定顺序存储集合中的文档。在对包含重复值的字段进行排序时,可能会以任何顺序返回包含这些值的文档。

如果希望排序顺序一致,请在排序中至少包含一个包含唯一值的字段。保证这一点的最简单方法就是在排序查询中包含 _id 字段。

请看下面的 restaurants 集合:

db.restaurants.insertMany([
  { "_id" : 1, "name" : "Central Park Cafe", "borough" : "Manhattan"},
  { "_id" : 2, "name" : "Rock A Feller Bar and Grill", "borough" : "Queens"},
  { "_id" : 3, "name" : "Empire State Pub", "borough" : "Brooklyn"},
  { "_id" : 4, "name" : "Stan's Pizzaria", "borough" : "Manhattan"},
  { "_id" : 5, "name" : "Jane's Deli", "borough" : "Brooklyn"},
]);

以下命令使用 sort() 方法对 borough  字段进行排序:

db.restaurants.find().sort( { "borough": 1 } )

在本例中,排序顺序可能不一致,因为 borough 字段包含 Manhattan 和Brooklyn 重复值。文档按 borough  的字母顺序返回的,但在多次执行同一排序时,borough 值重复的文件的顺序可能并不相同。例如,以下是上述命令两次不同执行的结果:

> db.restaurants.find().sort( { "borough": 1 } )
{ "_id" : 3, "name" : "Empire State Pub", "borough" : "Brooklyn" }
{ "_id" : 5, "name" : "Jane's Deli", "borough" : "Brooklyn" }
{ "_id" : 1, "name" : "Central Park Cafe", "borough" : "Manhattan" }
{ "_id" : 4, "name" : "Stan's Pizzaria", "borough" : "Manhattan" }
{ "_id" : 2, "name" : "Rock A Feller Bar and Grill", "borough" : "Queens" }

> db.restaurants.find().sort( { "borough": 1 } )
{ "_id" : 5, "name" : "Jane's Deli", "borough" : "Brooklyn" }
{ "_id" : 3, "name" : "Empire State Pub", "borough" : "Brooklyn" }
{ "_id" : 4, "name" : "Stan's Pizzaria", "borough" : "Manhattan" }
{ "_id" : 1, "name" : "Central Park Cafe", "borough" : "Manhattan" }
{ "_id" : 2, "name" : "Rock A Feller Bar and Grill", "borough" : "Queens" }

虽然 borough 的值仍按字母顺序排序,但包含重复 borough 值(即Manhattan 和 Brooklyn)的文档的顺序却不一样。

要实现一致的排序,可在排序中添加一个只包含唯一值的字段。以下命令使用 sort()  方法对 borough 字段和 _id 字段进行排序:

db.restaurants.find().sort( { "borough": 1, "_id": 1 } )

由于 _id 字段总是保证包含唯一值,因此在多次执行同一排序时,返回的排序顺序总是相同的。

升序/降序排序

在排序参数中指定要排序的一个或多个字段,以及 1 或 -1 的值,以分别指定升序或降序排序。

以下操作首先按 age 字段以降序排列文档,然后按 posts 字段以升序排列文档:

db.users.find({ }).sort( { age : -1, posts: 1 } )

不同 BSON 类型的值时,MongoDB 使用以下从低到高的比较顺序:

  1. MinKey (internal type)

  2. Null

  3. Numbers (ints, longs, doubles, decimals)

  4. Symbol, String

  5. Object

  6. Array

  7. BinData

  8. ObjectId

  9. Boolean

  10. Date

  11. Timestamp

  12. Regular Expression

  13. MaxKey (internal type)

数值类型

为了进行比较,MongoDB 将某些类型视为等价类型。例如,数字类型会在比较前进行转换。

字符串

二进制比较

默认情况下,MongoDB 使用简单的二进制比较来比较字符串。

校对

校对允许用户指定特定语言的字符串比较规则,如字母大小写和重音符号规则。

校对规范的语法如下:

{
   locale: <string>,
   caseLevel: <boolean>,
   caseFirst: <string>,
   strength: <int>,
   numericOrdering: <boolean>,
   alternate: <string>,
   maxVariable: <string>,
   backwards: <boolean>
}

指定校对时,locale 字段是必填字段;所有其他校对字段都是可选字段。

如果没有为集合或操作指定校对,MongoDB 将使用先前版本中的简单二进制比较来进行字符串比较。

数组

组比较中:

  1. 小于比较或升序排序是按照 BSON 类型的排序顺序比较数组中最小的元素。

  2. 大于比较或降序排序是根据 BSON 类型的相反排序顺序比较数组中最大的元素。

  3. 比较值为单元素数组的字段(例如,[ 1 ])和非数组字段(例如,2)时,比较的对象是 1 和 2。

  4. 对空数组(例如 [ ])进行比较时,会将空数组视为小于空值或缺失的字段值。

对象(Object)

MongoDB 在比较 BSON 对象时使用以下顺序:

  • 按照键值对在 BSON 对象中出现的顺序递归比较键值对。

  • 比较字段类型。MongoDB 对字段类型使用以下从低到高的比较顺序: 

        1. MinKey (internal type)

        2. Null

        3. Numbers (ints, longs, doubles, decimals)

        4. Symbol, String

        5. Object

        6. Array

        7. BinData

        8. ObjectId

        9. Boolean

        10. Date

        11. Timestamp

        12. Regular Expression

        13. MaxKey (internal type)

  • 如果字段类型相同,则比较键字段名。

  • 如果键字段名相同,则比较字段值。

  • 如果字段值相等,则比较下一个键/值对(返回步骤 1)。没有更多字段对的对象小于有更多字段对的对象。

BinData

ngoDB 按以下顺序排列 BinData:

  1. 首先,按照数据的长度或大小排序。

  2. 然后,按 BSON 单字节子类型排序。

  3. 最后,按数据进行逐字节比较。

文本分值元数据排序

对于 $text 搜索,可以使用 { $meta: "textScore" } 表达式按相关性得分降序排序。

下面的示例文档根据 "textScore" 元数据指定了降序排序:

db.users.find(
  { $text: { $search: "operating" } },
  { score: { $meta: "textScore" }}        // 从 MongoDB 4.4 开始可选
).sort({ score: { $meta: "textScore" } })

textScore 元数据按降序排序。

排序和索引使用

ngoDB 可以从包含排序字段的索引中获取排序操作的结果。如果排序使用的索引与查询谓词相同,MongoDB 可以使用多个索引来支持排序操作。

如果 MongoDB 无法使用索引获取排序顺序,则必须对数据执行阻塞排序操作。阻塞排序表示 MongoDB 在返回结果之前必须消耗和处理排序的所有输入文档。阻塞排序不会阻塞对集合或数据库的并发操作。

使用索引的排序操作通常比阻塞排序具有更好的性能。有关创建索引以支持排序操作的更多信息,请参阅使用索引对查询结果进行排序。

allowDiskUse() 允许 MongoDB 在处理阻塞排序操作时,使用磁盘上的临时文件来存储超过 100 MB 系统内存限制的数据。

要检查 MongoDB 是否必须执行阻塞排序,请在查询中附加cursor.explain() 并检查 explain 结果。如果查询计划包含一个 SORT 阶段,那么 MongoDB 就必须执行阻塞排序操作,并受 100 MB 内存限制。

为防止阻塞排序占用过多内存:

  • 创建一个支持排序操作的索引。有关详细信息和示例,请参阅使用索引对查询结果排序。

  • 通过使用 cursor.limit() 和 cursor.sort() 限制要排序的数据量。有关详细信息和示例,请参阅限制结果。

与投影的交互

当一组结果既排序又投影时,MongoDB 查询引擎总是先应用排序。

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号