2016-03-23 15 views
3

Ich habe gesehen, dass diese Frage früher gestellt wurde (vor fast 3 Jahren), aber seitdem könnte es viele Änderungen in der reaktiven Mongo-Bibliothek geben.Wie bekomme ich die Dokumentobjekt-ID nach dem Einfügen in reactivemongo?

Ich benutze das Play-Plugin mit Version 2.4, aber die reacticmongo.api.commands.WriteResult scheint keine API zu haben, um die Dokumentobjekt-ID zu bekommen.

Jetzt kann ich beginnen, die Objekt-ID selbst einzustellen, aber ich finde es nicht eine überzeugende und richtige Idee, da ein Wert auf der Maschine, wo ich die ID erstellen nicht identisch mit der anderen Maschine und Dinge zu halten Ich möchte einfach, dass dies von mongo db gehandhabt wird.

Also, wenn es eine Möglichkeit gibt, dass ich die ID des eingefügten Dokuments bekommen kann, wird großartig sein oder sonst muss ich auf den Weg zur Festlegung der ID von mir selbst, die ich etwas versuche ich zu vermeiden.

+0

Sie können 'insertResult.map (_. Ergebnis [Person])' oder was auch immer Sie verwenden, auch 'BSONDocument' und holen Sie sich die ID aus. Gefunden in der Dokumentation hier: http://reactivemongo.org/releases/0.11/documentation/tutorial/write-documents.html –

+0

Das ist ein Design-Problem. Zu erwarten, dass die DB eine ID generiert, führt zu einem solchen Problem. Es ist besser, 'BSONObjectID.generate' (oder einen anderen UUID-Generator) zu verwenden. – cchantep

Antwort

1

Erstellen ObjectId auf der Client-Seite ist in Ordnung. Es ist, was casbah (ein blockierender MongoDB-Treiber für MongoDB) tut, wenn Sie in seinen Code graben. Wenn Sie sich die Felder ObjectId ansehen, finden Sie unter anderem a 3-byte machine identifier. Es ist kompliziert berechnet (mit InetAddress in Standard-Java-Treiber). Es garantiert, dass es keine Kollision zwischen 2 s geben kann, die gleichzeitig auf verschiedenen Maschinen erzeugt wird.

4

Es gibt mindestens zwei Möglichkeiten, dies zu lösen. Eigentlich ist der zweite eher ein Hack, aber er funktioniert richtig. Ich habe ein gist with a complete example (tested) erstellt.

generieren BSONObjectId auf dem Client

BSONObjectId hat eine Methode namens generate, die Sie leicht eine eindeutige ID generieren verwenden können. Die sich ergebende ID die gleiche Struktur wie IDs, die durch MongoDB Server hat sich:

+------------------------+------------------------+------------------------+------------------------+ 
+ timestamp (in seconds) + machine identifier + thread identifier +  increment  + 
+  (4 bytes)  +  (3 bytes)  +  (2 bytes)  +  (3 bytes)  + 
+------------------------+------------------------+------------------------+------------------------+ 

(für BSONObjectId.generate von ReactiveMongo des Javadoc- genommen)

Beispielcode:

val id = BSONObjectId.generate() 
collection.insert(BSONDocument("_id" -> id, "foo" -> "bar")) 

die resultierende ID wird nicht genau wie vom Server generiert werden. Insbesondere machine identifier und thread identifier wird höchstwahrscheinlich völlig anders sein. In den meisten (allen?) Fällen spielt es jedoch keine Rolle, denn das Risiko einer Kollision ist vernachlässigbar, daher ist dies meines Erachtens der beste Ansatz.

Sie sollten auch den von insert zurückgegebenen Wert überprüfen. Wenn dies fehlschlägt, können Sie eine neue ID generieren und erneut versuchen, einzufügen. Auf diese Weise sollte Ihr Code kugelsicher sein. Denken Sie daran, die Anzahl der Wiederholungen zu begrenzen und vielleicht zwischen jedem Versuch eine zufällige Verzögerung hinzuzufügen.

Verwenden BSONCollection.findAndUpdate

Wenn Sie wirklich brauchen, um IDs auf der Serverseite aus unerfindlichen Gründen zu erzeugen, sollte diese Lösung das tut, Rennen oder zusätzliche Abfragen zu vermeiden, so ist es ganz optimal, wenngleich etwas hacky. Der Trick besteht darin, MongoDB findAndModify zu verwenden. Auf diese Weise erhalten Sie FindAndModifyResult anstelle von WriteResult, mit dem Sie auf das eingefügte Dokument zugreifen können.Beispiel:

val resultFuture = collection.findAndUpdate(
    selector  = BSONDocument("foo" -> -1), // Assuming that there's no document with "foo" equal to -1, otherwise THIS CODE MAY EXPLODE 
    update   = BSONDocument("foo" -> "bar"), 
    fetchNewObject = true, 
    upsert   = true) 
val idFuture: Future[BSONObjectId] = resultFuture.map { result => 
    val doc = result.value.get // WARNING: I'm using get for simplicity, but you shouldn't: it may throw an exception 
    doc.getAs[BSONObjectId]("_id")) 
} 

Bitte beachten Sie, dass Sie ein selector bereitstellen muss, die nie ein Dokument auswählt, sonst wäre dies ein vorhandenes Dokument aktualisieren, anstatt einen neuen einzufügen. Außerdem müssen Sie diese Felder in update überschreiben, andernfalls würden Sie die Werte von selector verwenden.

Bonus: eine getrennte Sammlung für inkrementelle IDs verwenden

Sie können auch eine zusätzliche Sammlung schaffen für die Führung der jüngsten ID wie in MongoDB's documentation beschrieben. Dies sollte sicher sein, solange Sie findAndModify verwenden, um gleichzeitig eine neue ID zu erhalten und den Wert zu erhöhen. Nachteile? Eine zusätzliche Sammlung und eine zusätzliche Abfrage, aber es gibt einige Fälle, in denen Sie ohnehin automatisch inkrementierte eindeutige IDs benötigen. Daher ist es möglicherweise eine Überlegung wert.

+0

Hallo, das ist mein erster Post auf StackOverflow. Bitte lassen Sie es mich wissen, wenn Sie denken, ich hätte etwas Besseres machen können. –

+0

Interessante Verwendung von findAndUpdate, unglücklicherweise, dass Sie einen nicht passenden Selektor bereitstellen müssen, würde ich wahrscheinlich nicht empfehlen, diesen Ansatz in einer Produktionsanwendung zu verwenden. – qwwqwwq

+0

Ja, ** Ich würde es niemals ** benutzen, aber es ist immer noch besser als zB Einfügen eines Objekts und Abfragen der DB für die höchste ID (Race Condition). Wer weiß, vielleicht hat jemand einen guten Grund, serverseitig IDs zu generieren, also behalte ich diese Lösung. –

Verwandte Themen