3

私はmongoでMapReduceを使い始めたばかりで、そのキーに関連付けられた値(その日のその候補の総投票数)を返す複合キー(日付、候補)を使用したクエリがあります。私が実際に望んでいるのは、投票数が各日付で累積されることです。つまり、各日付/候補キーの値は、その日付以前にその候補に投じられたすべての投票の合計です。

これが私の現在のコードです:

入力例:

{
    "interaction" : {
        "type" : "draft",
        "parameters" : {
            "value" : [
                {
                    "candidate" : 453510,
                    "votes" : 2
                },
                {
                    "candidate" : 325786,
                    "votes" : 2
                }
            ]
        }
    },
    "created_at" : 1360796255
}

地図:

var mapFn = function() {
  var right = function(str, n) {
    return str.substring(str.length, str.length - n);
  }
  var toDate = function(epochTimeSec) {
    var d = new Date(1000 * epochTimeSec);
    var yr = d.getYear() + 1900;
    var mn = d.getMonth() + 1;
    var dt = d.getDate();
    return '' + right('0000' + yr, 4) + '-' + right('00' + mn, 2) + '-' + right('00' + dt, 2);
  }

  for(var i=0; i<this.interaction.parameters.value.length; i++)
  {
    vote = this.interaction.parameters.value[i];
    var creationDate = toDate(this.created_at);
    var votedCandidate = vote.candidate;
    emit( {date: creationDate, candidate: votedCandidate}, { quantity: candidate.quantity } );
  }
}

減らす:

var reduceFn = function(key, values) {
  var sum = 0;
  values.forEach(function(doc) {
    sum += doc.quantity;
  });
  return { quantity: sum };
}

出力:

db.collection_mr.drop();
db.collection.mapReduce(mapFn, reduceFn, { out: 'collection_mr' } );

function printData(r) {
  print(r._id.date + ", " + r._id.candidate + ", " + r.value.quantity);
}

db.collection_mr.find().forEach(printData);

助けてくれてありがとう!

編集

コメントに応えて、入力例を含めました。明確にするために-上記のコードは、各日付に投じられたすべての投票の合計を正しく返します。ただし、日付ごとに、その日付以前に投じられたすべての投票の合計を返すように調整したいと思います。

Asya Kamskyに回答するには、これはアドホックベースで手動で実行されるため、入力日(現在の日付ではない場合があります)より前にすべてのデータを出力する必要があります。

4

1 に答える 1

4

この問題は、mapReduceの代わりに集約フレームワークを使用することでより簡単に解決できます。マニュアルからの抜粋:

MongoDB集約フレームワークは、map-reduceを使用せずに集約値を計算する手段を提供します。map-reduceは強力ですが、フィールド値の合計や平均化など、多くの単純な集計タスクでは必要以上に難しいことがよくあります。

このaggregateコマンドは、期待される結果を返します。

// target date as unix timestamp, example: 2013-02-28T23:59:59)
targetDate = Date.UTC(2013,1,28,23,59,59)/1000; // month -1 !!

db.xx.aggregate([
    // select documents created_at up to target date
    // note: this match uses an index, if defined on created_at
    { $match: { 
        "created_at": { $lte: targetDate }
    }},
    // unwind the "value" array
    { $unwind: "$interaction.parameters.value" },
    // group by candidates, sum up votes
    { $group: { 
        _id: "$interaction.parameters.value.candidate",
        votes: { $sum: "$interaction.parameters.value.votes" }
    }},
])

結果:

{
    "result" : [
        {
            "_id" : 325786,
            "votes" : 2
        },
        {
            "_id" : 453510,
            "votes" : 2
        }
    ],
    "ok" : 1
}
于 2013-03-25T16:10:13.447 に答える