Найти все дубликаты документов в коллекции MongoDB по ключевому полю

Предположим, у меня есть коллекция с некоторым набором документов. что-то вроде этого.

{ "_id" : ObjectId("4f127fa55e7242718200002d"), "id":1, "name" : "foo"}
{ "_id" : ObjectId("4f127fa55e7242718200002d"), "id":2, "name" : "bar"}
{ "_id" : ObjectId("4f127fa55e7242718200002d"), "id":3, "name" : "baz"}
{ "_id" : ObjectId("4f127fa55e7242718200002d"), "id":4, "name" : "foo"}
{ "_id" : ObjectId("4f127fa55e7242718200002d"), "id":5, "name" : "bar"}
{ "_id" : ObjectId("4f127fa55e7242718200002d"), "id":6, "name" : "bar"}

Я хочу найти все дублированные записи в этой коллекции по полю "имя". Например," foo "появляется дважды, а" bar " появляется 3 раза.

4 ответов


Примечание: это решение является самым простым для понимания, но не самый лучший.

можно использовать mapReduce чтобы узнать, сколько раз документ содержит определенное поле:

var map = function(){
   if(this.name) {
        emit(this.name, 1);
   }
}

var reduce = function(key, values){
    return Array.sum(values);
}

var res = db.collection.mapReduce(map, reduce, {out:{ inline : 1}});
db[res.result].find({value: {$gt: 1}}).sort({value: -1});

принятый ответ ужасно медленный на больших коллекциях и не возвращает _ids дубликатов записей.

агрегация намного быстрее и может вернуть _ids:

db.collection.aggregate([
  { $group: {
    _id: { name: "$name" },   // replace `name` here twice
    uniqueIds: { $addToSet: "$_id" },
    count: { $sum: 1 } 
  } }, 
  { $match: { 
    count: { $gte: 2 } 
  } },
  { $sort : { count : -1} },
  { $limit : 10 }
]);

на первом этапе конвейера агрегации $group оператор агрегирует документы по формуле name поле и магазины в uniqueIds каждого _id значение сгруппированных записей. The $ sum оператор суммирует значения полей перешел к нему, в данном случае константа 1 - таким образом, подсчет количества сгруппированных записей в


для общего решения Mongo см. MongoDB кулинарная книга рецепт для поиска дубликатов с помощью group. Обратите внимание, что агрегация быстрее и мощнее в том, что она может вернуть _ids дубликатов записей.

на pymongo, принятый ответ (с использованием mapReduce) не так эффективен. Вместо этого мы можем использовать группа способ:

$connection = 'mongodb://localhost:27017';
$con        = new Mongo($connection); // mongo db connection

$db         = $con->test; // database 
$collection = $db->prb; // table

$keys       = array("name" => 1); Select name field, group by it

// set intial values
$initial    = array("count" => 0);

// JavaScript function to perform
$reduce     = "function (obj, prev) { prev.count++; }";

$g          = $collection->group($keys, $initial, $reduce);

echo "<pre>";
print_r($g);

вывод будет такой :

Array
(
    [retval] => Array
        (
            [0] => Array
                (
                    [name] => 
                    [count] => 1
                )

            [1] => Array
                (
                    [name] => MongoDB
                    [count] => 2
                )

        )

    [count] => 3
    [keys] => 2
    [ok] => 1
)

эквивалент SQL-запрос такой: SELECT name, COUNT(name) FROM prb GROUP BY name. Обратите внимание, что нам еще нужно отфильтровать элементы с количеством 0 из массива. Опять же, обратитесь к MongoDB кулинарная книга рецепт для поиска дубликатов с помощью group для канонического решения с использованием group.


Я нашел полезную информацию на официальном блоге mongo lab: http://blog.mongolab.com/2014/03/finding-duplicate-keys-with-the-mongodb-aggregation-framework/