У меня есть коллекция с полем "contact_id". В моей коллекции есть повторяющиеся регистры с этим ключом.

Как я могу удалить дубликаты, в результате чего останется только один регистр?

Я уже пробовал:

db.PersonDuplicate.ensureIndex({"contact_id": 1}, {unique: true, dropDups: true}) 

Но не сработало, потому что функция dropDups больше не доступна в MongoDB 3.x

Я использую 3.2

10
Jhonathan 29 Фев 2016 в 22:04

5 ответов

Лучший ответ

Да, dropDups больше не существует. Но вы определенно сможете достичь своей цели, приложив немного усилий.

Вам нужно сначала найти все повторяющиеся строки, а затем удалить все, кроме первой.

db.dups.aggregate([{$group:{_id:"$contact_id", dups:{$push:"$_id"}, count: {$sum: 1}}},
{$match:{count: {$gt: 1}}}
]).forEach(function(doc){
  doc.dups.shift();
  db.dups.remove({_id : {$in: doc.dups}});
});

Как видите, doc.dups.shift() удалит первый _id из массива, а затем удалит все документы с оставшимися _id из массива dups.

Приведенный выше сценарий удалит все повторяющиеся документы.

26
Saleem 29 Фев 2016 в 23:20

Мы также можем использовать $out этап удаления дубликатов из коллекции путем замены содержимого коллекции только одним экземпляром на дубликат.

Например, чтобы сохранить только один элемент на значение x:

// > db.collection.find()
//     { "x" : "a", "y" : 27 }
//     { "x" : "a", "y" : 4  }
//     { "x" : "b", "y" : 12 }
db.collection.aggregate(
  { $group: { _id: "$x", onlyOne: { $first: "$$ROOT" } } },
  { $replaceWith: "$onlyOne" }, // prior to 4.2: { $replaceRoot: { newRoot: "$onlyOne" } }
  { $out: "collection" }
)
// > db.collection.find()
//     { "x" : "a", "y" : 27 }
//     { "x" : "b", "y" : 12 }

Этот:

  • Документы $group по полю, определяющему, что такое дубликат (здесь x), и накапливают сгруппированные документы, сохраняя только один (найденный $first) и присваивая ему значение $$ROOT, который является самим документом. В конце этого этапа у нас есть что-то вроде:

    { "_id" : "a", "onlyOne" : { "x" : "a", "y" : 27 } }
    { "_id" : "b", "onlyOne" : { "x" : "b", "y" : 12 } }
    
  • $replaceWith все существующие поля в входной документ с содержанием поля onlyOne, которое мы создали на этапе $group, чтобы найти исходный формат обратно. В конце этого этапа у нас есть что-то вроде:

    { "x" : "a", "y" : 27 }
    { "x" : "b", "y" : 12 }
    

    $replaceWith доступен только начиная с Mongo 4.2. В предыдущих версиях мы можем использовать вместо этого $replaceRoot :

    { $replaceRoot: { newRoot: "$onlyOne" } }
    
  • $out вставляет результат конвейер агрегации в той же коллекции. Обратите внимание, что $out удобно заменяет содержимое указанной коллекции, делая это решение возможным.

0
Xavier Guihot 2 Апр 2020 в 19:12

Я использовал такой подход:

  1. Возьмем дамп монго конкретной коллекции.
  2. Очистить эту коллекцию
  3. Добавить уникальный ключевой индекс
  4. Восстановить дамп с помощью mongorestore.
1
Rajesh Patel 18 Ноя 2019 в 17:02

Это хороший шаблон для mongod 3+, который также гарантирует, что вы не будете запускать нашу память, что может случиться с действительно большими коллекциями. Вы можете сохранить это в файле dedup.js, настроить его и запустить в нужной базе данных с помощью: mongo localhost: 27017 / YOURDB dedup.js

var duplicates = [];

db.runCommand(
  {aggregate: "YOURCOLLECTION",
    pipeline: [
      { $group: { _id: { DUPEFIELD: "$DUPEFIELD"}, dups: { "$addToSet": "$_id" }, count: { "$sum": 1 } }},
      { $match: { count: { "$gt": 1 }}}
    ],
    allowDiskUse: true }
)
.result
.forEach(function(doc) {
    doc.dups.shift();
    doc.dups.forEach(function(dupId){ duplicates.push(dupId); })
})
printjson(duplicates); //optional print the list of duplicates to be removed

db.YOURCOLLECTION.remove({_id:{$in:duplicates}});
6
steveinatorx 25 Авг 2016 в 03:33

Может быть, будет хорошей попыткой создать tmpColection, создать уникальный индекс, затем скопировать данные из источника, и последним шагом будет замена имен?

Другая идея, которая у меня была, - получить удвоенные индексы в массив (с использованием агрегации), а затем выполнить цикл через вызов метода remove () с параметром justOne, установленным в значение true или 1.

 var itemsToDelete = db.PersonDuplicate.aggregate([
{$group: { _id:"$_id", count:{$sum:1}}},
{$match: {count: {$gt:1}}},
{$group: { _id:1, ids:{$addToSet:"$_id"}}}
])

И сделать цикл через массив идентификаторов имеет смысл для вас?

0
profesor79 29 Фев 2016 в 23:19