原文 http://xiaofancn.iteye.com/blog/1184712
参考文章
http://www.mongodb.org/display/DOCS/MapReduce
http://www.rainsts.net/article.asp?id=1044
https://github.com/mongodb/mongo-java-driver/commit/fa003adc5db8a6c4efe0279c74973af15d99e6ed#diff-2
MapReduce是一个编程模型,封装了并行计算、容错、数据分布、负载均衡等细节问题。
输入是一个key-value对的集合,中间输出也是key-value对的集合,用户使用两个函数:Map和Reduce。
在使用MongoDb的mapreduce功能时,我找Java代码找半天,结果练了半天的Javascript代码。
MongoDb是通过解析“Javascript”代码来计算的。所有我们先用Javascript代码调通,再使用Java代码拼接使用这个MapReduce功能。
- db.runCommand(
- {
- mapreduce : <collection>,
- map : <mapfunction>,
- reduce : <reducefunction>
- [, query : <query filter object>]
- [, sort : <sort the query. useful optimization>] for
- [, limit : <number of objects to from collection>] return
- [, out : <output-collection name>]
- [, keeptemp: < | >] true false
- [, finalize : <finalizefunction>]
- [, scope : <object where fields go into javascript global scope >]
- [, verbose : ] true
- });
db.runCommand(
{
mapreduce : <collection>,
map : <mapfunction>,
reduce : <reducefunction>
[, query : <query filter object>]
[, sort : <sort the query. useful optimization>] for
[, limit : <number of objects to from collection>] return
[, out : <output-collection name>]
[, keeptemp: < | >] true false
[, finalize : <finalizefunction>]
[, scope : <object where fields go into javascript global scope >]
[, verbose : ] true
});
参数说明:
- mapreduce: 要操作的目标集合。
- map: 映射函数 (生成键值对序列,作为 reduce 函数参数)。
- reduce: 统计函数。
- query: 目标记录过滤。
- sort: 目标记录排序。
- limit: 限制目标记录数量。
- out: 统计结果存放集合 (不指定则使用临时集合,在客户端断开后自动删除)。
- keeptemp: 是否保留临时集合。
- finalize: 最终处理函数 (对 reduce 返回结果进行最终整理后存入结果集合)。
- scope: 向 map、reduce、finalize 导入外部变量。
- verbose: 显示详细的时间统计信息
先看看我们的文档结果
我使用上一篇文章插入数据 http://xiaofancn.iteye.com/blog/1163200
讲讲我们要实现的功能,就是按照名字name统计记录个数。
- D:\Program Files\mongodb>bin\mongo
- MongoDB shell version: 2.0.0
- connecting to: test
- > use test;
- switched to db test
- > map = function() {
- ... emit(this.name, {count:1});
- ... };
- function () {
- emit(this.name, {count:1});
- }
- > reduce = function(key, values) {
- ... var total = 0;
- ... var index =0;
- ... for(var i=0;i<values.length;i++){
- ... total += values[i].count;
- ... index = i;
- ... }
- ... return {count:total};
- ... };
- function (key, values) {
- var total = 0;
- var index = 0;
- for (var i = 0; i < values.length; i++) {
- total += values[i].count;
- index = i;
- }
- return {count:total};
- }
- > db.person.mapReduce(map, reduce, {out : "resultCollection"});
- {
- "result" : "resultCollection",
- "timeMillis" : 112,
- "counts" : {
- "input" : 10,
- "emit" : 10,
- "reduce" : 2,
- "output" : 2
- },
- "ok" : 1,
- }
- > db.resultCollection.find();
- { "_id" : "xiaofancn", "value" : { "count" : 3 } }
- { "_id" : "小樊", "value" : { "count" : 7 } }
D:\Program Files\mongodb>bin\mongo
MongoDB shell version: 2.0.0
connecting to: test
> use test;
switched to db test
> map = function() {
... emit(this.name, {count:1});
... };
function () {
emit(this.name, {count:1});
}
> reduce = function(key, values) {
... var total = 0;
... var index =0;
... for(var i=0;i<values.length;i++){
... total += values[i].count;
... index = i;
... }
... return {count:total};
... };
function (key, values) {
var total = 0;
var index = 0;
for (var i = 0; i < values.length; i++) {
total += values[i].count;
index = i;
}
return {count:total};
}
> db.person.mapReduce(map, reduce, {out : "resultCollection"});
{
"result" : "resultCollection",
"timeMillis" : 112,
"counts" : {
"input" : 10,
"emit" : 10,
"reduce" : 2,
"output" : 2
},
"ok" : 1,
}
> db.resultCollection.find();
{ "_id" : "xiaofancn", "value" : { "count" : 3 } }
{ "_id" : "小樊", "value" : { "count" : 7 } }
- map = function() {
- emit(this.name, {count:1});
- };
map = function() {
emit(this.name, {count:1});
};
此函数是形成下面的key-values结构的,emit就是指定key和value的,也是结果的数据结构。
xiaofancn [{count:1},{count:1},{count:1}]
由于name字段为xiaofancn的person有三个,所以形成三个{count:1}数组。
- reduce = function(key, values) {
- var total = 0;
- for(var i=0;i<values.length;i++){
- total += values[i].count;
- }
- return {count:total};
- };
reduce = function(key, values) {
var total = 0;
for(var i=0;i<values.length;i++){
total += values[i].count;
}
return {count:total};
};
reduce函数中参数key和map函数的emit指定的key(this.name)是同一个key(name),values就是map函数形成的values( [{count:1},{count:1},{count:1}])
经过reduce函数处理就形成了key和一个最终的 {count:3}数据结构。定义好的函数,需要MongoDB执行
- db.person.mapReduce(map, reduce, {out : "resultCollection"});
- db.resultCollection.find();
db.person.mapReduce(map, reduce, {out : "resultCollection"});
db.resultCollection.find();
db代表当前的数据库,person当前的文档,mapReduce调用函数,out:是指定输出的文档名称。
好了,会使用Javascript,使用Java就是拼接我们的Javascript代码了。
- public void MapReduce() {
- DBCollection personColl = mongoOperations.getCollection(mongoOperations
- .getCollectionName(Person.class));
- String map = "function() { emit(this.name, {count:1});}";
- String reduce = "function(key, values) {";
- reduce=reduce+"var total = 0;";
- reduce=reduce+"for(var i=0;i<values.length;i++){total += values[i].count;}";
- reduce=reduce+"return {count:total};}";
- String result = "resultCollection";
- MapReduceOutput mapReduceOutput = personColl.mapReduce(map,
- reduce.toString(), result, null);
- DBCollection resultColl = mapReduceOutput.getOutputCollection();
- DBCursor cursor= resultColl.find();
- while (cursor.hasNext()) {
- System.out.println(cursor.next());
- }
- }
public void MapReduce() {
DBCollection personColl = mongoOperations.getCollection(mongoOperations
.getCollectionName(Person.class));
String map = "function() { emit(this.name, {count:1});}";
String reduce = "function(key, values) {";
reduce=reduce+"var total = 0;";
reduce=reduce+"for(var i=0;i<values.length;i++){total += values[i].count;}";
reduce=reduce+"return {count:total};}";
String result = "resultCollection";
MapReduceOutput mapReduceOutput = personColl.mapReduce(map,
reduce.toString(), result, null);
DBCollection resultColl = mapReduceOutput.getOutputCollection();
DBCursor cursor= resultColl.find();
while (cursor.hasNext()) {
System.out.println(cursor.next());
}
}