Как удалить дубликаты на основе ключа в Mongodb?
У меня есть коллекция в MongoDB, где есть (~ 3 миллиона записей). Моя запись будет выглядеть так:
{ "_id" = ObjectId("50731xxxxxxxxxxxxxxxxxxxx"),
"source_references" : [
"_id" : ObjectId("5045xxxxxxxxxxxxxx"),
"name" : "xxx",
"key" : 123
]
}
У меня есть много дубликатов записей в коллекции, имеющих те же source_references.key
. (По Duplicate я имею в виду source_references.key
не _id
).
Я хочу удалить повторяющиеся записи на основе source_references.key
, я подумываю написать некоторый PHP-код для прохождения каждой записи и удалить запись, если она существует.
Есть ли способ удалить дубликаты в внутренней командной строке Mongo?
Ответы
Ответ 1
Если вы уверены, что source_references.key
идентифицирует повторяющиеся записи, вы можете обеспечить уникальный индекс с помощью опции dropDups:true
в MongoDB 2.6 и старше:
db.things.ensureIndex({'source_references.key' : 1}, {unique : true, dropDups : true})
Это сохранит первый уникальный документ для каждого значения source_references.key
и отбросит любые последующие документы, которые в противном случае вызывают нарушение дублирующего ключа.
Важные примечания:
- Параметр
dropDups
был удален в MongoDB 3.0, поэтому потребуется другой подход. Например, вы можете использовать агрегацию, как было предложено: MongoDB дублировать документы даже после добавления уникального ключа.
- Любые документы, отсутствующие в поле
source_references.key
, будут считаться имеющими нулевое значение, поэтому последующие документы, пропускающие поле ключа, будут удалены. Вы можете добавить параметр sparse:true
, чтобы индекс применялся только к документам с полем source_references.key
.
Очевидная осторожность: сделайте резервную копию своей базы данных и сначала попробуйте это в промежуточной среде, если вас беспокоит непредвиденная потеря данных.
Ответ 2
Это самый простой запрос, который я использовал на моем MongoDB 3.2
db.myCollection.find({}, {myCustomKey:1}).sort({_id:1}).forEach(function(doc){
db.myCollection.remove({_id:{$gt:doc._id}, myCustomKey:doc.myCustomKey});
})
Покажите свой customKey
перед запуском, чтобы увеличить скорость
Ответ 3
Удалить дубликаты структура агрегации.
а. Если вы хотите удалить за один раз.
var duplicates = [];
db.collectionName.aggregate([
// discard selection criteria, You can remove "$match" section if you want
{ $match: {
source_references.key: { "$ne": '' }
}},
{ $group: {
_id: { source_references.key: "$source_references.key"}, // can be grouped on multiple properties
dups: { "$addToSet": "$_id" },
count: { "$sum": 1 }
}},
{ $match: {
count: { "$gt": 1 } // Duplicates considered as count greater than one
}}
]) // You can display result until this and check duplicates
.forEach(function(doc) {
doc.dups.shift(); // First element skipped for deleting
doc.dups.forEach( function(dupId){
duplicates.push(dupId); // Getting all duplicate ids
}
)
})
// If you want to Check all "_id" which you are deleting else print statement not needed
printjson(duplicates);
// Remove all duplicates in one go
db.collectionName.remove({_id:{$in:duplicates}})
б. Вы можете удалять документы по одному.
db.collectionName.aggregate([
// discard selection criteria, You can remove "$match" section if you want
{ $match: {
source_references.key: { "$ne": '' }
}},
{ $group: {
_id: { source_references.key: "$source_references.key"}, // can be grouped on multiple properties
dups: { "$addToSet": "$_id" },
count: { "$sum": 1 }
}},
{ $match: {
count: { "$gt": 1 } // Duplicates considered as count greater than one
}}
]) // You can display result until this and check duplicates
.forEach(function(doc) {
doc.dups.shift(); // First element skipped for deleting
db.collectionName.remove({_id : {$in: doc.dups }}); // Delete remaining duplicates
})
Ответ 4
Хотя @Stennie является действительным ответом, это не единственный способ. Infact руководство MongoDB просит вас быть очень осторожным при этом. Есть еще две опции
Ответ 5
pip install mongo_remove_duplicate_indexes
- создать script на любом языке
- итерация по вашей коллекции
- создайте новую коллекцию и создайте новый индекс в этой коллекции с уникальным значением true, помните, что этот индекс должен быть таким же, как индекс u хочет удалить дубликаты из оригинальной коллекции ur с тем же именем
для ex-u есть коллекция игр, и в этой коллекции у есть жанр поля, который содержит дубликаты, которые вы хотите удалить, поэтому просто создайте новую коллекцию
db.createCollection( "CNAME" )
создать новый индекс
db.cname.createIndex({ 'жанр': 1}, уникальный: 1)
теперь, когда u будет вставлять документ с похожим жанром, только первый будет принят, другой будет отклонен с ошибкой ключа duplicae.
- теперь просто вставляем значения формата json u, полученные в новую коллекцию и обрабатываем исключение, используя обработку исключений
для ex pymongo.errors.DuplicateKeyError
проверьте исходный код пакета для mongo_remove_duplicate_indexes для лучшего понимания
Ответ 6
Если у вас достаточно памяти, вы можете в scala сделать что-то вроде этого:
cole.find().groupBy(_.customField).filter(_._2.size>1).map(_._2.tail).flatten.map(_.id)
.foreach(x=>cole.remove({id $eq x})
Ответ 7
Вот несколько более "ручной" способ сделать это:
По сути, сначала получите список всех уникальных ключей, которые вам интересны.
Затем выполните поиск с использованием каждого из этих ключей и удалите, если этот поиск вернется больше одного.
db.collection.distinct("key").forEach((num)=>{
var i = 0;
db.collection.find({key: num}).forEach((doc)=>{
if (i) db.collection.remove({key: num}, { justOne: true })
i++
})
});