Быстрый способ поиска дубликатов на индексированном столбце в 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)
});
Ответы
Ответ 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}, чтобы вы могли пропустить агрегацию, и она будет очень быстрой, когда вам нужно для удаления дубликатов.