在MongoDB中使用MapReduce
MapReduce是聚合工具的明星。Count、distinct、group能做的上述事情,MapReduce都能做。它是一个能轻松并行化到多个服务器的聚合方法。它会拆分问题,再将各个部分发送到不同的机器上,让每台机器都完成一部分。当所有的机器都完成的时候,再把结果汇集起来形成最终完整的结果。
MapReduce的步骤。 Map->Shuffle->Reduce
Map:将操作映射到集合中的每个文档。这个操作要么“无作为”,要么产生“一些键和几个值”。
Shuffle: 将Map的结果按照键进行分组,并将产生的键值组成列表放到对应的键中。
Reduce: 将每个键对应的列表中的值化简成一个单值。 这个值被返回,然后接着Shuffle,直到每个键的列表中只有一个值为止,这个值就是最终的结果。
MapReduce的函数类似这样:
db.runCommand({“mapReduce” : ”集合名” , ”map” : “map函数” , ”reduce” : ”reduce函数”});
不同语言有对应的接口:比如java中,可以直接调用executeCommad()方法,或者在集成SpringMongo后,调用mapReduce()接口。
调用示例(SpringMongoDB方式):
searchMongoTemplate.mapReduce(query, collection, map, reduce,
option.outputCollection(out), Map.class);
searchMongoTemplate 是一个MongoOperations对象;
query是一个Query的对象, mapReduce操作只对查询结果进行;
map类似于"classpath:mapReduce/mapDayDetail.js" ,是map的js文件存放地址,或者可以直接将map的js文件读成String;
reduce的意义类似map,对应reduce的js文件;
out: 是输出文件的集合名称
Map.class: 输出的对象类型,可以是自定义对象
Map.js与Reduce.js举例说明:
Map.js
function() {
var renewTime = this.renewTime;
var date = new Date(renewTime);
var time = date.getFullYear()*10000+(date.getMonth()+1)*100+date.getDate();
emit({
"year" : date.getFullYear(),
"month" : date.getMonth() + 1,
"day" : date.getDate(),
"level" : this.productLevel,
"time" : time
}, {
'count' : 1
});
emit({
"year" : date.getFullYear(),
"month" : date.getMonth() + 1,
"day" : date.getDate(),
"node" : this.nodeName,
"time" : time
}, {
'count' : 1
});
emit({
"year" : date.getFullYear(),
"month" : date.getMonth() + 1,
"day" : date.getDate(),
"satellite" : this.satelliteName,
"time" : time
}, {
'count' : 1
});
};
Map.js的脚本作用于输入的每一个文档,this指代当前文档,用this.属性名的方式获取文档中的属性,用emit方式返回键值对结果。本例中,对于每个文档,返回三个结果,emit函数中,第一个参数是key,第二个参数是value.
Reduce.js
function(key, emits) {
var result = {
"count" : 0
};
for (var i in emits) {
result.count += emits[i].count;
}
return result;
}
传递给reduce的参数可以是map的结果,也可以是reduce自己的结果。所以reduce必须能被反复调用,reduce返回的结果必须能作为reduce第二个参数中的一个元素。Reduce中只需要返回value即可,返回的key默认是传进来的key.