Docs 菜单
Docs 主页
/
MongoDB Manual
/ / /

聚合

在此页面上

  • 定义
  • 兼容性
  • 语法
  • 会话
  • 事务
  • Stable API
  • 例子
aggregate

使用聚合管道执行聚合操作。管道允许用户通过一系列基于阶段的操作来处理集合或其他来源的数据。

提示

mongosh 中,此命令也可通过 db.aggregate()db.collection.aggregate() 助手方法或 watch() 助手方法来运行。

辅助方法对 mongosh 用户来说很方便,但它们返回的信息级别可能与数据库命令不同。如果不追求方便或需要额外的返回字段,请使用数据库命令。

此命令可用于以下环境中托管的部署:

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

重要

此命令在 M 0 、 M 2和 M 5集群中提供有限支持。 有关更多信息,请参阅不支持的命令。

5.0 版本中的更改

该命令的语法如下:

{
aggregate: "<collection>" || 1,
pipeline: [ <stage>, <...> ],
explain: <boolean>,
allowDiskUse: <boolean>,
cursor: <document>,
maxTimeMS: <int>,
bypassDocumentValidation: <boolean>,
readConcern: <document>,
collation: <document>,
hint: <string or document>,
comment: <any>,
writeConcern: <document>,
let: <document> // Added in MongoDB 5.0
}

aggregate 命令将以下字段作为参数:

字段
类型
说明
aggregate
字符串
充当聚合管道的输入的集合或视图的名称。使用 1 执行与集合无关的命令。
pipeline
阵列
聚合管道阶段的数组,这些阶段将文档流作为聚合管道的一部分进行处理和转换。
explain
布尔

可选。指定返回关于管道处理的信息。

不适用于多文档事务

allowDiskUse

布尔

可选。 允许写入临时文件。 设置为true时,聚合阶段可以将数据写入dbPath目录中的_tmp子目录。

如果聚合阶段因为内存限制而写入临时文件,则分析器日志消息诊断日志消息包含一个 usedDisk 指示器。

cursor
文档

指定包含控制游标对象创建选项的文档。

MongoDB 删除了对不带 aggregate 选项的 cursor 命令的使用,该命令包含 explain 选项的情况除外。除非包含 explain 选项,否则必须指定游标选项。

  • 要指示具有默认批次大小的游标,请指定 cursor: {}

  • 要指示具有非默认批次大小的游标,请使用 cursor: { batchSize: <num> }

maxTimeMS
non-negative integer

可选。

指定时间限制(以毫秒为单位)。如果您未指定 maxTimeMS 值,操作将不会超时。如果值为 0 ,则显式指定默认无限制行为。

MongoDB 使用与 db.killOp() 相同的机制终止超过分配的时间限制的操作。MongoDB 仅在指定的中断点之一中终止操作。

bypassDocumentValidation
布尔

可选。仅当您指定 $out$merge 聚合阶段时使用。

启用 aggregate 可在操作过程中绕过文档验证。这样就可以插入不符合验证要求的文档。

readConcern
文档

可选。指定读关注

readConcern 选项的语法如下:readConcern: { level: <value> }

可能的读关注级别是:

有关读关注级别的更多信息,请参阅读关注级别

$out 阶段不能与读关注 "linearizable" 一起使用。如果为 db.collection.aggregate() 指定 "linearizable" 读关注,则不能将 $out 阶段包括在管道中。

$merge 阶段不能与读关注 "linearizable" 一起使用。换言之,如果您为 db.collection.aggregate() 指定 "linearizable" 读关注,则不能将 $merge 阶段包括在管道中。

collation
文档

可选。

指定用于操作的排序规则

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

排序规则选项的语法如下:

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

指定排序规则时,locale 字段为必填字段;所有其他排序规则字段均为可选字段。有关字段的说明,请参阅排序规则文档

如果未指定排序规则,但集合具有默认排序规则(请参阅 db.createCollection()),则操作将使用为集合指定的排序规则。

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

您不能为一个操作指定多个排序规则。例如,您不能为每个字段指定不同的排序规则,或者如果执行带排序的查找,则不能使用一种排序规则进行查找而另一种排序规则进行排序。

版本 3.4 中的新增功能

hint
字符串或文档

可选。用于该聚合的索引。该索引位于运行聚合的初始集合/视图上。

通过索引名称或索引规范文档来指定索引。

hint 不适用于 $lookup$graphLookup 阶段。

comment
any

可选。用户提供的待附加到该命令的注释。设置后,该注释将与该命令的记录一起出现在以下位置:

注释可以是任何有效的 BSON 类型(字符串、整型、对象、数组等)。

注意

aggregate命令上设立的任何注释都会被使用aggregate命令返回的相同cursorId运行的任何后续getMore命令继承。

writeConcern
文档

可选。一个表达写关注的文档,该写关注会与 $out$merge 阶段一起配合使用。

$out$merge 阶段省略使用默认写关注。

let
文档

可选。

指定包含变量列表的文档。这样可以将变量与查询文本分开,从而提高命令的可读性。

文档语法为:

{
<variable_name_1>: <expression_1>,
...,
<variable_name_n>: <expression_n>
}

变量设置为表达式返回的值,并且之后不能再进行更改。

要访问命令中的变量值,请使用双美元符号前缀 ($$) 以及 $$<variable_name> 形式的变量名称。例如:$$targetTotal

要使用变量筛选管道 $match 阶段的结果,必须在 $expr 运算符中访问该变量。

有关使用 let 和变量的完整示例,请参阅let 中使用变量

版本 5.0 中的新增功能

MongoDB 删除了对不带 aggregate 选项的 cursor 命令的使用,该命令包含 explain 选项的情况除外。除非包含 explain 选项,否则必须指定游标选项。

  • 要指示具有默认批次大小的游标,请指定 cursor: {}

  • 要指示具有非默认批次大小的游标,请使用 cursor: { batchSize: <num> }

有关聚合管道聚合管道聚合参考聚合管道限制的更多信息。

对于在一个会话内创建的游标,不能在该会话外调用 getMore

同样,对于在会话外创建的游标,不能在会话内调用 getMore

MongoDB 驱动程序和 mongosh 将所有操作与服务器会话相关联,未确认的写入操作除外。对于未与会话显式关联的操作(即使用 Mongo.startSession()),MongoDB 驱动程序和 mongosh 会创建隐式会话并将其与该操作关联。

如果会话空闲时间超过 30 分钟,MongoDB Server 会将该会话标记为已过期,并可能随时将其关闭。当 MongoDB Server 关闭会话时,它还会终止任何正在进行的操作并打开与会话关联的游标。这包括使用超过 30 分钟的 noCursorTimeout()maxTimeMS() 配置的游标。

对于返回游标的操作,如果游标的空闲时间可能超过 30 分钟,则使用 Mongo.startSession() 在显式会话中发出操作,并使用 refreshSessions 命令定期刷新会话。更多信息,请参阅会话空闲超时

aggregate 可以在分布式事务中使用。

但是,事务中不允许有以下阶段:

此外,您不能指定 explain 选项。

  • 对于在 ACID 事务外部创建的游标,无法在 ACID 事务内部调用 getMore

  • 对于在事务中创建的游标,无法在事务外部调用 getMore

重要

在大多数情况下,与单文档写入操作相比,分布式事务会产生更高的性能成本,并且分布式事务的可用性不应取代有效的模式设计。在许多情况下,非规范化数据模型(嵌入式文档和数组)仍然是数据和使用案例的最佳选择。换言之,对于许多场景,适当的数据建模将最大限度地减少对分布式事务的需求。

有关其他事务使用注意事项(如运行时间限制和 oplog 大小限制),另请参阅生产注意事项

对于不包含 $out$merge 阶段的 aggregate 操作:

从 MongoDB 4.2 开始,如果在操作完成之前,发出 aggregate 的客户端断开连接,MongoDB 将使用killOpaggregate 标记为终止。

使用 Stable API V1 时:

MongoDB 删除了对不带 aggregate 选项的 cursor 命令的使用,该命令包含 explain 选项的情况除外。除非包含 explain 选项,否则必须指定游标选项。

  • 要指示具有默认批次大小的游标,请指定 cursor: {}

  • 要指示具有非默认批次大小的游标,请使用 cursor: { batchSize: <num> }

大多数用户应该使用 mongosh 提供的 db.collection.aggregate() 助手或驱动程序中的等效助手,而不是直接运行 aggregate 命令。在 2.6 及更高版本中,db.collection.aggregate() 助手始终会返回游标。

除演示命令语法的前两个示例外,本页中的其他示例均使用 db.collection.aggregate() 助手。

集合 articles 包含如下文档:

{
_id: ObjectId("52769ea0f3dc6ead47c9a1b2"),
author: "abc123",
title: "zzz",
tags: [ "programming", "database", "mongodb" ]
}

以下示例将对 articles 集合执行 aggregate 操作,以计算集合中出现的 tags 数组内每个不同元素的数量。

db.runCommand( {
aggregate: "articles",
pipeline: [
{ $project: { tags: 1 } },
{ $unwind: "$tags" },
{ $group: { _id: "$tags", count: { $sum : 1 } } }
],
cursor: { }
} )

mongosh 中,该操作可以使用 db.collection.aggregate() 助手,如下所示:

db.articles.aggregate( [
{ $project: { tags: 1 } },
{ $unwind: "$tags" },
{ $group: { _id: "$tags", count: { $sum : 1 } } }
] )

以下示例将对管理员数据库上运行附带两个阶段的管道。第一个阶段会运行 $currentOp 操作,而第二个阶段则会筛选此操作的结果。

db.adminCommand( {
aggregate : 1,
pipeline : [ {
$currentOp : { allUsers : true, idleConnections : true } }, {
$match : { shard : "shard01" }
}
],
cursor : { }
} )

注意

aggregate 命令不指定集合,而是采用 {aggregate: 1} 形式。这是因为初始 $currentOp 阶段不从集合中提取输入。它生成自己的数据,供管道其余部分使用。

添加了新的 db.aggregate() 助手,帮助运行类似本示例的无集合聚合。上述聚合也可以像这个示例一样运行。

以下聚合操作会将可选字段 explain 设为 true 以返回有关此聚合操作的信息。

db.orders.aggregate([
{ $match: { status: "A" } },
{ $group: { _id: "$cust_id", total: { $sum: "$amount" } } },
{ $sort: { total: -1 } }
],
{ explain: true }
)

注意

解释输出可能因版本而异。

提示

另请参阅:

每个单独的管道阶段的 RAM 限制为100 MB 。 默认情况下,如果某个阶段超过此限制,MongoDB 会产生错误。 要允许管道处理占用更多空间,请将allowDiskUse选项设置为true以允许将数据写入临时文件,如以下示例所示:

db.stocks.aggregate( [
{ $sort : { cusip : 1, date: 1 } }
],
{ allowDiskUse: true }
)

如果聚合阶段因为内存限制而写入临时文件,则分析器日志消息诊断日志消息包含一个 usedDisk 指示器。

要指定初始批量大小,请在 cursor 字段中指定 batchSize,如下面示例所示:

db.orders.aggregate( [
{ $match: { status: "A" } },
{ $group: { _id: "$cust_id", total: { $sum: "$amount" } } },
{ $sort: { total: -1 } },
{ $limit: 2 }
],
{ cursor: { batchSize: 0 } }
)

{ cursor: { batchSize: 0 } } 文档指定初始批处理大小,表示首次批处理为空。此批处理大小对于快速返回游标或失败消息非常有用,无需执行大量服务器端工作。

要为后续 getMore 操作(初始批次后)指定批次大小,请在运行 getMore 命令时使用 batchSize 字段。

版本 3.4 中的新增功能

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

集合 myColl 包含以下文档:

{ _id: 1, category: "café", status: "A" }
{ _id: 2, category: "cafe", status: "a" }
{ _id: 3, category: "cafE", status: "a" }

以下聚合操作包括排序规则选项:

db.myColl.aggregate(
[ { $match: { status: "A" } }, { $group: { _id: "$category", count: { $sum: 1 } } } ],
{ collation: { locale: "fr", strength: 1 } }
);

有关排序规则字段的说明,请参阅排序规则文档

版本 3.6 中的新增功能

使用以下文档创建集合 foodColl

db.foodColl.insertMany( [
{ _id: 1, category: "cake", type: "chocolate", qty: 10 },
{ _id: 2, category: "cake", type: "ice cream", qty: 25 },
{ _id: 3, category: "pie", type: "boston cream", qty: 20 },
{ _id: 4, category: "pie", type: "blueberry", qty: 15 }
] )

创建以下索引:

db.foodColl.createIndex( { qty: 1, type: 1 } );
db.foodColl.createIndex( { qty: 1, category: 1 } );

下面的聚合操作包含 hint 选项,强制使用指定的索引:

db.foodColl.aggregate(
[ { $sort: { qty: 1 }}, { $match: { category: "cake", qty: 10 } }, { $sort: { type: -1 } } ],
{ hint: { qty: 1, category: 1 } }
)

若要覆盖默认的读关注级别,请使用 readConcern 选项。getMore 命令使用原始 aggregate 命令中指定的 readConcern 级别。

您不能将 $out$merge 阶段与读关注 "linearizable" 一起使用。换言之,如果将 "linearizable" 读关注为 db.collection.aggregate() 指定,则不能在管道中包含任一阶段。

对副本集执行以下操作可以指定读关注 "majority",以读取确认已写入大多数节点的数据的最新副本。

重要

  • 您可以为包含 $out 阶段的聚合指定读关注级别 "majority"

  • 无论读关注级别如何,节点上的最新数据可能无法反映系统中数据的最新版本。

db.restaurants.aggregate(
[ { $match: { rating: { $lt: 5 } } } ],
{ readConcern: { level: "majority" } }
)

为确保单个线程可以读取自己的写入内容,请对副本集的主节点使用 "majority" 读关注和 "majority" 写关注。

版本 5.0 中的新增功能

要定义可在命令中其他位置访问的变量,请使用 let 选项。

注意

要通过在管道 $match 阶段使用变量筛选结果,必须访问 $expr 运算符中的变量。

创建包含不同口味蛋糕销售额的集合 cakeSales

db.cakeSales.insertMany( [
{ _id: 1, flavor: "chocolate", salesTotal: 1580 },
{ _id: 2, flavor: "strawberry", salesTotal: 4350 },
{ _id: 3, flavor: "cherry", salesTotal: 2150 }
] )

如下示例:

  • 检索 salesTotal 大于 3000 的蛋糕,即 _id 为 2 的蛋糕

  • let 中定义了一个 targetTotal 变量,该变量在 $gt 中被引用为 $$targetTotal

db.runCommand( {
aggregate: db.cakeSales.getName(),
pipeline: [
{ $match: {
$expr: { $gt: [ "$salesTotal", "$$targetTotal" ] }
} },
],
cursor: {},
let: { targetTotal: 3000 }
} )

提示

另请参阅:

后退

聚合(Aggregation)