Это на самом деле относится к давним проблемам в http://jira.mongodb.org/browse/SERVER-1243 где на самом деле существует ряд проблем для четкого синтаксиса, который поддерживает «все случаи», в которых найдены совпадения между несколькими массивами. На самом деле существуют уже существующие методы «помощи» в решении этой проблемы, такие как Bulk Operations, которые были реализованы после этого оригинального сообщения.
До сих пор невозможно обновить более одного элемента массива в одном операторе обновления, поэтому даже при обновлении «multi» все, что вы когда-либо сможете обновить, - это всего лишь один элемент в массиве для каждого документ в этом единственном заявлении.
В настоящее время наилучшим решением является поиск и объединение всех согласованных документов и обработка массовых обновлений, которые, по меньшей мере, позволят выполнять множество операций в одном запросе с сингулярным ответом. При желании вы можете использовать .aggregate()
уменьшить содержимое массива, возвращенное в результате поиска только те, которые соответствуют условиям для выбора обновления:
db.collection.aggregate([
{ "$match": { "events.handled": 1 } },
{ "$project": {
"events": {
"$setDifference": [
{ "$map": {
"input": "$events",
"as": "event",
"in": {
"$cond": [
{ "$eq": [ "$$event.handled", 1 ] },
"$$el",
false
]
}
}},
[false]
]
}
}}
]).forEach(function(doc) {
doc.events.forEach(function(event) {
bulk.find({ "_id": doc._id, "events.handled": 1 }).updateOne({
"$set": { "events.$.handled": 0 }
});
count++;
if (count % 1000 == 0) {
bulk.execute();
bulk = db.collection.initializeOrderedBulkOp();
}
});
});
if (count % 1000 != 0)
bulk.execute();
.aggregate()
части будет работать, когда есть «уникальный» идентификатор для массив или весь контент для каждого элемента формирует «уникальный» элемент. Это связано с оператором «set» в $setDifference
, используемым для фильтрации любых значений false
, возвращаемых из операции $map
, используемой для обработки массива для совпадений.
Если содержимое массива не имеет уникальные элементы, которые вы можете попробовать альтернативный подход с $redact
:
db.collection.aggregate([
{ "$match": { "events.handled": 1 } },
{ "$redact": {
"$cond": {
"if": {
"$eq": [ { "$ifNull": [ "$handled", 1 ] }, 1 ]
},
"then": "$$DESCEND",
"else": "$$PRUNE"
}
}}
])
Где это ограничение в том, что если «обработано», на самом деле поле означало присутствовать в другом документе то вы, скорее всего, получите неизученные результаты, но это нормально, когда это поле появляется только в одной позиции документа и является совпадением равенства.
Будущие версии (после 3.1 MongoDB) в письменной форме будет иметь $filter
операцию, которая проще:
db.collection.aggregate([
{ "$match": { "events.handled": 1 } },
{ "$project": {
"events": {
"$filter": {
"input": "$events",
"as": "event",
"cond": { "$eq": [ "$$event.handled", 1 ] }
}
}
}}
])
И все выпуски, которые поддерживают .aggregate()
можно использовать следующий подход с $unwind
, но использование этого оператора делает это наименее эффективный подход из-за расширение массива в трубопроводе:
db.collection.aggregate([
{ "$match": { "events.handled": 1 } },
{ "$unwind": "$events" },
{ "$match": { "events.handled": 1 } },
{ "$group": {
"_id": "$_id",
"events": { "$push": "$events" }
}}
])
во всех случаях, когда версия MongoDB поддерживает «курсор» от агрегатного outpu t, то это просто вопрос выбора подхода и повторение результатов с помощью того же блока кода, который показан для обработки операторов массового обновления. Массовые операции и «курсоры» из совокупной производительности вводятся в той же версии (MongoDB 2.6) и поэтому обычно работают рука об руку для обработки.
В еще более ранних версий, то это, вероятно, лучше всего использовать .find()
, чтобы вернуть курсор, и отфильтровывать выполнение инструкций для всего количества раз элемент массива согласован для .update()
итераций:
db.collection.find({ "events.handled": 1 }).forEach(function(doc){
doc.events.filter(function(event){ return event.handled == 1 }).forEach(function(event){
db.collection.update({ "_id": doc._id },{ "$set": { "events.$.handled": 0 }});
});
});
Если вы решительно настроены делать «несколько» обновлений или считаете, что это в конечном счете более эффективно, чем обработка нескольких обновлений для каждого согласованного документа, вы всегда можете определить максимальное количество возможных совпадений массива и просто выполнить обновление «multi» что много раз, пока в основном нет больше документов для обновления.
Подходящий подход для MongoDB 2.4 и 2.2 версии можно также использовать .aggregate()
найти это значение:
var result = db.collection.aggregate([
{ "$match": { "events.handled": 1 } },
{ "$unwind": "$events" },
{ "$match": { "events.handled": 1 } },
{ "$group": {
"_id": "$_id",
"count": { "$sum": 1 }
}},
{ "$group": {
"_id": null,
"count": { "$max": "$count" }
}}
]);
var max = result.result[0].count;
while (max--) {
db.collection.update({ "events.handled": 1},{ "$set": { "events.$.handled": 0 }},{ "multi": true })
}
В любом случае, есть определенные вещи, которые вы делаете не хотите сделать в обновлении:
Не «один shot "обновить массив: Где, если вы считаете, что более эффективно обновлять весь контент массива в коде, а затем всего $set
весь массив в каждом документе. Это может показаться более быстрым для обработки, но нет гарантии, что содержимое массива не изменилось с момента его чтения и обновления. Хотя $set
по-прежнему является атомарным оператором, он будет обновлять массив только тем, что он «думает», является правильными данными и, следовательно, может переписать любые изменения, происходящие между чтением и записью.
Не вычислять значение индекса для обновления: Где похожих на «один выстрел» подход просто работать, что позиция 0
и положение 2
(и так далее) являются элементы для обновления и код их с использованием и в конечном итоге заявление, как:
{ "$set": {
"events.0.handled": 0,
"events.2.handled": 0
}}
Опять же проблема здесь является «презумпцией», что эти значения индекса найдены, когда документ был прочитан тем же значение индекса в массиве го во время обновления. Если новые элементы добавляются в массив таким образом, который изменяет порядок, то эти позиции более недействительны, а неправильные элементы фактически обновляются.
Так до тех пор, пока разумный синтаксис определяется для предоставления нескольких соответствующих элементов массива, которые будут обрабатываться в одном операторе обновления, то основной подход является либо обновить для каждого элемент массива в indvidual оператора (в идеале в массе) или по существу, выработать максимальные элементы массива для обновления или обновления, пока не будут возвращены больше измененных результатов. Во всяком случае, вы должны «всегда» обрабатывать positional $
обновления в соответствующем элементе массива, даже если это обновление только одного элемента за оператор.
Массовые операции на самом деле являются «обобщенными» решениями для обработки любых операций, которые работают как «множественные операции», и поскольку для этого существует больше приложений, чем просто обновление элементов нескольких элементов с одинаковым значением, конечно, уже реализовано, и в настоящее время это лучший подход к решению этой проблемы.
Обновление подмножества или всех элементов массива было добавлено в mongodb 3.6: https://docs.mongodb.com/manual/reference/operator/update/positional-all/#up._S_ [] – Jaap
не забудьте проверить out arrayFilters и рассмотрим, какой запрос использовать, чтобы сделать обновление эффективным. Обратите внимание на ответ Нила Лунна: https://stackoverflow.com/a/46054172/337401 – Jaap