1

私は C# ドライバー (NuGet の v1.8.3) を使用していますが、$addtoSet/upsert操作によって実際に新しい項目が特定の配列に追加されたのか、それとも項目が既に存在していたのかを判断するのに苦労しています。

新しいアイテムの追加は、ドキュメントがまったく存在せず、アップサートによって作成されたばかりである場合と、ドキュメントは存在するが配列が存在しないか、指定されたアイテムが含まれていない場合の 2 つのケースに分類される可能性があります。

これを行う必要がある理由は、MongoDB にロードする大量のデータ セットがあり、処理中に壊れる可能性があるためです (すべきではありませんが、壊れる可能性があります)。これが発生した場合、ダウンストリーム処理を重複させずに最初からバックアップを開始できるようにする必要があります (処理を冪等に保ちます)。私のフローでは、項目が新しく追加されたと判断された場合、その項目のダウンストリーム処理をキューに入れます。ドキュメントに既に追加されていると判断された場合、それ以上のダウンストリーム作業は必要ありません。私の問題は、アイテムが配列に既に存在し、実際には何も変更されていない場合でも、呼び出しが 1 つのドキュメントを変更したという結果が常に返されることです。

C# ドライバー API に関する私の理解に基づいて、 を使用して呼び出しを行いWriteConcern.Acknowledged、 をチェックして、WriteConcernResult.DocumentsAffected実際にドキュメントが更新されたかどうかを確認できるはずです。

私の問題は、すべての場合において、書き込み懸念の結果が 1 つのドキュメントが更新されたことを返すことです。:/

これは、私のコードが呼び出しているドキュメントの例です。$addToSetこの特定のアイテムが「アイテム」リストに含まれている場合と含まれていない場合があります。

{
    "_id" : "some-id-that-we-know-wont-change",
    "items" : [ 
        {                
            "s" : 4,
            "i" : "some-value-we-know-is-static",
        }
    ]
}

私のクエリは常に_id、処理メタデータに基づいて既知の値を使用します。

var query = new QueryDocument
{
     {"_id", "some-id-that-we-know-wont-change"}                       
};

私の更新は次のとおりです。

var result = mongoCollection.Update(query, new UpdateDocument()
{
     {                                                
          "$addToSet", new BsonDocument()
               {
                    { "items", new BsonDocument()
                         {
                              { "s", 4 },
                              { "i", "some-value-we-know-is-static" }                                                                            
                          } 
                    }
               }
     }
}, new MongoUpdateOptions() { Flags = UpdateFlags.Upsert, WriteConcern = WriteConcern.Acknowledged }); 

if(result.DocumentsAffected > 0 || result.UpdatedExisting)
{
     //DO SOME POST PROCESSING WORK THAT SHOULD ONLY HAPPEN ONCE PER ITEM                                                
}

空のコレクションに対してこのコードを 1 回実行すると、ドキュメントが追加され、期待どおりの応答が得られます ( DocumentsAffected = 1, UpdatedExisting = false)。再度 (何度でも) 実行すると、ドキュメントは変更されていないため更新されていないように見えますが、結果は予期しないものになりました ( DocumentsAffected = 1UpdatedExisting = true)。

DocumentsAffected = 0ドキュメントが変更されていない場合、これは返されるべきではありませんか?

これらの呼び出しを 1 日に何百万回も行う必要があるため、このロジックをアイテムごとに複数の呼び出しに変えることをためらっています (最初にアイテムが指定されたドキュメント配列に存在するかどうかを確認し、次に追加/キューに入れるか、単にスキップします)すべて可能です。

これを1回の呼び出しで機能させる方法はありますか?

4

1 に答える 1

1

もちろん、ここで行っていることは、ドキュメントが更新または挿入されたかどうか、または実際にはどちらの操作も行われていないかどうかを示す応答を実際にチェックすることです。$addToSetこれは、更新を実行してからドキュメントを更新するための最良の指標です。

$addToSetオペレーター自体は重複を生成できません。これがオペレーターの性質です。しかし、実際にはロジックに問題がある可能性があります。

{                                                
      "$addToSet", new BsonDocument()
           {
                { "items", new BsonDocument()
                     {
                          { "id", item.Id },
                          { "v", item.Value } 
                     }
                }
           }
 }

したがって、「セット」内のアイテムが 2 つのフィールドで構成されていることを明確に示しているため、そのコンテンツが何らかの形で異なる場合 (つまり、同じ ID で値が異なる場合)、そのアイテムは実際にはセットの「一意の」メンバーであり、追加されます。たとえば、$addToSetオペレーターが一意の識別子としての「id」に基づいて新しい値を追加しないようにする方法はありません。実際にコードでそれをロールバックする必要があります。

ここでの重複の 2 つ目の可能性は、クエリ部分が更新する必要があるドキュメントを正しく検出していないことです。この結果、「セット」で新しく指定されたメンバーのみを含む新しいドキュメントが作成されます。したがって、よくある使用上の間違いは次のようなものです。

db.collection.update(
    { 
        "id": ABC,
        "items": { "$elemMatch": {
            "id": 123, "v": 10
         }},
    {
        "$addToSet": {
            "items": {
                "id": 123, "v": 10
            }
        }
    },
    { "upsert": true }
)

既存のドキュメントには「セット」に指定された要素が含まれていなかったため、そのような操作の結果は常に新しいドキュメントを作成します。正しい実装は、「セット」メンバーの存在をチェックせ$addToSet、作業を許可することです。

実際に、サブドキュメントのすべての要素がまったく同じ「セット」で真の重複エントリが発生している場合は、存在するか過去の他のコードが原因です。

新しいエントリが作成されていることが確実な場合は$push、同じフィールドで動作しているように見えるコード内のまたは実際の配列操作のインスタンスのコードを調べます。

しかし、オペレーターを正しく使用していれば、$addToSet意図したとおりに動作します。

于 2014-04-10T00:26:08.793 に答える