MongoDB-更新文档中的对象';s数组(嵌套更新)

MongoDB-更新文档中的对象';s数组(嵌套更新),mongodb,Mongodb,假设我们有以下集合,对此我有几个问题: { "_id" : ObjectId("4faaba123412d654fe83hg876"), "user_id" : 123456, "total" : 100, "items" : [ { "item_name" : "my_item_one", "price" : 20 },

假设我们有以下集合,对此我有几个问题:

{
    "_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-我想提高“项目名称”的价格:“我的项目名称”如果不存在,则应附加到“项目”数组中

2-如何同时更新两个字段。例如,提高“my_item_three”的价格,同时增加“total”(具有相同的值)

我更喜欢在MongoDB端执行此操作,否则我必须在客户端(Python)中加载文档,并构建更新的文档,然后用MongoDB中现有的文档替换它

更新 这是我尝试过的,如果对象存在,效果很好:

db.test_invoice.update({user_id : 123456 , "items.item_name":"my_item_one"} , {$inc: {"items.$.price": 10}})
但如果钥匙不存在,它就什么也不做。 此外,它仅更新嵌套对象。此命令无法同时更新“总计”字段。

对于问题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);
db.bar.update( {"items.item_name" : {$ne : "my_item_three" }} ,
               {$inc : {total : 1 , "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,答案更简单。要在任何包含“my_item_three”的文档中增加item_three的总价和价格,可以同时在多个字段上使用$inc运算符。比如:

 db.bar.update( {user_id : 123456 , "items.item_name" : "my_item_two" } , 
                {$inc : {"items.$.price" : 1} } , 
                false , 
                true);
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);
否则

无需添加条件{$ne:“我的第二项”}


此外,在多线程环境中,您必须小心,一次只能有一个线程执行第二个线程(如果未找到文档,则插入大小写),否则将插入重复的嵌入文档。

我们可以使用
$set
操作符更新对象字段中的嵌套数组,更新值

db.getCollection('geolocations').update( 
   {
       "_id" : ObjectId("5bd3013ac714ea4959f80115"), 
       "geolocation.country" : "United States of America"
   }, 
   { $set: 
       {
           "geolocation.$.country" : "USA"
       } 
    }, 
   false,
   true
);

我想你不能在mongo这样做,除非使用eval会很痛苦。Mongo在数据操作方面非常有限。@Haapala:mongodb有$inc和更新upsert@jdi是的,是的,但在这里没有多大帮助,但他需要的是多个$inc,有条件地,如果该项目不存在,则需要$push。感谢您的回复。问题#2的答案是完美的,效果很好:)但问题#1的问题是,您应该按“用户id”搜索文档,而不是按“项目.项目名称”搜索文档。在这种情况下,我不能使用位置运算符,因为我没有在搜索查询中指定数组。我也希望这两个部分都在一个镜头完成。(如果可能的话)很好的例子。我觉得从我的答案中的链接可以明显看出这个方向,但我不断受到阻力,说这不是他想要的。我猜OP只需要查看如何执行多个$inc的示例。对于问题#1,您应该仍然能够通过将用户id添加到每个更新的查询位(我将相应地修改答案)分为两部分来完成。将不得不更多地考虑是否可能在一次拍摄中完成。更新方法中的第3个和第4个参数是什么?为什么?@skmahawar关于第3个和第4个参数,表示这些选项分别用于“向上插入”和“多个”。对于upsert,如果设置为true,则在没有文档与查询条件匹配时创建新文档。默认值为false,当未找到匹配项时,不会插入新文档。“对于multi,如果设置为true,则更新满足查询条件的多个文档。如果设置为false,则更新一个文档。默认值为false。否,可以在单个查询中执行此操作,请参阅above@MartijnScheffer,我看不出在这个线程中任何地方都可以通过单个查询来实现这一点。甚至“答案”也使用了与此答案相同的两个查询。我错过什么了吗?我还希望有一种方法可以在一个查询中做到这一点,以防止任何多线程issues@Udit,如何使用物品。$。条件内的价格?当我尝试添加一个条件,例如“仅当价格大于0时才增加”,它不起作用-基本上没有更新。我可以在where条件下使用它,还是我遗漏了什么?物品。$.price:{$gt:0}一定有什么东西消失了:)