聚合管道是建立在数据处理管道概念基础上的数据聚合框架。文档进入多级管道,将文档转换为聚合结果。例如:
db.orders.aggregate([ { $match: { status: "A" } }, { $group: { _id: "$cust_id", total: { $sum: "$amount" } } } ])
第一阶段:$match 按 status 字段过滤文档,并将 status 等于 “A” 的文档传递到下一阶段。
第二阶段:$group 阶段将文档按 cust_id 字段分组,以计算每个唯一 cust_id 的数量之和。
MongoDB 聚合管道由各个阶段组成。文档在通过管道时,每个阶段都对它们进行转换。管道的阶段不需要为每个输入文件生成一个输出文档;例如,某些阶段可能生成新文档或过滤掉文档。
管道的阶段可以在管道中多次出现,但 $out、$Merge 和 $GeoNear 阶段除外。MongoDB 在 mongo shell 中提供 db.collection.aggregate() 方法,运行聚合管道的聚合命令。
从MongoDB 4.2开始,您可以使用聚合管道进行更新:
例如:下面是 findOneAndUpdate、findAndModify、updateOne、updateManry 和 update 方法中使用管道。如下:
db.students2.findOneAndUpdate( { _id : 1 }, [ { $set: { "total" : { $sum: "$grades.grade" } } } ], { returnNewDocument: true } ) db.students2.findAndModify( { query: { "_id" : 1 }, update: [ { $set: { "total" : { $sum: "$grades.grade" } } } ], new: true }) db.members.updateOne( { _id: 1 }, [ { $set: { status: "Modified", comments: [ "$misc1", "$misc2" ], lastUpdate: "$$NOW" } }, { $unset: [ "misc1", "misc2" ] } ] ) db.members.updateMany( { }, [ { $set: { status: "Modified", comments: [ "$misc1", "$misc2" ], lastUpdate: "$$NOW" } }, { $unset: [ "misc1", "misc2" ] } ] ) db.members.update( { }, [ { $set: { status: "Modified", comments: [ "$misc1", "$misc2" ], lastUpdate: "$$NOW" } }, { $unset: [ "misc1", "misc2" ] } ], { multi: true } )
有些管道阶段以管道表达式作为操作数。指定的管道表达式应用于输入文档的转换。表达式具有文档结构,可以包含其他表达式。管道表达式只能在管道中的当前文档上操作,不能引用来自其他文档中的数据:管道表达式提供了文档在内存中进行转换的操作。
通常,表达式是无状态的,只有在聚合过程看到一个例外情况时才进行计算:累加器表达式。在 $group 阶段中使用的累加器在文档通过管道时保持其状态(例如:总计、最大值、最小值和相关数据)。
3.2版本中更改
一些累加器可以在 $project 阶段使用;但是,当在 $project 阶段使用时,累加器不会跨文档维护它们的状态。
在 MongoDB 中,聚合命令对单个集合进行操作,逻辑上将整个集合传递到聚合管道中。若要优化操作,请尽可能使用以下策略以避免扫描整个集合。
MongoDB 的查询计划器分析聚合管道,以确定是否可以使用索引来提高管道性能。例如,以下管道阶段可以利用索引:
注意:以下列举的管道阶段并不代表可以使用索引的所有阶段列表。
$match 阶段如果发生在管道的开头,则可以使用索引过滤文档。
$sort 阶段可以使用一个索引,只要它之前没有 $project、$unwind 或 $group 阶段。
如果满足以下所有条件,$group 阶段有时可以使用索引在每个组中查找第一个文档:
$group 阶段之前有 $sort 阶段,该阶段对字段进行分组排序。
分组字段上有一个索引,该索引与排序顺序相关。
$group 阶段使用的唯一累加器是 $first。
$geoNear 管道操作符利用地理空间索引。在使用 $geoNear 时,$geoNear 管道操作必须作为聚合管道中的第一阶段出现。
3.2版本中的变化
从MongoDB 3.2开始,索引可以覆盖聚合管道。在MongoDB 2.6和3.0中,索引不能覆盖聚合管道,因为即使管道使用索引,聚合仍然需要访问实际的文档。
如果聚合操作只需要集合中数据的一个子集,那么可以使用 $match、$limit 和 $skip 阶段来限制在管道开始时输入的文档。当放置在管道的开头时,$match 操作使用合适的索引只扫描集合中匹配的文档。
在管道的开始处放置一个 $match 管道阶段,然后放置一个 $sort 阶段,这在逻辑上等同于一个带有 sort 的查询,并且可以使用索引。如果可能,在管道的开头放置 $match 操作符。