1

何百万ものドキュメントを処理し、さまざまな方法でレポートできるシステムを設計しています。mongoDb map\reduce タスクは、私が実装しようとしているものです (現在、いくつかの調査を行っています)。非常に基本的な文書構造は

db.test.insert(
{
        "_id" : ObjectId("4f6063601caf46303c36eb27"),
        "verbId" : NumberLong(1506281),
        "sentences" : [
                {
                        "sId" : NumberLong(2446630),
                        "sentiment" : 2,
                        "categories" : [
                                NumberLong(3257),
                                NumberLong(3221),
                                NumberLong(3291)
                        ]
                },
                {
                        "sId" : NumberLong(2446631),
                        "sentiment" : 0,
                        "categories" : [
                                NumberLong(2785),
                                NumberLong(2762),
                                NumberLong(2928),
                                NumberLong(2952)
                        ]
                },
                {
                        "sId" : NumberLong(2446632),
                        "sentiment" : 0,
                        "categories" : [
                                NumberLong(-2393)
                        ]
                },
                {
                        "sId" : NumberLong(2446633),
                        "sentiment" : 0,
                        "categories" : [
                                NumberLong(-2393)
                        ]
                }
        ]
})

各ドキュメントに文が含まれるように、異なるカテゴリに属する​​可能性があります。私が取得しようとしているレポートは、カテゴリ内の文の数です (逐語の割合)。

次の map-reduce ジョブをfinalizeメソッドで実行して、さまざまな平均をカウントしています。

var map = function() {
        var docCategories = new Array();
        var catValues = new Array();
        for (var i = 0; i < this.sentences.length; i++) { //iterate over sentences.
            sentence = this.sentences[i];
            for (var j = 0; j < sentence.categories.length; j++) {//iterate over categories
                catId= sentence.categories[j].toNumber();
                if (docCategories.indexOf(catId) < 0) {
                    docCategories.push(catId);
                    catValues.push({sentiment : sentence.sentiment, sentenceCnt: 1});
                } else {
                    categoryIdx = docCategories.indexOf(catId);
                    catValue = catValues[categoryIdx];
                    catValue.sentiment = catValue.sentiment + sentence.sentiment;
                    catValue.sentenceCnt = catValue.sentenceCnt + 1;
                }
            }

        }
        totalCount++; //here we do try to count distinctCases see scope.
        for (var i = 0; i < docCategories.length; i ++) {
            emit(docCategories[i], {count: 1, sentenceCnt: catValues[i].sentenceCnt, sentiment: catValues[i].sentiment, totalCnt : totalCount});
        }

    };

var reduce = function(key, values) {
    var res = {count : 0, sentenceCnt : 0, sentiment : 0};
    for ( var i = 0; i < values.length; i ++ ) {
        res.count += values[i].count;
        res.sentenceCnt += values[i].sentenceCnt;
        res.sentiment += values[i].sentiment;
    }

    return res;
};

var finalize = function(category, values) {
    values.sentimentAvg = values.sentiment / values.sentenceCnt; 
    values.percentOfVerbatim = values.count / totalCount //scope variable (global)
    return values;
};


var res = db.runCommand( { mapreduce:'test',
                  map:map,
                  reduce:reduce,
                  out: 'cat_volume',
                  finalize:finalize,
                  scope:{totalCount : 0},
                });

ここで最も興味深い部分は、totalCount を使用していることです。つまり、出力している verbatim の数を数えます。totalCount はスコープ (グローバル) 変数です。1 つの mongoDb インストールではすべてがうまくいきましたが、シャード インスタンスに移動すると、 percentOfVerbatimの「無限」が得られます。

実際にはその場合、totalCountは単にdb.test.count() (ドキュメントの数) になりますが、将来的には、ドキュメントをカウントするためのさまざまな条件を追加する予定です。db は非常に重いため、他のクエリを実行することは非常に望ましくありません。

マルチインスタンス mongodb インストールでグローバル (スコープ) 変数を使用する他の方法はありますか? または、何か他のものを使用する必要がありますか?

4

2 に答える 2

2

スコープ変数はシャード間で共有されません。グローバル定数として扱うことができます。値の更新は、異なるシャードで実行されている map または reduce 関数には表示されません。

于 2012-03-15T19:06:22.560 に答える
0

最後に、発行しているドキュメントの数を数える方法を見つけました。私にとってうまくいった唯一の方法は、documentIdを発行し、IDをreduceの配列に入れることです。クライアント側(私はJavaプログラムを書いています)では、すべての個別のIDを数えなければなりません。だから、マップをしている間、私は放出します

emit(docCategories[i], {verbIds : [this.verbId.toNumber()], count: 1, sentenceCnt: catValues[i].sentenceCnt, sentiment: catValues[i].sentiment, totalCnt : totalCount});

Reduce 関数は次のとおりです。

var reduce = function(key, values) {
    var res = {verbIds : [], count : 0, sentenceCnt : 0, sentiment : 0};
    for ( var i = 0; i < values.length; i ++ ) {
//      res.verbIds = res.verbIds.concat(values[i].verbIds); //works slow
        for ( var j = 0; j < values[i].verbIds.length; j ++ ) {
            res.verbIds.push(values[i].verbIds[j]);
        }
        res.count += values[i].count;
        res.sentenceCnt += values[i].sentenceCnt;
        res.sentiment += values[i].sentiment;
    }

    return res;
};

Java サイド プログラムは、すべての結果に対して個別の ID をカウントするだけです。

実際、110 万のドキュメントの場合、実行速度が大幅に低下します。

于 2012-03-16T09:57:02.893 に答える