1. ホーム
  2. mongodb

[解決済み] MongoDB select count(distinct x) on indexed column - 大きなデータセットでユニークな結果をカウントする

2023-07-12 01:04:34

質問

いくつかの記事やサンプルを見てきましたが、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
}

二度目の挑戦

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

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 または別の 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/

どのように解決するのですか?

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で行いたい場合は、行うことができます。 最初のフェーズでは、キーに対応するすべての異なる値のリストで新しいコレクションを構築します。 第二段階では、この新しいコレクションに対して 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()の戻り値から結果の数を取得することができます。