某些聚合操作符(例如 $avg、$sum 和 $max)会出现在多个上下文中,每个上下文都会赋予操作符不同的行为。示例:
作为表达式,
$avg返回大量中元素的平均值。作为累加器,
$avg返回一群组文档值的平均值。作为窗口函数,
$avg返回窗口中文档值的平均值。
包含操作符的阶段决定了其行为。相同的操作符符名称可以接受不同的参数,并根据使用位置返回不同的结果。本页介绍了这三种上下文以及如何消除歧义。
下图从概念上说明了表达式、累加器和窗口函数的执行方式:
下表总结了三种操作符类型的不同之处。
运算符类型 | 行为 | 在哪里可以使用它 |
|---|---|---|
表达式(expression) | 对每个输入文档进行求值,并为每个文档返回一个值。 |
|
累加器 | 聚合一群组输入文档,并为群组返回一个聚合值。 | |
窗口函数 | 对输入文档窗口进行求值,并为每个文档返回一个值。 |
|
表达式
表达式解析为单个输入文档的值。表达式不会改变其输入。
表达式操作符接受参数大量并具有以下通用形式:
{ <operator>: [ <argument1>, <argument2> ... ] }如果操作符接受单个参数,则可以省略外部大量。
表达式还为累加器和窗口函数提供参数。示例,在 { $sum: "$price" } 中,字段路径(Field Path)"$price" 是一个表达式。
累加器
累加器计算可变数量的输入文档的单个聚合值,例如总和、最大值或最小值。在实际实现中,累加器在执行分组操作时维护它们进程的文档之间的状态。
$group 阶段返回的输出文档数等于群组数。对于每个群组,累加器会计算整个群组的聚合值。
您可以在执行分组操作的聚合管道阶段使用累加器:
窗口功能
窗口函数返回一个基于窗口的值,窗口是相对于当前文档定义的一系列文档。您可以使用 $setWindowFields 阶段的 partitionBy、sortBy 和 window 字段定义窗口。
对于同时用作累加器和窗口函数的操作符,区别在于操作符聚合的内容和返回的值的数量:
作为累加器,该操作符对一群组文档进行聚合,并返回该群组的单个值。
作为窗口函数,该操作符对文档窗口进行聚合,并为每个文档返回一个值。
您只能在 $setWindowFields 阶段使用窗口函数。 $setWindowFields 还支持特定于窗口的操作符,例如 $rank 和 $shift。
例子
本页上的示例使用来自 sample_mflix 数据集的数据。有关如何将此数据集加载到您的自管理 MongoDB 部署中的详细信息,请参阅 加载示例数据集。如果您对示例数据库进行了任何修改,您可能需要删除并重新创建数据库,才能运行此页面上的示例。
以下示例在所有三种上下文中都使用了 $sum。每个操作符都返回不同的结果,这显示了上下文如何决定操作符的行为。
表达式示例
以下示例使用 $sum 作为表达式,在每个文档中添加两个字段。管道会为其匹配的每个文档返回一个总计。
db.movies.aggregate( [ { $match: { title: { $in: [ "The Godfather", "Inception", "The Dark Knight" ] } } }, { $project: { _id: 0, title: 1, totalAwards: { $sum: [ "$awards.wins", "$awards.nominations" ] } } }, { $sort: { totalAwards: -1 } }] )累加器示例
以下示例使用 $sum 作为累加器,对群组中每个文档的一个字段求和。该管道会为每个内容分级返回一个总计,而不是为每个文档返回一个总计。
db.movies.aggregate( [ { $match: { rated: { $in: [ "G", "PG", "PG-13", "R" ] }, "imdb.votes": { $type: "int" } } }, { $group: { _id: "$rated", totalVotes: { $sum: "$imdb.votes" } } }, { $sort: { _id: 1 } }] )窗口函数示例
以下示例使用 $sum 作为窗口函数,对文档窗口中的一个字段进行总计。管道会返回每个文档的运行总计,而该总计取决于文档的窗口。
db.movies.aggregate( [ { $match: { genres: "Musical", year: { $in: [ 2009, 2010 ] }, "imdb.rating": { $type: "double" }, "imdb.votes": { $gt: 0 } } }, { $setWindowFields: { partitionBy: "$year", sortBy: { title: 1 }, output: { cumulativeVotesInYear: { $sum: "$imdb.votes", window: { documents: [ "unbounded", "current" ] } }, rankInYear: { $documentNumber: {} } } } }, { $project: { _id: 0, title: 1, year: 1, "imdb.votes": 1, cumulativeVotesInYear: 1, rankInYear: 1 } }, { $sort: { year: 1, title: 1 } }] )