2

MongoDB の MapReduce で何か不足していることはわかっています。タグ頻度コレクションを構築しようとしていますが、機能が「同じ」ように見えても、異なる結果が得られmapますreduce

文書の例 (値 100、45 は忘れてください...私はそれらを使用していません):

{
    ...
    tags: [['Rock', 100], ['Indie Pop', 45], ...]
}

スカラー値の発行1:

var map = function () {
    if (this.tags) {
        this.tags.forEach(function (tag) {
            emit(tag[0], 1); // Emit just 1
        });
    }
};

var reduce = function (key, vals) { // Vals should be [1, ...]
    return vals.length; // Count the length of the array
};

db.tracks.mapReduce(map, reduce, { out: 'mapreduce_out' });
db.mapreduce_out.find().sort({ value: -1 }).limit(3);

出力は次のとおりです。

{ "_id" : "rubyrigby1", "value" : 9 }
{ "_id" : "Dom", "value" : 7 }
{ "_id" : "Feel Better", "value" : 7 }

オブジェクトの放出{ count: 1 }:

var map = function () {
    if (this.tags) {
         this.tags.forEach(function (tag) {
            emit(tag[0], { count: 1 }); // Emit an object
         });
    }
};

var reduce = function (key, vals) { // vals should be [{ count: 1 }, ...]
    var count = 0;

    vals.forEach(function (val) {
        count += val.count; // Accumul
    });

    return { count: count };
};

db.tracks.mapReduce(map, reduce, { out: 'mapreduce_out' });
db.mapreduce_out.find().sort({ 'value.count': -1 }).limit(3);

結果は異なり、「正しい」ように見えます。

{ "_id" : "rock", "value" : { "count" : 9472 } }
{ "_id" : "pop", "value" : { "count" : 7103 } }
{ "_id" : "electronic", "value" : { "count" : 5727 } }

最初のアプローチの何が問題になっていますか?

4

1 に答える 1

4

タグ「tagname」が付いた 1000 のドキュメントのコレクションを考えてみましょう。

for (var i = 0; i < 1000; i++) {
    db.collection.insert({tags: [['tagname']]});
}

適切な mapReduce を作成すると、出力が得られるはずです{"_id": "tagname", "count": 1000}。しかし、map 関数と reduce 関数を使用すると、1000 ではなく 101 のカウントが得られます。

その理由は、MongoDB が、結果のバッチが大きすぎてメモリに保持されないようにするために、中間結果を使用して reduce 関数を繰り返し呼び出すためです。これは、reduce に print ステートメントを入れることで実際に確認できます。

var reduce = function (key, vals) {
    print(vals);
    return vals.length; // Count the length of the array
};

印刷出力はサーバー ログに表示されます。reduce 関数は最初の 100 個の 1 で呼び出され、100 を返します。次に、MongoDB は、最初の reduce の出力と次の 100 個の 1 を使用して再度呼び出します。

reduce([100, 1, 1, ..., 1]) // 100 plus 100 more 1's

101 が返されるのは、それが配列の長さだからです。しかし、明らかに、配列の合計である 200 を返す必要があります。したがって、正しい結果を得るには、reduce 関数を次のように変更します。

reduce = function (key, vals) {
    var sum = 0;
    vals.forEach(function(val) { sum += val; });
    return sum;
}
于 2013-05-29T21:05:31.447 に答える