84

私はいくつかの記事と例を調べましたが、MongoDB でこの SQL クエリを実行する効率的な方法をまだ見つけていません (数百万のドキュメント)

最初の試み

(たとえば、このほぼ重複した質問から - SQLの SELECT DISTINCT? に相当するMongo

db.myCollection.distinct("myIndexedNonUniqueField").length

データセットが巨大であるため、明らかにこのエラーが発生しました

Thu Aug 02 12:55:24 uncaught exception: distinct failed: {
        "errmsg" : "exception: distinct too big, 16mb cap",
        "code" : 10044,
        "ok" : 0
}

2 回目の試行

グループを作ってみることにしました

db.myCollection.group({key: {myIndexedNonUniqueField: 1},
                initial: {count: 0}, 
                 reduce: function (obj, prev) { prev.count++;} } );

しかし、代わりに次のエラー メッセージが表示されました。

exception: group() can't handle more than 20000 unique keys

3 回目の試行

まだ試していませんが、関連するいくつかの提案がありますmapReduce

例えば

また

.distinctカウントのみを返す必要があることを言及するメソッドを修正する GitHub にプル リクエストがあるようですが、まだ開いています: https://github.com/mongodb/mongo/pull/34

しかし、この時点で、ここで質問する価値があると思いました。この件に関する最新情報は何ですか? 個別のカウントのために、SQL または別の NoSQL DB に移動する必要がありますか? または効率的な方法はありますか?

アップデート:

MongoDB の公式ドキュメントに関するこのコメントは心強いものではありませんが、これは正確ですか?

http://www.mongodb.org/display/DOCS/Aggregation#comment-430445808

アップデート2:

新しい Aggregation Framework が上記のコメントに答えているようです... (MongoDB 2.1/2.2 以降、開発プレビューが利用可能、本番用ではありません)

http://docs.mongodb.org/manual/applications/aggregation/

4

3 に答える 3

75

1) これを行う最も簡単な方法は、集計フレームワークを使用することです。これには 2 つの「$group」コマンドが必要です。最初のコマンドは個別の値でグループ化し、2 番目のコマンドはすべての個別の値をカウントします。

pipeline = [ 
    { $group: { _id: "$myIndexedNonUniqueField"}  },
    { $group: { _id: 1, count: { $sum: 1 } } }
];

//
// Run the aggregation command
//
R = db.runCommand( 
    {
    "aggregate": "myCollection" , 
    "pipeline": pipeline
    }
);
printjson(R);

2) Map/Reduce でこれを行いたい場合は可能です。これも 2 段階のプロセスです。最初の段階では、キーのすべての個別の値のリストを含む新しいコレクションを作成します。次に、新しいコレクションに対して count() を実行します。

var SOURCE = db.myCollection;
var DEST = db.distinct
DEST.drop();


map = function() {
  emit( this.myIndexedNonUniqueField , {count: 1});
}

reduce = function(key, values) {
  var count = 0;

  values.forEach(function(v) {
    count += v['count'];        // count each distinct value for lagniappe
  });

  return {count: count};
};

//
// run map/reduce
//
res = SOURCE.mapReduce( map, reduce, 
    { out: 'distinct', 
     verbose: true
    }
    );

print( "distinct count= " + res.counts.output );
print( "distinct count=", DEST.count() );

map/reduce の結果をインラインで返すことはできないことに注意してください。これは、16MB のドキュメント サイズ制限を超える可能性があるためです。計算をコレクションに保存してから count() コレクションのサイズを取得するか、mapReduce() の戻り値から結果の数を取得できます。

于 2012-08-02T22:39:32.033 に答える
37
db.myCollection.aggregate( 
   {$group : {_id : "$myIndexedNonUniqueField"} }, 
   {$group: {_id:1, count: {$sum : 1 }}});

ストレートに結果:

db.myCollection.aggregate( 
   {$group : {_id : "$myIndexedNonUniqueField"} }, 
   {$group: {_id:1, count: {$sum : 1 }}})
   .result[0].count;
于 2013-03-04T21:32:13.427 に答える