2016-12-08 3 views
2

Скажем, у меня есть документ, как это:MongoDB upsert в поддокументе в массиве и вернуть обновленный поддокумент

{ 
    "_id" : ObjectId(1234), 
    "name": "foo", 
    "bars": [ 
     { "name": "n1", "myBool": true }, 
     { "name": "n2", "myBool": false }, 
     { "name": "n3", "myBool": false } 
    ] 
} 

я хотел бы найти name (или весь поддокумент, это действительно не имеет значения) И измените myBool на true для первого вложенного документа в bars, который в настоящее время false для данного _id в атомной операции. Например, с приведенными выше данными я хотел бы вернуть n2 И установить myBool в true для n2 в документе, которое имеет _id, равное ObjectId(1234).

Как я могу это достичь?

ответ

2

Запустите findAndModify команда с новым параметром, установленным в true, и, таким образом, возвращает измененный документ, а не оригинал.

Обновление должно использовать $ positional operator, который идентифицирует элемент в массиве для обновления без явного указания положения элемента в массиве. Для этого поле массива должно появиться как часть документа запроса.

Выходной документ имеет value поле, которое содержит возвращаемый документ данной команды:

var result = db.runCommand({ 
    "findAndModify": "collection", 
    "query": { 
     "_id" : 1234, 
     "bars.myBool": false 
    }, 
    "update": { "$set": { "bars.$.myBool": true } }, 
    "new": true 
}); 
printjson(result.value); 

Пример вывода

{ 
    "_id" : 1234, 
    "name" : "foo", 
    "bars" : [ 
     { 
      "name" : "n1", 
      "myBool" : true 
     }, 
     { 
      "name" : "n2", 
      "myBool" : true 
     }, 
     { 
      "name" : "n3", 
      "myBool" : false 
     } 
    ] 
} 
+0

Может ли быть изменен только вернуть поддокумент вместо всего документа? То есть Я хотел бы знать, какой поддокумент (имя) был изменен. – Johan

+1

К сожалению, вы не можете сделать это в рамках атомного обновления. Вам нужно будет отфильтровать массив вручную, т. Е. Запустить два отдельных запроса 1. найти документ с помощью «_id»: 1234, «bars.myBool»: false } 'и затем запустить указанное выше обновление, которое возвращает измененный документ , Когда вы получите два документа, вы можете найти разницу в поддокусах массива 'bars'. – chridam

+0

Ну, к сожалению, это не сработает в высококонкурентной среде, так как два или несколько потоков могут делать это одновременно. То есть Я не могу сначала прочитать, а затем сравнить результат, поскольку другой поток также мог обновить «myBool», а затем я не знаю, какой из них был изменен. – Johan

0

Обновлено: Может быть, это работа вокруг, но отрабатывает ваш запрос, используйте следующий запрос:

db.test.findAndModify({ 
    query: { "_id": 123456, "bars.myBool": false}, 
    update: {$set: {"bars.$.myBool": true}}, 
     fields: { 
      "bars": {$elemMatch: {"myBool" : false }} 
     } 
}) 

Это взломанный документ, который возвращает исходный документ, который будет изменен.

возвращает

{ 
    "_id" : 123456, 
    "bars" : [ 
     { 
      "name" : "n2", 
      "myBool" : false 
     } 
    ] 
} 

Где можно получить документ, который обновляемой

так result содержит массив с одним поддокументом, с помощью которого вы можете получить имя.

Подробнее о findAndModify

+0

Спасибо, но моя проблема в том, что я не знаю названия. Я хочу получить имя (и обновить myBool) в атомной операции. – Johan

+0

Извините, что вы не понимаете OP, после проверки обновленного ответа. –

Смежные вопросы