前言
MongoDB不像关系型数据库,普通的查询不支持汇总,要进行复杂的分组汇总,需要使用聚合管道,$group
可以说是MongoDB聚合管道进行数据分析最常用的一个阶段。该阶段根据分组键值(组键)把文档分成若干组,每个唯一的键值对应一个文档。组键通常是一个或多个字段,也可以是表达式的结果。$group
阶段输出的结果中,_id
字段的值就是组键的值,输出文档中还可以包含汇总表达式的字段,汇总表达式的功能非常丰富,下面的列表会简单介绍,具体的使用方法可以参考详细说明。
$group的语法
1 2 3 4 5 6 7 8 |
|
字段说明:
字段 | 说明 |
_id | 不可省略,通过 |
field | 可选,汇总表达式计算的结果 |
_id和field可以是任何合法的表达式。 |
分组汇总操作符
分组汇总操作符比较多,功能丰富且强大,这里简要介绍其用途,详细的用法后续再专文介绍。
操作符 | 用途介绍 |
| 返回累加结果 |
| 把分组中不重复的表达式的值作为数组返回,注意数组的元素无序的,类似分组内的distinct |
| 返回数值的平均值。非数值会被忽略 |
| 按照指定的顺序返回分组中最后一个元素 |
| 按照指定的顺序返回分组中最后N个元素字段的集合,如果分组元素数量小于N,则返回全部 |
| 返回分组内的元素数量 |
| 返回分组内第一个元素表达式的结果 |
| 返回分组内前n个元素的聚合。只有文档有序时才有意义 |
| 返回分组中最后一个文档的表达式的结果 |
| 返回分组内最后n个元素的聚合。只有文档有序时才有意义 |
| 返回每个分组表达式值的最大值 |
| 返回分组内最大的n个元素的集合 |
| 返回分组中的中位数 |
| 返回分组合并后的文档 |
| 返回分组内表达式的最小值 |
| 返回与指定百分位数值相对应的值的数组 |
| 返回每个分组表达式值的数组 |
| 返回标准差 |
| 返回样本标准差 |
| 返回合计值,忽略空值 |
| 根据指定的顺序返回组内最前面的元素 |
| 根据指定的顺序返回组内前N个元素的聚合 |
注意
$group
使用内存不能超过100M,超过会报错。如果想要处理更多数据或者少用一些内存,可使用allowDiskUse选项把数据写入临时文件。- 当使用
$first、$last
等操作符时,可以考虑在参与排序的分组字段上添加索引,某些情况下,这些操作可以使用索引快速定位到相应的记录。
一些例子
统计数量
创建并插入数据:
1 2 3 4 5 6 7 8 9 10 |
|
统计sales全部文档数量
相当于collection.find({}).count()
1 2 3 4 5 6 7 8 |
|
结果:
{ "_id" : null, "count" : 8 }
检索不同的值,等价于distinct
仍以上例的sales集合数据为例
1 |
|
结果:
{ "_id" : "abc" }
{ "_id" : "jkl" }
{ "_id" : "def" }
{ "_id" : "xyz" }
等价于:
1 |
|
按Item分组
下面的聚合先按照item进行分组,计算每个item销售总额,并且返回大于等于100的item。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
阶段1:$group
阶段,根据item进行分组,并计算每个item的销售总额。
阶段2:$math
阶段,过滤结果文档,只返回销售总额totalSaleAmount
大于等于100的文档。
结果:
{ "_id" : "abc", "totalSaleAmount" : Decimal128("170") }
{ "_id" : "xyz", "totalSaleAmount" : Decimal128("150") }
{ "_id" : "def", "totalSaleAmount" : Decimal128("112.5") }
计算总数、合计和平均值
创建一个sales集合并插入记录:
1 2 3 4 5 6 7 8 9 10 |
|
按照日期分组
下面的聚合管道计算2014年的销售总额、平均销量和销售数量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
阶段1使用$math
只允许2014年的数据进入下一阶段.
阶段2使用$group
根据日期进行分组,统计每个分组的销售总额、平均销量和销售数量。
阶段3使用$sort
按照销售总额进行降序排序
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
按控制null分组
下面的聚合操作,指定分组_id为空,计算集合中所有文档的总销售额、平均数量和计数。
1 2 3 4 5 6 7 8 9 10 |
|
结果:
{
"_id" : null,
"totalSaleAmount" : Decimal128("452.5"),
"averageQuantity" : 7.875,
"count" : 8
}
数据透视
创建books集合并插入数据
1 2 3 4 5 6 7 |
|
根据作者对标题分组
下面的聚合操作将books集合中的数据透视为按作者分组的标题。
1 2 3 |
|
结果
{ "_id" : "Homer", "books" : [ "The Odyssey", "Iliad" ] }
{ "_id" : "Dante", "books" : [ "The Banquet", "Divine Comedy", "Eclogues" ] }
根据作者对文档分组
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
阶段1:分组$group
,根据作者对文档进行分组,使用$$ROOT
把文档作为books的元素。
阶段2:$addFields
会在输出文档中添加一个字段,即每位作者的图书总印数。
结果:
{
"_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
}
以上内容参考mongodb官方文档整理而来