Существует, по крайней мере, два правильных способа решения этой проблемы. На самом деле, второй - это скорее взлом, но он работает правильно. Я создал gist with a complete example (tested).
BSONObjectId
Генерация на клиенте
BSONObjectId
имеет метод generate
, который вы можете использовать, чтобы легко создавать уникальный идентификатор. В результате ID имеет такую же структуру, как идентификаторы, генерируемую сам MongoDB сервер:
+------------------------+------------------------+------------------------+------------------------+
+ timestamp (in seconds) + machine identifier + thread identifier + increment +
+ (4 bytes) + (3 bytes) + (2 bytes) + (3 bytes) +
+------------------------+------------------------+------------------------+------------------------+
(взят из Javadoc ReactiveMongo для BSONObjectId.generate
)
Примера код:
val id = BSONObjectId.generate()
collection.insert(BSONDocument("_id" -> id, "foo" -> "bar"))
Полученный ID не будет быть точно таким же, как сгенерированный сервером. В частности, machine identifier
и thread identifier
будут совершенно разными, скорее всего. Однако в большинстве случаев (все?) Это не имеет значения, поскольку риск столкновения ничтожен, поэтому, на мой взгляд, это лучший подход.
Вы также должны проверить значение, возвращаемое insert
. Если это не удается, вы можете создать новый идентификатор и повторить попытку вставки. Таким образом, ваш код должен быть пуленепробиваемым. Не забудьте ограничить количество попыток и, возможно, добавить некоторую случайную задержку между каждой попыткой.
Использование BSONCollection.findAndUpdate
Если вам действительно нужно генерировать идентификаторы на стороне сервера по какой-то неясной причине, это решение должно сделать это, избегая гонки условий или какие-либо дополнительные запросы, так что вполне оптимально, хотя и несколько Hacky. Хитрость заключается в использовании MongoDB's findAndModify
. Таким образом, вы получите FindAndModifyResult
вместо WriteResult
, что даст вам доступ к вставленному документу.Пример:
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"))
}
Пожалуйста, обратите внимание, что вы должны предоставить selector
, который никогда не выбирает документ, в противном случае это будет обновить существующий документ вместо того, чтобы вставить новый. Кроме того, вам необходимо переопределить эти поля в update
, иначе он будет использовать значения из selector
.
Bonus: Используйте отдельную коллекцию для дополнительных идентификаторов
Вы также можете создать дополнительный сбор для хранения самых последних ID, как описано в MongoDB's documentation. Это должно быть безопасно, если вы используете findAndModify
, чтобы одновременно получить новый идентификатор и увеличить значение. Недостатки? Одна дополнительная коллекция и один дополнительный запрос, но есть случаи, когда вам нужны автоматические инкрементные уникальные идентификаторы в любом случае, поэтому это может стоить внимания.
вы можете сделать 'insertResult.map (_. Result [Person])' или то, что вы используете, даже «BSONDocument» и получить от него идентификатор. Найдено в документах: http://reactivemongo.org/releases/0.11/documentation/tutorial/write-documents.html –
Это проблема дизайна. Ожидая, что БД для генерации идентификатора приведет к такой проблеме. Лучше использовать 'BSONObjectID.generate' (или любой другой генератор UUID). – cchantep