2016-02-03 43 views
1

我创建了一个MapReduce工作下列数据结构:的MongoDB - MapReduce的敲定创建NaN值

{ "_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 } 

我想GROUP BY术语,然后计算平均分。

这是我的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函数?

回答

5

你已经错过了处理的MapReduce运算的基本规则从documentation

MongoDB中可以调用功能降低不止一次相同的密钥。在这种情况下,该键的reduce函数的前一个输出将成为该键的下一个reduce函数调用的输入值之一。

这意味着“两者”映射器和减速功能必须发射完全相同的结构,并考虑输入该结构。当然问题在于,如果您在reduce函数中输出不同的结构,那么下次它返回到reduce时,输入的结构不是预期的结果。

这是MapReduce的如何处理大数据对于同一个密钥,通过gradurally减少,一遍又一遍,直到只有一个给定键一个结果:

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相比,光标是另一个优势。