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写了一段代码,如下:
得到的结果如下:
其中的_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
注意:我在使用中发现当我们map中的value为单个字符,而reduce返回的为json的时候会出现异常,且reduce中的value值中字段的数目不相等也会出现异常。所以:
1、如果reduce返回的是json格式数据的时候map中的value也得是json格式的
2、map和reduce中的value值中字段的数目必须相等
如上图中的情况,得到的结果将出现异常
下面我们看一个例子巩固下上面的知识:
我先往数据库插入一些数据: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) {})
然后得出统计结果和统计时的资源使用情况: