MongoDB-ドキュメントの配列内のオブジェクトを更新する(ネストされた更新)

2012-05-10 mongodb

次のコレクションがあると仮定します。

{
    "_id" : ObjectId("4faaba123412d654fe83hg876"),
    "user_id" : 123456,
    "total" : 100,
    "items" : [
            {
                    "item_name" : "my_item_one",
                    "price" : 20
            },
            {
                    "item_name" : "my_item_two",
                    "price" : 50
            },
            {
                    "item_name" : "my_item_three",
                    "price" : 30
            }
    ]
}

1-「item_name」:「my_item_two」の価格を上げたいのです存在しない場合は 、「items」配列に追加する必要があります。

2-2つのフィールドを同時に更新するにはどうすればよいですか。たとえば、「my_item_three」の価格を引き上げると同時に、「合計」を引き上げます(値は同じ)。

私はこれをMongoDB側で行うことを好みます。そうでない場合は、クライアント側(Python)でドキュメントをロードし、更新されたドキュメントを作成して、MongoDBの既存のドキュメントで置き換える必要があります。

更新 これは私が試したものであり、 オブジェクトが存在する場合に正常に動作します:

db.test_invoice.update({user_id : 123456 , "items.item_name":"my_item_one"} , {$inc: {"items.$.price": 10}})

しかし、キーが存在しない場合は何もしません。 また、ネストされたオブジェクトのみを更新します。このコマンドでは、「合計」フィールドも更新する方法はありません。

Answers

質問1では、2つの部分に分けましょう。まず、「items.item_name」が「my_item_two」と等しいドキュメントをインクリメントします。このためには、定位置 "$"演算子を使用する必要があります。何かのようなもの:

 db.bar.update( {user_id : 123456 , "items.item_name" : "my_item_two" } , 
                {$inc : {"items.$.price" : 1} } , 
                false , 
                true);

これは、配列で最初に一致したサブドキュメントのみをインクリメントすることに注意してください(「item_name」が「my_item_two」に等しい別のドキュメントが配列にある場合、インクリメントされません)。しかし、これはあなたが望むものかもしれません。

2番目の部分はトリッキーです。次のように、「my_item_two」なしで新しい項目を配列にプッシュできます。

 db.bar.update( {user_id : 123456, "items.item_name" : {$ne : "my_item_two" }} , 
                {$addToSet : {"items" : {'item_name' : "my_item_two" , 'price' : 1 }} } ,
                false , 
                true);

質問2の場合、答えは簡単です。 「my_item_three」を含むドキュメントのitem_threeの合計と価格を増やすには、複数のフィールドで同時に$ inc演算子を使用できます。何かのようなもの:

db.bar.update( {"items.item_name" : {$ne : "my_item_three" }} ,
               {$inc : {total : 1 , "items.$.price" : 1}} ,
               false ,
               true);

単一のクエリでこれを行う方法はありません。最初のクエリでドキュメントを検索する必要があります。

ドキュメントが存在する場合:

db.bar.update( {user_id : 123456 , "items.item_name" : "my_item_two" } , 
                {$inc : {"items.$.price" : 1} } , 
                false , 
                true);

そうしないと

db.bar.update( {user_id : 123456 } , 
                {$addToSet : {"items" : {'item_name' : "my_item_two" , 'price' : 1 }} } ,
                false , 
                true);

条件{$ne : "my_item_two" }を追加する必要はありません。

また、マルチスレッド環境では、2番目のスレッド(ドキュメントが見つからなかった場合は挿入)を一度に1つのスレッドしか実行できないことに注意する必要があります。そうしないと、重複する埋め込みドキュメントが挿入されます。

$set演算子を使用して、オブジェクトフィールド内のネストされた配列を更新し、値を更新できます

db.getCollection('geolocations').update( 
   {
       "_id" : ObjectId("5bd3013ac714ea4959f80115"), 
       "geolocation.country" : "United States of America"
   }, 
   { $set: 
       {
           "geolocation.$.country" : "USA"
       } 
    }, 
   false,
   true
);

Related