Предположим, что мы имеем следующий сборник, в котором у меня есть несколько вопросов о:
{
"_id" : ObjectId("4faaba123412d654fe83hg876"),
"user_id" : 123456,
"total" : 100,
"items" : [
{
"item_name" : "my_item_one",
"price" : 20
},
{
"item_name" : "my_item_two",
"price" : 50
},
{
"item_name" : "my_item_three",
"price" : 30
}
]
}
1 - Я хочу увеличить цену для "item_name": "my_item_two" , и если он не существует, он должен быть добавлен к массиву "items".
2 - Как я могу обновить два поля одновременно. Например, увеличьте цену для "my_item_three" и в то же время увеличьте "общее" (с тем же значением).
Я предпочитаю делать это на стороне MongoDB, иначе мне нужно загрузить документ на стороне клиента (Python) и создать обновленный документ и заменить его на существующий в MongoDB.
UPDATE Это то, что я пробовал и работает отлично ЕСЛИ ИМЕЕТСЯ ИСТОРИЯ:
db.test_invoice.update({user_id : 123456 , "items.item_name":"my_item_one"} , {$inc: {"items.$.price": 10}})
Но если ключ не существует, он ничего не делает. Также он обновляет только вложенный объект. У этой команды нет способа обновить поле "total".
В вопросе №1 разложите его на две части. Во-первых, увеличьте любой документ, который имеет "items.item_name", равный "my_item_two" . Для этого вам придется использовать позиционный оператор "$". Что-то вроде:
db.bar.update( {user_id : 123456 , "items.item_name" : "my_item_two" } ,
{$inc : {"items.$.price" : 1} } ,
false ,
true);
Обратите внимание, что это только увеличит первый согласованный поддокумент в любом массиве (так что если у вас есть другой документ в массиве с "item_name", равным "my_item_two" , он не будет увеличиваться). Но это может быть то, что вы хотите.
Вторая часть сложнее. Мы можем вывести новый элемент в массив без "my_item_two" следующим образом:
db.bar.update( {user_id : 123456, "items.item_name" : {$ne : "my_item_two" }} ,
{$addToSet : {"items" : {'item_name' : "my_item_two" , 'price' : 1 }} } ,
false ,
true);
По вашему вопросу №2 ответ проще. Чтобы увеличить общую сумму и цену item_three в любом документе, который содержит "my_item_three", вы можете одновременно использовать оператор $inc на нескольких полях. Что-то вроде:
db.bar.update( {"items.item_name" : {$ne : "my_item_three" }} ,
{$inc : {total : 1 , "items.$.price" : 1}} ,
false ,
true);
В одном запросе нет способа сделать это. Вы должны искать документ в первом запросе:
Если документ существует:
db.bar.update( {user_id : 123456 , "items.item_name" : "my_item_two" } ,
{$inc : {"items.$.price" : 1} } ,
false ,
true);
Else
db.bar.update( {user_id : 123456 } ,
{$addToSet : {"items" : {'item_name' : "my_item_two" , 'price' : 1 }} } ,
false ,
true);
Не нужно добавлять условие {$ne : "my_item_two" }
.
Также в многопоточной среде вы должны быть осторожны, чтобы только один поток мог выполнить второй (вставить регистр, если документ не был найден) за раз, в противном случае будут вставлены дублирующие вложенные документы.