【问题标题】:mongoDB - MapReduce finalize creates NaN valuesmongoDB - MapReduce finalize 创建 NaN 值
【发布时间】:2016-02-03 09:51:11
【问题描述】:

我为以下数据结构创建了一个 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 函数有什么问题?

【问题讨论】:

    标签: mongodb mapreduce mongodb-query aggregation-framework


    【解决方案1】:

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

    【讨论】:

      猜你喜欢
      • 2011-04-22
      • 2017-10-02
      • 2021-04-07
      • 1970-01-01
      • 1970-01-01
      • 2015-09-27
      • 2014-09-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多