web-dev-qa-db-ja.com

MongoDB-$ setを更新または配列要素をプッシュ

製品コレクションでは、viewedByとviewedDateの2つのフィールドを持つ最近のビューの配列があります。

シナリオでviewedbyのレコードがすでにある場合、それを更新する必要があります。たとえば、私がこのような配列を持っている場合:-

"recentviews" : [ 
        {
            "viewedby" : "abc",
            "vieweddate" : ISODate("2014-05-08T04:12:47.907Z")
        }
    ]

そしてユーザーはabcなので、上記を更新する必要があります&abcのレコードがない場合は$Push

私が試してみました $set 次のように :-

db.products.update( { _id: ObjectId("536c55bf9c8fb24c21000095") },
                    { $set: 
                        { "recentviews": 
                            { 
                                viewedby: 'abc',
                                vieweddate: ISODate("2014-05-09T04:12:47.907Z") 
                            }
                        } 
                     }
            )

上記のクエリは、配列内の他のすべての要素を消去します。

17
Anup

実際、あなたがしているように見えることを行うのは単一の操作ではありませんが、これを行うために、または他の考えられる状況をカバーするために必要な部分について説明します。

あなたが探しているのは、一部には positional $ 演算子です。必要な配列の要素も「検索」するには、クエリの一部が必要です。

db.products.update(
    { 
        "_id": ObjectId("536c55bf9c8fb24c21000095"),
        "recentviews.viewedby": "abc"
    },
    { 
        "$set": { 
            "recentviews.$.vieweddate": ISODate("2014-05-09T04:12:47.907Z")
        }
    }
)

したがって、$は配列内の一致した位置を表し、更新部分は配列内のどの項目を更新するかを認識します。配列内のドキュメントの個々のフィールドにアクセスしたり、ドキュメント全体を指定してその位置で更新したりできます。

db.products.update(
    { 
        "_id": ObjectId("536c55bf9c8fb24c21000095"),
        "recentviews.viewedby": "abc"
    },
    { 
        "$set": { 
            "recentviews.$": {
                 "viewedby": "abc",
                 "vieweddate": ISODate("2014-05-09T04:12:47.907Z")
        }
    }
)

フィールドが実際には変更されず、まったく同じものが存在しない場合に新しい配列要素を挿入するだけの場合は、 $addToSet

db.products.update(
    { 
        "_id": ObjectId("536c55bf9c8fb24c21000095"),
        "recentviews.viewedby": "abc"
    },
    { 
        $addToSet:{ 
            "recentviews": {
                 "viewedby": "abc",
                 "vieweddate": ISODate("2014-05-09T04:12:47.907Z")
        }
    }
)

ただし、それが存在しない場合に特異キー値による配列への「プッシュ」を単に探している場合は、最初に配列内の要素が存在するかどうかを確認してから- $Push ないステートメント。

更新によって影響を受けるドキュメントの数を追跡することにより、これを行う際にmongooseメソッドからいくつかの助けを得ます。

Product.update(
    { 
        "_id": ObjectId("536c55bf9c8fb24c21000095"),
        "recentviews.viewedby": "abc"
    },
    { 
        "$set": { 
            "recentviews.$": {
                 "viewedby": "abc",
                 "vieweddate": ISODate("2014-05-09T04:12:47.907Z")
        }
    },
    function(err,numAffected) {

        if (numAffected == 0) {
            // Document not updated so you can Push onto the array
            Product.update(
                { 
                    "_id": ObjectId("536c55bf9c8fb24c21000095")
                },
                { 
                    "$Push": { 
                        "recentviews": {
                            "viewedby": "abc",
                            "vieweddate": ISODate("2014-05-09T04:12:47.907Z")
                        }
                    }
                },
                function(err,numAffected) {

                }
            );
        }            

    }
);

ここでの唯一の注意点は、MongoDB 2.6から以前のバージョンへのwriteConcernメッセージの実装が少し変更されていることです。 mongoose APIが実際にコールバックでnumAffected引数の戻りをどのように実装するかについて不明な点があるため、違いが何かを意味している可能性があります。

以前のバージョンでは、最初の更新で送信したデータが既存の要素と完全に一致し、実際の変更が必要ない場合でも、実際には何も更新されていなくても、「変更された」量は1として返されました。

MongoDB 2.6以降、書き込み関連の応答には2つの部分が含まれます。 1つの部分は変更されたドキュメントを示し、もう1つの部分は一致を示します。そのため、既存の要素と一致するクエリ部分によって一致が返されますが、実際に変更が必要な場合、実際に変更されたドキュメントの数は0として返されます。

したがって、戻り値が実際にマングースでどのように実装されているかによっては、実際には $addToSet を使用する方が安全な場合があります=影響を受けるドキュメントがゼロである理由が、正確な要素がすでに存在していることだけではないことを確認するための、内部更新の演算子。

20
Neil Lunn