#mongodb
#mongodb
Вопрос:
Допустим, у меня есть следующие документы в коллекции sample
:
{_id: 1, comp_index1: "one", comp_index2: "AAA", field: "lots of text" }
{_id: 2, comp_index1: "two", comp_index2: "BBB", field: "mucho texto" }
{_id: 3, comp_index1: "one", comp_index2: "CCC", field: "more text" }
{_id: 4, comp_index1: "two", comp_index2: "AAA", field: "más texto" }
{_id: 5, comp_index1: "one", comp_index2: "AAA", field: "lots of text" }
Я хочу создать comp_index1
и comp_index2
фактический уникальный составной индекс.
Если я запущу db.sample.createIndex( { comp_index1: 1, comp_index2: 1}, { unique: true } )
, это приведет E11000 duplicate key error collection
к сбою, поэтому я решил сначала удалить дубликаты (из-за удаления опции выпадающих списков).
Прямо сейчас у меня есть этот алгоритм грубой силы, который выполняет эту работу:
db.sample.aggregate([
{
$group: {
_id: {
comp_index1: "$comp_index1",
comp_index2: "$comp_index2"
},
count: { $sum: 1 }
}
},
{
$match: { count: { $gt: 1 } }
}
], { allowDiskUse: true }).forEach(function (doc) {
for (i = 1; i < doc.count; i ) {
db.sample.remove({
comp_index1: doc._id.comp_index1,
comp_index2: doc._id.comp_index2
},
{
justOne: true
});
}
print("Removed " (i-1) " dups of <" doc._id.comp_index1 " " doc._id.comp_index2 ">")
})
Проблема в том, что у меня более 1,4 млн документов и почти 200 000 дубликатов, так что на это уходит целая вечность, поэтому мне было интересно, есть ли более быстрый и эффективный подход.
Ответ №1:
Через несколько часов мне наконец удалось найти решение в 1000 раз быстрее.
var ids = [];
db.sample.aggregate([
{
$group: {
_id: {
comp_index1: "$comp_index1",
comp_index2: "$comp_index2"
},
unique_ids: { $addToSet: "$_id" }
count: { $sum: 1 }
}
},
{
$match: { count: { $gt: 1 } }
}
], { allowDiskUse: true }).forEach(function (doc) {
var i = 0;
doc.unique_ids.forEach(function (id) {
if (i > 0) ids.push(id);
})
})
db.sample.remove({"_id": {$in: ids}});
Несмотря на то, что в целом используется один и тот же подход, сохранение в ОЗУ всех идентификаторов для удаления и последующее выполнение remove
с помощью оператора $in
намного быстрее. Для выполнения этого потребовалось всего несколько секунд.
Если вы придумаете другое решение, которое не требует использования оперативной памяти, пожалуйста, поделитесь.
Ответ №2:
Недавно я создал код для удаления дублированных документов из MongoDB, это должно сработать:
const query = [
{
$group: {
_id: {
comp_index1: "$comp_index1",
comp_index2: "$comp_index2"
},
dups: {
$addToSet: "$_id",
},
count: {
$sum: 1,
},
},
},
{
$match: {
count: {
$gt: 1,
},
},
},
];
const cursor = collection.aggregate(query).cursor({ batchSize: 10 }).exec();
cursor.eachAsync((doc, i) => {
doc.dups.shift(); // First element skipped for deleting
doc.dups.map(async (dupId) => {
await collection.findByIdAndDelete({ _id: dupId });
});
});