web-dev-qa-db-ja.com

MongoDBのオブジェクトを部分的に更新して、新しいオブジェクトが既存のオブジェクトにオーバーレイ/マージされるようにする方法

このドキュメントがMongoDBに保存された場合

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

外部からのparam2およびparam3に関する新しい情報を持つオブジェクトを保存する必要があります

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に新しいフィールドのみを(明示的に1つずつ説明せずに)更新させる方法は何ですか?これを取得するには:

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

私はJavaクライアントを使用していますが、どんな例でも歓迎されます

145
Eran Medan

質問を正しく理解している場合は、別のドキュメントの内容でドキュメントを更新しますが、まだ存在しないフィールドのみを更新し、既に設定されているフィールドを完全に無視します(別の値であっても)。

単一のコマンドでそれを行う方法はありません。

最初にドキュメントを照会し、$setの対象を見つけてから更新する必要があります(一致するフィルターとして古い値を使用して、その間に同時更新が発生しないようにします)。


あなたの質問の別の読み方は、あなたは$setに満足しているが、すべてのフィールドを明示的に設定したくないということです。それでは、データをどのように渡しますか?

次のことができることを知っています。

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

私は自分の機能でそれを解決しました。文書内の指定されたフィールドを更新する場合は、明確に対処する必要があります。

例:

{
    _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'));
93
mehmatrix

オブジェクトの他のプロパティに影響を与えることなく、ドット表記を使用して、オブジェクトの奥深くにあるフィールドにアクセスして設定できます。

上記で指定したオブジェクトを考えます:

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

好きなだけ深く掘り下げることができます。これは、既存のプロパティに影響を与えずにオブジェクトに新しいプロパティを追加する場合にも役立ちます。

16
Samir Talwar

最良の解決策は、オブジェクトからプロパティを抽出し、それらをフラットなドット表記のキーと値のペアにすることです。たとえば、このライブラリを使用できます。

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

.flatten関数を使用すると、既存のDBオブジェクトのプロパティが不要に削除/上書きされることを心配せずに、オブジェクトを$ set修飾子に渡すことができるフラットなプロパティセットに変更できます。

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
  }
}
*/

そして、それは完璧なセレクタを形成します-指定されたプロパティのみを更新します。編集:私は時々考古学者になりたいです;)

10
SzybkiSasza

Mongoでは、.規則を使用して、ネストされたドキュメントを更新できます。ご覧ください: mongodbのネストされたドキュメントの更新 。あなたが探しているような、マージ更新に関する過去からの別の質問があります: 「マージ」ドキュメントによるMongoDBアトミック更新

7
Pavel Veller

この方法で成功しました:

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

profileの更新を動的に処理する関数があります

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

注:「プロファイル」は私の実装に固有のものであり、変更したいキーの文字列です。

4
Matt Smith

isPartialObject を設定できるように見えるので、これで目的を達成できます。

1
Patrick Tescher

ええ、このコメントで言及されているように、最良の方法はオブジェクト表記をフラットなキーと値の文字列表現に変換することです: 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オブジェクトをいじる必要があるが、Mongoで作業する際に一貫性がある場合、このライブラリは前述のmongo-dot-notation NPMモジュールよりも多くのオプションを提供します。

P.Sもともとこれをコメントとして言及したかったのですが、どうやら私のS/O担当者はコメントを投稿するほど高くありません。そのため、SzybkiSaszaのコメントに力を入れずに、代替モジュールの提供を強調したかっただけです

1
Ashwin Shankar
    // 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
Vino

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" } }
  • 最初の部分{}は一致クエリで、更新するドキュメント(この場合はすべてのドキュメント)をフィルタリングします。

  • 2番目の部分[{ $addFields: { some_key: new_info } }]は、更新集約パイプラインです。

    • 集約パイプラインの使用を示す角括弧に注意してください。
    • これは集約パイプラインであるため、 $addFields を使用できます。
    • $addFieldsは、必要なことを正確に実行します。新しいオブジェクトが既存のオブジェクトにオーバーレイ/マージされるようにオブジェクトを更新します。
    • この場合、{ param2: "val2_new", param3: "val3_new" }は、some_keyをそのままにして、param1param2の両方を追加または置換することにより、既存のparam3にマージされます。
  • { multi: true }を忘れないでください。そうしないと、最初に一致したドキュメントのみが更新されます。

0
Xavier Guihot

必要なドットパスを含むプロパティ名で更新オブジェクトを作成します。 (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
Andrew

findAndModify()を使用して、既存のオブジェクトの特定のフィールドを更新しようとしました。

https://docs.mongodb.com/manual/reference/method/db.collection.findAndModify/

0
nick-s

$ setを使用してこのプロセスを実行します

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