在mongodb中更新嵌套数组

时间:2010-11-08 06:15:36

标签: arrays mongodb nested

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

{
    id: 1,
    items: [
        {
            id: 2,
            blocks: [
                {
                    id: 3
                    txt: 'hello'
                }
            ]
        }
    ] 
}

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

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

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

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

5 个答案:

答案 0 :(得分:27)

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

以下是我如何构建这个:

{
    id: 1,
    items: 
        { 
          "2" : { "blocks" : { "3" : { txt : 'hello' } } },
          "5" : { "blocks" : { "1" : { txt : 'foo'}, "2" : { txt : 'bar'} } }
        }
}

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

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

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

此功能已在3.6中添加expressive updates

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

答案 1 :(得分:3)

您使用的ID是线性数字,它必须来自某个地方,例如“max_idx”或类似的附加字段。 这意味着一次查找id然后更新。 UUID / ObjectId可以用于id,这将确保您也可以使用分布式CRUD。

答案 2 :(得分:1)

MongoDB 3.6已添加all positional operator $[],因此,如果您知道需要更新的块的ID,则可以执行以下操作:

router.beforeEach((to, from, next) => {

  // load styles
  if (to.meta.styles) {
    to.meta.styles.forEach((item, i) => {
      let element = document.querySelector(`[rel="stylesheet"][href="${item}"][class="appStyles"]`);

      if (!element) {
        var link = document.createElement( "link" );
        link.href = item;
        link.type = "text/css";
        link.rel = "stylesheet";
        link.setAttribute("class", "appStyles");
        document.getElementsByTagName( "head" )[0].appendChild( link );
      }
    });
  }

  // disable styles
  let existingStyleSheet = document.querySelectorAll(`[rel="stylesheet"][class="appStyles"]`);
  if (existingStyleSheet) {
    // console.log(to.meta.styles);
    existingStyleSheet.forEach((item, i) => {
      if (to.meta.styles) {
        let needed = 0;
        to.meta.styles.forEach((stylesheet, i) => {
          if (item.href == stylesheet) {
            needed += 1;
          }
        });

        // console.log(needed);

        item.disabled = false
        if (needed == 0) {
          item.disabled = true
          item.parentNode.removeChild(item)
        }
      }
    });
  }

...

答案 3 :(得分:0)

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

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

答案 4 :(得分:0)

db.col.update({"items.blocks.id": 3},
{ $set: {"items.$[].blocks.$[b].txt": "bonjour"}},
{ arrayFilters: [{"b.id": 3}] }
)

https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/#update-nested-arrays-in-conjunction-with