MongoDB使用group做聚合,首先必须先选定分组所用到的键,然后MongoDB就会将集合依据选定的键的按照不同值进行分组,最终得到一组文档。
1.先插入测试数据
> for(var i=1; i<20; i++){
... var num=i%6;
... db.test.insert({"_id":i,"name":"user_"+i,"age":num});
... }
> db.test.find()
{ "_id" : 1, "name" : "user_1", "age" : 1 }
{ "_id" : 2, "name" : "user_2", "age" : 2 }
{ "_id" : 3, "name" : "user_3", "age" : 3 }
{ "_id" : 4, "name" : "user_4", "age" : 4 }
{ "_id" : 5, "name" : "user_5", "age" : 5 }
{ "_id" : 6, "name" : "user_6", "age" : 0 }
{ "_id" : 7, "name" : "user_7", "age" : 1 }
{ "_id" : 8, "name" : "user_8", "age" : 2 }
{ "_id" : 9, "name" : "user_9", "age" : 3 }
{ "_id" : 10, "name" : "user_10", "age" : 4 }
{ "_id" : 11, "name" : "user_11", "age" : 5 }
{ "_id" : 12, "name" : "user_12", "age" : 0 }
{ "_id" : 13, "name" : "user_13", "age" : 1 }
{ "_id" : 14, "name" : "user_14", "age" : 2 }
{ "_id" : 15, "name" : "user_15", "age" : 3 }
{ "_id" : 16, "name" : "user_16", "age" : 4 }
{ "_id" : 17, "name" : "user_17", "age" : 5 }
{ "_id" : 18, "name" : "user_18", "age" : 0 }
{ "_id" : 19, "name" : "user_19", "age" : 1 }
2.普通的分组查询:
> db.test.group({key:{age:true}, initial:{num:0}, $reduce:function(doc,prev) {
...prev.num++
...}});
[
{
"age" : 1,
"num" : 4
},
{
"age" : 2,
"num" : 3
},
{
"age" : 3,
"num" : 3
},
{
"age" : 4,
"num" : 3
},
{
"age" : 5,
"num" : 3
},
{
"age" : 0,
"num" : 3
}
]
> db.runCommand({group:
... {
... ns:"test",
... key:{age:true},
... initial:{num:0},
... $reduce:function(doc,prev){
... prev.num++}
... }
... });
{
"retval" : [
{
"age" : 1,
"num" : 4
},
{
"age" : 2,
"num" : 3
},
{
"age" : 3,
"num" : 3
},
{
"age" : 4,
"num" : 3
},
{
"age" : 5,
"num" : 3
},
{
"age" : 0,
"num" : 3
}
],
"count" : 19,
"keys" : 6,
"ok" : 1
}
以上使用group对age进行分组。
ns:指定要分组的集合。
key:指定文档分组依据的键。
initial:每一组reduce()函数调用的初始时间,会作为初始文档传递给后续过程。每一组的所有成员都会使用这个累加器,所有的改变都会保留。
$reduce:每个文档都对用一次该调用。系统会传递两个参数,当前文档和累加器文档。
3.对于age大于2的文档进行分组
> db.test.group({key:{age:true},initial:{num:0},$reduce:function(doc,prev){
... prev.num++
... },
... condition:{age:{$gt:2}}
... });
[
{
"age" : 3,
"num" : 3
},
{
"age" : 4,
"num" : 3
},
{
"age" : 5,
"num" : 3
}
]
> db.runCommand({group:
... {
... ns:"test",
... key:{age:true},
... initial:{num:0},
... $reduce:function(doc,prev){
... prev.num++},
... condition:{age:{$gt:2}}
... }
... });
{
"retval" : [
{
"age" : 3,
"num" : 3
},
{
"age" : 4,
"num" : 3
},
{
"age" : 5,
"num" : 3
}
],
"count" : 9,
"keys" : 3,
"ok" : 1
}
>
以上代码使用group对于age大于2的文档进行分组。
4.group分组和$where查询结合
> db.test.group({key:{age:true},initial:{num:0},$reduce:function(doc,prev){
... prev.num++
... },
... condition:{$where:function(){
... return this.age>2;
... }
... }
... });
[
{
"age" : 3,
"num" : 3
},
{
"age" : 4,
"num" : 3
},
{
"age" : 5,
"num" : 3
}
]
>
5.使用完成器
完成器(Finalize)用以精简从数据库传到用户数据。特别需要注意,group命令的输出一定要放在耽搁数据库中响应:
> db.test.group({$keyf:function(doc){return {age:doc.age};},initial:{num:0},$reduce:function(doc,prev){
... prev.num++
... },
... finalize: function(doc){ doc.count=doc.num;delete doc.num; }
... });
[
{
"age" : 1,
"count" : 4
},
{
"age" : 2,
"count" : 3
},
{
"age" : 3,
"count" : 3
},
{
"age" : 4,
"count" : 3
},
{
"age" : 5,
"count" : 3
},
{
"age" : 0,
"count" : 3
}
]
> db.runCommand({group:
... {
... ns:"test",
... $keyf:function(doc){return {age:doc.age};},
... initial:{num:0},
... $reduce:function(doc,prev){
... prev.num++},
... finalize: function(doc){ doc.count=doc.num;delete doc.num; }
... }
... });
{
"retval" : [
{
"age" : 1,
"count" : 4
},
{
"age" : 2,
"count" : 3
},
{
"age" : 3,
"count" : 3
},
{
"age" : 4,
"count" : 3
},
{
"age" : 5,
"count" : 3
},
{
"age" : 0,
"count" : 3
}
],
"count" : 19,
"keys" : 6,
"ok" : 1
}
>
本例通过完成器精简版从数据库传到用户的数据,注意,group命令的输出一定要放在耽搁数据响应中。
5.将函数作为键使用
有时分组所依据的条件非常复杂,不仅是一个键,还有可能是函数,定义函数就要用到$keyf(注意不是key)
> db.test.group({$keyf:function(doc){return {age:doc.age};},initial:{num:0},$reduce:function(doc,prev){
... prev.num++
... }
... });
[
{
"age" : 1,
"num" : 4
},
{
"age" : 2,
"num" : 3
},
{
"age" : 3,
"num" : 3
},
{
"age" : 4,
"num" : 3
},
{
"age" : 5,
"num" : 3
},
{
"age" : 0,
"num" : 3
}
]
> db.runCommand({group:
... {
... ns:"test",
... $keyf:function(doc){return {age:doc.age};},
... initial:{num:0},
... $reduce:function(doc,prev){
... prev.num++}
... }
... });
{
"retval" : [
{
"age" : 1,
"num" : 4
},
{
"age" : 2,
"num" : 3
},
{
"age" : 3,
"num" : 3
},
{
"age" : 4,
"num" : 3
},
{
"age" : 5,
"num" : 3
},
{
"age" : 0,
"num" : 3
}
],
"count" : 19,
"keys" : 6,
"ok" : 1
}
>
上述中我们使用$keyf定义一个函数,作为参数传入到group中,作为一个复制的分组依据来完成分组。
参考《深入云计算:MongoDB管理与开发实战详解》