mongodb Aggregation聚合操作之group分组

在上一篇mongodb聚合操作之Aggregation Pipeline中详细介绍了什么是mongodb聚合操作中的Aggregation Pipeline以及参数细节。本篇将开始介绍Aggregation聚合操作中的group分组操作,相当于mysql的group by聚合。

1.?简介


说明:

按照指定的_id表达式对输入文档进行分组,并对每个不同的分组输出一个文档。每个输出文档的_id字段包含惟一的group by值。输出文档还可以包含包含某些累加器表达式值的计算字段,不对其输出文档排序


语法:

{

??$group:

????{

??????_id: <expression>, // Group By Expression

??????<field1>: { <accumulator1> : <expression1> },

??????...

????}

?}


参数讲解:

_id:必须的。如果您将_id值指定为null或任何其他常数值,则$group阶段将计算所有输入文档作为一个整体的累计值。参见Null分组示例。_id和累加器操作符可以接受任何有效的表达式

field:可选的。使用累加器操作符计算。

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

$addToSet:返回每个组的惟一表达式值数组。数组元素的顺序未定义

$avg:返回数值的平均值。忽略了非数字值。

$first:为每个组从第一个文档返回一个值。只有在文档按已定义的顺序排列时才定义顺序。

$last:从最后一个文档中为每个组返回一个值。只有在文档按已定义的顺序排列时才定义顺序。

$max:返回每个组的最大表达式值。

$min:返回每个组的最小表达式值。

$push:返回每个组的表达式值数组。

$sum:返回数值的和。忽略了非数字值。

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

$stdDevPop:返回输入值的总体标准差。

$stdDevSamp:返回输入值的样本标准差。


2.?示例


初始化数据:

db.groupExample.insertMany([

??{ "_id" : 1, "item" : "abc", "price" : NumberDecimal("10"), "quantity" : NumberInt("2"), "date" : ISODate("2014-03-01T08:00:00Z") },

??{ "_id" : 2, "item" : "jkl", "price" : NumberDecimal("20"), "quantity" : NumberInt("1"), "date" : ISODate("2014-03-01T09:00:00Z") },

??{ "_id" : 3, "item" : "xyz", "price" : NumberDecimal("5"), "quantity" : NumberInt( "10"), "date" : ISODate("2014-03-15T09:00:00Z") },

??{ "_id" : 4, "item" : "xyz", "price" : NumberDecimal("5"), "quantity" : ?NumberInt("20") , "date" : ISODate("2014-04-04T11:21:39.736Z") },

??{ "_id" : 5, "item" : "abc", "price" : NumberDecimal("10"), "quantity" : NumberInt("10") , "date" : ISODate("2014-04-04T21:23:13.331Z") },

??{ "_id" : 6, "item" : "def", "price" : NumberDecimal("7.5"), "quantity": NumberInt("5" ) , "date" : ISODate("2015-06-04T05:08:13Z") },

??{ "_id" : 7, "item" : "def", "price" : NumberDecimal("7.5"), "quantity": NumberInt("10") , "date" : ISODate("2015-09-10T08:43:00Z") },

??{ "_id" : 8, "item" : "abc", "price" : NumberDecimal("10"), "quantity" : NumberInt("5" ) , "date" : ISODate("2016-02-06T20:20:13Z") },

])



2.1.?计算文档总数


示例:下面的聚合操作使用$group阶段来计算groupExample集合中的文档数量:

db.groupExample.aggregate( [

??{

????$group: {

???????_id: null,

???????count: { $sum: 1 }

????}

??}

] )


结果:8

2.2.?对某一字段进行分组


示例:对item字段进行分组

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


结果:

{

????"_id" : "xyz"

}

{

????"_id" : "jkl"

}

{

????"_id" : "def"

}

{

????"_id" : "abc"

}

2.3.?对某一字段进行分组后having


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

db.groupExample.aggregate(

??[

?{

??????$group :

????????{

??????????_id : "$item",

??????????totalSaleAmount: { $sum: { $multiply: [ "$price", "$quantity" ] } }

????????}

?????},

?????{

???????$match: { "totalSaleAmount": { $gte: 100 } }

?????}

???]

?)


以上操作相当于mysql中:SELECT item,

???Sum(( price * quantity )) AS totalSaleAmount

FROM ??groupExample

GROUP ?BY item

HAVING totalSaleAmount >= 100


结果:

{ "_id" : "abc", "totalSaleAmount" : NumberDecimal("170") }

{ "_id" : "xyz", "totalSaleAmount" : NumberDecimal("150") }

{ "_id" : "def", "totalSaleAmount" : NumberDecimal("112.5") }



2.4.?计算计数、总和和平均值


示例:计算2014年的总销售额、平均销售额、每天的销售额并排序:

db.groupExample.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 }

??}

?])


以上操作相当于mysql中:

SELECT date,

???????Sum(( price * quantity )) AS totalSaleAmount,

???????Avg(quantity) ????????????AS averageQuantity,

???????Count(*) ?????????????????AS Count

FROM ??groupExample

GROUP ?BY Date(date)

ORDER ?BY totalSaleAmount DESC


结果:

{ "_id" : "2014-04-04", "totalSaleAmount" : NumberDecimal("200"), "averageQuantity" : 15, "count" : 2 }

{ "_id" : "2014-03-15", "totalSaleAmount" : NumberDecimal("50"), "averageQuantity" : 10, "count" : 1 }

{ "_id" : "2014-03-01", "totalSaleAmount" : NumberDecimal("40"), "averageQuantity" : 1.5, "count" : 2 }

2.5.?分组ID字段是null,计算总数


示例:下面的聚合操作将组_id指定为null,用于计算总销售额、平均数量和集合中所有文档的计数。

db.groupExample.aggregate([

??{

????$group : {

???????_id : null,

???????totalSaleAmount: { $sum: { $multiply: [ "$price", "$quantity" ] } },

???????averageQuantity: { $avg: "$quantity" },

???????count: { $sum: 1 }

????}

??}

?])


以上操作相当于mysql中:

SELECT Sum(price * quantity) AS totalSaleAmount,

???????Avg(quantity) ????????AS averageQuantity,

???????Count(*) ?????????????AS Count

FROM ??groupExample


结果:

{

??"_id" : null,

??"totalSaleAmount" : NumberDecimal("452.5"),

??"averageQuantity" : 7.875,

??"count" : 8

}

2.6.?对某一个字段进行分组,并查询出分组下数据加到数组中


初始化数据:

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 }

])


示例:

db.books.aggregate([

???{ $group : { _id : "$author", books: { $push: "$title" } } }

?])


以上操作相当于mysql中:SELECT author,GROUP_CONCAT(title) as books FROM books GROUP BY author


结果:

{

????"_id" : "Homer",

????"books" : [

????????"The Odyssey",

????????"Iliad"

????]

}

{

????"_id" : "Dante",

????"books" : [

????????"The Banquet",

????????"Divine Comedy",

????????"Eclogues"

????]

}

2.7.?使用$$ROOT系统变量对整个文档进行分组


初始化数据:

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 }

])


示例:

db.books.aggregate([

???// First Stage

???{

?????$group : { _id : "$author", books: { $push: "$$ROOT" } }

???},

???// Second Stage

???{

?????$addFields:

???????{

?????????totalCopies : { $sum: "$books.copies" }

???????}

???}

?])


结果:


{

????"_id" : "Homer",

????"books" : [

????????{

????????????"_id" : 7000.0,

????????????"title" : "The Odyssey",

????????????"author" : "Homer",

????????????"copies" : 10.0

????????},

????????{

????????????"_id" : 7020.0,

????????????"title" : "Iliad",

????????????"author" : "Homer",

????????????"copies" : 10.0

????????}

????],

????"totalCopies" : 20.0

}


{

????"_id" : "Dante",

????"books" : [

????????{

????????????"_id" : 8751.0,

????????????"title" : "The Banquet",

????????????"author" : "Dante",

????????????"copies" : 2.0

????????},

????????{

????????????"_id" : 8752.0,

????????????"title" : "Divine Comedy",

????????????"author" : "Dante",

????????????"copies" : 1.0

????????},

????????{

????????????"_id" : 8645.0,

????????????"title" : "Eclogues",

????????????"author" : "Dante",

????????????"copies" : 2.0

????????}

????],

????"totalCopies" : 5.0

}

3.?注意事项


注意内存说明:

$group阶段的RAM有100mb字节的限制。默认情况下,如果阶段超过这个限制,$group将返回一个错误。要允许处理大型数据集,请将allowDiskUse选项设置为true。此标志允许$group操作写入临时文件。有关更多信息,请参见db.collection.aggregate()方法和aggregate命令。

版本2.6中的变化:MongoDB为$group阶段引入了100mb的RAM限制,并引入allowDiskUse选项来处理大型数据集的操作。

?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,128评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,316评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,737评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,283评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,384评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,458评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,467评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,251评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,688评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,980评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,155评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,818评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,492评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,142评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,382评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,020评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,044评论 2 352

推荐阅读更多精彩内容