Mongodb更新了深层嵌套的子文档

rep*_*cus 58 mongodb

我有一个深层嵌套的文档结构,如下所示:

{id: 1, 
 forecasts: [ { 
             forecast_id: 123, 
             name: "Forecast 1", 
             levels: [ 
                { level: "proven", 
                  configs: [
                            { 
                              config: "Custom 1",
                              variables: [{ x: 1, y:2, z:3}]
                            }, 
                            { 
                              config: "Custom 2",
                              variables: [{ x: 10, y:20, z:30}]
                            }, 
                    ]
                }, 
                { level: "likely", 
                  configs: [
                            { 
                              config: "Custom 1",
                              variables: [{ x: 1, y:2, z:3}]
                            }, 
                            { 
                              config: "Custom 2",
                              variables: [{ x: 10, y:20, z:30}]
                            }, 
                    ]
                }
            ]
        }, 
    ]

}
Run Code Online (Sandbox Code Playgroud)

我正在尝试更新集合以插入新的配置,如下所示:

newdata =  {
  config: "Custom 1", 
  variables: [{ x: 111, y:2222, z:3333}]
}
Run Code Online (Sandbox Code Playgroud)

我在mongo中尝试这样的东西(在Python中):

db.myCollection.update({"id": 1, 
                        "forecasts.forecast-id": 123, 
                        "forecasts.levels.level": "proven", 
                        "forecasts.levels.configs.config": "Custom 1"
                         },
                         {"$set": {"forecasts.$.levels.$.configs.$": newData}}
                      )
Run Code Online (Sandbox Code Playgroud)

我得到"无法应用位置运算符而没有包含数组的相应查询字段"错误.在mongo中执行此操作的正确方法是什么?这是mongo v2.4.1.

Joh*_*yHK 47

遗憾的是,$每个键不能多次使用运算符,因此必须使用其余的数值.如:

db.myCollection.update({
    "id": 1, 
    "forecasts.forecast-id": 123, 
    "forecasts.levels.level": "proven", 
    "forecasts.levels.configs.config": "Custom 1"
  },
  {"$set": {"forecasts.$.levels.0.configs.0": newData}}
)
Run Code Online (Sandbox Code Playgroud)

MongoDB对更新嵌套数组的支持很差.因此,如果您需要经常更新数据,并考虑使用多个集合,那么最好避免使用它们.

一种可能性:创建forecasts自己的集合,并假设您有一组固定的level值,创建level一个对象而不是一个数组:

{
  _id: 123,
  parentId: 1,
  name: "Forecast 1", 
  levels: {
    proven: { 
      configs: [
        { 
          config: "Custom 1",
          variables: [{ x: 1, y:2, z:3}]
        }, 
        { 
          config: "Custom 2",
          variables: [{ x: 10, y:20, z:30}]
        }, 
      ]
    },
    likely: {
      configs: [
        { 
          config: "Custom 1",
          variables: [{ x: 1, y:2, z:3}]
        }, 
        { 
          config: "Custom 2",
          variables: [{ x: 10, y:20, z:30}]
        }, 
      ]
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以用以下方法更新它:

db.myCollection.update({
    _id: 123,
    'levels.proven.configs.config': 'Custom 1'
  },
  { $set: { 'levels.proven.configs.$': newData }}
)
Run Code Online (Sandbox Code Playgroud)

  • @JohnnyHK,我认为答案的第一部分有错误.在'{"$ set":{"forecast.0.levels.0.configs.$":newData}}'中,$运算符放错了,因为运算符返回第一个嵌套列表中对象的索引,这意味着它在当前查询中始终为0.您可以通过更改查询以使用""forecasts.levels.configs.config":"自定义2"来测试这一点,您将看到更新的文档是具有"config":"自定义1"的文档.正确的应该是'{"$ set":{"forecast.$.levels.0.configs.0":newData}}' (2认同)

小智 18

管理使用猫鼬解决它:

您需要知道的是链中所有子文档的'_id'(mongoose为每个子文档自动创建'_id').

例如 -

  SchemaName.findById(_id, function (e, data) {
      if (e) console.log(e);
      data.sub1.id(_id1).sub2.id(_id2).field = req.body.something;

      // or if you want to change more then one field -
      //=> var t = data.sub1.id(_id1).sub2.id(_id2);
      //=> t.field = req.body.something;

      data.save();
  });
Run Code Online (Sandbox Code Playgroud)

有关mongoose文档中的子文档_id方法的更多信息.

解释:_id用于SchemaName,_id1用于sub1,_id2用于sub2 - 你可以保持链接.

*你不必使用findById方法,但在我看来这是最方便的,因为你需要知道'_id的其余部分.

  • 点符号就像一个魅力。巨大的帮助。过去 2 天一直在寻找这个简单的解决方案!谢谢! (2认同)

NIK*_*C M 12

MongoDB引入了ArrayFilters来解决版本3.5.2及更高版本中的这个问题.

版本3.6中的新功能.

从MongoDB 3.6开始,在更新数组字段时,您可以指定arrayFilters来确定要更新的数组元素.

[ https://docs.mongodb.com/manual/reference/method/db.collection.update/#specify-arrayfilters-for-an-array-update-operations][1]

让我们说Schema设计如下:

var ProfileSchema = new Schema({
    name: String,
    albums: [{
        tour_name: String,
        images: [{
            title: String,
            image: String
        }]
    }]
});
Run Code Online (Sandbox Code Playgroud)

创建的文档如下所示:

{
   "_id": "1",
   "albums": [{
            "images": [
               {
                  "title": "t1",
                  "url": "url1"
               },
               {
                  "title": "t2",
                  "url": "url2"
               }
            ],
            "tour_name": "london-trip"
         },
         {
            "images": [.........]: 
         }]
}
Run Code Online (Sandbox Code Playgroud)

说我想更新图像的"网址".鉴于 - "document id", "tour_name" and "title"

为此更新查询:

Profiles.update({_id : req.body.id},
    {
        $set: {

            'albums.$[i].images.$[j].title': req.body.new_name
        }
    },
    {
        arrayFilters: [
            {
                "i.tour_name": req.body.tour_name, "j.image": req.body.new_name   // tour_name -  current tour name,  new_name - new tour name 
            }]
    })
    .then(function (resp) {
        console.log(resp)
        res.json({status: 'success', resp});
    }).catch(function (err) {
    console.log(err);
    res.status(500).json('Failed');
})
Run Code Online (Sandbox Code Playgroud)


vic*_*osa 5

这是MongoDB中一个非常老的错误

https://jira.mongodb.org/browse/SERVER-831


THE*_*BLE 5

我今天面临同样的问题,经过在 google/stackoverflow/github 上的大量探索,我认为这arrayFilters是解决这个问题的最佳方法。这适用于 mongo 3.6 及更高版本。这个链接终于救了我的一天:https : //thecodebarbarian.com/a-nodejs-perspective-on-mongodb-36-array-filters.html

const OrganizationInformationSchema = mongoose.Schema({
user: {
    _id: String,
    name: String
},
organizations: [{
    name: {
        type: String,
        unique: true,
        sparse: true
    },
    rosters: [{
        name: {
            type: String
        },
        designation: {
            type: String
        }
    }]
}]
}, {
    timestamps: true
});
Run Code Online (Sandbox Code Playgroud)

并在 express 中使用猫鼬,更新给定 id 的花名册名称。

const mongoose = require('mongoose');
const ControllerModel = require('../models/organizations.model.js');
module.exports = {
// Find one record from database and update.
findOneRosterAndUpdate: (req, res, next) => {
    ControllerModel.updateOne({}, {
        $set: {
            "organizations.$[].rosters.$[i].name": req.body.name
        }
    }, {
        arrayFilters: [
            { "i._id": mongoose.Types.ObjectId(req.params.id) }
        ]
    }).then(response => {
        res.send(response);
    }).catch(err => {
        res.status(500).send({
            message: "Failed! record cannot be updated.",
            err
        });
    });
}
}
Run Code Online (Sandbox Code Playgroud)