1

Map/Reduce に関する別の質問はまだありません。

次のようなコレクション " example" があります。

{
"userid" : "somehash",
"channel" : "Channel 1"
}

私の Map/Reduce 関数は次のようになります。

var map = function () {
    emit(this.channel, {user:this.userid, count: 1});
}

var reduce = function (key, values) {
    var result = {total:0, unique:0};
    var temp = [];
    values.forEach(function (value) {
        result.total += value.count;

        if (temp.indexOf(value.user) == -1) {
            temp.push(value.user);
        }
    });

    result.unique += temp.length;

    return result;
}

残念ながら、それは私にいくつかの本当に奇妙な結果をもたらします:

{ "_id" : "Channel 1", "value" : { "total" : NaN, "unique" : 47 } }
{ "_id" : "Channel 2", "value" : { "total" : NaN, "unique" : 12 } }
{ "_id" : "Channel 3", "value" : { "total" : 6, "unique" : 6 } }

また、「ユニーク」も正しい値ではないようですvalue.countnull私がやりたいことは、各チャネルのすべての値をカウントし、各ユーザーの一意の値を確認できるように計算することです。つまり、このコレクション内のドキュメント はexample複数回出現する可能性があります。私はすべての時間とユニークな時間を知りたい.

私はこのガイドに従いました: http://www.mongodb.org/display/DOCS/MapReduce#MapReduce-ReduceFunctionnullそして、なぜ私が顔に投げられるのかわからないのですか? 非常に奇妙ですが、この件に関して何か良いアイデアはありますか?

アドバイスとより良い知恵をありがとう。

4

2 に答える 2

4

これが発生する理由は、map/reduce が時々それ自体に対して起動するためです。つまり、reduce が reduce の結果に対して起動されます。しかし、reduce の結果にはフィールドがありませんcount。マップの出力と縮小の結果が同じ形式であることを常に確認する必要があります。詳細については、ドキュメントを参照してください。

編集これを修正する方法の簡単なデモを次に示します。

var map = function () {
    emit(this.channel, { user: [this.userid], count: 1 });
}

var reduce = function (key, values) {
    var result = { user: [], count: 0 };
    values.forEach(function (value) {
        result.count += value.count;

        value.user.forEach(function(usr) {
            if (result.user.indexOf( usr ) == -1) {
                result.user.push( usr );
            }
        });
    });

    return result;
}

これresult.user.lengthで、一意のユーザーが得られるはずです。テストはしませんでしたが、動作するはずです。

EDIT 2遅いはずです.indexOfが、かなり高価な機能です。2 つの map/reduce ジョブを作成すると、高速化できます。最初に、次のようにコレクションをマップ/縮小します。

var map = function() {
    // make a key unique per channel and userid
    emit( this.channel + '_' + this.userid,
        { count: 1, channel: this.channel }
    );
}

var reduce = function(key, values) {
    var result = { count: 0, channel: null };
    values.forEach(function( value ) {
        result.count += value.count;
        // Don't worry about these substitutions,
        // these values can't change anyway per key.
        result.channel = value.channel;
    });
    return result;
}

countこのコレクションでは、多数のユニークなエントリが得られます。合計数を取得するには、次のような結果に対して 2 番目の map/reduce を実行します。

var map = function() {
    // Note the key!!!
    emit( this.value.channel, { count: this.value.count } );
}

var reduce = function(key, values) {
    var result = { count: 0 };
    values.forEach(function( value ) {
        result.count += value.count;
    });
    return result;
}

これはずっと速くなるはずです。

于 2012-08-02T13:44:47.307 に答える
3

ドキュメントから:

reduce 関数は同じキーに対して複数回呼び出される可能性があるため、reduce 関数によって返されるオブジェクトの構造は、map 関数の発行された値の構造と同一である必要があります。

そうしないと、reduce は and の代わりに and を使用してオブジェクトtotalunique返しcountますuser。形式をユーザーごとのグループ化を表す形式に変更するか、ファイナライズ関数を使用します。

于 2012-08-02T13:54:56.763 に答える