“Map Reduce”减少函数发现值未定义

时间:2013-07-30 07:27:36

标签: mongodb mongo-java

我有以下系列:

{
  "_id" : ObjectId("51f1fcc08188d3117c6da351"),
  "cust_id" : "abc123",
  "ord_date" : ISODate("2012-10-03T18:30:00Z"),
  "status" : "A",
  "price" : 25,
  "items" : [{
      "sku" : "ggg",
      "qty" : 7,
      "price" : 2.5
    }, {
      "sku" : "ppp",
      "qty" : 5,
      "price" : 2.5
    }]
}

我的地图功能是:

 var map=function(){emit(this._id,this);}

出于调试目的,我按如下方式覆盖emit方法:

var emit = function (key,value){
  print("emit");
  print("key: " + key + "value: " + tojson(value));
  reduceFunc2(key, toJson(value));
}

和reduce函数如下:

var reduceFunc2 = function reduce(key,values){
  var val = values;
  print("val",val);
  var items = [];
  val.items.some(function (entry){
    print("entry is:::"+entry);
    if (entry.qty>5 && entry.sku=='ggg'){
      items.push(entry)
    }
  });
  val.items = items;
  return val;
}

但是当我将地图应用为:

var myDoc = db.orders.findOne({
  _id: ObjectId("51f1fcc08188d3117c6da351")
});
map.apply(myDoc);

我收到以下错误:

emit key: 51f1fcc08188d3117c6da351 value:
{
  "_id":" ObjectId(\"51f1fcc08188d3117c6da351\")",
  "cust_id":"abc123",
  "ord_date":" ISODate(\"2012-10-03T18:30:00Z\")",
  "status":"A",
  "price":25,
  "items":[
    {
       "sku":"ggg",
       "qty":7,
       "price":2.5
    },
    {
       "sku":"ppp",
       "qty":5,
       "price":2.5
    }
  ]
}

value:: undefined
Tue Jul 30 12:49:22.920 JavaScript execution failed: TypeError: Cannot call method 'some' of undefined

你可以发现它们是打印的值中的一个项目字段,它是数组类型的,即使这样抛出错误也无法调用一些未定义的,如果有人可以告诉我哪里出错了。

1 个答案:

答案 0 :(得分:2)

您的reduceFunc2功能出错:

var reduceFunc2 = function reduce(key,values){
  var val = values[0]; //values is an array!!!
  // ...
}

Reduce函数意味着将使用相同key发出的元素数组减少到单个文档。所以,它接受一个数组。你只发射一次每个键,所以它是一个带有单个元素的数组。

现在您将能够call your MapReduce normally

db.orders.mapReduce(map, reduceFunc2, {out: {inline: 1}});

您覆盖emit功能的方式已被破坏,因此您不应使用它。

<强>更新即可。如果只有一个与给定键相关联的文档,Mongo可能会跳过reduce操作,因为减少单个文档没有意义。

MapReduce的想法是,您将每个文档映射到一个键值对数组,以便在下一步中减少。如果有多个与给定密钥关联的值,Mongo会运行reduce操作以将其减少为单个文档。 Mongo希望reduce函数以与发出的元素相同的格式返回简化文档。这就是为什么Mongo可以为每个密钥运行reduce次操作的次数(最多为emits)。如果没有任何要减少的内容(例如,如果只有一个元素),也无法保证将reduce操作完全被调用。

因此,最好将map逻辑移到适当的位置。

更新2。无论如何,你为什么在这里使用MapReduce?您只需查询所需的文档:

db.orders.find({}, {
  items: {
    $elemMatch: {
      qty: {$gt: 5},
      sku: 'qqq'
    }
  }
})

更新3。如果你真的想用MapReduce来做,请试试这个:

db.runCommand({
  mapreduce: 'orders',
  query: {
    items: {
      $elemMatch: {
        qty: {$gt: 5},
        sku: 'ggg'
      }
    }
  },
  map: function map (){
    this.items = this.items.filter(function (entry) {
      return (entry.qty>5 && entry.sku=='ggg')
    });
    emit(this._id,this);
  },
  reduce: function reduce (key, values) {
    return values[0];
  },
  verbose: true,
  out: {
    merge: 'map_reduce'
  }
})