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

$ 群组 (聚合阶段)

$group

$group 阶段根据群组密钥将具有相同字段或表达式的多个文档合并为一个文档。结果是每个唯一的群组密钥对应一个文档。

组键通常是一个字段或一组字段。组键也可以是表达式的结果。使用 $group 管道阶段中的 _id 字段来设置组键。请参阅下面的使用示例。

在 $group 阶段输出中,_id 字段被设为该文档的组键。

输出文档还可以包含使用累加器表达式设置的其他字段。

注意

$group 不会对其输出文档进行排序。

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

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

$group 阶段具有以下原型形式:

{ $group: { _id: <expression>, // Group key <field1>: { <accumulator1> : <expression1> }, ... } }
字段
说明

_id

必填。_id 表达式指定组键。如果指定的 _id 值为空值或任何其他常量值,$group 阶段将返回聚合所有输入文档值的单个文档。请参阅按空值分组示例。

field

可选。使用累加器操作符进行计算。

_id和累加器操作符可以接受任何有效的expression 。 有关表达式的更多信息,请参阅表达式。

$group 是一个阻塞阶段,这会导致管道在处理数据前等待为阻塞阶段检索所有输入数据。阻塞阶段可能会降低性能,因为它会减少具有多个阶段的管道的并行处理。对于大型数据集,阻塞阶段还可能使用大量内存。

<accumulator> 操作符必须是以下累加器操作符之一:

名称
说明

返回用户定义的累加器函数的结果。

返回每个群组的唯一表达式值数组。未定义数组元素的排序。

返回数值的平均值。

根据指定的排序顺序返回组内的底部元素。

根据指定的排序顺序,返回群组内后 n 个字段的聚合。

返回群组中的文档数。

有别于 $count 管道阶段。

返回群组中第一个文档的表达式结果。

返回群组内前 n 个元素的聚合。仅当文档按定义的顺序排列时才有意义。与 $firstN 数组操作符不同。

返回群组中最后一份文档的表达式结果。

返回对组中最后 n 个元素评估指定表达式的结果。

返回每个群组的最大表达式值。

返回群组中 n 个最大值元素的聚合。与 $maxN 数组操作符不同。

返回组中文档的中位数(第 50 百分位数)值的近似值。

7.0版本新增。

返回通过组合每个组的输入文档创建的文档。

返回每个群组的最小表达式值。

返回组中 n 个最小值元素的聚合。与 $minN 数组操作符不同。

返回与指定的各百分位数一一对应的标量值数组。

7.0版本新增。

返回每组中文档的大量表达式值。

返回组中文档值的总体标准差。

返回组内文档值的示例标准差。

返回数值的总和。忽略非数字值。

根据指定的排序顺序返回群组内第一个元素。

根据指定的排序顺序,返回群组内前 n 个字段的聚合。

如果 $group 阶段超过 100 兆字节 RAM,MongoDB 会将数据写入临时文件。但是,如果将 allowDiskUse 选项设置为 false,$group 将返回错误。有关更多信息,请参阅聚合管道限制。

此行为适用于 $group 阶段本身的状态。某些累加器(例如 $push 和 $addToSet)可能会导致单个累加对象超过内存阈值。这些累加器无法将临时文件写入磁盘。如果累加对象超过内存限制,即使 allowDiskUse 选项为 true,操作也会返回错误。

本节将介绍为提高 $group 性能而进行的优化。有些优化可以手动执行,有些优化由 MongoDB 在内部执行。

如果同一字段和 $group 阶段的管道 sorts 和 groups 仅使用 $first 或 $last 累加器操作符,请考虑在分组字段上添加与排序顺序匹配的索引。在某些情况下,$group 阶段可以使用索引来快速找到每组的第一个文档。

例子

如果 movies集合包含索引{ year: 1, title: 1 },则以下管道可以使用该索引来查找群组的第一个 document:

db.movies.aggregate([ { $sort: { year: 1, title: 1 } }, { $group: { _id: { year: "$year" }, title: { $first: "$title" } } }])

注意

从版本 7.0.17 开始,对于补丁版本的 7.0,默认不再启用基于插槽的查询执行引擎。如果您希望查询使用基于插槽的查询执行引擎,请升级到版本 8.0,默认下处于启用状态。

从版本 5.2 开始,如果满足以下任一条件,MongoDB 将使用基于插槽的执行查询引擎来执行 $group 阶段:

  • $group 是管道中的第一个阶段。

  • 管道中的所有先前阶段也可以由基于槽位的执行引擎执行。

有关更多信息,请参阅 $group 优化。

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

以下聚合操作使用 $group 阶段来计算 movies 集合中的文档数量:

db.movies.aggregate([ { $group: { _id: null, count: { $count: {} } } }])
[ { _id: null, count: 21349 }]

这个聚合操作相当于以下 SQL 语句:

SELECT COUNT(*) AS count FROM movies

以下聚合操作使用 $group 阶段从 movies集合中检索不同的 rated 值:

db.movies.aggregate( [ { $group : { _id : "$rated" } } ] )
[ { _id: 'TV-PG' }, { _id: 'PG' }, { _id: 'TV-14' }, { _id: 'OPEN' }, { _id: 'Not Rated' }, { _id: 'GP' }, { _id: 'TV-Y7' }, { _id: 'G' }, { _id: 'PG-13' }, { _id: null }, { _id: 'M' }, { _id: 'R' }, { _id: 'TV-MA' }, { _id: 'APPROVED' }, { _id: 'PASSED' }, { _id: 'Approved' }, { _id: 'AO' }, { _id: 'TV-G' }]

注意

当您使用$group 检索分片的集合中的不同值时,如果操作结果为DISTINCT_SCAN ,则结果可能包含孤立文档。

受影响的唯一语义正确的管道实际上是 命令的逻辑等效项,其中在管道的开头或附近有一个distinct 阶段,并且$group $group前面没有$sort 阶段。

示例,以下形式的 $group 操作可能会产生 DISTINCT_SCAN:

{ $group : { _id : "$<field>" } }

有关检索非重复值行为的更多信息,请参阅非重复命令行为。

要查看操作是否会产生DISTINCT_SCAN ,请检查操作的解释结果。

以下聚合操作按 rated字段对document进行分组,计算每个评级的总运行时间,并仅返回总运行时间大于或等于 100000 的评级:

db.movies.aggregate( [ // First Stage { $group: { _id: "$rated", totalRuntime: { $sum: "$runtime" } } }, // Second Stage { $match: { "totalRuntime": { $gte: 100000 } } } ])
[ { _id: 'PG-13', totalRuntime: 250843 }, { _id: 'R', totalRuntime: 582318 }, { _id: null, totalRuntime: 967127 }, { _id: 'PG', totalRuntime: 191204 }]
第一个阶段:
$group 阶段按 rated 对文档进行分组,以检索不同的评级值。此阶段返回每个评级群组的 totalRuntime。
第二个阶段:
$match 阶段筛选生成的 document,仅返回 totalRuntime 大于或等于 100000 的评分。

这个聚合操作相当于以下 SQL 语句:

SELECT rated, Sum(runtime) AS totalRuntimeFROM moviesGROUP BY ratedHAVING totalRuntime >= 100000

提示

$match

以下管道计算 1910之前每年的总运行时间、平均运行时间和电影数量:

db.movies.aggregate([ { $match: { "year": { $lt: 1910 } } }, { $group: { _id: "$year", totalRuntime: { $sum: "$runtime" }, averageRuntime: { $avg: "$runtime" }, count: { $sum: 1 } } }, { $sort: { totalRuntime: -1 } }])
[ { _id: 1909, totalRuntime: 14, averageRuntime: 14, count: 1 }, { _id: 1903, totalRuntime: 11, averageRuntime: 11, count: 1 }, { _id: 1896, totalRuntime: 2, averageRuntime: 1, count: 2 }]
第一个阶段:
$match 阶段会筛选 document,仅将 1910 之前发布的电影传递到下一阶段。
第二个阶段:
$group 阶段按年份对 document 进行分组,并计算群组 document 的总运行时间、平均运行时间和总数。
第三个阶段:
$sort 阶段按群组的总运行时间对结果进行降序排序。

这个聚合操作相当于以下 SQL 语句:

SELECT year, Sum(runtime) AS totalRuntime, Avg(runtime) AS averageRuntime, Count(*) AS countFROM moviesWHERE year < 1910GROUP BY yearORDER BY totalRuntime DESC

提示

以下聚合操作指定 null 的 _id群组,计算集合中所有 document 的总运行时间、平均运行时间和计数。

db.movies.aggregate([ { $group: { _id: null, totalRuntime: { $sum: "$runtime" }, averageRuntime: { $avg: "$runtime" }, count: { $sum: 1 } } }])
[ { _id: null, totalRuntime: 2167458, averageRuntime: 103.65652797704448, count: 21349 }]

这个聚合操作相当于以下 SQL 语句:

SELECT Sum(runtime) AS totalRuntime, Avg(runtime) AS averageRuntime, Count(*) AS countFROM movies

提示

以下聚合操作对 movies集合中的数据进行透视,以按年份对书名进行群组:

db.movies.aggregate([ { $match: { year: { $lt: 1910 } } }, { $group: { _id: "$year", titles: { $push: "$title" } } }, { $sort: { _id: 1 } }])
[ { _id: 1896, titles: [ 'The Kiss', 'The Kiss' ] }, { _id: 1903, titles: [ 'The Great Train Robbery' ] }, { _id: 1909, titles: [ 'A Corner in Wheat' ] }]

以下聚合操作按年份对document进行分组:

db.movies.aggregate([ { $match: { year: { $lt: 1910 } } }, { $group: { _id: "$year", movies: { $push: "$$ROOT" } } }, { $addFields: { totalRuntime: { $sum: "$movies.runtime" } } }, { $sort: { _id: 1 } }])
[ { _id: 1896, movies: '...', totalRuntime: 2 }, { _id: 1903, movies: '...', totalRuntime: 11 }, { _id: 1909, movies: '...', totalRuntime: 14 }]
第一个阶段:
$match filters the document,仅将 1910之前发布的电影传递到下一阶段。
第二个阶段:
$group 使用 $$ROOT 系统变量按年份对整个 document 进行群组。
第三个阶段:

$addFields 在输出中添加一个字段,其中包含每年的电影总放映时间。

注意

生成的文档不得超过 16 MiB 的 BSON 文档大小限制。

第四阶段:
$sort 按 _id 升序对生成的 document 进行排序。

Group and Total Data 教程提供了常见使用案例中 $group 操作符的广泛示例。

要学习;了解有关相关管道阶段的更多信息,请参阅 $addFields 指南。

获得技能徽章

免费掌握“数据转换基础”!

了解详情

给本页内容打分