4

この質問は以前 (ほぼ 3 年前) に尋ねられたようですが、それ以来、リアクティブ mongo ライブラリに多くの変更が加えられている可能性があります。

バージョン 2.4 の play プラグインを使用していますが、reactivemongo.api.commands.WriteResult にはドキュメント オブジェクト ID を取得するための API がないようです。

これで、自分でオブジェクトIDの設定を開始できますが、IDを作成するマシンで一意の値が他のマシンと同じではない可能性があるため、説得力のある正しいアイデアではありません。これはmongo dbによって処理されます。

そうです、挿入されたドキュメントのIDを取得できる方法があれば素晴らしいでしょう。そうでなければ、IDを自分で設定する方法にフォールバックする必要があります。

4

2 に答える 2

4

これを解決するには、少なくとも 2 つの適切な方法があります。実際には、2 番目のものはよりハックですが、適切に動作します。完全な例 (テスト済み) を含む要旨を作成しました。

BSONObjectIdクライアント上で生成

BSONObjectIdgenerateには、一意の 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 identifierまったくthread identifier異なる可能性があります。ただし、衝突のリスクは無視できるため、ほとんどの場合 (すべて?) は問題になりません。私の意見では、これが最善の方法です。

によって返される値も確認する必要がありますinsert。失敗した場合は、新しい ID を生成して、もう一度挿入してみてください。このようにして、コードは防弾になるはずです。再試行の回数を制限し、試行ごとにランダムな遅延を追加することを忘れないでください。

使用するBSONCollection.findAndUpdate

なんらかの理由でサーバー側で ID を生成する必要がある場合、このソリューションは競合状態や追加のクエリを回避して生成する必要があるため、ややハックではありますが、非常に最適です。秘訣は、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 には別のコレクションを使用する

MongoDB のドキュメントで説明されているように、最新の ID を保持するための追加のコレクションを作成することもできます。findAndModifyこれは、新しい ID の取得と値のインクリメントを同時に行うために使用している限り、安全です。欠点?1 つの追加のコレクションと 1 つの追加のクエリですが、いずれにせよ自動インクリメントされた一意の ID が必要な場合があるため、検討する価値があるかもしれません。

于 2016-11-18T01:46:51.720 に答える