在mongodb中更新嵌套数组

src*_*091 44 arrays nested mongodb

我在mongodb中有一个文档,我需要更新2层深度嵌套的对象数组,如下所示:

{
    id: 1,
    items: [
        {
            id: 2,
            blocks: [
                {
                    id: 3
                    txt: 'hello'
                }
            ]
        }
    ] 
}
Run Code Online (Sandbox Code Playgroud)

如果只有一个级别的深度数组,我可以使用位置运算符来更新其中的对象,但对于第二级,我唯一的选择是使用位置运算符和嵌套对象的索引,如下所示:

db.objects.update({'items.id': 2}, {'$set': {'items.$.blocks.0.txt': 'hi'}})
Run Code Online (Sandbox Code Playgroud)

这种方法有效,但对我来说似乎很危险,因为我正在构建一个Web服务,索引号应该来自客户端,它可以发送100000作为索引,这将迫使mongodb创建一个具有空值的100000索引的数组.

有没有其他方法来更新这样的嵌套对象,我可以引用对象的ID而不是它的位置,或者可能是在查询中使用它之前检查提供的索引是否超出范围的方法?

Gat*_* VP 27

这是一个大问题,你需要利用Mongo的"addToSet"和"推送"操作吗?如果您真的打算只修改数组中的各个项目,那么您应该将这些数组构建为对象.

这是我如何构建这个:

{
    id: 1,
    items: 
        { 
          "2" : { "blocks" : { "3" : { txt : 'hello' } } },
          "5" : { "blocks" : { "1" : { txt : 'foo'}, "2" : { txt : 'bar'} } }
        }
}
Run Code Online (Sandbox Code Playgroud)

这基本上将所有内容转换为JSON对象而不是数组.你失去了使用的能力$push,$addToSet但我认为这使一切变得更容易.例如,您的查询将如下所示:

db.objects.update({'items.2': {$exists:true} }, {'$set': {'items.2.blocks.0.txt': 'hi'}})

您还会注意到我已经转储了"ID".当您嵌套这样的东西时,通常只需使用该数字作为索引来替换"ID".现在暗示了"ID"概念.

此功能已在3.6中添加,具有表现力的更新.

db.objects.update( {id: 1 }, { $set: { 'items.$[itm].blocks.$[blk].txt': "hi", } }, { multi: false, arrayFilters: [ { 'itm.id': 2 }, { 'blk.id': 3} ] } )


Pie*_*eau 5

基于盖茨的回答,我想出了这个适用于嵌套对象数组的解决方案:

db.objects.updateOne({
  ["items.id"]: 2
}, {
  $set: {
    "items.$.blocks.$[block].txt": "hi",
  },
}, {
  arrayFilters: [{
    "block.id": 3,
  }],
});
Run Code Online (Sandbox Code Playgroud)