文档章节

MongoDB聚合(Map-Reduce)(二)

LUIS1983
 LUIS1983
发布于 2017/04/26 15:55
字数 1883
阅读 9
收藏 0

MapReduce要实现两个函数:Map和Reduce。Map函数调用emit(key,value)遍历一个或多个集合中所有的记录,进行分组(group by),然后将key与value传给Reduce函数进行处理,输出结果。

   
            (1)MapReduce使用自定义JavaScript函数执行map和reduce操作,所以是基于js引擎,单线程执行,效率不高,比Aggregation复杂,适合用做后台统计等。
            (2)MapReduce支持分片操作,可以进行拆分,分发到不同的机器上执行(多服务器并行做数据集合处理),然后再将不同的机器处理的结果汇集起 来,输出结果,。
(3)MapReduce能执行单一聚合的所有操作count、distinct、group,但group 在当数据量非常大的时候,处理能力就不太好,先筛选再分组,不支持 分片,对数据量有所限制,效率不高。

 
  MapReduce语法】
   
          先对MapReduce语法认识一下,各个参数有什么作用,接下去的操作理解起来会比较容易。
          

[sql] view plain copy

 在CODE上查看代码片派生到我的代码片

  1. <span style="font-size:18px;">  db.collection.mapReduce(  
  2.                          <map>,  
  3.                          <reduce>,  
  4.                          {  
  5.                            out: <collection>,  
  6.                            query: <document>,  
  7.                            sort: <document>,  
  8.                            limit: <number>,  
  9.                            finalize: <function>,  
  10.                            scope: <document>,  
  11.                            jsMode: <boolean>,  
  12.                            verbose: <boolean>,  
  13.                            bypassDocumentValidation: <boolean>  
  14.                          }  
  15.                        )</span>  


   参数说明:
    map:是JavaScript 函数,负责将每一个输入文档转换为零或多个文档,通过key进行分组,生成键值对序列,作为 reduce 函数参数。
    reduce:是JavaScript 函数,对map操作的输出做合并的化简的操作(将key-values变成key-value,也就是把values数组变成一个单一的值value)。
    out:reduce执行完,存放的集合,如果不指定集合,则使用默认的临时集合,在MapReduce的连接关闭后自动就被删除了。
out: { <action>: <collectionName>
[, db: <dbName>]
[, sharded: <boolean> ]
[, nonAtomic: <boolean> ] }
    query:过滤的条件,对符合条件的文档执行map函数。(query。limit,sort可以随意组合)。
    sort :对文档进行排序,sort和limit结合的sort排序参数(也是在发往map函数前给文档排序),可以优化分组机制。
    limit :发往map函数的文档数量的上限(要是没有limit,单独使用sort的用处不大)。
  finalize:可以对reduce输出结果再一次修改,跟group的finalize一样,不过MapReduce没有group的4MB文档的输出限制。
  scope:向map、reduce、finalize导入外部变量。
   verbose:是否包括结果信息中的时间信息,默认为fasle。
     "timing" : {
                "mapTime" : 0,
                "emitLoop" : 2,
                "reduceTime" : 0,
                "mode" : "mixed",
                "total" : 0
        }


 

   【map函数】

        map是JavaScript 函数,负责将每一个输入文档转换为零或多个文档,通过key进行分组,生成键值对序列,作为 reduce 函数参数。
  function() {
            emit(key, value);
        }
  

  key对文档进行分组,value是要统计的数据,value可以是JSON对象(emit只能容纳MongoDB的最大BSON文件大小的一半)。我们对订单的详细统计每个产品类型卖出了多少个。我们先通过 pnumber进行分组,然后在对 quantity相加 相当于select pnumber,sum(quantity) from item   group by pnumber

 

[sql] view plain copy

 在CODE上查看代码片派生到我的代码片

  1. <span style="font-size:18px;">db.item.insert( [  
  2.   {  
  3.    "quantity" : 2,  
  4.    "price" : 5.0,  
  5.    "pnumber" : "p003"  
  6.   },{  
  7.    "quantity" : 2,  
  8.    "price" : 8.0,  
  9.    "pnumber" : "p002"  
  10.   },{  
  11.    "quantity" : 1,  
  12.    "price" : 4.0,  
  13.    "pnumber" : "p002"  
  14.   },{  
  15.    "quantity" : 2,  
  16.    "price" : 4.0,  
  17.    "pnumber" : "p001"  
  18.   },{  
  19.    "quantity" : 4,  
  20.    "price" : 10.0,  
  21.    "pnumber" : "p003"  
  22.   },{  
  23.    "quantity" : 10,  
  24.    "price" : 20.0,  
  25.    "pnumber" : "p001"  
  26.   },{  
  27.    "quantity" : 10,  
  28.    "price" : 20.0,  
  29.    "pnumber" : "p003"  
  30.   },{  
  31.    "quantity" : 5,  
  32.    "price" : 10.0,  
  33.    "pnumber" : "p002"  
  34.   }  
  35. ])  
  36.   
  37.   
  38. > var map = function() { emit(this.pnumber,this.quantity)}  
  39. > var reduce=function(key,values){return {'pumber':key,'quantity':Array.sum(values)}}  
  40. > db.item.mapReduce( map,  
  41.                      reduce,  
  42.                     { out"map_reduce_data" }    
  43.                    )  
  44.  > db.map_reduce_data.find()  
  45. "_id" : "p001""value" : { "pumber" : "p001""quantity" : 12 } }  
  46. "_id" : "p002""value" : { "pumber" : "p002""quantity" : 8 } }  
  47. "_id" : "p003""value" : { "pumber" : "p003""quantity" : 20 } }</span>  


 

【query过滤的条件】

 

   对符合条件的文档将会执行map函数。(query。limit,sort可以随意组合), 我们对订单的详细的每次每种产品卖出的数量要大于5的并统计每个产品类型卖出了多少个。我们先通过 pnumber进行分组,然后在对 quantity相加 相当于select pnumber,sum(quantity) from item where quantity>5   group by pnumber

 

[sql] view plain copy

 在CODE上查看代码片派生到我的代码片

  1. <span style="font-size:18px;">> var map = function() { emit(this.pnumber,this.quantity)}  
  2. > var reduce=function(key,values){return {'pumber':key,'quantity':Array.sum(values)}}  
  3. > db.item.mapReduce( map,                        
  4.                        reduce,                    
  5.                        { query:{'quantity':{$gt:5}},  
  6.                           out"map_reduce_data" }      )  
  7. {  
  8.         "result" : "map_reduce_data",  
  9.         "timeMillis" : 5,  
  10.         "counts" : {  
  11.                 "input" : 2,  
  12.                 "emit" : 2,  
  13.                 "reduce" : 0,  
  14.                 "output" : 2  
  15.         },  
  16.         "ok" : 1  
  17. }  
  18. > db.map_reduce_data.find()  
  19. "_id" : "p001""value" : 10 }  
  20. "_id" : "p003""value" : 10 }</span>  



 【value是JSON对象】
  
     value可以是JSON格式,我们对订单的详细统计每个产品类型出现次数。我们先通过 pnumber进行分组,然后在对 quantity相加 相当于select pnumber,count(*) from item   group by pnumber。
  

[sql] view plain copy

 在CODE上查看代码片派生到我的代码片

  1. <span style="font-size:18px;">  >var map = function() {emit(this.pnumber,{count:1});}   
  2.   > var reduce=function(key,values){  
  3.     var count=0;     
  4.     values.forEach(function(val){ count+=val.count;});    
  5.     return {'pumber':key,"count":count};  
  6.   }  
  7. > db.item.mapReduce( map,  
  8.                      reduce,  
  9.                     { out"map_reduce_data" }    
  10.                    )  
  11. {  
  12.         "result" : "map_reduce_data",  
  13.         "timeMillis" : 6,  
  14.         "counts" : {  
  15.                 "input" : 10,  
  16.                 "emit" : 10,  
  17.                 "reduce" : 3,  
  18.                 "output" : 3  
  19.         },  
  20.         "ok" : 1  
  21. }  
  22. > db.map_reduce_data.find()  
  23. "_id" : "p001""value" : { "pumber" : "p001""count" : 2 } }  
  24. "_id" : "p002""value" : { "pumber" : "p002""count" : 3 } }  
  25. "_id" : "p003""value" : { "pumber" : "p003""count" : 5 } }</span>  


【emit多次的循环】

    可以对emit多次的循环,可以根据输入文档的项目字段中的元素的数量(键,值)多次调用:
  function() {
        this.items.forEach(function(item){ emit(key, value); });
     }

 

  我们对统计订单中对应的产品销售了多少个,我们先通过 pnumber进行分组,然后在对 quantity相加。

 

[sql] view plain copy

 在CODE上查看代码片派生到我的代码片

  1. <span style="font-size:18px;">       
  2.        
  3. db.orders.insert( [  
  4. {  
  5.  "onumber" : "001",  
  6.  "item" : [{  
  7.    "quantity" : 2,  
  8.    "price" : 5.0,  
  9.    "pnumber" : "p003"  
  10.   },{  
  11.    "quantity" : 2,  
  12.    "price" : 8.0,  
  13.    "pnumber" : "p002"  
  14.   }]  
  15. },{  
  16.  "onumber" : "002",  
  17.  "item" : [{  
  18.    "quantity" : 1,  
  19.    "price" : 4.0,  
  20.    "pnumber" : "p002"  
  21.   },{  
  22.    "quantity" : 2,  
  23.    "price" : 4.0,  
  24.    "pnumber" : "p001"  
  25.   },{  
  26.    "quantity" : 4,  
  27.    "price" : 10.0,  
  28.    "pnumber" : "p003"  
  29.   }]  
  30. },{  
  31.  "onumber" : "003",  
  32.  "item" : [{  
  33.    "quantity" : 10,  
  34.    "price" : 20.0,  
  35.    "pnumber" : "p001"  
  36.   },{  
  37.    "quantity" : 10,  
  38.    "price" : 20.0,  
  39.    "pnumber" : "p003"  
  40.   }]  
  41. },{  
  42.  "onumber" : "004",  
  43.  "item" : [{  
  44.    "quantity" : 5,  
  45.    "price" : 10.0,  
  46.    "pnumber" : "p002"  
  47.   }]  
  48. }  
  49. ])    
  50.   
  51. > var map = function() { this.item.forEach(function(it){ emit(it.pnumber,it.quantity); })}  
  52. > var reduce=function(key,values){return {'pumber':key,'quantity':Array.sum(values)}}  
  53. > db.orders.mapReduce( map,                        
  54.                        reduce,                    
  55.                        { out"map_reduce_data" }      )  
  56. {  
  57.         "result" : "map_reduce_data",  
  58.         "timeMillis" : 51,  
  59.         "counts" : {  
  60.                 "input" : 4,  
  61.                 "emit" : 8,  
  62.                 "reduce" : 3,  
  63.                 "output" : 3  
  64.         },  
  65.         "ok" : 1  
  66. }  
  67. > db.map_reduce_data.find()  
  68. "_id" : "p001""value" : { "pumber" : "p001""quantity" : 12 } }  
  69. "_id" : "p002""value" : { "pumber" : "p002""quantity" : 8 } }  
  70. "_id" : "p003""value" : { "pumber" : "p003""quantity" : 16 } }</span>  


> var map = function() { this.item.forEach(function(it){ emit(it.pnumber,it.quantity); })}
也可以这样写
> var map = function() { for(var i=0;i<this.item.length;i++){ var it=item[i]; emit(it.pnumber,it.quantity); }}

【reduce函数

 

  reduce是JavaScript 函数,对map操作的输出做合并的化简的操作(将key-values变成key-value,也就是把values数组变成一个单一的值value)。  

    function(key, values) {
       ...
       return result;
    }


  values:值参数是一个数组,返回对象的类型必须与由map函数发出的值的类型相同。
  reduce函数应该交换:即如果中元素的顺序不影响reduce函数的输出。
            reduce( key, [ A, B ] ) == reduce( key, [ B, A ] ) 

    对map操作的输出做合并的化简的操作(将key-values变成key-value,也就是把values数组变成一个单一的值value),我们对订单的详细统计每个产品类型卖出了多少个和每种产品出现次数。我们先通过 pnumber进行分组,然后在对 quantity相加 相当于select pnumber,count(*),sum(quantity) from item   group by pnumber
  

[sql] view plain copy

 在CODE上查看代码片派生到我的代码片

  1. <span style="font-size:18px;">>var map = function() {   
  2.      var value={count:1, quantity:this.quantity};  
  3.     emit(this.pnumber,value);  
  4.   }   
  5. >var reduce=function(key,values){   
  6.                      var reducedVal = { count: 0, quantity: 0 };  
  7.                      for (var i = 0; i < values.length; i++) {  
  8.                          reducedVal.count += values[i].count;  
  9.                          reducedVal.quantity += values[i].quantity;  
  10.                      }  
  11.                      return reducedVal;  
  12.             }  
  13.   
  14.   
  15. {  
  16.         "result" : "map_reduce_data",  
  17.         "timeMillis" : 7,  
  18.         "counts" : {  
  19.                 "input" : 10,  
  20.                 "emit" : 10,  
  21.                 "reduce" : 3,  
  22.                 "output" : 3  
  23.         },  
  24.         "ok" : 1  
  25. }  
  26. > db.map_reduce_data.find()  
  27. "_id" : "p001""value" : { "count" : 2, "quantity" : 12 } }  
  28. "_id" : "p002""value" : { "count" : 3, "quantity" : 8 } }  
  29. "_id" : "p003""value" : { "count" : 5, "quantity" : 20 } }</span>  


【MapReduce 执行结果信息】

  我们执行MapReduce输出结果时,有打印MapReduce信息

[sql] view plain copy

 在CODE上查看代码片派生到我的代码片

  1. <span style="font-size:18px;">   
  2. {  
  3.         "result" : "map_reduce_data",  
  4.         "timeMillis" : 7,  
  5.         "counts" : {  
  6.                 "input" : 10,  
  7.                 "emit" : 10,  
  8.                 "reduce" : 3,  
  9.                 "output" : 3  
  10.         },  
  11.         "ok" : 1  
  12. }  
  13. </span>  


result:reduce执行完,存放的集合,如果不指定集合,则使用默认的临时集合,在MapReduce的连接关闭后自动就被删除了。我们这边有指定集合的名称map_reduce_data。
timeMillis:执行MapReduce所花费的时间(毫秒)。
input:满足条件被发送到map函数的文档个数。
emit:在map函数中emit被调用的次数,也就是所有集合中的数据总量。
ouput:输出到集合中的结果文档个数。
ok:是否成功,成功为1。

本文转载自:

LUIS1983
粉丝 4
博文 72
码字总数 79567
作品 0
深圳
私信 提问
MongoDB Map-reduce 如何避免全局锁

在MongoDB中驱动分析的两个重要特性是: 聚合 Map-Reduce 一般来说大多数的聚合架构并不需要任何全局写锁,但是当把reducer(规约)结果写到现存的或新的结果集时,Map-Reduce则需要全局写锁。...

oschina
2014/04/06
1K
1
浅尝辄止MongoDB:高级查询

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wzy0623/article/details/82983209 目录 一、全文检索 1. 建立索引 2. 执行搜索 二、聚合 三、MapReduce 1. ...

wzy0623
2018/10/09
0
0
linux-mongodb你会尝试一下吗?

  简介   MongoDB是C++写一个开源的NoSQL数据库,是基于分布式文件存储,在高并发的情况下,可以增加更多的节点来保证服务的正常运行。MongoDB为WEB应用提供了可扩展的高性能的数据存储解...

linux运维菜
2018/09/11
0
0
Yum安装MongoDB及数据库管理

MongoDB简介 MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统。在高负载的情况下,添加更多的节点,可以保证服务器性能。MongoDB旨在为WEB应用提供可扩展的高性能数据...

cchenyz
2018/07/13
0
0
MongoDB高级一点点的操作

在MongoDB数据库中常见的聚合操作有:count,distinct,group,mapReduce。现在将它们一一的记录下来: 一、count操作 这个操作顾名思义就是达到统计的效果啦,用来统计符合某一种查询条件的...

BravoZu
2014/01/21
1K
0

没有更多内容

加载失败,请刷新页面

加载更多

安全组和云防火墙的区别

前言 熟悉云平台的朋友可能都会注意到这样一个事情:无论公有云还是私有云,创建虚拟机的时候都需要选择安全组,来对虚拟机进行安全防护;有的云平台在VPC里,还能选择防火墙,ZStack在3.6版...

ZStack社区版
28分钟前
2
0
教育性app开发的重要性和好处

在这个精通技术的世界中,流行的app主导着无聊的教育系统。当我们将技术和教育结合在一起时,它将带来当代以及强大的学习资源。因此,将教育移动app集成到您的学习过程中,并根据自己的信念把...

a429011717
29分钟前
3
0
IE6/7/8如何兼容CSS3属性

本文转载于:专业的前端网站➩IE6/7/8如何兼容CSS3属性 最近在工作中总是要求IE8兼容CSS3属性,在网上搜了搜主要是引入了一个htc文件(ie-css3.htc或者PIE.htc。个人认为这两个文件的作用差不...

前端老手
44分钟前
3
0
手把手教你ALLEGRO的约束规则的设置教程!

约束规则的设置 分三步, 定义规则(一、基本约束规则设置:1、线间距设置;2、线宽设置;3、设置过孔;4、区域约束规则设置;5、设置阻抗;6、设置走线的长度范围;7、设置等长:7.1、不过电阻的NET 等...

demyar
46分钟前
4
0
完美解决H5滚动滑动穿透方案:不使用系统滚动

网上有很多黑科技解决这个问题,都不是从根本去解决,例如通过js控制弹出时html加上position:fixed; 弹窗关闭后再去掉该样式,总觉得不太对,像是打补丁。 今天终于找到了滚动穿透的原因和完...

未来cc
50分钟前
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部