2017-06-15 3 views
1

Ich habe einige Dokumente in einer MongoDB, die so aussieht:Conditional Upsert (Einsatz), wenn das Dokument in MongoDB Aktualisierung

{type: type1, version: 2, data: ...} 
{type: type1, version: 3, data: ...} 
{type: type2, version: 1, data: ...} 
{type: type2, version: 2, data: ...} 
... 

Ich möchte die Daten aktualisieren für den Abgleich Typ UND Version oder erstellen ein neues Dokument für gegebene Typ wenn Version Mismatch möchte aber neue Dokumente mit neuen Art verbieten, die Schaffung wenn ich tun:

db.getCollection('products').update({"type": "unknown_type", "version" : "99"}, {$set: {"version": 99, "data": new data}}, {"upsert": true}) 

es ein neues Dokument erstellt:

{type: unknown_type, version: 99, data: ...} 

das ist genau das, was ich verbieten möchte. Gibt es eine Möglichkeit, diese Operation in einem Aufruf auszuführen? Gibt es eine Möglichkeit, Werte für einige Felder einzuschränken?

+0

Verwenden Sie dann nicht upsert. Der einzige Grund, warum Sie etwas Neues bekommen, ist, dass Sie die Option auf "wahr" setzen. Was erwartest du noch? –

+0

Wie ich in meiner Frage sagte: "Ich möchte die Daten für den passenden Typ UND Version aktualisieren oder ein neues Dokument für den gegebenen Typ erstellen, wenn die Version nicht übereinstimmt" – karlitos

+0

Dann nicht in einem Befehl. Nein. Sie können die Funktion ausschalten, indem Sie sie einfach nicht setzen und nur das Verhalten für das Update zulassen. Wenn dann nichts von der Operation aktualisiert wird, können Sie stattdessen eine Einfügung durchführen. Sie könnten möglicherweise Bulk-Operationen mit beiden Anweisungen als "ungeordnet" ausführen, die dann den doppelten Schlüsselfehler bei einem Einfügeversuch ignorieren würden. Das ist ungefähr so ​​gut wie es geht. –

Antwort

0

Die beste Handhabung ich für diesen Anwendungsfall sehen kann, ist mit "Bulk Operations", um sowohl „update“ senden und „Einfügen“ Befehle in der gleichen Anfrage. Wir brauchen auch einen eindeutigen Index, um zu erzwingen, dass Sie keine neuen Kombinationen der beiden Felder erstellen.

Beginnend mit diesen Dokumenten:

{ "type" : "type1", "version" : 2 } 
{ "type" : "type1", "version" : 3 } 
{ "type" : "type2", "version" : 1 } 
{ "type" : "type2", "version" : 2 } 

und einen eindeutigen Index für die beiden Felder zu erstellen:

db.products.createIndex({ "type": 1, "version": 1 },{ "unique": true }) 

Dann versuchen wir und tun etwas, das tatsächlich einsetzen wird, sowohl für die Bulk-Operationen mit Das Update und die Einfügung:

db.products.bulkWrite(
    [ 
    { "updateOne": { 
     "filter": { "type": "type3", "version": 1 }, 
     "update": { "$set": { "data": {} } } 
    }}, 
    { "insertOne": { 
     "document": { "type": "type3", "version": 1, "data": { } } 
    }} 
    ], 
    { "ordered": false } 
) 

Wir sollten eine Antwort wie folgt erhalten:

{ 
     "acknowledged" : true, 
     "deletedCount" : 0, 
     "insertedCount" : 1, 
     "matchedCount" : 0, 
     "upsertedCount" : 0, 
     "insertedIds" : { 
       "1" : ObjectId("594257b6fc2a40e470719470") 
     }, 
     "upsertedIds" : { 

     } 
} 

hier die die "update" Operation reflektierenden matchedCount war 0 Anmerkung:

 "matchedCount" : 0, 

Wenn ich die gleiche Sache wieder tat, mit verschiedenen Daten:

db.products.bulkWrite(
    [ 
    { "updateOne": { 
     "filter": { "type": "type3", "version": 1 }, 
     "update": { "$set": { "data": { "a": 1 } } } 
    }}, 
    { "insertOne": { 
     "document": { "type": "type3", "version": 1, "data": { "a": 1 } } 
    }} 
    ], 
    { "ordered": false } 
) 

Dann sehen wir:

BulkWriteError({ 
     "writeErrors" : [ 
       { 
         "index" : 1, 
         "code" : 11000, 
         "errmsg" : "E11000 duplicate key error collection: test.products index: type_1_version_1 dup key: { : \"type3\", : 1.0 }", 
         "op" : { 
           "_id" : ObjectId("5942583bfc2a40e470719471"), 
           "type" : "type3", 
           "version" : 1, 
           "data" : { 
             "a" : 1 
           } 
         } 
       } 
     ], 
     "writeConcernErrors" : [ ], 
     "nInserted" : 0, 
     "nUpserted" : 0, 
     "nMatched" : 1, 
     "nModified" : 1, 
     "nRemoved" : 0, 
     "upserted" : [ ] 
}) 

Welche wird konsequent einen Fehler in allen Fahrern werfen, aber wir können auch im Detail der Antwort sehen:

 "nMatched" : 1, 
     "nModified" : 1, 

Was bedeutet, dass, obwohl die „Einfügen“ nicht bestanden, die „update“ tatsächlich tat es Job. Wichtig ist hierbei zu beachten ist, dass, während „Fehler“ in dem „Batch“ auftreten kann, können wir sie behandeln, wenn sie von der Art vorhergesagt sind, was der 11000 Code für doppelte Schlüssel Fehler, die uns erwartet haben.

So sieht die Enddaten natürlich wie:

{ "type" : "type1", "version" : 2 } 
{ "type" : "type1", "version" : 3 } 
{ "type" : "type2", "version" : 1 } 
{ "type" : "type2", "version" : 2 } 
{ "type" : "type3", "version" : 1, "data" : { "a" : 1 } } 

das ist, was Sie hier erreichen wollte.

So werden die Operationen eine Ausnahme erzeugen, sondern durch die Markierung als „ungeordnet“ mit der { "ordered": false } Option .bulkWrite() dann wird es zumindest alle Anweisungen verpflichten, das nicht zu einem Fehler geführt hat.

In diesem Fall ist das typische Ergebnis, dass entweder die "Einfügung" funktioniert und es keine Aktualisierung gibt, oder die "Einfügung" fehlschlägt, wo die "Aktualisierung" gilt. Wenn der Fehler in der Antwort zurückgegeben wird, können Sie überprüfen, ob der "Index" des Fehlers 1 ist und angibt, dass der erwartete Fehler "Einfügen" fehlschlägt und dass der Fehlercode 11000 wegen des erwarteten "doppelten Schlüssels" lautet.

Die Fehler im "erwarteten" Fall können daher ignoriert werden und Sie müssten nur die "unerwarteten" Fehler für einen anderen Code und/oder eine andere Position in der ausgegebenen Massenanweisung behandeln.

+0

Danke, das ist, was ich gesucht habe – karlitos