source

Mongodb가 중첩된 하위 문서 업데이트

ittop 2023. 5. 2. 22:59
반응형

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}]
                            }, 
                    ]
                }
            ]
        }, 
    ]

}

다음과 같은 새 구성을 삽입하기 위해 컬렉션을 업데이트하려고 합니다.

newdata =  {
  config: "Custom 1", 
  variables: [{ x: 111, y:2222, z:3333}]
}

저는 몽고(파이썬)에서 다음과 같은 것을 시도하고 있습니다.

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

하지만 "어레이를 포함하는 해당 쿼리 필드가 없으면 위치 연산자를 적용할 수 없습니다" 오류가 발생합니다.몽고에서 이것을 하는 적절한 방법은 무엇입니까?이것은 mongo v2.4.1입니다.

유스럽게도, 을 사용할 수 없습니다.$연산자는 키당 두 번 이상이므로 나머지 값은 숫자 값을 사용해야 합니다.다음과 같이:

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}}
)

중첩 배열 업데이트에 대한 MongoDB의 지원이 부족합니다.따라서 데이터를 자주 업데이트하고 대신 여러 컬렉션을 사용하는 것이 좋습니다.

가지 가능성: 가지가성: 기만들능.forecasts그것의 자체 컬렉션, 그리고 당신이 고정된 세트를 가지고 있다고 가정합니다.level make 값, 기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}]
        }, 
      ]
    }
  }
}

그런 다음 다음을 사용하여 업데이트할 수 있습니다.

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

mongoose를 사용하여 문제를 해결했습니다.

체인에 있는 모든 하위 문서의 '_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();
  });

mongoose 문서의 subdocument_id 메서드에 대해 자세히 알아봅니다.

설명: _id는 SchemaName에 대한 것이고 _id1은 sub1에 대한 것이고 _id2는 sub2에 대한 것입니다. 계속해서 체인을 연결할 수 있습니다.

*findById 메서드를 사용할 필요는 없지만, 나머지 '_id'를 어차피 알아야 하기 때문에 가장 편리할 것 같습니다.

MongoDB는 버전 3.5.2 이상에서 이 문제를 해결하기 위해 ArrayFilters를 도입했습니다.

버전 3.6의 새로운 기능.

MongoDB 3.6부터는 어레이 필드를 업데이트할 때 업데이트할 어레이 요소를 결정하는 arrayFilters를 지정할 수 있습니다.

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

스키마 설계를 다음과 같이 가정합니다.

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

작성된 문서는 다음과 같습니다.

{
   "_id": "1",
   "albums": [{
            "images": [
               {
                  "title": "t1",
                  "url": "url1"
               },
               {
                  "title": "t2",
                  "url": "url2"
               }
            ],
            "tour_name": "london-trip"
         },
         {
            "images": [.........]: 
         }]
}

이미지의 "url"을 업데이트하고 싶다고 합니다.- 어진주 -"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');
})

이것은 MongoDB에 있는 매우 오래된 버그입니다.

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

나는 오늘 같은 종류의 문제에 직면했고, 구글/스택 오버플로/기트허브에서 많은 탐색을 한 후에, 나는 생각했습니다.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
});

그리고 속달로 몽구스를 사용하여 주어진 아이디의 로스터 이름을 업데이트합니다.

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
        });
    });
}
}

고정되어 있습니다.https://jira.mongodb.org/browse/SERVER-831

그러나 이 기능은 MongoDB 3.5.12 개발 버전부터 사용할 수 있습니다.

참고: 이 질문은 다음과 같습니다.Aug 11 2013그리고 그것은 결정됩니다.Aug 11 2017

MongoDB가 이것에 대한 좋은 메커니즘을 제공하지 않는 것처럼 보이는 것을 고려할 때, 저는 mongoose를 사용하여 단순히 mongo 컬렉션에서 요소를 추출하는 것이 신중하다고 생각합니다..findOne(...)관련 하위 요소에 대해 루프 검색을 실행합니다(예: Object로 검색)ID), 해당 JSON을 수정한 다음 수행합니다.Schema.markModified('your.subdocument'); Schema.save();효율적이지 않을 수도 있지만, 매우 간단하고 잘 작동합니다.

저는 약 5시간 동안 이것에 대해 검색했고 마침내 가장 좋고 쉬운 해결책을 찾았습니다: MONGO DB의 중첩된 하위 문서를 업데이트하는 방법.

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

쿼리:

db.weather.updateOne({
                "_id": ObjectId("1"), //this is level O select
                "forecasts": {
                    "$elemMatch": {
                        "forecast_id": ObjectId("123"), //this is level one select
                        "levels.levelid": ObjectId("1221") // this is level to select
                    }
                }
            },
                {
                    "$set": {
                        "forecasts.$[outer].levels.$[inner].levelname": "New proven",
                    }
                },
                {
                    "arrayFilters": [
                        { "outer.forecast_id": ObjectId("123") }, 
                        { "inner.levelid": ObjectId("1221") }
                    ]
                }).then((result) => {
                    resolve(result);
                }, (err) => {
                    reject(err);
                });

배운 교훈을 공유하는 것.최근에 중첩된 어레이 항목을 업데이트해야 하는 동일한 요구 사항에 직면했습니다.저의 구조는 다음과 같습니다.

  {
    "main": {
      "id": "ID_001",
      "name": "Fred flinstone Inc"
    },
    "types": [
      {
        "typeId": "TYPE1",
        "locations": [
          {
            "name": "Sydney",
            "units": [
              {
                "unitId": "PHG_BTG1"
              }
            ]
          },
          {
            "name": "Brisbane",
            "units": [
              {
                "unitId": "PHG_KTN1"
              },
              {
                "unitId": "PHG_KTN2"
              }
            ]
          }
        ]
      }
    ]
  }

제 요구 사항은 특정 단위[]에 필드를 추가하는 것입니다.내 해결책은 먼저 중첩 배열 항목의 인덱스를 찾는 것입니다(예: foundUnitIdx). 내가 사용한 두 가지 기법은

  1. $set 키워드 사용
  2. [] 구문을 사용하여 동적 필드를 $set로 지정합니다.

                query = {
                    "locations.units.unitId": "PHG_KTN2"
                };
                var updateItem = {
                    $set: {
                        ["locations.$.units."+ foundUnitIdx]: unitItem
                    }
                };
                var result = collection.update(
                    query,
                    updateItem,
                    {
                        upsert: true
                    }
                );
    

이것이 다른 사람들에게 도움이 되기를 바랍니다.:)

Mongodb 3.2+를 위한 간편한 솔루션 https://docs.mongodb.com/manual/reference/method/db.collection.replaceOne/

저도 비슷한 상황이 있었는데 이렇게 해결했습니다.저는 몽구스를 사용하고 있었지만 바닐라 몽고DB에서는 여전히 작동할 것입니다.누군가에게 유용하길 바랍니다.

const MyModel = require('./model.js')
const query = {id: 1}

// First get the doc
MyModel.findOne(query, (error, doc) => {

    // Do some mutations
    doc.foo.bar.etc = 'some new value'

    // Pass in the mutated doc and replace
    MyModel.replaceOne(query, doc, (error, newDoc) => {
         console.log('It worked!')
    })
}

사용 사례에 따라 초기 findOne()을 건너뛸 수 있습니다.

좋아요. 우리는 mongodb에 있는 우리의 중첩된 하위 문서를 업데이트할 수 있습니다.이것이 우리의 스키마입니다.

var Post = new mongoose.Schema({
    name:String,
    post:[{
        like:String,
        comment:[{
            date:String,
            username:String,
            detail:{
                time:String,
                day:String
            }
        }]
    }]
})

이 스키마에 대한 솔루션

  Test.update({"post._id":"58206a6aa7b5b99e32b7eb58"},
    {$set:{"post.$.comment.0.detail.time":"aajtk"}},
          function(err,data){
//data is updated
})

언급URL : https://stackoverflow.com/questions/18173482/mongodb-update-deeply-nested-subdocument

반응형