code

Mongodb 업데이트 깊이 중첩 하위 문서

codestyles 2020. 12. 25. 09:53
반응형

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

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

그래도 "배열을 포함하는 해당 쿼리 필드없이 위치 연산자를 적용 할 수 없습니다"오류가 발생합니다. mongo에서 이것을 수행하는 적절한 방법은 무엇입니까? 이것은 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집합이 있다고 가정 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 }}
)

몽구스를 사용하여 해결했습니다.

알아야 할 것은 체인에있는 모든 하위 문서의 '_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 문서 의 하위 문서 _id 메서드 대한 추가 정보 .

description : _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/#specify-arrayfilters-for-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


MongoDB가 이에 대한 좋은 메커니즘을 제공하지 않는 것처럼 보이기 때문에 mongoose를 사용하여를 사용하여 mongo 컬렉션에서 요소를 추출 .findOne(...)하고 관련 하위 요소에 대해 for 루프 검색을 실행하는 것이 현명하다는 것을 알았습니다 (ObjectID라고 함). JSON을 수정 한 다음 수행하십시오. Schema.markModified('your.subdocument'); Schema.save();아마도 효율적이지는 않지만 매우 간단하고 잘 작동합니다.


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

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

참고 :이 질문에 대한 질문은 다음 날짜에 Aug 11 2013해결되었습니다.Aug 11 2017


나는 오늘 같은 종류의 문제에 직면하고 있었고 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
});

그리고 익스프레스에서 몽구스를 사용하여 주어진 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
        });
    });
}
}

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

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

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


Okkk.we can update our nested subdocument in mongodb.this is our schema.

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

solution for this schema

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

EASY SOLUTION FOR Mongodb 3.2+ https://docs.mongodb.com/manual/reference/method/db.collection.replaceOne/

I had a similar situation and solved it like this. I was using mongoose, but it should still work in vanilla MongoDB. Hope it's useful to someone.

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

Depending on your use case, you might be able to skip the initial findOne()

ReferenceURL : https://stackoverflow.com/questions/18173482/mongodb-update-deeply-nested-subdocument

반응형