0

私は次のコレクションを持っています:

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

それらは、印刷された値の項目フィールドであり、配列の種類であることがわかります。それでも、誰かが私が間違っている場所を知ることができれば、undefinedでいくつかを呼び出すことができないというエラーがスローされます。

4

1 に答える 1

2

関数にエラーがありますreduceFunc2:

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

key同じで発行された要素の配列を 1 つのドキュメントに減らすための関数を減らします。したがって、配列を受け入れます。各キーを 1 回だけ発行しているため、単一の要素を持つ配列です。

これで、通常どおりに呼び出すMapReduceことができるようになります:

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

関数をオーバーライドする方法emitが壊れているため、使用しないでください。

更新します。単一のドキュメントを削減しても意味がないため、指定されたキーに関連付けられているドキュメントが 1 つしかない場合、Mongo は削減操作をスキップすることがあります。

MapReduce各ドキュメントをキーと値のペアの配列にマップして、次のステップで削減するという考え方です。指定されたキーに複数の値が関連付けられている場合Mongo、操作を実行reduceしてそれを 1 つのドキュメントに減らします。Mongo はreduce、関数が出力された要素と同じ形式で縮小されたドキュメントを返すことを期待しています。そのため、Mongo はreduceキーごとに何度でも操作を実行できます (最大 の回数までemits)。reduceまた、削減するものが何もない場合 (たとえば、要素が 1 つしかない場合) に操作が呼び出されるという保証もありません。

したがって、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'
  }
})
于 2013-07-30T07:56:54.767 に答える