对于 AI 代理:可在 https://www.mongodb.com/zh-cn/docs/llms.txt 获取文档索引—通过在任何 URL 路径后添加 .md 可获取所有页面的 Markdown 版本。
Docs 菜单

$sort(聚合阶段)

$sort

将所有输入文档进行排序,然后按照排序将其返回至管道。

可以使用 $sort 查找托管在以下环境中的部署:

  • MongoDB Atlas:用于云中 MongoDB 部署的完全托管服务

$sort 采用以下形式:

{ $sort: { <field1>: <sort order>, <field2>: <sort order> ... } }

$sort 采用指定要作为排序依据的字段及其排序顺序的文档。 <sort order> 可以是以下值之一:

值
说明

1

升序排序。

-1

降序排序。

{ $meta: "textScore" }

按计算出的 textScore元数据进行降序排序。有关示例,请参阅文本分数元数据排序。

对多个字段进行排序时,$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 $limit之前并且没有修改文档数量的中间阶段时,优化器可以将 合并到$limit $sort中。这允许$sort 操作在推进时仅维护前n 个结果(其中n 为指定的限制),并确保MongoDB仅需要在内存中存储n 个项目。当allowDiskUse 为true 且n 项超过聚合内存限制时,此优化仍然适用。

优化可能因版本而异。

默认下,需要超过 100 MB 内存才能执行的管道阶段会将临时文件写入磁盘。这些临时文件在管道执行期间持续存在,并且可能影响实例上的存储空间。

单个 find 和 aggregate 命令可以通过以下任一方式覆盖 allowDiskUseByDefault 参数:

  • 使用 { allowDiskUse: true } 以允许在 allowDiskUseByDefault 设置为 false 时将临时文件写入磁盘

  • 使用 { allowDiskUse: false } 以禁止在 allowDiskUseByDefault 设置为 true 时将临时文件写入磁盘

注意

对于MongoDB Atlas,建议配置存储自动伸缩,以防止长时间运行的查询用临时文件填满存储。

如果您的Atlas 集群使用存储自动伸缩,则临时文件可能会导致集群扩展到下一个存储层级。

有关更多详细信息,请参阅聚合管道限制。

当 $sort操作符是管道中的第一阶段或前面仅是 $match 阶段时,可以使用索引。

当您在分片集群上使用 $sort 时,每个分分片都会使用可用的索引对其结果文档进行排序。然后,mongos 或其中一个分片执行流式合并排序。

本页上的示例使用sample_mflix示例数据集中的数据。有关如何将此数据集加载到自管理MongoDB 部署中的详细信息,请参阅加载示例数据集。如果对示例数据库进行了任何修改,则可能需要删除并重新创建数据库才能运行本页上的示例。

对于每个排序字段,将值设立为 1(升序)或 -1(降序)。示例:

db.movies.aggregate(
[
{ $sort: { year: -1, title: 1 } },
{ $limit: 5 }
]
)

此操作对 movies集合中的文档按 year 降序排序,然后按 title 升序排序,并返回前五个结果。

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

  1. MinKey(内部类型)

  2. null

  3. 数值(int、long、double、decimal)

  4. 符号,字符串

  5. 对象

  6. 阵列

  7. BinData

  8. ObjectId

  9. 布尔

  10. Date

  11. 时间戳

  12. 正则表达式

  13. JavaScript代码

  14. 带作用域的 JavaScript 代码

  15. MaxKey(内部类型)

有关特定类型的比较/排序顺序的详细信息,请参阅比较/排序顺序。

注意

$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。

本页上的C#示例使用Atlas示例数据集中的 sample_mflix数据库。要学习;了解如何创建免费的MongoDB Atlas 群集并加载示例数据集,请参阅MongoDB .NET/ C#驱动程序文档中的入门。

以下 Movie 类对 sample_mflix.movies 集合中的文档进行建模:

[BsonIgnoreExtraElements]
public class Movie
{
[BsonId]
public ObjectId Id { get; set; }
[BsonElement("title")]
public string Title { get; set; } = null!;
[BsonElement("year")]
public int? Year { get; set; }
[BsonElement("runtime")]
public int? Runtime { get; set; }
[BsonElement("rated")]
public string? Rated { get; set; }
[BsonElement("metacritic")]
public int Metacritic { get; set; }
[BsonElement("plot")]
public string? Plot { get; set; }
[BsonElement("type")]
public string? Type { get; set; }
[BsonElement("cast")]
public string[]? Cast { get; set; }
[BsonElement("directors")]
public string[]? Directors { get; set; }
[BsonElement("writers")]
public string[]? Writers { get; set; }
[BsonElement("imdb")]
public ImdbData? Imdb { get; set; }
}

要使用MongoDB .NET/ C#驾驶员将$sort PipelineDefinition阶段添加到聚合管道,请对 对象调用 UnionWith() 方法。

以下示例创建了一个管道阶段,该阶段首先过滤 Movie 文档,其中 Metacritic 等于 100,然后按 Year 字段降序排序结果,再按 Title 字段升序排序:

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 的完整聚合示例,请参阅完整聚合管道教程。