Как получить идентификатор объекта документа после вставки в реактив-монго?
Я видел, что этот вопрос, кажется, задавался ранее (почти 3 года назад), но с тех пор в библиотеке реактивного монго может быть много изменений.
Я использую плагин play с версией 2.4, но в реактивном реактиве mongo.api.commands.WriteResult, похоже, нет API для получения идентификатора объекта документа.
Теперь я могу начать устанавливать идентификатор объекта самостоятельно, но я не нахожу это убедительной и правильной идеей, поскольку какое-то уникальное значение на машине, где я создаю идентификатор, может не совпадать с другой машиной, и для простоты я хочу, чтобы пусть этим занимается монго дб.
Так что да, если есть какой-то способ, которым я могу получить идентификатор вставленного документа, будет замечательно, или же мне придется вернуться к способу установки идентификатора самостоятельно, чего я пытаюсь избежать.
2 ответа
Создание ObjectId
на стороне клиента все в порядке. Это что casbah
(блокирующий драйвер MongoDB для MongoDB) работает, если вы копаетесь в его коде.
Если вы посмотрите на ObjectId
поля, то вы найдете a 3-byte machine identifier
среди других. Это рассчитано сложно (с участием InetAddress
в стандартном драйвере Java). Это гарантирует, что не может быть столкновения между 2 ObjectId
s, генерируемые одновременно на разных машинах.
Есть по крайней мере два правильных способа решить эту проблему. На самом деле второй - скорее хак, но он работает правильно. Я создал суть с полным примером (проверено).
генерировать BSONObjectId
на клиенте
BSONObjectId
имеет метод, называемый generate
который вы можете использовать, чтобы легко создать уникальный идентификатор. Результирующий идентификатор имеет ту же структуру, что и идентификаторы, сгенерированные самим сервером 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"))
Полученный идентификатор не будет точно таким же, как сгенерированный сервером. В частности, machine identifier
а также thread identifier
будет совершенно другим, скорее всего. Однако в большинстве (всех?) Случаев это не имеет значения, поскольку риск столкновения незначителен, поэтому, на мой взгляд, это лучший подход.
Вы также должны проверить значение, возвращаемое insert
, Если это не удается, вы можете сгенерировать новый идентификатор и попробовать вставить снова. Таким образом, ваш код должен быть пуленепробиваемым. Не забудьте ограничить количество повторных попыток и, возможно, добавить некоторую случайную задержку между каждой попыткой.
использование BSONCollection.findAndUpdate
Если вам действительно необходимо сгенерировать идентификаторы на стороне сервера по какой-то непонятной причине, это решение должно сделать это, избегая условий гонки или каких-либо дополнительных запросов, поэтому он является довольно оптимальным, хотя и несколько хакерским. Хитрость в том, чтобы использовать MongoDB 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
,
Бонус: используйте отдельную коллекцию для дополнительных идентификаторов
Вы также можете создать дополнительную коллекцию для хранения самого последнего идентификатора, как описано в документации MongoDB. Это должно быть безопасно, пока вы используете findAndModify
одновременно получить новый идентификатор и увеличить значение. Недостатки? Одна дополнительная коллекция и один дополнительный запрос, но в некоторых случаях вам все равно нужно автоматически увеличивать уникальные идентификаторы, поэтому стоит подумать.