设为首页 收藏本站
查看: 1576|回复: 0

[经验分享] MongoDB中的聚合操作

[复制链接]
发表于 2015-7-6 07:52:38 | 显示全部楼层 |阅读模式
  根据MongoDB的文档描述,在MongoDB的聚合操作中,有以下五个聚合命令。
  其中,count、distinct和group会提供很基本的功能,至于其他的高级聚合功能(sum、average、max、min),就需要通过mapReduce来实现了。
  在MongoDB2.2版本以后,引入了新的聚合框架(聚合管道,aggregation pipeline ,使用aggregate命令),是一种基于管道概念的数据聚合操作。



  Name

  Description

  count

  Counts the number of documents in a collection.

  distinct

  Displays the distinct values found for a specified key in a collection.

  group

  Groups documents in a collection by the specified key and performs simple aggregation.

  mapReduce

  Performs map-reduce aggregation for large data sets.

  aggregate

  Performs aggregation tasks such as group using the aggregation framework.

  下面就开始对这些聚合操作进行介绍,所有的测试数据都是基于上一篇文章。
  

count
  首先,我们看下MongoDB文档中,count命令可以支持的选项:



1 { count: , query: , limit: , skip: , hint:  }

  • count:要执行count的collection
  • query(optional):过滤条件
  • limit(optional):查询匹配文档数量的上限
  • skip(optional):跳过匹配文档的数量
  • hint(optional):使用那个索引
  例子:查看男学生的数量



1 > db.runCommand({"count":"school.students", "query":{"gender":"Male"}})
2 { "n" : 5, "ok" : 1 }
3 >
  在MongoDB中,对count操作有一层包装,所以也可以通过shell直接运行db."collectionName".count()。
  但是为了保持风格一致,我还是倾向于使用db.runCommand()的方式。
  

distinct
  接下来看看distinct命令,下面列出可以支持的选项:



1 { distinct: "", key: "", query:  }

  • distinct:要执行distinct的collection
  • key:要执行distinct的键
  • query(optional):过滤条件
  例子:查看所有学生年龄的不同值



1 > db.runCommand({"distinct":"school.students","key":"age"})
2 {
3         "values" : [
4                 20,
5                 21,
6                 22,
7                 23,
8                 24
9         ],
10         "stats" : {
11                 "n" : 10,
12                 "nscanned" : 10,
13                 "nscannedObjects" : 0,
14                 "timems" : 0,
15                 "cursor" : "BtreeCursor age_1"
16         },
17         "ok" : 1
18 }
  

group
  group命令相比前两就稍微复杂了一些。



1 {
2   group:
3    {
4      ns: ,
5      key: ,
6      $reduce: ,
7      initial:
8      $keyf: ,
9      cond: ,
10      finalize:
11    }
12 }
  


  • ns:要执行group的collection
  • key:要执行group的键,可以是多个键;和keyf两者必须有一个
  • $reduce:在group操作中要执行的聚合function,该function包括两个参数,当前文档和聚合结果文档
  • initial:reduce中使用变量的初始化
  • $keyf(optional):可以接受一个function,用来动态的确定分组文档的字段
  • cond(optional):过滤条件
  • finalize(optional):在reduce执行完成,结果集返回之前对结果集最终执行的函数
  例子:统计不同年龄、性别分组的学生数量



1 > db.runCommand({
2 ...     "group":{
3 ...         "ns":"school.students",
4 ...         "key":{"age":true, "gender":true},
5 ...         "initial":{"count":0},
6 ...         "$reduce": function(cur, result){ result.count++;},
7 ...         "cond":{"age":{"$lte":22}}
8 ...     }
9 ... })
10 {
11         "retval" : [
12                 {
13                         "age" : 20,
14                         "gender" : "Female",
15                         "count" : 2
16                 },
17                 {
18                         "age" : 20,
19                         "gender" : "Male",
20                         "count" : 1
21                 },
22                 {
23                         "age" : 21,
24                         "gender" : "Male",
25                         "count" : 2
26                 },
27                 {
28                         "age" : 22,
29                         "gender" : "Female",
30                         "count" : 1
31                 }
32         ],
33         "count" : 6,
34         "keys" : 4,
35         "ok" : 1
36 }
37 >
  通过finalize选项,可以在结果返回之前进行一些自定义设置。



1 > db.runCommand({
2 ...     "group":{
3 ...         "ns":"school.students",
4 ...         "key":{"age":true, "gender":true},
5 ...         "initial":{"count":0},
6 ...         "$reduce": function(cur, result){
7 ...                             result.count++;
8 ...                         },
9 ...         "cond":{"age":{"$lte":22}},
10 ...         "finalize": function(result){
11 ...                             result.percentage = result.count/10;
12 ...                             delete result.count;
13 ...                         }
14 ...     }
15 ... })
16 {
17         "retval" : [
18                 {
19                         "age" : 20,
20                         "gender" : "Female",
21                         "percentage" : 0.2
22                 },
23                 {
24                         "age" : 20,
25                         "gender" : "Male",
26                         "percentage" : 0.1
27                 },
28                 {
29                         "age" : 21,
30                         "gender" : "Male",
31                         "percentage" : 0.2
32                 },
33                 {
34                         "age" : 22,
35                         "gender" : "Female",
36                         "percentage" : 0.1
37                 }
38         ],
39         "count" : 6,
40         "keys" : 4,
41         "ok" : 1
42 }
43 >
  

mapReduce
  前面三个聚合操作提供了最基本的功能,如果要用到更加复杂的聚合操作,我们就需要自己通过mapReduce来实现了。
  mapReduce更重要的用法是实现多个服务器上的聚合操作。
  根据MongoDB文档,得到mapReduce的原型如下:



1 {
2     mapReduce: ,
3     map: ,
4     reduce: ,
5     out: ,
6     query(optional): ,
7     sort(optional): ,
8     limit(optional): ,
9     finalize(optional): ,
10     scope(optional): ,
11     jsMode(optional): ,
12     verbose(optional):
13 }

  • mapReduce:要执行map-reduce操作的collection
  • map:map function,生成键/值对,可以理解为映射函数
  • reduce:reduce function,对map的结果进行统计,可以理解为统计函数
  • out:统计结果存放集合 (不指定则使用临时集合,在客户端断开后自动删除)
  • query:过滤条件
  • sort:排序条件
  • limit:map函数可以接受的文档数量的最大值
  • finalize:在reduce执行完成后,结果集返回之前对结果集最终执行的函数
  • scope:向 map、reduce、finalize 导入外部变量
  • jsMode:设置是否把map和reduce的中间数据转换成BSON格式
  • verbose:设置是否显示详细的时间统计信息
  注意:map、reduce和finalize的函数实现都有特定的要求,具体的要求请参考MongoDB文档
  例子:
  查询男生和女生的最大年龄



1 > db.runCommand({
2 ...     "mapReduce": "school.students",
3 ...     "map": function(){
4 ...                     emit({gender: this.gender}, this.age);
5 ...                 },
6 ...     "reduce": function(key, values){
7 ...                         var max = 0;
8 ...                         for(var i = 0; i < values.length; i++)
9 ...                             max = max>values?max:values;
10 ...                         return max;
11 ...                    },
12 ...     "out": {inline: 1},
13 ...
14 ... })
15 {
16         "results" : [
17                 {
18                         "_id" : {
19                                 "gender" : "Female"
20                         },
21                         "value" : 24
22                 },
23                 {
24                         "_id" : {
25                                 "gender" : "Male"
26                         },
27                         "value" : 24
28                 }
29         ],
30         "timeMillis" : 2,
31         "counts" : {
32                 "input" : 10,
33                 "emit" : 10,
34                 "reduce" : 2,
35                 "output" : 2
36         },
37         "ok" : 1
38 }
39 >
  
  分别得到男生和女生的平均年龄



1 > db.runCommand({
2 ...     "mapReduce": "school.students",
3 ...     "map": function(){
4 ...                     emit({gender: this.gender}, this.age);
5 ...                 },
6 ...     "reduce": function(key, values){
7 ...                         var result = {"total": 0, "count": 0};
8 ...                         for(var i = 0; i < values.length; i++)
9 ...                             result.total += values;
10 ...                         result.count = values.length;
11 ...                         return result;
12 ...                    },
13 ...     "out": {inline: 1},
14 ...     "finalize": function(key, reducedValues){
15 ...                         return reducedValues.total/reducedValues.count;
16 ...                    }
17 ... })
18 {
19         "results" : [
20                 {
21                         "_id" : {
22                                 "gender" : "Female"
23                         },
24                         "value" : 22
25                 },
26                 {
27                         "_id" : {
28                                 "gender" : "Male"
29                         },
30                         "value" : 21.8
31                 }
32         ],
33         "timeMillis" : 55,
34         "counts" : {
35                 "input" : 10,
36                 "emit" : 10,
37                 "reduce" : 2,
38                 "output" : 2
39         },
40         "ok" : 1
41 }
42 >
  
  小技巧:关于自定义js函数
  在MongoDB中,可以通过db.system.js.save命令(其中system.js是一个存放js函数的collections)来创建并保存JavaScript函数,这样在就可以在MongoDB shell中重用这些函数。
  比如,下面两个函数是网上网友实现的



  SUM

  db.system.js.save( { _id : "Sum" ,
  value : function(key,values)
  {
   var total = 0;
   for(var i = 0; i < values.length; i++)
   total += values;
   return total;
  }});

  AVERAGE

  db.system.js.save( { _id : "Avg" ,
  value : function(key,values)
  {
   var total = Sum(key,values);
   var mean = total/values.length;
   return mean;
  }});

  
  通过利用上面两个函数,我们的"分别得到男生和女生的平均年龄"例子就可以通过以下方式实现。
  这个例子中,我们还特殊设置了"out"选项,把返回值存入了"average_age"这个collection中。



1 > db.runCommand({
2 ...     "mapReduce": "school.students",
3 ...     "map": function(){
4 ...                     emit({gender: this.gender}, this.age);
5 ...                 },
6 ...     "reduce": function(key, values){
7 ...                         avg = Avg(key, values);
8 ...                         return avg;
9 ...                    },
10 ...     "out": {"merge": "average_age"}
11 ... })
12 {
13         "result" : "average_age",
14         "timeMillis" : 30,
15         "counts" : {
16                 "input" : 10,
17                 "emit" : 10,
18                 "reduce" : 2,
19                 "output" : 2
20         },
21         "ok" : 1
22 }
23 >
  
  通过以下命令,我们可以看到新增的collection,并且查看里面的内容。



1 > show collections
2 average_age
3 school.students
4 system.indexes
5 system.js
6 >
7 > db.average_age.find()
8 { "_id" : { "gender" : "Female" }, "value" : 22 }
9 { "_id" : { "gender" : "Male" }, "value" : 21.8 }
10 >
11 > db.system.js.find()
12 { "_id" : "Sum", "value" : function (key,values)
13 {
14     var total = 0;
15     for(var i = 0; i < values.length; i++)
16         total += values;
17     return total;
18 } }
19 { "_id" : "Avg", "value" : function (key,values)
20 {
21     var total = Sum(key,values);
22     var mean = total/values.length;
23     return mean;
24 } }
25 >
  

总结
  通过这篇文章,介绍了MongoDB中count、distinct、group和mapReduce的基本使用。没有一次把所有的聚合操作都看完,聚合管道只能放在下一次了。
  Ps: 文章中使用的例子可以通过以下链接查看
  http://files.iyunv.com/wilber2013/aggregation.js
  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-83549-1-1.html 上篇帖子: 【摘录】入门MongoDB,你需要注意的4个技巧 下篇帖子: MongoDB在Windows2003上得安装配置及使用
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表