インデックス付き列のMongoDBselect count(distinct x)-大きなデータセットの一意の結果をカウントします


82

私はいくつかの記事と例を調べましたが、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

例えば

また

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

しかし、この時点で私はここで尋ねる価値があると思いました、この主題に関する最新のものは何ですか?個別のカウントのためにSQLまたは別のNoSQLDBに移動する必要がありますか?または効率的な方法はありますか?

更新:

MongoDBの公式ドキュメントに対するこのコメントは勇気づけられませんが、これは正確ですか?

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

Update2:

新しいアグリゲーションフレームワークが上記のコメントに答えているようです...(MongoDB 2.1 / 2.2以降、開発プレビューが利用可能で、本番環境ではありません)

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


これを頻繁に行う必要があると思います。そうしないと、パフォーマンスはそれほど重要ではありません。その場合、その大きなコレクションで個別の値を実行しようとするのではなく、新しいドキュメントを挿入したときに更新される個別のコレクションに個別の値を格納します。それか、MongoDbの使用を再評価して、別の場所に移動する可能性があります。ご存知のように、MongoDbは現在、あなたがやろうとしていることをうまくやっていない。
ティムゴーティエ2012

私は、これらの統計のためのMySQLにそれを挿入するために、今の時間を費やすと思います:)の前に... @TimGautierおかげで、私は恐れていそうだった、それはすべてのそれらの値を挿入するために時間を要した、と私はそのことを考えている必要があります
エランMedan 2012

基本的に集計データのデルタインデックスをエミュレートするインクリメンタルMRを実行することもできます。つまり、何を使用するかについて、いつ結果が必要になるかによって異なります。MySQLは多くのIOを取得し、これを実行しないと想像できます(インデックス上でインラインで10万のドキュメントを区別することで小さなサーバーを強制終了できます)が、この種のもののクエリではさらに柔軟性があると思います。
Sammaye 2012

私は、mongoがこの種のことを上手くできないことに同意しません。モンゴが得意なのはこの種のことです。
超高輝度2015

1
残念ながら、モデレーターは、重複した質問にも投稿した私の回答を削除しました。私はそこでそれを削除してここに再投稿することはできませんので、リンク:stackoverflow.com/a/33418582/226895
エキスパート

回答:


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段階のプロセスです。最初の段階では、キーのすべての個別の値のリストを使用して新しいコレクションを作成します。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のドキュメントサイズ制限を超える可能性があるためです。あなたがすることができ、コレクションに計算を保存した後(カウント)コレクションのサイズを、またはあなたがのMapReduceの戻り値から結果の数を取得することができます()。


5
Mongo 2.2 RC0をダウンロードし、最初の提案を使用しましたが、機能します。そして速い!ありがとう(よくできた10gen ...)ここに要点を作成しました(shortcut集計コマンドを使用して1行にまとめました)gist.github.com/3241616
Eran Medan

@EranMedan警告する必要がありますが、2.2 rc0はまだ完全な展開の準備ができていないため、集約フレームワークを提案しませんでした。念頭に置いておくべきことです。集約の展開を推奨する前に、2.2の完全なリリースまで待ちます。フレームワーク。
Sammaye 2012

@Sammayeはい、私はそれを知っていますが、まだ本番環境には移行しません。内部統計のためにそれが必要であり、可能であればデータをSQLに移動することを避けたいと思いました(そして私の好奇心を消し去りました)
Eran Medan 2012

Mongoが:this.plugins.X-Powered-By.stringを受け入れないのはなぜですか?どうすればこれを回避できますか?
EarlyPoster 2012

この答えがシャード環境で信頼できるかどうか疑問に思います。私が理解しているように、シャードはそれぞれ独自の集計を行い、結果を返し、そこで結果が集計されます。したがって、このシナリオでは$group、mongosに返される前に、2番目のステートメントで個別の値が失われているため、重複が存在する可能性はありませんか?
verran 2015

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;

1
そうです、それは良いことです。しかし、それはウィリアムがすでに提供したのと同じ答えではありませんか?
JohnnyHK 2013年

2
同様ですが、私はそれが一行にあるという事実が好きです。ただし、「未定義のプロパティ「0」を読み取れません」というエラーが発生しました。最後の行を削除すると、正常に機能します。
ニコ

そして、本当に巨大なデータベースについて話す場合は、{allowDiskUse:true}を忘れないでください。したがって、db.myCollection.aggregate([{$ group ..}、{$ group:}]、{allowDiskUse:true})。result [ 0] .count;
hi_artem

3

次の解決策は私のために働いた

db.test.distinct( 'user'); ["alex"、 "England"、 "France"、 "Australia"]

db.countries.distinct( 'country')。length 4

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.