Подтвердить что ты не робот

Быстрый способ поиска дубликатов на индексированном столбце в mongodb

У меня есть коллекция md5 в mongodb. Я бы хотел найти все дубликаты. Столбец md5 индексируется. Знаете ли вы быстрый способ сделать это с помощью уменьшения карты. Или я должен просто перебирать все записи и проверять наличие дубликатов вручную?

Мой текущий подход с использованием карты уменьшает итерацию по коллекции почти в два раза (при условии, что имеется очень небольшое количество дубликатов):

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)
});
4b9b3361

Ответ 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;

});

Этот маленький script сортирует записи md5 и циклически перемещается по ним. Если md5 повторяется, после сортировки они будут "назад к спине". Поэтому мы просто держим указатель на previous_md5 и сравниваем его current.md5. Если мы найдем дубликат, я помещаю его в коллекцию duplicates (и используя $inc для подсчета количества дубликатов).

Этот script означает, что вам нужно только прокрутить первичный набор данных один раз. Затем вы можете прокрутить коллекцию duplicates и выполнить очистку.

Ответ 2

Я лично нашел, что на больших базах данных (1 ТБ и более) принятый ответ ужасно медленный. Агрегация происходит намного быстрее. Пример ниже:

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

Он ищет документы, чей extra_info.id используется два или более раз, сортирует результаты в порядке убывания заданного поля и печатает первые 5 значений.

Ответ 3

Вы можете сделать группу по этому полю, а затем запросить, чтобы получить дубликат (имея счетчик > 1). http://www.mongodb.org/display/DOCS/Aggregation#Aggregation-Group

Хотя самой быстрой задачей может быть просто запрос, который возвращает это поле, а затем выполняет агрегацию в клиенте. Group/Map-Reduce необходимо предоставить доступ ко всему документу, который намного дороже, чем просто предоставление данных из индекса (который теперь рассматривается в 1.7.3 +).

Если это общая проблема, вам нужно периодически запускать, вы можете сохранить коллекцию, которая является просто {md5: value, count: value}, чтобы вы могли пропустить агрегацию, и она будет очень быстрой, когда вам нужно для удаления дубликатов.