4

我已经看到这个问题似乎更早(大约 3 年前)被问过,但从那时起反应式 mongo 库中可能会有很多变化。

我正在使用 2.4 版的 play 插件,但是 reactivemongo.api.commands.WriteResult 似乎没有任何 API 来获取文档对象 ID。

现在我可以自己开始设置对象 id,但我认为这不是一个令人信服和正确的想法,因为我创建 id 的机器上的某些唯一值可能与另一台机器不同,为了保持简单,我想让这由 mongo db 处理。

所以是的,如果有某种方法可以让我获得插入文档的 id 会很棒,否则我必须回退到自己设置 id 的方式,这是我试图避免的事情。

4

2 回答 2

4

至少有两种适当的方法可以解决这个问题。实际上,第二个更像是一种黑客攻击,但它可以正常工作。我创建了一个带有完整示例(经过测试)的要点

BSONObjectId在客户端生成

BSONObjectId有一个名为的方法generate,您可以使用该方法轻松生成唯一 ID。生成的 ID 与 MongoDB 服务器自身生成的 ID 具有相同的结构:

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

(取自 ReactiveMongo 的 javadoc for BSONObjectId.generate

示例代码:

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

生成的 ID 不会与服务器生成的完全相同。具体来说,machine identifierthread identifier可能会完全不同。但是,在大多数(所有?)情况下,这并不重要,因为碰撞的风险可以忽略不计,所以在我看来这是最好的方法。

您还应该检查返回的值insert。如果失败,您可以生成一个新 ID 并再次尝试插入。这样你的代码应该是防弹的。请记住限制重试次数,并可能在每次尝试之间添加一些随机延迟。

利用BSONCollection.findAndUpdate

如果您确实需要在服务器端生成 ID 出于某种模糊的原因,这个解决方案应该这样做,避免竞争条件或任何额外的查询,所以它是相当优化的,虽然有点 hacky。诀窍是使用 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

奖励:对增量 ID 使用单独的集合

您还可以创建一个额外的集合来保存最新的 ID,如MongoDB 的文档中所述。只要您findAndModify用于同时获取新 ID 并增加值,这应该是安全的。缺点?一个额外的集合和一个额外的查询,但在某些情况下,无论如何您都需要自动递增的唯一 ID,因此可能值得考虑。

于 2016-11-18T01:46:51.720 回答
1

ObjectId在客户端创建很好。如果您深入研究它的代码,这就是casbah(MongoDB 的阻塞 MongoDB 驱动程序)正在做的事情。

如果您查看ObjectId字段,那么您会发现a 3-byte machine identifier其他字段。它的计算很棘手(涉及InetAddress标准 Java 驱动程序)。ObjectId它保证在不同机器上同时产生的2 秒之间不会发生冲突。

于 2016-03-23T23:28:17.870 回答