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

$group(聚合)

在此页面上

  • 定义
  • 兼容性
  • 语法
  • 考虑因素
  • 举例
  • 其他资源
$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 。有关表达式的更多信息,请参阅表达式操作符。

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

5.0 版本中的更改

名称
说明
返回用户定义的累加器函数的结果。

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

5.0 版中的更改:可在 $setWindowFields 阶段使用。

返回数值的平均值。忽略非数字值。

5.0 版中的更改:可在 $setWindowFields 阶段使用。

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

5.2 版本中的新增功能

可在$group$setWindowFields阶段使用。

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

5.2 版本中的新增功能

可在$group$setWindowFields阶段使用。

返回群组中的文档数。

有别于 $count 管道阶段。

5.0 版新增功能:可在 $group$setWindowFields 阶段使用。

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

5.0 版中的更改:可在 $setWindowFields 阶段使用。

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

5.2 版新增功能:可在 $group表达式$setWindowFields 阶段使用。

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

5.0 版中的更改:可在 $setWindowFields 阶段使用。

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

5.2 版新增功能:可在 $group表达式$setWindowFields 阶段使用。

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

5.0 版中的更改:可在 $setWindowFields 阶段使用。

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

5.2 版本中的新增功能

$group$setWindowFields中可用,也可作为表达式使用。

返回中位数(第 50 百分位数)的近似标量值。

7.0 版本中的新增功能

此操作符可在以下阶段用作累加器:

它也可用作聚合表达式

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

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

5.0 版中的更改:可在 $setWindowFields 阶段使用。

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

5.2 版本中的新增功能

$group$setWindowFields中可用,也可作为表达式使用。

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

7.0 版本中的新增功能

此操作符可在以下阶段用作累加器:

它也可用作聚合表达式

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

5.0 版中的更改:可在 $setWindowFields 阶段使用。

返回输入值的总体标准偏差。

5.0 版中的更改:可在 $setWindowFields 阶段使用。

返回输入值的样本标准偏差。

5.0 版中的更改:可在 $setWindowFields 阶段使用。

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

5.0 版中的更改:可在 $setWindowFields 阶段使用。

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

5.2 版本中的新增功能

可在$group$setWindowFields阶段使用。

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

5.2 版本中的新增功能

可在$group$setWindowFields阶段使用。

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

本节介绍为提高$group性能而进行的优化。您可以手动进行优化,MongoDB 内部也会进行优化。

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

例子

如果名为 foo 的集合包含索引 { x: 1, y: 1 },则以下管道可以使用该索引来查找每个组的第一个文档:

db.foo.aggregate([
{
$sort:{ x : 1, y : 1 }
},
{
$group: {
_id: { x : "$x" },
y: { $first : "$y" }
}
}
])

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

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

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

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

mongosh中,使用以下文档创建名为sales的示例集合:

db.sales.insertMany([
{ "_id" : 1, "item" : "abc", "price" : Decimal128("10"), "quantity" : Int32("2"), "date" : ISODate("2014-03-01T08:00:00Z") },
{ "_id" : 2, "item" : "jkl", "price" : Decimal128("20"), "quantity" : Int32("1"), "date" : ISODate("2014-03-01T09:00:00Z") },
{ "_id" : 3, "item" : "xyz", "price" : Decimal128("5"), "quantity" : Int32( "10"), "date" : ISODate("2014-03-15T09:00:00Z") },
{ "_id" : 4, "item" : "xyz", "price" : Decimal128("5"), "quantity" : Int32("20") , "date" : ISODate("2014-04-04T11:21:39.736Z") },
{ "_id" : 5, "item" : "abc", "price" : Decimal128("10"), "quantity" : Int32("10") , "date" : ISODate("2014-04-04T21:23:13.331Z") },
{ "_id" : 6, "item" : "def", "price" : Decimal128("7.5"), "quantity": Int32("5" ) , "date" : ISODate("2015-06-04T05:08:13Z") },
{ "_id" : 7, "item" : "def", "price" : Decimal128("7.5"), "quantity": Int32("10") , "date" : ISODate("2015-09-10T08:43:00Z") },
{ "_id" : 8, "item" : "abc", "price" : Decimal128("10"), "quantity" : Int32("5" ) , "date" : ISODate("2016-02-06T20:20:13Z") },
])

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

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

操作返回以下结果:

{ "_id" : null, "count" : 8 }

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

SELECT COUNT(*) AS count FROM sales

提示

另请参阅:

以下聚合操作使用$group阶段从sales集合中检索不同项目值:

db.sales.aggregate( [ { $group : { _id : "$item" } } ] )

操作返回以下结果:

{ "_id" : "abc" }
{ "_id" : "jkl" }
{ "_id" : "def" }
{ "_id" : "xyz" }

下面的聚合操作按 item 字段对文档进行分组,计算每个列项的总销售额,然后只返回总销售额大于或等于 100 的项目:

db.sales.aggregate(
[
// First Stage
{
$group :
{
_id : "$item",
totalSaleAmount: { $sum: { $multiply: [ "$price", "$quantity" ] } }
}
},
// Second Stage
{
$match: { "totalSaleAmount": { $gte: 100 } }
}
]
)
第一个阶段:
$group阶段按item对文档进行分组,以检索不同的项目值。此阶段返回每一项的totalSaleAmount
第二个阶段:
$match 阶段会对生成的文档进行筛选,从而只返回 totalSaleAmount 大于或等于 100 的项目。

操作返回以下结果:

{ "_id" : "abc", "totalSaleAmount" : Decimal128("170") }
{ "_id" : "xyz", "totalSaleAmount" : Decimal128("150") }
{ "_id" : "def", "totalSaleAmount" : Decimal128("112.5") }

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

SELECT item,
Sum(( price * quantity )) AS totalSaleAmount
FROM sales
GROUP BY item
HAVING totalSaleAmount >= 100

提示

另请参阅:

mongosh中,使用以下文档创建名为sales的示例集合:

db.sales.insertMany([
{ "_id" : 1, "item" : "abc", "price" : Decimal128("10"), "quantity" : Int32("2"), "date" : ISODate("2014-03-01T08:00:00Z") },
{ "_id" : 2, "item" : "jkl", "price" : Decimal128("20"), "quantity" : Int32("1"), "date" : ISODate("2014-03-01T09:00:00Z") },
{ "_id" : 3, "item" : "xyz", "price" : Decimal128("5"), "quantity" : Int32( "10"), "date" : ISODate("2014-03-15T09:00:00Z") },
{ "_id" : 4, "item" : "xyz", "price" : Decimal128("5"), "quantity" : Int32("20") , "date" : ISODate("2014-04-04T11:21:39.736Z") },
{ "_id" : 5, "item" : "abc", "price" : Decimal128("10"), "quantity" : Int32("10") , "date" : ISODate("2014-04-04T21:23:13.331Z") },
{ "_id" : 6, "item" : "def", "price" : Decimal128("7.5"), "quantity": Int32("5" ) , "date" : ISODate("2015-06-04T05:08:13Z") },
{ "_id" : 7, "item" : "def", "price" : Decimal128("7.5"), "quantity": Int32("10") , "date" : ISODate("2015-09-10T08:43:00Z") },
{ "_id" : 8, "item" : "abc", "price" : Decimal128("10"), "quantity" : Int32("5" ) , "date" : ISODate("2016-02-06T20:20:13Z") },
])

以下管道计算 2014 年每一天的总销售额、平均销售数量和销售数量:

db.sales.aggregate([
// First Stage
{
$match : { "date": { $gte: new ISODate("2014-01-01"), $lt: new ISODate("2015-01-01") } }
},
// Second Stage
{
$group : {
_id : { $dateToString: { format: "%Y-%m-%d", date: "$date" } },
totalSaleAmount: { $sum: { $multiply: [ "$price", "$quantity" ] } },
averageQuantity: { $avg: "$quantity" },
count: { $sum: 1 }
}
},
// Third Stage
{
$sort : { totalSaleAmount: -1 }
}
])
第一个阶段:
$match 阶段会对这些文档进行筛选,已仅将从 2014 年开始的文档传递到下一阶段。
第二个阶段:
$group阶段按日期对文档进行分组,并计算每组中文档的总销售额、平均数量和总数。
第三个阶段:
$sort 阶段按每个组的总销售金额对结果进行降序排序。

操作返回以下结果:

{
"_id" : "2014-04-04",
"totalSaleAmount" : Decimal128("200"),
"averageQuantity" : 15, "count" : 2
}
{
"_id" : "2014-03-15",
"totalSaleAmount" : Decimal128("50"),
"averageQuantity" : 10, "count" : 1
}
{
"_id" : "2014-03-01",
"totalSaleAmount" : Decimal128("40"),
"averageQuantity" : 1.5, "count" : 2
}

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

SELECT date,
Sum(( price * quantity )) AS totalSaleAmount,
Avg(quantity) AS averageQuantity,
Count(*) AS Count
FROM sales
WHERE date >= '01/01/2014' AND date < '01/01/2015'
GROUP BY date
ORDER BY totalSaleAmount DESC

提示

另请参阅:

下面的聚合操作指定了 null_id 组,计算集合中所有文档的总销售额、平均数量和计数。

db.sales.aggregate([
{
$group : {
_id : null,
totalSaleAmount: { $sum: { $multiply: [ "$price", "$quantity" ] } },
averageQuantity: { $avg: "$quantity" },
count: { $sum: 1 }
}
}
])

操作返回以下结果:

{
"_id" : null,
"totalSaleAmount" : Decimal128("452.5"),
"averageQuantity" : 7.875,
"count" : 8
}

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

SELECT Sum(price * quantity) AS totalSaleAmount,
Avg(quantity) AS averageQuantity,
Count(*) AS Count
FROM sales

提示

另请参阅:

mongosh中,使用以下文档创建名为books的示例集合:

db.books.insertMany([
{ "_id" : 8751, "title" : "The Banquet", "author" : "Dante", "copies" : 2 },
{ "_id" : 8752, "title" : "Divine Comedy", "author" : "Dante", "copies" : 1 },
{ "_id" : 8645, "title" : "Eclogues", "author" : "Dante", "copies" : 2 },
{ "_id" : 7000, "title" : "The Odyssey", "author" : "Homer", "copies" : 10 },
{ "_id" : 7020, "title" : "Iliad", "author" : "Homer", "copies" : 10 }
])

以下聚合操作将books集合中的数据转换为按作者分组的标题。

db.books.aggregate([
{ $group : { _id : "$author", books: { $push: "$title" } } }
])

该操作将返回以下文档:

{ "_id" : "Homer", "books" : [ "The Odyssey", "Iliad" ] }
{ "_id" : "Dante", "books" : [ "The Banquet", "Divine Comedy", "Eclogues" ] }

以下聚合操作按 author 对文档进行分组:

db.books.aggregate([
// First Stage
{
$group : { _id : "$author", books: { $push: "$$ROOT" } }
},
// Second Stage
{
$addFields:
{
totalCopies : { $sum: "$books.copies" }
}
}
])
第一个阶段:

$group使用$$ROOT系统变量按作者对整个文档进行分组。此阶段将以下文档传递给下一阶段:

{ "_id" : "Homer",
"books" :
[
{ "_id" : 7000, "title" : "The Odyssey", "author" : "Homer", "copies" : 10 },
{ "_id" : 7020, "title" : "Iliad", "author" : "Homer", "copies" : 10 }
]
},
{ "_id" : "Dante",
"books" :
[
{ "_id" : 8751, "title" : "The Banquet", "author" : "Dante", "copies" : 2 },
{ "_id" : 8752, "title" : "Divine Comedy", "author" : "Dante", "copies" : 1 },
{ "_id" : 8645, "title" : "Eclogues", "author" : "Dante", "copies" : 2 }
]
}
第二个阶段:

$addFields 向输出添加一个字段,其中包含每位作者的图书总份数。

注意

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

该操作将返回以下文档:

{
"_id" : "Homer",
"books" :
[
{ "_id" : 7000, "title" : "The Odyssey", "author" : "Homer", "copies" : 10 },
{ "_id" : 7020, "title" : "Iliad", "author" : "Homer", "copies" : 10 }
],
"totalCopies" : 20
}
{
"_id" : "Dante",
"books" :
[
{ "_id" : 8751, "title" : "The Banquet", "author" : "Dante", "copies" : 2 },
{ "_id" : 8752, "title" : "Divine Comedy", "author" : "Dante", "copies" : 1 },
{ "_id" : 8645, "title" : "Eclogues", "author" : "Dante", "copies" : 2 }
],
"totalCopies" : 5
}

提示

另请参阅:

邮政编码数据集的聚合教程提供了常见用例中$group操作符的广泛示例。

← $graphLookup(聚合)