229

次のコレクションがあるとします。これについていくつか質問があります。

{
    "_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」の価格を上げ、同時に「total」を上げます (同じ値で)。

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

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

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

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

4

4 に答える 4

263

質問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);
于 2012-05-09T20:57:28.373 に答える
25

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

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

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

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

于 2013-08-02T19:22:40.030 に答える