定义
兼容性
可以使用 $sort 查找托管在以下环境中的部署:
- MongoDB Atlas:用于云中 MongoDB 部署的完全托管服务 
- MongoDB Enterprise:基于订阅、自我管理的 MongoDB 版本 
- MongoDB Community:源代码可用、免费使用且可自行管理的 MongoDB 版本 
语法
$sort 阶段具有以下原型形式:
{ $sort: { <field1>: <sort order>, <field2>: <sort order> ... } } 
$sort 接受指定要作为排序依据的字段以及相应排序顺序的文档。<sort order> 可能具有以下任意一个值:
如果对多个字段进行排序,则按从左到右的顺序进行排序。例如,在上面的表单中,文档首先按 <field1> 排序。然后,具有相同 <field1> 值的文档将按 <field2> 进一步排序。
行为
性能
$sort 是一个阻塞阶段,这会导致管道在处理数据前等待为阻塞阶段检索所有输入数据。阻塞阶段可能会降低性能,因为它会减少具有多个阶段的管道的并行处理。对于大型数据集,阻塞阶段还可能使用大量内存。
限制
- 您最多可以对 32 个键进行排序。 
- 为排序模式提供重复字段会导致错误。 
排序一致性
MongoDB 不按特定顺序将文档存储在集合中。对包含重复值的字段进行排序时,可能会以任何顺序返回包含这些值的文档。
$sort 操作不是“稳定排序”,这意味着具有等效排序键的文档在输出中不一定能保持与输入相同的相对顺序。
如果排序条件中指定的字段在两个文档中都不存在,那么它们排序所依据的值是相同的。这两个文档可以以任何顺序返回。
如果需要一致的排序顺序,请在排序中至少纳入一个包含唯一值的字段。最简单方法是在排序查询中纳入 _id 字段。
考虑以下restaurant集合:
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.aggregate(    [      { $sort : { borough : 1 } }    ] ) 
在此示例中,排序顺序可能不一致,因为 borough 字段包含 Manhattan 和 Brooklyn 的重复值。文档按 borough 的字母顺序返回,但具有 borough 重复值的文档的顺序在多次执行同一排序中可能不相同。例如,以下是上述命令两次不同执行的结果:
{ _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" } { _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.aggregate(    [      { $sort : { borough : 1, _id: 1 } }    ] ) 
由于 _id 字段始终保证包含唯一值,因此在同一排序的多次执行中返回的排序顺序将始终相同。
按数组字段排序
当 MongoDB 按数组值字段对文档进行排序时,排序键取决于排序是升序还是降序:
- 在升序排序中,排序键是数组中的最低值。 
- 在降序排序中,排序键是数组中的最高值。 
查询过滤器不影响排序键的选择。
例如,使用以下文档创建 shoes 集合:
db.shoes.insertMany( [    { _id: 'A', sizes: [ 7, 11 ] },    { _id: 'B', sizes: [ 8, 9, 10 ] } ] ) 
以下查询按 sizes 字段以升序和降序对文档进行排序:
// Ascending sort db.shoes.aggregate( [    {       $sort: { sizes: 1 }    } ] ) // Descending sort db.shoes.aggregate( [    {       $sort: { sizes: -1 }    } ] ) 
前两个查询均首先返回包含 _id: 'A' 的文档,因为 7 和 11 分别是 sizes 数组中条目中的最小值和最大值。
按数组字段进行过滤和排序
当您按包含数组的字段进行过滤和排序时,过滤器不会影响用作排序键的值。此排序总是会将所有数组值视为潜在的排序键。
例如,以下查询将查找尺码大于 9 码的鞋子,并按尺码升序对结果进行排序:
db.shoes.aggregate( [    {       $match: { sizes: { $gt: 9 } }    },    {       $sort: { sizes: 1 }    } ] ) 
排序是升序的,这意味着排序键是 sizes 数组中的最低值:
- 在文档 - _id: 'A'中,最低的- sizes元素是- 7。即使该值与过滤器- { sizes: { $gt: 9 }不匹配,也将其用作排序键。
- 在文档 - _id: 'B'中,最低的- sizes元素是- 8。同样,即使此值与过滤器不匹配,它仍被用作排序键。
查询首先返回带有 _id: 'A' 的文档。
$sort 操作符和内存
$sort +$limit 内存优化
当 $sort 在 $limit 之前,并且不存在修改文档数量的干预阶段,那么优化器可以将 $limit 合并到 $sort 中。这可以让 $sort 操作在进行过程中仅保留前 n 项结果(其中 n 是指定的限制值),并确保 MongoDB 只需在内存中存储 n 个项目。当 allowDiskUse 为 true 且 n 个项目超过了聚合内存限制时,此优化仍然适用。
优化可能因版本而异。
$sort 和内存限制
从 MongoDB 6.0 开始,需要 100 兆字节以上内存容量的管道阶段默认将临时文件写入磁盘。这些临时文件在管道执行期间持续存在,并且可能影响实例上的存储空间。在 MongoDB 的早期版本中,您必须将 { allowDiskUse: true } 传递给单个 find 和 aggregate 命令才能启用此行为。
单个 find 和 aggregate 命令可以通过以下任一方式覆盖 allowDiskUseByDefault 参数:
- 使用 - { allowDiskUse: true }以允许在- allowDiskUseByDefault设置为- false时将临时文件写入磁盘
- 使用 - { allowDiskUse: false }以禁止在- allowDiskUseByDefault设置为- true时将临时文件写入磁盘
注意
对于MongoDB Atlas,建议配置存储自动伸缩,以防止长时间运行的查询用临时文件填满存储。
如果您的Atlas 集群使用存储自动伸缩,则临时文件可能会导致集群扩展到下一个存储层级。
有关更多详细信息,请参阅聚合管道限制。
$sort 操作符和性能
$sort 操作符如果用在管道的第一阶段或者其前面仅有 $match 阶段,则该操作符可以利用索引。
当您在分片集群上使用 $sort 时,每个分片都会使用可用索引对其结果文档排序。然后 mongos 或一个分片会执行流式合并排序。
示例
升序/降序排序
对于要作为排序依据的一个或多个字段,请将排序顺序设置为 1 或 -1 以分别指定升序或降序,如下例所示:
db.users.aggregate(    [      { $sort : { age : -1, posts: 1 } }    ] ) 
此操作对 users 集合中的文档进行排序,先根据 age 字段降序排列,再根据 posts 字段中的值升序排序。
在排序操作中比较不同 BSON 类型的值时,MongoDB 使用以下从低到高的比较顺序:
- MinKey(内部类型) 
- null 
- 数值(int、long、double、decimal) 
- 符号,字符串 
- 对象 
- 阵列 
- BinData 
- ObjectId 
- 布尔 
- Date 
- 时间戳 
- 正则表达式 
- JavaScript代码 
- 带作用域的 JavaScript 代码 
- MaxKey(内部类型) 
有关特定类型的比较/排序顺序的详细信息,请参阅比较/排序顺序。
Text Score Metadata Sort
注意
$text 为自管理(非 Atlas)部署提供文本查询功能。对于MongoDB上托管的数据, MongoDB还提供改进的全文查询解决方案MongoDB Search。
对于包含 $text 的管道,您可以使用 { $meta: "textScore"
} 表达式按相关性分数降序排序。在 { <sort-key> } 文档中,将 { $meta: "textScore" } 表达式设置为任意字段名称。查询系统将忽略该字段名称。例如:
db.users.aggregate(    [      { $match: { $text: { $search: "operating" } } },      { $sort: { score: { $meta: "textScore" }, posts: -1 } }    ] ) 
此操作使用 $text 操作符匹配文档,然后首先按 "textScore" 元数据降序排序,然后按 posts 字段降序排序。查询系统会忽略排序文档中的 score 字段名称。在此管道中, "textScore" 元数据不包含在投影中,也不作为匹配文档的一部分返回。请参阅 $meta 以了解更多信息。
本页上的C#示例使用Atlas示例数据集中的 sample_mflix数据库。要学习;了解如何创建免费的MongoDB Atlas 群集并加载示例数据集,请参阅MongoDB .NET/ C#驱动程序文档中的入门。
以下 Movie 类对 sample_mflix.movies 集合中的文档进行建模:
public class Movie {     public ObjectId Id { get; set; }     public int Runtime { get; set; }          public string Title { get; set; }     public string Rated { get; set; }     public List<string> Genres { get; set; }     public string Plot { get; set; }          public ImdbData Imdb { get; set; }     public int Year { get; set; }     public int Index { get; set; }          public string[] Comments { get; set; }         []     public DateTime LastUpdated { get; set; } } 
注意
用于 Pascal Case 的 ConventionPack
此页面上的 C# 类在其属性名称中使用 Pascal 命名法,而 MongoDB 集合中的字段名称则使用 camel 命名法。为了解决这种差异,可以在应用程序启动时使用以下代码注册一个 ConventionPack:
var camelCaseConvention = new ConventionPack { new CamelCaseElementNameConvention() }; ConventionRegistry.Register("CamelCase", camelCaseConvention, type => true); 
要使用MongoDB .NET/ C#驾驶员将 $sort 阶段添加到聚合管道,请对 PipelineDefinition对象调用 Sort() 方法。
以下示例创建了一个管道阶段,该阶段首先按 Year字段对输入 Movie 文档进行降序排序,然后按 Title字段进行升序排序:
var pipeline = new EmptyPipelineDefinition<Movie>()     .Sort(Builders<Movie>.Sort.Combine(         Builders<Movie>.Sort.Descending(m => m.Year),         Builders<Movie>.Sort.Ascending(m => m.Title))); 
本页上的 Node.js 示例使用Atlas示例数据集中的 sample_mflix数据库。要学习;了解如何创建免费的MongoDB Atlas 群集并加载示例数据集,请参阅MongoDB Node.js驾驶员文档中的入门。
要使用MongoDB Node.js驾驶员将 $sort 阶段添加到聚合管道,请在管道对象中使用 $sort操作符。
以下示例创建了一个管道阶段,该阶段首先按 year 字段对输入 movie 文档进行降序排序,然后按 title 字段进行升序排序。然后,示例运行聚合管道:
const pipeline = [{ $sort: { year: -1, title: 1 } }]; const cursor = collection.aggregate(pipeline); return cursor;