1. ホーム
  2. mongodb

[解決済み】ドキュメントの配列内のオブジェクトを更新する方法(入れ子更新)について)

2022-04-05 11:22:28

質問

次のようなコレクションがあると仮定して、いくつか質問があります。

{
    "_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"の価格を上げたいのですが、どうすればいいですか? と表示され、それが存在しない場合は の場合、配列に追加される必要があります。

  2. 2つのフィールドを同時に更新するにはどうすればよいですか?例えば、"my_item_three"の価格を上げると同時に、"total"(同じ値で)を上げることができます。

そうしないと、クライアント側(Python)でドキュメントをロードして、更新されたドキュメントを構築して、MongoDBで既存のドキュメントと置き換える必要があるからです。

これは私が試したもので、問題なく動作します もし オブジェクトが存在します。

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

しかし、キーが存在しない場合は何もしません。 また、ネストしたオブジェクトを更新するだけです。このコマンドでは、"total" フィールドを同様に更新する方法はありません。

解決方法は?

質問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);