我为以下数据结构创建了MapReduce作业:
{ "_id" : 1), "docid" : 119428, "term" : 5068, "score" : 0.198 }
{ "_id" : 2), "docid" : 154690, "term" : 5068, "score" : 0.21 }
{ "_id" : 3), "docid" : 156278, "term" : 5068, "score" : 0.128 }
{ "_id" : 4), "docid" : 700, "term" : "fire", "score" : 0.058 }
{ "_id" : 5), "docid" : 857, "term" : "fire", "score" : 0.133 }
{ "_id" : 6), "docid" : 900, "term" : "fire", "score" : 0.191 }
{ "_id" : 7), "docid" : 902, "term" : "fire", "score" : 0.047 }
我希望按照术语进行分组,然后计算平均分数。
这是我的MapReduce函数:
db.keywords.mapReduce(
function(){
emit( this.term, this.score );
},
function(key, values) {
rv = { cnt : 0, scoresum : 0};
rv.cnt = values.length; rv.scoresum = Array.sum(values);
return rv;
},
{
out: "mr_test" ,
finalize: function(key, reduceVal) {
reduceVal.avg = reduceVal.scoresum / reduceVal.cnt;
return reduceVal;
}
}
)
某些计算值是正确的:
{ "_id" : 5068, "value" : { "cnt" : 5, "scoresum" : 0.887, "avg" : 0.1774 } }
但是其他人正在创造一些奇怪的结构:
{ "_id" : "fire", "value" : { "cnt" : 333, "scoresum" : "[object
BSON][object BSON]0.176[object BSON]0.1010.181[object BSON][object .....BSON]
[object BSON][object BSON]0.1910.1710.2010.363[object BSON][object BSON]", "avg" : NaN } }
我的MapReduce功能出了什么问题?
答案 0 :(得分:5)
您错过了从documentation处理mapReduce操作的基本规则:
MongoDB可以为同一个密钥多次调用reduce函数。在这种情况下,该键的reduce函数的先前输出将成为该键的下一个reduce函数调用的输入值之一。
这意味着“两个”映射器和缩减器函数必须发出完全相同的结构并考虑该输入结构。问题当然是如果你在reduce函数中输出一个不同的结构,那么下次它返回到reduce时,输入的结构就不是预期的了。
这就是mapReduce如何处理同一个键的大数据,通过逐步减少,一遍又一遍,直到给定键只有一个结果:
db.keywords.mapReduce(
function(){
emit( this.term, { "cnt": 1, "score": this.score } );
},
function(key, values) {
rv = { "cnt" : 0, "score" : 0 };
values.forEach(function(value) {
rv.cnt += value.cnt;
rv.score += value.score;
});
return rv;
},
{
"out": "mr_test" ,
"finalize": function(key, reduceVal) {
reduceVal.avg = reduceVal.score / reduceVal.cnt;
return reduceVal;
}
}
)
但实际上使用.aggregate()
方法可以更有效地完成整个事情:
db.keywords.aggregate([
{ "$group": {
"_id": "$term",
"cnt": { "$sum": 1 },
"score": { "$sum": "$score" },
"avg": { "$avg": "$score" }
}},
{ "$out": "aggtest" }
])
甚至还有$avg
聚合累加器,它可以在一次传递中为你提供平均值。
与mapReduce不同,此处使用的运算符在本机代码中执行,而不是解释JavaScript。结果更快,并且通过数据的次数更少。
实际上$group
上只有一个传递,$out
只是集合的可选输出,而不是返回默认的游标。而游标是mapReduce的另一个优势。