4

この問題でmongoのヘルプが必要です:コレクションにコレクション統計(UserId、EventId、Count、Date)がありますデータです

UserID | EventId | カウント| 日にち

1     |     1    |    10  |   01.01.2012
1     |     1    |   15   | 01.02.2012
1     |     2    |   12   | 01.01.2012
2     |     1    |    5   |   01.01.2012
3     |     2    |    10  |   01.01.2012

この結果が必要です

UserId | Count_Event_1 | Count_EventId_2

  1     |     25          |      12         
  2     |     5           |      0
  3     |     0           |      10

MapReduceなしでMongoでその可能性はありますか?助けてくれてありがとう。

4

2 に答える 2

4

aggregate()!を使用して作業を行う方が簡単で、はるかに高速です。

を使用し$projectて各イベントのカウンター フィールドを作成し、イベントが一致する場合はドキュメントからカウントを入力し、そうでない場合はゼロにします。次に$group、ユーザー ID ごとに、すべてのイベント カウンターを合計します。

説明のために、最初に、例の 2 つの異なるイベント (1 と 2) に対してこれがどのようにハードコーディングされているかを示しましょう。

db.xx.aggregate([
    { $project: { userid:1,
                  cnt_e1: { $cond: [ { $eq: [ "$event", 1 ] }, "$count", 0 ] },
                  cnt_e2: { $cond: [ { $eq: [ "$event", 2 ] }, "$count", 0 ] },
    } },
    { $group: { _id: "$userid", cnt_e1: { $sum: "$cnt_e1" }, cnt_e2: { $sum: "$cnt_e2" } } },  
    { $sort: { _id: 1 } },
])

指定されたコレクションの場合:

> db.xx.find({},{_id:0})
{ "userid" : 1, "event" : 1, "count" : 10 }
{ "userid" : 1, "event" : 1, "count" : 15 }
{ "userid" : 1, "event" : 2, "count" : 12 }
{ "userid" : 2, "event" : 1, "count" : 5 }
{ "userid" : 3, "event" : 2, "count" : 10 }

結果は次のとおりです。

{
    "result" : [
        {
            "_id" : 1,
            "cnt_e1" : 25,
            "cnt_e2" : 12
        },
        {
            "_id" : 2,
            "cnt_e1" : 5,
            "cnt_e2" : 0
        },
        {
            "_id" : 3,
            "cnt_e1" : 0,
            "cnt_e2" : 10
        }
    ],
    "ok" : 1
}

可変イベントに対してこれを行うには、プロジェクションとグループ化を生成する必要があります。コマンドを使用して、考えられるすべてのイベントの配列を取得しますdistinct()(「イベント」にインデックスを定義したい場合があります)。次に、配列をループして、2 つのステートメントを JSON オブジェクトとして作成します。

project = {};
project.$project = {};
project.$project.userid = 1;

group = {};
group.$group = {};
group.$group._id = '$userid'

events = db.xx.distinct( "event" );
events.forEach( function( e ) {
    field = "cnt_e" + e;

    eval("project.$project." + field + " = {}");
    eval("project.$project." + field + ".$cond = []");
    eval("project.$project." + field + ".$cond[0] = {}");
    eval("project.$project." + field + ".$cond[0].$eq = []");
    eval("project.$project." + field + ".$cond[0].$eq[0] = '$event'");
    eval("project.$project." + field + ".$cond[0].$eq[1] = " + e );
    eval("project.$project." + field + ".$cond[1] = '$count'");
    eval("project.$project." + field + ".$cond[2] = 0");

    eval("group.$group." + field + " = {}");
    eval("group.$group." + field + ".$sum = '$" + field + "'");
});

//printjson(project);
//printjson(group);

db.xx.aggregate([
    project,
    group,
    { $sort: { _id: 1 } },
])

そして、結果は上記と同じです。

注:上記は数値イベントで機能します。それらが文字列の場合、ジェネレーターを適応させる必要があります。

一見すると、これは @Philipp の mapReduce よりも複雑に見えるかもしれません。ただし、それは各ユーザーのすべてのイベントを返すわけではなく、カウントがあるイベントのみを返します。垂直方向から水平方向への完全なマッピングを行うには、map と reduce 関数も生成する必要があります。

aggregate() の詳細については、http://docs.mongodb.org/manual/aggregation/を参照してください。

于 2013-04-07T09:10:16.183 に答える
0

MapReduce操作でこれを行う必要があります。

マップ関数は次のようになります: (未テスト!):

var mapFunction = function() {
                   var ret = {};
                   ret["Count_Event_" + this.EventId] = this.Count;
                   emit(this.UserId, ret);
               };

これにより、UserId と、値としてカウントを持つ単一の別の名前の属性を持つオブジェクトで構成される一連のペアが発行されます。

次に、reduce 関数は結果を 1 つに結合します (未テスト - 存在しないプロパティをインクリメントできるかどうかはわかりません。今はテストできません)。

var reduceFunction = function(UserId, values_array) {
                   var ret = {};

                   for (var i = 0; i < values_array.length; i++) {
                       var values = values_array[i];
                       for (key in values) {
                           ret[key] += values[key]; // Can you increment a non-existing attribute? Not sure, try it, please.
                       }
                   }                       

                   return ret;
               };

次に、これを次のように呼び出します。

 db.yourCollection.mapReduce(
                 mapFunction,
                 reduceFunction,
                 out: { inline: 1 }
               )

この行out: { inline: 1 }は結果をコンソールに出力します。通常、MapReduce を使用して、結果を含む新しいコレクションを作成します。詳細については、ドキュメントを参照してください。

于 2013-03-19T12:26:50.237 に答える