Вы можете сделать это двумя способами
- В одном вызове БД: Используя оператор агрегации $ out , Возможно, вы также можете использовать $ merge , но это не очень полезно для вашего случая.
- В двух вызовах БД: Как будто вы думаете, что
$out
деструктивен и с миллионами документов в коллекции может быть проблема в производственной среде, то вы можете сначала прочитать все _id
документы, которые нужно удалить, и использовать .deleteMany () , чтобы удалить все документы сразу. (Вы можете использовать любой уникальный идентификатор в do c вместо _id
, но я использовал _id
, поскольку он проиндексирован по умолчанию, что может помочь запустить deleteMany()
быстрее).
Шаг 1:
Использование $out
- Как я уже сказал, это деструктивно, потому что он переопределит всю коллекцию, если имя ввода совпадает, или создаст новую коллекцию с помощью результат вашего запроса агрегирования. Поэтому хорошо проверьте свой запрос на агрегирование, прежде чем использовать $out
в качестве последнего этапа. Также записывайте данные во временную коллекцию и переименовывайте коллекции, когда все в порядке. Учитывайте время простоя при переименовании коллекций
Запрос:
db.collection.aggregate([
{
$group: { _id: { name: "$name", value: "$value" },
doc: { $last: "$$ROOT" } // Retrieve only last doc in a group
}
},
{
$replaceRoot: { newRoot: "$doc" } // replace doc as object as new root of document
},
{ $out : 'collection_new' } // Test above aggregation & then use this
])
Тест: mongoplayground
Шаг 2:
- Используя запрос агрегирования, вы получите список
_ids
, которые нужно удалить из коллекции.
Запрос:
db.collection.aggregate([
/**
* Group on matching docs :
* { name: "duplicate", value: false},
* { name: "duplicate", value: true},
* { name: "duplicate-yes", value: true},
* { name: "notDuplicate", value: true}
* */
{
$group: {
_id: { name: "$name", value: "$value" },
_idsNeedsToBeDeleted: { $push: "$$ROOT._id" } // push all `_id`'s to an array
}
},
/** Remove first element - which is removing a doc */
{
$project: {
_id: 0,
_idsNeedsToBeDeleted: { $slice: [ "$_idsNeedsToBeDeleted", 1, { $size: "$_idsNeedsToBeDeleted" } ] }
}
},
{
$unwind: "$_idsNeedsToBeDeleted" // Unwind `_idsNeedsToBeDeleted`
},
/** Group without a condition & push all `_idsNeedsToBeDeleted` fields to an array */
{
$group: { _id: "", _idsNeedsToBeDeleted: { $push: "$_idsNeedsToBeDeleted" } }
},
{$project : { _id : 0 }} // Optional stage
/** At the end you'll have an [{ _idsNeedsToBeDeleted: [_ids] }] or [] */
])
Тест: mongoplayground
Теперь используется
.deleteMany()
- удалите все документы:
Запрос:
db.collection.deleteMany( { "_id" : {$in : [_ids]} } );
Рассмотрение до .deleteMany()
необходимо проверить агрегирование Результат не является пустым массивом []
и имеет поле do c с _idsNeedsToBeDeleted
, которое является массивом. Кроме того, поскольку мы сопоставляем _id
в БД - массив агрегатов _idsNeedsToBeDeleted
будет массивом строк - Итак, перебираем массив, конвертируем строку в ObjectId()
и используем этот массив ObjectId()
в запросе на удаление.
Примечание:
Независимо от того, какой шаг вы выберете - поскольку мы группируемся по name + value
, вам необходимо убедиться, что все ваши документы имеют эти поля.