MongoDB-Atom "findOrCreate": findOne, einfügen, falls nicht vorhanden, aber nicht aktualisieren

114

Wie der Titel schon sagt, möchte ich mit _id eine Suche (eins) für ein Dokument durchführen. Wenn es nicht vorhanden ist, lassen Sie es erstellen. Ob es gefunden wurde oder erstellt wurde, lassen Sie es im Rückruf zurückgeben.

Ich möchte es nicht aktualisieren, wenn es existiert, wie ich es bei findAndModify gelesen habe. Ich habe viele andere Fragen zu Stackoverflow zu diesem Thema gesehen, möchte aber auch hier nichts aktualisieren.

Ich bin mir nicht sicher, ob das durch das Erstellen (oder Nicht-Vorhandensein) tatsächlich das Update ist, von dem alle sprechen. Es ist alles so verwirrend :(

Discipol
quelle

Antworten:

192

Ab MongoDB 2.4 ist es nicht mehr erforderlich, sich für atomähnliche findOrCreateOperationen auf einen eindeutigen Index (oder eine andere Problemumgehung) zu verlassen .

Dies ist dem$setOnInsert neuen Operator 2.4 zu verdanken , mit dem Sie Aktualisierungen festlegen können, die nur beim Einfügen von Dokumenten erfolgen sollen.

In Kombination mit der upsertOption bedeutet dies, dass Sie findAndModifyeine findOrCreateatomähnliche Operation erzielen können .

db.collection.findAndModify({
  query: { _id: "some potentially existing id" },
  update: {
    $setOnInsert: { foo: "bar" }
  },
  new: true,   // return new doc if one is upserted
  upsert: true // insert the document if it does not exist
})

Da $setOnInsertnur eingefügte Dokumente betroffen sind, werden keine Änderungen vorgenommen, wenn ein vorhandenes Dokument gefunden wird. Wenn kein Dokument vorhanden ist, wird eines mit der angegebenen _id aktualisiert, und dann wird nur das Einfügen festgelegt. In beiden Fällen wird das Dokument zurückgegeben.

Nummern 1311407
quelle
3
@Gank Wenn Sie den nativen mododb-Treiber für den Knoten verwenden, ist die Syntax eher ähnlich collection.findAndModify({_id:'theId'}, <your sort opts>, {$setOnInsert:{foo: 'bar'}}, {new:true, upsert:true}, callback). Siehe die Dokumente
Nummern 1311407
7
Gibt es eine Möglichkeit festzustellen, ob das Dokument aktualisiert oder eingefügt wurde?
Untergang
3
Wenn Sie überprüfen möchten , ob die obige Abfrage ( db.collection.findAndModify({query: {_id: "some potentially existing id"}, update: {$setOnInsert: {foo: "bar"}}, new: true, upsert: true})) ein Dokument einfügt (upsert) , sollten Sie die Verwendung in Betracht ziehen db.collection.updateOne({_id: "some potentially existing id"}, {$setOnInsert: {foo: "bar"}}, {upsert: true}). Es wird zurückgegeben, {"acknowledged": true, "matchedCount": 0, "modifiedCount": 0, "upsertedId": ObjectId("for newly inserted one")}wenn das Dokument eingefügt wurde und {"acknowledged": true, "matchedCount": 1, "modifiedCount": 0}das Dokument bereits vorhanden ist.
18онстантин Ван
8
scheint in Geschmack als veraltet zu sein findOneAndUpdate, findOneAndReplaceoderfindOneAndDelete
Ravshan Samandarov
5
Hier muss man allerdings vorsichtig sein. Dies funktioniert nur, wenn der Selektor von findAndModify / findOneAndUpdate / updateOne ein Dokument durch _id eindeutig identifiziert. Andernfalls wird der Upsert auf dem Server in eine Abfrage und ein Update / Insert aufgeteilt. Das Update wird weiterhin atomar sein. Die Abfrage und das Update zusammen werden jedoch nicht atomar ausgeführt.
Anon
14

Treiberversionen> 2

Mit dem neuesten Treiber (> Version 2) verwenden Sie findOneAndUpdate als findAndModifyveraltet. Die neue Methode nimmt drei Argumente, die filterdas updateObjekt (das Ihre Standardeigenschaften enthält, die für ein neues Objekt eingefügt werden soll), und optionswo Sie die Upsert Operation angeben.

Mit der Versprechenssyntax sieht es so aus:

const result = await collection.findOneAndUpdate(
  { _id: new ObjectId(id) },
  {
    $setOnInsert: { foo: "bar" },
  },
  {
    returnOriginal: false,
    upsert: true,
  }
);

const newOrUpdatedDocument = result.value;
hansmaad
quelle
Danke, returnOriginalsollte returnNewDocumentich denken - docs.mongodb.com/manual/reference/method/…
Dominic
Egal, der Knotentreiber hat es anders implementiert und Ihre Antwort ist richtig
Dominic
6

Es ist ein bisschen schmutzig, aber Sie können es einfach einfügen.

Stellen Sie sicher, dass der Schlüssel einen eindeutigen Index enthält (wenn Sie die _id verwenden, ist dies in Ordnung, es ist bereits eindeutig).

Auf diese Weise wird, wenn das Element bereits vorhanden ist, eine Ausnahme zurückgegeben, die Sie abfangen können.

Wenn es nicht vorhanden ist, wird das neue Dokument eingefügt.

Aktualisiert: Eine ausführliche Erläuterung dieser Technik in der MongoDB-Dokumentation

Madarco
quelle
ok, das ist eine gute Idee, aber für den bereits vorhandenen Wert wird ein Fehler zurückgegeben, aber NICHT der Wert selbst, oder?
Discipol
3
Dies ist tatsächlich eine der empfohlenen Lösungen für isolierte Abfolgen von Operationen (finden, erstellen, erstellen, wenn nicht gefunden, vermutlich) docs.mongodb.org/manual/tutorial/isolate-sequence-of-operations
numbers1311407
@Discipol Wenn Sie eine Reihe von atomaren Operationen ausführen möchten, sollten Sie zuerst das Dokument sperren, dann ändern und am Ende freigeben. Dies erfordert mehr Abfragen, aber Sie können für das beste Szenario optimieren und meistens nur 1-2 Abfragen durchführen. siehe: docs.mongodb.org/manual/tutorial/perform-two-phase-commits
Madarco
1

Folgendes habe ich getan (Ruby MongoDB-Treiber):

$db[:tags].update_one({:tag => 'flat'}, {'$set' => {:tag => 'earth' }}, { :upsert => true })}

Es wird aktualisiert, falls vorhanden, und eingefügt, wenn dies nicht der Fall ist.

Vidar
quelle