web-dev-qa-db-ja.com

mongodbのインデックス付き列で重複を見つける高速な方法

私はmongodbにmd5のコレクションを持っています。すべての重複を見つけたいのですが。 md5列にインデックスが付けられます。 mapreduceを使用してそれを行うための高速な方法を知っていますか。または、すべてのレコードを繰り返し処理して、重複を手動でチェックする必要がありますか?

Map reduceを使用する現在のアプローチでは、コレクションをほぼ2回繰り返します(重複が非常に少ないと仮定)。

res = db.files.mapReduce(
    function () {
        emit(this.md5, 1);
    }, 
    function (key, vals) {
        return Array.sum(vals);
    }
)

db[res.result].find({value: {$gte:1}}).forEach(
function (obj) {
    out.duplicates.insert(obj)
});
19
Piotr Czapla

1回のパスでそれを行う最も簡単な方法は、md5でソートしてから、適切に処理することです。

何かのようなもの:

var previous_md5;
db.files.find( {"md5" : {$exists:true} }, {"md5" : 1} ).sort( { "md5" : 1} ).forEach( function(current) {

  if(current.md5 == previous_md5){
    db.duplicates.update( {"_id" : current.md5}, { "$inc" : {count:1} }, true);
  }

  previous_md5 = current.md5;

});

その小さなスクリプトは、md5エントリを並べ替え、それらを順番にループします。 md5が繰り返されると、ソート後に「連続」になります。したがって、previous_md5へのポインタを保持し、それをcurrent.md5と比較します。重複が見つかった場合は、それをduplicatesコレクションにドロップします(そして$ incを使用して重複の数をカウントします)。

このスクリプトは、プライマリデータセットを1回だけループする必要があることを意味します。次に、duplicatesコレクションをループして、クリーンアップを実行できます。

30
Gates VP

私は個人的に、大きなデータベース(1TB以上)では受け入れられた答えがひどく遅いことに気づきました。集約ははるかに高速です。例を以下に示します。

db.places.aggregate(
    { $group : {_id : "$extra_info.id", total : { $sum : 1 } } },
    { $match : { total : { $gte : 2 } } },
    { $sort : {total : -1} },
    { $limit : 5 }
    );

extra_info.idが2回以上使用されているドキュメントを検索し、指定されたフィールドの降順で結果を並べ替えて、最初の5つの値を出力します。

66
expert

そのフィールドでグループ化してから、クエリを実行して複製を取得できます(カウントが1より大きい)。 http://www.mongodb.org/display/DOCS/Aggregation#Aggregation-Group

ただし、最速の方法は、そのフィールドのみを返すクエリを実行してから、クライアントで集計を実行することです。 Group/Map-Reduceは、ドキュメント全体へのアクセスを提供する必要があります。これは、インデックスからデータを提供するよりもはるかにコストがかかります(現在、1.7.3以降で説明されています)。

これが定期的に実行する必要がある一般的な問題である場合は、{md5:value、count:value}だけのコレクションを保持して、集計をスキップできるようにすることをお勧めします。重複をカリングする必要がある場合は、非常に高速になります。 。

5
Scott Hernandez