2016-03-23 4 views
3

Я видел, что этот вопрос, кажется, был задан раньше (почти 3 года назад), но с тех пор в реактивной библиотеке монго может быть много изменений.Как получить идентификатор объекта документа после вставки в реактивировать?

Я использую плагин для игры с версией 2.4, но у responseivemongo.api.commands.WriteResult нет API-интерфейса, чтобы получить идентификатор объекта документа.

Теперь я могу самостоятельно задать идентификатор объекта, но я не считаю его убедительной и правильной идеей, так как некоторая ценность, уникальная на машине, где я создаю идентификатор, может быть не такой же, как на другой машине, и сохранять вещи Я просто хочу, чтобы это обрабатывалось mongo db.

Так что да, если есть способ, которым я могу получить идентификатор вставленного документа, это будет здорово, иначе я должен вернуться к способу самостоятельно установить идентификатор, который я пытаюсь избежать.

+0

вы можете сделать 'insertResult.map (_. Result [Person])' или то, что вы используете, даже «BSONDocument» и получить от него идентификатор. Найдено в документах: http://reactivemongo.org/releases/0.11/documentation/tutorial/write-documents.html –

+0

Это проблема дизайна. Ожидая, что БД для генерации идентификатора приведет к такой проблеме. Лучше использовать 'BSONObjectID.generate' (или любой другой генератор UUID). – cchantep

ответ

1

Создание ObjectId на стороне клиента в порядке. Это то, что делает casbah (блокирующий драйвер MongoDB для MongoDB), если вы вникнете в его код.

Если вы посмотрите на поля ObjectId, вы найдете a 3-byte machine identifier и другие. Он вычисляется сложным (с участием InetAddress в стандартном Java-драйвере). Это гарантирует, что не может быть столкновения между 2 ObjectId с, сгенерированным одновременно на разных машинах.

4

Существует, по крайней мере, два правильных способа решения этой проблемы. На самом деле, второй - это скорее взлом, но он работает правильно. Я создал 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, чтобы одновременно получить новый идентификатор и увеличить значение. Недостатки? Одна дополнительная коллекция и один дополнительный запрос, но есть случаи, когда вам нужны автоматические инкрементные уникальные идентификаторы в любом случае, поэтому это может стоить внимания.

+0

Привет, это мой первый пост на StackOverflow. Пожалуйста, дайте мне знать, если вы думаете, что я мог бы сделать что-то лучше. –

+0

Интересное использование findAndUpdate, к сожалению, вам нужно предоставить несовместимый селектор, я бы, вероятно, не рекомендовал бы использовать этот подход в производственном приложении. – qwwqwwq

+0

Да, ** Я никогда не буду использовать его ** самостоятельно, но это еще лучше, чем, например, вставка объекта и запрос БД для самого высокого ID (состояние гонки). Кто знает, может быть, у кого-то есть веская причина для генерации идентификаторов на стороне сервера, поэтому я сохраню это решение. –

 Смежные вопросы

  • Нет связанных вопросов^_^