在NodeJs中使用MongoDB中的MapReduce

来源:互联网 发布:java多线程用法 编辑:程序博客网 时间:2024/06/05 18:06

   MapReduce是一个编程模型,封装了并行计算、容错、数据分布、负载均衡等细节问题。

   输入是一个key-value对的集合,中间输出也是key-value对的集合,用户使用两个函数:Map和Reduce。

   主要用来进行统计,有点像sql中的group  by但是比groupby功能更强大。

官网示例:http://mongodb.github.com/node-mongodb-native/api-generated/collection.html?highlight=mapreduce#mapReduce

   假设我们数据库现在存在四个数据如下:


我用NodeJs写了一段代码,如下:


得到的结果如下:


在NodeJs中使用MongoDB中的MapReduce
其中的_id就是对应的上面写的map中的key也就是age字段,

从统计结果可以看出gender为man的记录中age为20的有两个,age为23的有1个。

参数说明:

   mapreduce:要操作的目标集合。

   map: 映射函数 (生成键值对序列,作为 reduce函数参数)。

   reduce:统计函数。

   query: 一个筛选条件,只有满足条件的行才会加入mapreduce集合,而这个筛选过程是先于整个mapreduce流程而执行的

   sort: 和query结合的sort排序参数(需要排序的参数必须先建立索引:db.wiki.ensureIndex({title:1}))

   limit:限制目标记录数量。

   out: 结果输出的collection的名字 (不指定则使用临时集合,在客户端断开后自动删除。replace、merge、reduce、inline)。参考:http://www.mongodb.org/display/DOCS/MapReduce

   keeptemp: true或false,表明结果输出到的collection是否是临时的,如果为true,则会在客户端连接中断后自动删除,如果你用的是MongoDB的mongo客户端连接,那必须exit后才会删除。如果是脚本执行,脚本退出或调用close会自动删除结果collection

   finalize: 和map,reduce一样是一个函数,它可以在reduce得出一个结果后再对key和value进行一次计算并返回一个最终结果。参考上面的示例

   scope: 设置参数值,在这里设置的值在map,reduce,finalize函数中可见

   verbose:在执行过程中的调试信息

使用out参数时要注意当使用inline时文档的数据不能太大,否则返回结果将出现错误,官网说明如下: Notethat this option is possible only when the result set fits withinthe 16MB limit of a single document.


如果verbose:true的时候callback返回中多一个stats字段



返回结果参数如下

  • result:储存结果的collection的名字
  • input:满足条件的数据行数
  • emit:emit调用次数,也就是所有集合中的数据总量
  • ouput:返回结果条数
  • timeMillis:执行时间,毫秒为单位
  • ok:是否成功,成功为1
  • err:如果失败,这里可以有失败原因,原因一般比较模糊,作用不大

使用scope参数我们可以在map、reduce、finalize中引入外界函数或变量:

参考官网教程:mapReduce

在NodeJs中使用MongoDB中的MapReduce



注意:我在使用中发现当我们map中的value为单个字符,而reduce返回的为json的时候会出现异常,且reduce中的value值中字段的数目不相等也会出现异常。所以:

   1、如果reduce返回的是json格式数据的时候map中的value也得是json格式的

   2、map和reduce中的value值中字段的数目必须相等


如上图中的情况,得到的结果将出现异常

在NodeJs中使用MongoDB中的MapReduce


下面我们看一个例子巩固下上面的知识:

我先往数据库插入一些数据:health/routes/add.js

数据结构是这样的:

{"name":"张三","age":58,"gender":"男","birthday":"1905-12-09","mobile":"15815222297","note":"北京"}
我们现在来统计下刚刚插入的数据的姓名中一共有多少个姓,每个姓有多少人

所以统计的结构应该是这样的:key就是姓,然后value中是统计的人数


先写map函数:(在map中我们取出每个collections名字中的姓)

var map = function () {    vara=this.name.substring(0,1);    emit(a, {count:1}); //map时姓相同的为一组传给reduce,传的值中key为name中的姓};

然后再写一个reduce处理函数:(统计每个姓的人数)

var reduce = function (key, values) {    var x = 0;    values.forEach(function (v){       x += v.count;    });    return{count:x};//统计出每个姓一共有多少人};

然后我们用finalize函数根据统计的结果給每个姓写一个comment:

functionfinalize(key,rval){   returncomment(rval);}

其中comment函数是我从外界引入的所以需要在scope中申明一下 

function comment(input){   if(input.count>100){      input.comment="该姓人数较多!"   }   return input;}

然后开始执行统计函数: health/routes/mapReduces

  testProvider.mapReduce(map, reduce, {out:{replace:'tempCollection'},                               scope:{comment:newCode(comment.toString())},                                                       finalize:finalize,verbose:true},                                                       function (err,collection, stats) {})

然后得出统计结果和统计时的资源使用情况:




原创粉丝点击