ChatGPT解决这个技术问题 Extra ChatGPT

如何更新文档数组中的对象(嵌套更新)

假设我们有以下集合,我对此几乎没有疑问:

{
    "_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
            }
    ]
}

我想提高 "item_name":"my_item_two" 的价格,如果它不存在,则应将其附加到 "items" 数组中。如何同时更新两个字段?例如,增加“my_item_three”的价格,同时增加“total”(具有相同的值)。

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

如果对象存在,这是我尝试过的并且工作正常:

db.test_invoice.update({user_id : 123456 , "items.item_name":"my_item_one"} , {$inc: {"items.$.price": 10}})

但是,如果密钥不存在,它什么也不做。此外,它只更新嵌套对象。此命令也无法更新“总计”字段。

我认为您不能在 mongo 中执行此操作,除非使用 eval 可能会很痛苦。 Mongo 在数据操作方面非常有限。
@Haapala:mongodb 有 $inc 并使用 upsert 更新
@jdi 是的,是的,但这在这里没有多大帮助,但他需要的是多个 $incs,有条件地,如果该项目不存在,则需要 $push。

k
k107

对于问题 #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,答案更容易。要在包含“my_item_three”的任何文档中增加 item_three 的总计和价格,您可以同时在多个字段上使用 $inc 运算符。就像是:

db.bar.update( {"items.item_name" : {$ne : "my_item_three" }} ,
               {$inc : {total : 1 , "items.$.price" : 1}} ,
               false ,
               true);

谢谢回复。问题#2 的答案是完美的并且工作正常 :) 但是对于问题#1,问题是您应该通过“user_id”而不是“items.item_name”搜索文档。在这种情况下,我不能使用位置运算符,因为我没有在搜索查询中指定数组。我也希望这两个部分一次完成。 (如果可能的话)
很好的例子。我觉得我从我的答案中的链接中明确了这个方向,但我一直在抵制,说这不是他想要的。猜猜 OP 只需要查看有关如何执行多个 $inc 的示例。
对于问题 #1,您应该仍然可以通过将 user_id 添加到每个更新的查询位来分两部分进行(我将相应地修改答案)。将不得不更多地考虑是否可以一次性完成。
update 方法中的第 3 和第 4 个参数是什么?为什么?
@skmahawar 关于第三和第四个参数,docs.mongodb.com/manual/reference/method/db.collection.update 表示这些选项分别用于“upsert”和“multi”。对于 upsert,如果设置为 true,则在没有文档与查询条件匹配时创建一个新文档。默认值为false,未找到匹配时不插入新文档。”对于multi,如果设置为true,则更新多个满足查询条件的文档。如果设置为false,则更新一个文档。默认值为错误的。
g
gustavohenke

在单个查询中无法做到这一点。您必须在第一个查询中搜索文档:

如果文件存在:

db.bar.update( {user_id : 123456 , "items.item_name" : "my_item_two" } , 
                {$inc : {"items.$.price" : 1} } , 
                false , 
                true);

别的

db.bar.update( {user_id : 123456 } , 
                {$addToSet : {"items" : {'item_name' : "my_item_two" , 'price' : 1 }} } ,
                false , 
                true);

无需添加条件 {$ne : "my_item_two" }

同样在多线程环境中,您必须注意一次只有一个线程可以执行第二个线程(插入案例,如果未找到文档),否则将插入重复的嵌入文档。


不,有一种方法可以在单个查询中执行此操作,请参见上文
@MartijnScheffer,我看不到在此线程中的任何地方都可以将其作为单个查询来执行。甚至“答案”也使用与此答案相同的 2 个查询。我错过了什么吗?我也希望有一种方法可以在一个查询中执行此操作,以防止任何多线程问题
@Udit,我如何在条件内使用 items.$.price?当我尝试添加诸如“仅在价格大于 0 时增加”之类的条件时,它不起作用 - 基本上没有任何更新。我可以在 where 条件下使用它,还是我错过了什么? items.$.price : { $gt : 0 }
一定有什么东西消失了:)
K
KARTHIKEYAN.A

我们可以使用 $set 运算符来更新对象字段内的嵌套数组更新值

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

m
mockingmoon

确保“item_name”字段不重复的一种方法是执行与问题 #1 的答案 https://stackoverflow.com/a/10523963 中给出的相同操作,但顺序相反!

如果 "items.item_name":{"$ne":"my_name"} 进入文档 - 更新过滤器必须包含一些唯一索引字段!而 upsert 是错误的。如果“items.item_name”:“my_name”,则增加文档。

第一次更新应该是原子的,因此如果数组已经包含 item_name 为“my_name”的元素,它不会做任何事情。

到第二次更新发生时,必须有一个带有“item_name”=“my_name”的数组元素