定义
兼容性
可以使用 $sort 查找托管在以下环境中的部署:
- MongoDB Atlas: The fully managed service for MongoDB deployments in the cloud
MongoDB Enterprise:基于订阅、自我管理的 MongoDB 版本
MongoDB Community:源代码可用、免费使用且可自行管理的 MongoDB 版本
语法
$sort 采用以下形式:
{ $sort: { <field1>: <sort order>, <field2>: <sort order> ... } }
$sort 采用一个文档,该文档指定要作为排序依据的字段以及相应的排序顺序。<sort order> 可以具有以下值之一:
值 | 说明 |
|---|---|
| 升序排序。 |
| 降序排序。 |
| Sort by the computed |
在对多个字段进行排序时,$sort 会从左到右评估排序顺序。文档首先按 <field1> 排序,然后具有相同 <field1> 值的文档按 <field2> 排序。
行为
性能
$sort 是一个阻塞阶段,这会导致管道在处理数据前等待为阻塞阶段检索所有输入数据。阻塞阶段可能会降低性能,因为它会减少具有多个阶段的管道的并行处理。对于大型数据集,阻塞阶段还可能使用大量内存。
限制
您最多可以对 32 个键进行排序。
为排序模式提供重复字段会导致错误。
排序一致性
MongoDB 不按特定顺序将文档存储在集合中。对包含重复值的字段进行排序时,可能会以任何顺序返回包含这些值的文档。
$sort 操作不是“稳定排序”,这意味着具有等效排序键的文档在输出中不一定能保持与输入相同的相对顺序。
如果排序条件中指定的字段在两个文档中都不存在,那么它们排序所依据的值是相同的。这两个文档可以以任何顺序返回。
如果需要一致的排序顺序,请在排序中至少纳入一个包含唯一值的字段。最简单方法是在排序查询中纳入 _id 字段。
考虑 sample_mflix 示例数据集中的 movies 集合。以下命令按 year 升序对 1925 或 1926 年发行的电影进行排序:
db.movies.aggregate( [ { $match: { year: { $in: [ 1925, 1926 ] } } }, { $sort: { year: 1 } }, { $project: { _id: 0, title: 1, year: 1 } } ] )
由于 year 字段在 1925 和 1926 中具有重复值,因此具有相同值的文档在不同运行中的排序方式可能会有所不同。示例,运行两次上一个命令会产生不同的排序:
[ { title: "Clash of the Wolves", year: 1925 }, { title: "Lady Windermere's Fan", year: 1925 }, { title: "Grass: A Nation's Battle for Life", year: 1925 }, { title: "Beau Geste", year: 1926 }, { title: "The Black Pirate", year: 1926 }, { title: "Ella Cinders", year: 1926 }, { title: "For Heaven's Sake", year: 1926 }, { title: "The Son of the Sheik", year: 1926 }, { title: "The Strong Man", year: 1926 } ]
[ { title: "Lady Windermere's Fan", year: 1925 }, { title: "Grass: A Nation's Battle for Life", year: 1925 }, { title: "Clash of the Wolves", year: 1925 }, { title: "The Strong Man", year: 1926 }, { title: "The Black Pirate", year: 1926 }, { title: "Ella Cinders", year: 1926 }, { title: "Beau Geste", year: 1926 }, { title: "The Son of the Sheik", year: 1926 }, { title: "For Heaven's Sake", year: 1926 } ]
一个操作按升序对 year 的值进行排序,但包含 year 重复值的文档的顺序并不相同。示例,1925 和 1926 的重复值。
要实现一致的排序,请在排序中添加一个仅包含唯一值的字段。以下命令同时对 year 字段和 _id 字段进行排序:
db.movies.aggregate( [ { $match: { year: { $in: [ 1925, 1926 ] } } }, { $sort: { year: 1, _id: 1 } } ] )
_id 字段始终包含唯一值,并且多次排序执行的排序顺序保持一致。
按数组字段排序
当 MongoDB 按数组值字段对文档进行排序时,排序键取决于排序是升序还是降序:
在升序排序中,排序键是数组中的最低值。
在降序排序中,排序键是数组中的最高值。
查询过滤器不影响排序键的选择。
例如,考虑 sample_mflix 示例数据集中的 movies 集合中的这些文档:
{ _id: ObjectId("573a1395f29313caabce166c"), title: "The Comancheros", genres: [ "Action", "Western", "Adventure" ] } { _id: ObjectId("573a1391f29313caabcd8521"), title: "The Son of the Sheik", genres: [ "Adventure", "Drama" ] }
以下查询按 genres 字段以升序和降序对文档进行排序:
// Ascending sort db.movies.aggregate( [ { $match: { title: { $in: [ "The Comancheros", "The Son of the Sheik" ] } } }, { $sort: { genres: 1 } } ] )
// Descending sort db.movies.aggregate( [ { $match: { title: { $in: [ "The Comancheros", "The Son of the Sheik" ] } } }, { $sort: { genres: -1 } } ] )
上述两个查询均首先返回 The Comancheros,因为 Action 和 Western 分别是其 genres 数组中最低和最高的条目。
按数组字段进行过滤和排序
当您按包含数组的字段进行过滤和排序时,过滤器不会影响用作排序键的值。此排序总是会将所有数组值视为潜在的排序键。
示例,以下查询将查找其 genres 数组包含 "Drama" 或 "Western" 的电影,并按 genres 对结果进行升序排序:
db.movies.aggregate( [ { $match: { title: { $in: [ "The Comancheros", "The Son of the Sheik" ] }, genres: { $in: [ "Drama", "Western" ] } } }, { $sort: { genres: 1 } } ] )
排序为升序。当按字母顺序排序时,排序键是 genres 数组中的第一个值:
在
The Comancheros中,第一个genres元素是Action。即使该值与过滤器{ genres: { $in: [ "Drama", "Western" ] } }不匹配,也将其用作排序键。在
The Son of the Sheik中,第一个genres元素是Adventure。同样,即使此操作与过滤器不匹配,它仍使用Adventure作为排序键。
查询首先返回 The Comancheros。
$sort 操作符与内存
$sort 和 $limit 内存优化
When a $sort precedes a $limit and there are no intervening stages that modify the number of documents, the optimizer can coalesce the $limit into the $sort. This allows the $sort operation to only maintain the top n results as it progresses, where n is the specified limit, and ensures that MongoDB only needs to store n items in memory. This optimization still applies when allowDiskUse is true and the n items exceed the aggregation memory limit.
优化可能因版本而异。
$sort 和内存限制
默认下,需要超过 100 MB 内存才能执行的管道阶段会将临时文件写入磁盘。这些临时文件在管道执行期间持续存在,并且可能影响实例上的存储空间。
单个 find 和 aggregate 命令可以通过以下任一方式覆盖 allowDiskUseByDefault 参数:
使用
{ allowDiskUse: true }以允许在allowDiskUseByDefault设置为false时将临时文件写入磁盘使用
{ allowDiskUse: false }以禁止在allowDiskUseByDefault设置为true时将临时文件写入磁盘
注意
For MongoDB Atlas, it is recommended to configure storage auto-scaling to prevent long-running queries from filling up storage with temporary files.
如果您的Atlas 集群使用存储自动伸缩,则临时文件可能会导致集群扩展到下一个存储层级。
有关更多详细信息,请参阅聚合管道限制。
$sort 索引使用
如果 $sort 操作符是管道中的第一个阶段,或者其前面仅有 $match 阶段,则该操作符可以使用索引。
当您在分片集群上使用 $sort 时,每个分片都会使用可用索引对其结果文档排序。然后 mongos 或一个分片会执行流式合并排序。
示例
本页上的示例使用来自 sample_mflix 数据集的数据。有关如何将此数据集加载到您的自管理 MongoDB 部署中的详细信息,请参阅 加载示例数据集。如果您对示例数据库进行了任何修改,您可能需要删除并重新创建数据库,才能运行此页面上的示例。
升序和降序排序
对于每个排序字段,将值设置为 1 表示升序,或设置为 -1 表示降序。示例:
db.movies.aggregate( [ { $sort: { year: -1, title: 1 } }, { $limit: 5 } ] )
此操作对 movies 集合中的文档按 year 降序进行排序,然后按 title 升序排序,并返回前五个结果。
在排序操作中比较不同 BSON 类型的值时,MongoDB 使用以下从低到高的比较顺序:
MinKey(内部类型)
null
数值(int、long、double、decimal)
符号,字符串
对象
阵列
BinData
ObjectId
布尔
Date
时间戳
正则表达式
JavaScript代码
带作用域的 JavaScript 代码
MaxKey(内部类型)
有关特定类型的比较/排序顺序的详细信息,请参阅比较/排序顺序。
Text Score Metadata Sort
注意
$text 为自管理(非 Atlas)部署提供文本查询功能。对于MongoDB上托管的数据, MongoDB还提供改进的全文查询解决方案MongoDB Search。
以下示例使用了 sample_mflix 示例数据集中的 movies 集合。在运行之前,请在 plot 字段上创建一个文本索引:
db.movies.createIndex( { plot: "text" } )
对于包含 $text 的管道,您可以使用 { $meta: "textScore" } 表达式按相关性分数降序排序。在 { <sort-key> } 文档中,将 { $meta: "textScore" } 表达式设置为任意字段名称。查询系统将忽略该字段名称。例如:
db.movies.aggregate( [ { $match: { $text: { $search: "pirate" } } }, { $sort: { score: { $meta: "textScore" }, year: -1 } }, { $limit: 5 } ] )
此操作匹配具有 $text 的文档,然后按 "textScore" 元数据和 year 降序排序,并返回前五个结果。查询系统会忽略排序文档中的 score 字段名称。投影不包含 "textScore" 元数据,也不会将元数据作为匹配文档的一部分返回。请参阅 $meta 以了解更多信息。
以下 Movie 类对 sample_mflix.movies 集合中的文档进行建模:
[] public class Movie { [] public ObjectId Id { get; set; } [] public string Title { get; set; } = null!; [] public int? Year { get; set; } [] public int? Runtime { get; set; } [] public string? Rated { get; set; } [] public int Metacritic { get; set; } [] public string? Plot { get; set; } [] public string? Type { get; set; } [] public string[]? Cast { get; set; } [] public string[]? Directors { get; set; } [] public string[]? Writers { get; set; } [] public ImdbData? Imdb { get; set; } }
var pipeline = new EmptyPipelineDefinition<Movie>() .Match(Builders<Movie>.Filter.Eq(m => m.Metacritic, 100)) .Sort(Builders<Movie>.Sort.Combine( Builders<Movie>.Sort.Descending(m => m.Year), Builders<Movie>.Sort.Ascending(m => m.Title)));
要使用MongoDB Node.js驱动程序将 $sort 阶段添加到聚合管道,请在管道对象中使用 $sort操作符。
以下示例创建了一个管道阶段,该阶段首先按 year 字段对输入 movie 文档进行降序排序,然后按 title 字段进行升序排序。然后,示例运行聚合管道:
const pipeline = [{ $sort: { year: -1, title: 1 } }]; const cursor = collection.aggregate(pipeline); return cursor;
了解详情
有关使用 $sort 的完整聚合示例,请参阅完整聚合管道教程。