聚合管道 (Aggregation Pipeline)
聚合管道由一个或多个处理文档的 阶段组成:
每个阶段对输入文档执行一个操作。例如,某个阶段可以过滤文档、对文档进行分组并计算值。
从一个阶段输出的文档将传递到下一阶段。
一个聚合管道可以返回针对文档组的结果。例如,返回总值、平均值、最大值和最小值。
如使用通过聚合管道更新中显示的阶段,则可以通过聚合管道更新文档。
注意
使用 db.collection.aggregate()
方法运行的聚合管道不会修改集合中的文档,除非管道包含 $merge
或 $out
阶段。
您可以 在用户界面运行聚合管道,用于 MongoDB Atlas 中托管的部署。
当您在 MongoDB Atlas 用户界面中的 MongoDB Atlas 部署上运行聚合管道时,您可以预览每个阶段的结果。
完整的 Aggregation Pipeline 示例
本部分展示了使用以下披萨 orders
集合的聚合管道示例:
db.orders.insertMany( [ { _id: 0, name: "Pepperoni", size: "small", price: 19, quantity: 10, date: ISODate( "2021-03-13T08:14:30Z" ) }, { _id: 1, name: "Pepperoni", size: "medium", price: 20, quantity: 20, date : ISODate( "2021-03-13T09:13:24Z" ) }, { _id: 2, name: "Pepperoni", size: "large", price: 21, quantity: 30, date : ISODate( "2021-03-17T09:22:12Z" ) }, { _id: 3, name: "Cheese", size: "small", price: 12, quantity: 15, date : ISODate( "2021-03-13T11:21:39.736Z" ) }, { _id: 4, name: "Cheese", size: "medium", price: 13, quantity:50, date : ISODate( "2022-01-12T21:23:13.331Z" ) }, { _id: 5, name: "Cheese", size: "large", price: 14, quantity: 10, date : ISODate( "2022-01-12T05:08:13Z" ) }, { _id: 6, name: "Vegan", size: "small", price: 17, quantity: 10, date : ISODate( "2021-01-13T05:08:13Z" ) }, { _id: 7, name: "Vegan", size: "medium", price: 18, quantity: 10, date : ISODate( "2021-01-13T05:10:13Z" ) } ] )
计算总订单数量
以下聚合管道示例包含两个阶段,并返回按披萨名称分组后,各款中号披萨的总订单数量:
db.orders.aggregate( [ // Stage 1: Filter pizza order documents by pizza size { $match: { size: "medium" } }, // Stage 2: Group remaining documents by pizza name and calculate total quantity { $group: { _id: "$name", totalQuantity: { $sum: "$quantity" } } } ] )
$match
阶段:
从披萨订单文档过滤出
size
为medium
的披萨。将剩余文档传递到
$group
阶段。
$group
阶段:
按披萨
name
对剩余文档进行分组。使用
$sum
计算每种披萨name
的总订单quantity
。总数存储在聚合管道返回的totalQuantity
字段中。
示例输出:
[ { _id: 'Cheese', totalQuantity: 50 }, { _id: 'Vegan', totalQuantity: 10 }, { _id: 'Pepperoni', totalQuantity: 20 } ]
计算订单总值和平均订单数
以下示例计算了两个日期之间的披萨订单总额和平均订单数量:
db.orders.aggregate( [ // Stage 1: Filter pizza order documents by date range { $match: { "date": { $gte: new ISODate( "2020-01-30" ), $lt: new ISODate( "2022-01-30" ) } } }, // Stage 2: Group remaining documents by date and calculate results { $group: { _id: { $dateToString: { format: "%Y-%m-%d", date: "$date" } }, totalOrderValue: { $sum: { $multiply: [ "$price", "$quantity" ] } }, averageOrderQuantity: { $avg: "$quantity" } } }, // Stage 3: Sort documents by totalOrderValue in descending order { $sort: { totalOrderValue: -1 } } ] )
$match
阶段:
$group
阶段:
使用
$dateToString
按日期对文档进行分组。对于每个群组,计算:
将分组的文档传递到
$sort
阶段。
$sort
阶段:
按每组的总订单值以降序对文档进行排序 (
-1
)。返回排序文档。
示例输出:
[ { _id: '2022-01-12', totalOrderValue: 790, averageOrderQuantity: 30 }, { _id: '2021-03-13', totalOrderValue: 770, averageOrderQuantity: 15 }, { _id: '2021-03-17', totalOrderValue: 630, averageOrderQuantity: 30 }, { _id: '2021-01-13', totalOrderValue: 350, averageOrderQuantity: 10 } ]
其他聚合管道阶段详细信息
聚合管道由一个或多个处理文档的阶段组成:
阶段不必为每个输入文档输出一个文档。例如,某些阶段可能会产生新文档或过滤掉现有文档。
有关所有聚合阶段,请参阅聚合阶段。
聚合表达式和操作符
某些聚合管道阶段接受表达式。运算符根据输入表达式计算值。
在 MongoDB 查询语言中,您可以从以下组件构建表达式:
组件 | 例子 |
---|---|
常量 | 3 |
操作符 | |
字段路径表达式 | "$<path.to.field>" |
例如, { $add: [ 3, "$inventory.total" ] }
是一个由 $add
操作符和两个输入表达式组成的表达式:
常量
3
字段路径表达式
"$inventory.total"
表达式返回将 3 添加到输入文档中路径 inventory.total
处的值的结果。
字段路径
字段路径表达式用于访问权限输入文档中的字段。要指定字段路径,请在字段名称或虚线字段名称(如果该字段位于嵌入式文档中)前添加美元符号 $
。例如,用 "$user"
指定 user
字段的字段路径,或用 "$user.name"
指定嵌入式 "user.name"
字段的字段路径。
"$<field>"
等效于 "$$CURRENT.<field>"
,其中 CURRENT
为系统变量,而它默认为当前对象的根(除非在特定阶段另行说明)。
运行聚合管道
要运行聚合管道,请使用:
使用聚合管道更新文档
要使用聚合管道更新文档,请使用:
其他注意事项
聚合管道限制
聚合分析管道对值类型和结果大小有限制。请参阅聚合管道限制。
聚合管道和分片集合
聚合管道支持针对分片集合的操作。请参阅聚合管道和分片集合。
Aggregation Pipeline 作为 Map-Reduce 的替代方案
从 MongoDB 5.0 开始, map-reduce已弃用:
不应使用 map-reduce,而应使用聚合管道。聚合管道提供比 map-reduce 更好的性能和可用性。
对于需要自定义功能的 map-reduce 操作,可以使用
$accumulator
和$function
聚合操作符。可以使用这些操作符在 JavaScript 中定义自定义聚合表达式。
有关 map-reduce 的聚合管道替代方案的示例,请参阅:
了解详情
要了解有关聚合管道的详细信息,请参阅: