새로운 객체가 기존 객체와 오버레이 / 병합되도록 MongoDB에서 객체를 부분적으로 업데이트하는 방법


183

이 문서가 MongoDB에 저장된 경우

{
   _id : ...,
   some_key: { 
        param1 : "val1",
        param2 : "val2",
        param3 : "val3"
   }
}

새로운 정보를 가진 객체 param2param3외부 세계에서 저장해야

var new_info = {
    param2 : "val2_new",
    param3 : "val3_new"
};

param1이 제거되지 않도록 객체의 기존 상태 위에 새 필드를 병합 / 오버레이하고 싶습니다

이렇게

db.collection.update(  { _id:...} , { $set: { some_key : new_info  } } 

MongoDB가 요청 한대로 정확하게 작동하게하고 some_key를 해당 값으로 설정합니다. 오래된 것을 대체합니다.

{
   _id : ...,
   some_key: { 
      param2 : "val2_new",
      param3 : "val3_new"
   }
}

MongoDB가 새 필드 만 명시 적으로 업데이트하지 않는 방법은 무엇입니까? 이것을 얻으려면 :

{
   _id : ...,
   some_key: { 
        param1 : "val1",
        param2 : "val2_new",
        param3 : "val3_new"
   }
}

Java 클라이언트를 사용하고 있지만 모든 예를 들어 주시면 감사하겠습니다.


2
$ merge 이슈에 투표하십시오 : jira.mongodb.org/browse/SERVER-21094
Ali

답변:


107

질문을 올바르게 이해하면 다른 문서의 내용으로 문서를 업데이트하고 아직 존재하지 않는 필드 만 업데이트하고 이미 설정된 필드 (다른 값이더라도)를 완전히 무시하려고합니다.

단일 명령으로는 그렇게 할 수 없습니다.

먼저 문서를 쿼리하고 원하는 것을 $set파악한 다음 업데이트해야합니다 (이전 값을 일치하는 필터로 사용하여 사이에 동시 업데이트를받지 않도록해야합니다).


귀하의 질문에 대한 또 다른 독서는 당신이 만족 $set하지만 모든 필드를 명시 적으로 설정하고 싶지 않다는 것입니다. 그러면 데이터를 어떻게 전달 하시겠습니까?

다음을 수행 할 수 있다는 것을 알고 있습니다.

db.collection.update(  { _id:...} , { $set: someObjectWithNewData } 

모든 필드를 무조건 설정하려면 $ multi 매개 변수를 다음과 같이 사용할 수 있습니다 . db.collection.update( { _id:...} , { $set: someObjectWithNewData, { $multi: true } } )
Villager

12
$ multi 매개 변수가 없습니다. 다중 옵션 (연결된 옵션)이 있지만 필드 업데이트 방법에는 영향을 미치지 않습니다. 단일 문서 또는 쿼리 매개 변수와 일치하는 모든 문서를 업데이트할지 여부를 제어합니다.
밥 컨

1
이것은 아무 의미가 없습니다. 몽고를 다룰 때 업데이트가 원 자성 작업을 갖는 것이 바람직합니다. 먼저 읽을 때 다른 사람이 데이터를 변경할 때마다 더티 읽습니다. 그리고 mongo는 거래가 없기 때문에 행복하게 데이터를 손상시킬 수 있습니다.
froginvasion 2012 년

@froginvasion : 업데이트에서 일치하는 필터로 이전 값을 사용하여 원 자성으로 만들 수 있습니다. 그러면 동시에 충돌하는 업데이트가 있으면 업데이트가 실패합니다. 그런 다음이를 감지하고 그에 따라 조치를 취할 수 있습니다. 이것을 낙관적 잠금이라고합니다. 그러나 예, 응용 프로그램을 올바르게 구현하면 Mongo 자체에 기본 제공 트랜잭션이 없습니다.
Thilo

나는 이것이 자바 스크립트에서 두 객체를 병합하는 더 일반적인 질문이라고 생각합니다. IIRC의 아이디어는 단순히 새로운 것에 대한 속성을 기존에 할당하는 것입니다
information_interchange

110

나는 내 자신의 기능으로 그것을 해결했다. 문서에서 지정된 필드를 업데이트하려면 명확하게 해결해야합니다.

예:

{
    _id : ...,
    some_key: { 
        param1 : "val1",
        param2 : "val2",
        param3 : "val3"
    }
}

param2 만 업데이트하려면 다음을 수행하십시오.

db.collection.update(  { _id:...} , { $set: { some_key : new_info  } }  //WRONG

다음을 사용해야합니다.

db.collection.update(  { _id:...} , { $set: { some_key.param2 : new_info  } } 

그래서 나는 그런 함수를 썼습니다 :

function _update($id, $data, $options=array()){

    $temp = array();
    foreach($data as $key => $value)
    {
        $temp["some_key.".$key] = $value;
    } 

    $collection->update(
        array('_id' => $id),
        array('$set' => $temp)
    );

}

_update('1', array('param2' => 'some data'));

11
이것이 정답입니다. 더 나은 flattenObject 기능을 참조 gist.github.com/penguinboy/762197
steph643

답변 해 주셔서 감사합니다. 사용자 컬렉션에서 사용자의 특정 키 값을 다음과 같이 수정할 수있었습니다.db.users.update( { _id: ObjectId("594dbc3186bb5c84442949b1") }, { $set: { resume: { url: "http://i.imgur.com/UFX0yXs.jpg" } } } )
Luke Schoen

이 작업은 원자적이고 격리되어 있습니까? 2 개 병렬 스레드의 시도는 같은 문서의 2 개 개의 다른 필드를 설정하는 경우 경쟁 조건과 예측할 수없는 결과에 그 결과는 것이다 의미
그래서 - 랜덤 친구를

21

점 표기법을 사용하여 객체의 다른 속성에 영향을주지 않고 객체 내부의 필드에 액세스하고 설정할 수 있습니다.

위에서 지정한 객체가 주어지면 :

> db.test.insert({"id": "test_object", "some_key": {"param1": "val1", "param2": "val2", "param3": "val3"}})
WriteResult({ "nInserted" : 1 })

우리는 단지 업데이트 할 수 있습니다 some_key.param2some_key.param3:

> db.test.findAndModify({
... query: {"id": "test_object"},
... update: {"$set": {"some_key.param2": "val2_new", "some_key.param3": "val3_new"}},
... new: true
... })
{
    "_id" : ObjectId("56476e04e5f19d86ece5b81d"),
    "id" : "test_object",
    "some_key" : {
        "param1" : "val1",
        "param2" : "val2_new",
        "param3" : "val3_new"
    }
}

원하는만큼 깊이 파고들 수 있습니다. 또한 기존 속성에 영향을주지 않고 객체에 새 속성을 추가 할 때 유용합니다.


"일부 키"와 같은 새 키를 추가 할 수 있습니까 : ""param1 ":"val1 ","param2 ":"val2_new ","param3 ":"val3_new ","param4 ":"val4_new ",}
Urvashi Soni

17

가장 좋은 해결책은 객체에서 속성을 추출하여 평평한 점 표기법 키-값 쌍으로 만드는 것입니다. 예를 들어이 라이브러리를 사용할 수 있습니다.

https://www.npmjs.com/package/mongo-dot-notation

그것은이 .flatten필요없이 기존 DB 객체의 속성이 될 것이라고 우려 삭제 / 덮어 쓰기하지 않고, 당신이 다음 $ 설정 수정에 제공 될 수있는 성질의 평면 집합으로 개체를 변경할 수있는 기능.

mongo-dot-notation문서 에서 가져온 것 :

var person = {
  firstName: 'John',
  lastName: 'Doe',
  address: {
    city: 'NY',
    street: 'Eighth Avenu',
    number: 123
  }
};



var instructions = dot.flatten(person)
console.log(instructions);
/* 
{
  $set: {
    'firstName': 'John',
    'lastName': 'Doe',
    'address.city': 'NY',
    'address.street': 'Eighth Avenu',
    'address.number': 123
  }
}
*/

그런 다음 완벽한 선택자를 형성합니다. 주어진 속성 만 업데이트합니다. 편집 : 나는 몇 번 고고학자가되고 싶다.)



6

나는 이런 식으로 성공했다 :

db.collection.update(  { _id:...} , { $set: { 'key.another_key' : new_info  } } );

프로필 업데이트를 동적으로 처리하는 기능이 있습니다

function update(prop, newVal) {
  const str = `profile.${prop}`;
  db.collection.update( { _id:...}, { $set: { [str]: newVal } } );
}

참고 : 'profile'은 구현에 따라 다르며 수정하려는 키 문자열입니다.


1

원하는 것을 달성 할 수있는 isPartialObject 를 설정할 수있는 것처럼 보입니다 .


그것을 시도했다. 내가 실수하지 않으면 부분 객체를 저장할 수는 없지만 ... 감사합니다.
Eran Medan

1
    // where clause DBObject
    DBObject query = new BasicDBObject("_id", new ObjectId(id));

    // modifications to be applied
    DBObject update = new BasicDBObject();

    // set new values
    update.put("$set", new BasicDBObject("param2","value2"));

   // update the document
    collection.update(query, update, true, false); //3rd param->upsertFlag, 4th param->updateMultiFlag

업데이트 할 필드가 여러 개인 경우

        Document doc = new Document();
        doc.put("param2","value2");
        doc.put("param3","value3");
        update.put("$set", doc);

1

시작 Mongo 4.2, db.collection.update()같은 집계 연산자를 사용하여 허용 집계 파이프 라인, 받아 들일 수 $addFields입력 문서 및 새로 추가 된 필드에서 기존의 모든 필드를 출력한다 :

var new_info = { param2: "val2_new", param3: "val3_new" }

// { some_key: { param1: "val1", param2: "val2", param3: "val3" } }
// { some_key: { param1: "val1", param2: "val2"                 } }
db.collection.update({}, [{ $addFields: { some_key: new_info } }], { multi: true })
// { some_key: { param1: "val1", param2: "val2_new", param3: "val3_new" } }
// { some_key: { param1: "val1", param2: "val2_new", param3: "val3_new" } }
  • 첫 번째 부분 {}은 일치 쿼리이며 업데이트 할 문서 (이 경우 모든 문서)를 필터링합니다.

  • 두 번째 부분 [{ $addFields: { some_key: new_info } }]은 업데이트 집계 파이프 라인입니다.

    • 집계 파이프 라인 사용을 나타내는 대괄호는주의하십시오.
    • 이것은 집계 파이프 라인이므로 사용할 수 있습니다 $addFields.
    • $addFields 새 객체가 기존 객체와 겹쳐 지거나 병합되도록 객체를 업데이트합니다.
    • 이 경우, { param2: "val2_new", param3: "val3_new" }기존에 병합됩니다 some_key유지에 의해 param1훼손되지 않은 및 중 하나를 추가 또는 둘 모두를 교체 param2하고 param3.
  • 잊지 마십시오 { multi: true }. 그렇지 않으면 첫 번째 일치하는 문서 만 업데이트됩니다.


1
db.collection.update(  { _id:...} , { $set: { some_key : new_info  } } 

db.collection.update( { _id: ..} , { $set: { some_key: { param1: newValue} } } ); 

이 도움을 바랍니다!


1

오히려 upsert를 수행 할 수 있습니다. MongoDB 의이 작업은 문서를 수집에 저장하는 데 사용됩니다. 문서가 쿼리 기준과 일치하면 업데이트 작업을 수행하고 그렇지 않으면 새 문서를 컬렉션에 삽입합니다.

아래와 비슷한 것

db.employees.update(
    {type:"FT"},
    {$set:{salary:200000}},
    {upsert : true}
 )

0

$ set 사용이 과정을 수행

.update({"_id": args.dashboardId, "viewData._id": widgetId}, {$set: {"viewData.$.widgetData": widgetDoc.widgetData}})
.exec()
.then(dashboardDoc => {
    return {
        result: dashboardDoc
    };
}); 

0

필요한 도트 경로를 포함하여 속성 이름으로 업데이트 개체를 만듭니다. (OP의 예제에서 "somekey."+)를 사용한 다음 업데이트를 사용하십시오.

//the modification that's requested
updateReq = { 
   param2 : "val2_new",
   param3 : "val3_new"   
}

//build a renamed version of the update request
var update = {};
for(var field in updateReq){
    update["somekey."+field] = updateReq[field];
}

//apply the update without modifying fields not originally in the update
db.collection.update({._id:...},{$set:update},{upsert:true},function(err,result){...});

0

예,이 의견에서 언급 한 것처럼 객체 표기법을 플랫 키-값 문자열 표현으로 변환하는 것이 가장 좋습니다. https://stackoverflow.com/a/39357531/2529199

이 NPM 라이브러리를 사용하는 대체 방법을 강조하고 싶습니다 : https://www.npmjs.com/package/dot-object 도트 표기법을 사용하여 다른 객체를 조작 할 수 있습니다.

이 패턴을 사용하여 키-값을 함수 변수로 받아 들일 때 다음과 같이 프로그래밍 방식으로 중첩 객체 속성을 만들었습니다.

const dot = require('dot-object');

function(docid, varname, varvalue){
  let doc = dot.dot({
      [varname]: varvalue 
  });

  Mongo.update({_id:docid},{$set:doc});
}

이 패턴을 사용하면 중첩 된 속성과 단일 수준 속성을 서로 바꾸어 사용할 수 있으며 Mongo에 깔끔하게 삽입 할 수 있습니다.

Mongo 이외의 JS Objects, 특히 클라이언트 측에서 놀아야하지만 Mongo로 작업 할 때 일관성이있는 경우이 라이브러리는 앞에서 언급 한 mongo-dot-notationNPM 모듈 보다 더 많은 옵션을 제공합니다 .

추신 : 나는 원래 이것을 이것을 코멘트로 언급하고 싶었지만 내 S / O 담당자는 코멘트를 게시하기에 충분히 높지 않은 것 같습니다. 따라서 SzybkiSasza의 의견을 밝히지 않고 대안 모듈 제공을 강조하고 싶었습니다.



0

객체를 상호 교환하여 업데이트 한 다음 업데이트 된 필드와 함께 객체를 저장하면됩니다. 아래 같은 일

function update(_id) {
    return new Promise((resolve, reject) => {

        ObjModel.findOne({_id}).exec((err, obj) => {

            if(err) return reject(err)

            obj = updateObject(obj, { 
                some_key: {
                    param2 : "val2_new",
                    param3 : "val3_new"
                }
            })

            obj.save((err, obj) => {
                if(err) return reject(err)
                resolve(obj)
            })
        })
    })
}


function updateObject(obj, data) {

    let keys = Object.keys(data)

    keys.forEach(key => {

        if(!obj[key]) obj[key] = data[key]

        if(typeof data[key] == 'object') 
            obj[key] = updateObject(obj[key], data[key])
        else
            obj[key] = data[key]
    })

    return obj
}

0

포함 된 문서를 사용해야합니다 (경로 객체를 문자열로 표시)

let update = {}
Object.getOwnPropertyNames(new_info).forEach(param => {
   update['some_key.' + param] = new_info[param]
})

따라서 JavaScript에서는 Spread Operator (...)를 사용하여 업데이트 할 수 있습니다.

db.collection.update(  { _id:...} , { $set: { ...update  } } 
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.