2016-02-03 55 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相比,光標是另一個優勢。