2012-02-29 6 views
46

Предположим, у меня есть коллекция с некоторым набором документов. что-то вроде этого.Найти все дубликаты документов в коллекции 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 раза.

+0

Для удаления дубликатов можно использовать [это решение] (HTTP: // StackOverflow.com/a/33364353/1045444) –

ответ

16

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

Вы можете использовать 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}); 
5

Для общего решения Монго см MongoDB cookbook recipe for finding duplicates using group. Обратите внимание, что агрегация быстрее и мощнее в том, что она может вернуть _id с дубликатов записей.

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

$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 cookbook recipe for finding duplicates using group для канонического решения, используя group.

+0

Ссылка на поваренную книгу MongoDB устарела и возвращается 404. – udachny

131

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

Агрегирование происходит гораздо быстрее, и может вернуть _id S:

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 значение из сгруппированные записи. Оператор $sum добавляет значения полей, переданных ему, в этом случае константу 1 - тем самым подсчитывая количество сгруппированных записей в поле count.

На втором этапе трубопровода мы используем $match для фильтрации документов с count не менее 2, то есть дубликатов.

Затем мы сортируем наиболее часто повторяющихся первых, и ограничить результаты топ-10

Этот запрос будет выводиться до $limit записей с одинаковыми именами, вместе с их _id с. Например:

{ 
    "_id" : { 
    "name" : "Toothpick" 
}, 
    "uniqueIds" : [ 
    "xzuzJd2qatfJCSvkN", 
    "9bpewBsKbrGBQexv4", 
    "fi3Gscg9M64BQdArv", 
    ], 
    "count" : 3 
}, 
{ 
    "_id" : { 
    "name" : "Broom" 
    }, 
    "uniqueIds" : [ 
    "3vwny3YEj2qBsmmhA", 
    "gJeWGcuX6Wk69oFYD" 
    ], 
    "count" : 2 
} 
+0

Для удаления дубликатов вы можете использовать [это решение] (http://stackoverflow.com/a/33364353/1045444) –

+0

Теперь как можно Я называю это с помощью C#? – blueprintChris

+0

Использует ли это решение существующий индекс на ключе? Моя проблема связана с очень большими коллекциями, где сгруппированные документы могут не вписываться в память. – Iravanchi