複合演算子
Overview
このガイドでは、MongoDB Kotlin ドライバーを使用して複合操作を実行する方法を学習できます。
複合操作 は、1 つのアトミック操作として実行される読み取り操作と書込み操作で構成されます。 アトミック操作とは、完全に完了するか、まったく完了しない操作です。 アトミック操作は部分的には完了できません。
アトミック操作は、コード内の競合状態を回避するのに役立ちます。 コードの動作が制御できないイベントの順序に依存している場合、競合状態が発生します。
MongoDB は次の複合操作をサポートしています。
1 つのドキュメントを検索して更新
1 つのドキュメントを検索して置換
1 つのドキュメントを検索して削除
複数のドキュメントの読み取りと書き込みなど、より複雑なタスクをアトミックに実行する必要がある場合は、トランザクションを使用します。 トランザクションは MongoDB やその他のデータベースの機能であり、データベースコマンドの任意のシーケンスをアトミック操作として定義できます。
アトミック操作とアトミック性の詳細については、 の MongoDB マニュアル エントリの「 アトミック性とトランザクション 」を参照してください。
トランザクションの詳細については、 トランザクション に関するMongoDB マニュアルのエントリを参照してください。
複合演算子の使用方法
このセクションでは、MongoDB Kotlin ドライバーで各複合操作を使用する方法を説明します。
次の例では、これら 2 つのサンプル ドキュメントを含むコレクションを使用します。
{"_id": 1, "food": "donut", "color": "green"} {"_id": 2, "food": "pear", "color": "yellow"}
このデータは、次の Kotlin データ クラスでモデル化されます。
data class FoodOrder( val id: Int, val food: String, val color: String )
注意
書き込みの前、後は?
デフォルトでは、各複合操作は、書込み操作の前の状態で見つかったドキュメントを返します。 複合操作に対応するオプション クラスを使用すると、書込み (write) 操作後に 状態で見つかったドキュメントを検索できます。 この構成の例については、以下の「 検索と置換 の例」を参照してください。
検索と更新
1 つのドキュメントを検索して更新するには、 MongoCollection
クラスの findOneAndUpdate()
メソッドを使用します。 findOneAndUpdate()
null
メソッドは見つかったドキュメントを返します。クエリに一致するドキュメントがない場合は メソッドは返します。
例
次の例では、 findOneAndUpdate()
メソッドを使用して、 color
フィールドが"green"
に設定されているドキュメントを検索し、そのドキュメントのfood
フィールドを"pizza"
に更新します。
この例では、 FindOneAndUpdateOptions
インスタンスを使用して次のオプションも指定しています。
クエリに一致するドキュメントがない場合に、クエリフィルターで指定されたドキュメントを挿入する アップサート を指定します。
MongoDB インスタンスで、この操作の最大実行時間を 5 秒に設定します。 操作に時間がかかる場合、
findOneAndUpdate()
メソッドはMongoExecutionTimeoutException
をスローします。
val filter = Filters.eq(FoodOrder::color.name, "green") val update = Updates.set(FoodOrder::food.name, "pizza") val options = FindOneAndUpdateOptions() .upsert(true) .maxTime(5, TimeUnit.SECONDS) /* The result variable contains your document in the state before your update operation is performed or null if the document was inserted due to upsert being true */ val result = collection.findOneAndUpdate(filter, update, options) println(result)
FoodOrder(id=1, food=donut, color=green)
Projections
クラスの詳細については、 プロジェクション ビルダに関するガイドを参照してください。
アップサート操作の詳細については、 アップサート に関するガイドをご覧ください。
このセクションで説明されるメソッドとクラスの詳細については、次の API ドキュメントを参照してください。
検索と置換
1 つのドキュメントを検索して置き換えるには、 MongoCollection
クラスのfindOneAndReplace()
メソッドを使用します。 findOneAndReplace()
null
メソッドは見つかったドキュメントを返します。クエリに一致するドキュメントがない場合は メソッドは返します。
例
次の例では、 findOneAndReplace()
メソッドを使用して、 color
フィールドが"green"
に設定されているドキュメントを検索し、それを次のドキュメントに置き換えます。
{"music": "classical", "color": "green"}
この例ではまた、 FindOneAndReplaceOptions
インスタンスを使用して、置換操作後に返されるドキュメントが の状態であることを指定しています。
data class Music( val id: Int, val music: String, val color: String ) val filter = Filters.eq(FoodOrder::color.name, "green") val replace = Music(1, "classical", "green") val options = FindOneAndReplaceOptions() .returnDocument(ReturnDocument.AFTER) val result = collection.withDocumentClass<Music>().findOneAndReplace(filter, replace, options) println(result)
Music(id=1, music=classical, color=green)
このセクションで説明されるメソッドとクラスの詳細については、次の API ドキュメントを参照してください。
検索と削除
1 つのドキュメントを検索して削除するには、 MongoCollection
クラスのfindOneAndDelete()
メソッドを使用します。 findOneAndDelete()
null
メソッドは見つかったドキュメントを返します。クエリに一致するドキュメントがない場合は メソッドは返します。
例
次の例では、 findOneAndDelete()
メソッドを使用して、 _id
フィールドの 値が最も大きいドキュメントを検索して削除します。
この例では、 FindOneAndDeleteOptions
インスタンスを使用して、 _id
フィールドで降順の並べ替えを指定しています。
val sort = Sorts.descending("_id") val filter = Filters.empty() val options = FindOneAndDeleteOptions().sort(sort) val result = collection.findOneAndDelete(filter, options) println(result)
FoodOrder(id=2, food=pear, color=yellow)
Sorts
クラスの詳細については、 ソート ビルダに関するガイド を参照してください。
このセクションで説明されるメソッドとクラスの詳細については、次の API ドキュメントを参照してください。
競合状態の回避
このセクションでは、2 つの例について説明します。 最初の例には競合状態が含まれており、2 番目の例では 複合操作 を使用して最初の例に存在する競合状態を回避しています。
両方の例では、1 つの部屋を持つプロパティを実行し、この部屋をゲスト チェックアウトするのに役立つ小さな Kotlin プログラムがあるとします。
MongoDB 内の次のドキュメントは、この部屋を表しています。
{"_id": 1, "guest": null, "room": "Blue Room", "reserved": false}
このデータは、次の Kotlin データ クラスでモデル化されます。
data class HotelRoom( val id: Int, val guest: String? = null, val room: String, val reserved: Boolean = false )
競合状態の例
アプリがこのbookARoomUnsafe
メソッドを使用して、部屋をゲスト用にチェックアウトするとします。
suspend fun bookARoomUnsafe(guestName: String) { val filter = Filters.eq("reserved", false) val myRoom = hotelCollection.find(filter).firstOrNull() if (myRoom == null) { println("Sorry, we are booked, $guestName") return } val myRoomName = myRoom.room println("You got the $myRoomName, $guestName") val update = Updates.combine(Updates.set("reserved", true), Updates.set("guest", guestName)) val roomFilter = Filters.eq("_id", myRoom.id) hotelCollection.updateOne(roomFilter, update) }
Jan と Pat の 2 人の別々の利用者が、この方法で同時に部屋を予約しようとしているとします。
Jan には次の出力が表示されます。
You got the Blue Room, Jan
そして、Pat は次の出力を確認します。
You got the Blue Room, Pat
データベースを確認すると、次のことが表示されます。
{"_id": 1, "guest": "Jan", "room": "Blue Room", "reserved": false}
パッチは Atlas がお客様のインスタンスに表示されると、Jan は自分の部屋を占有することになります。 何が起こるでしたか。
MongoDB インスタンスの観点から発生したイベントの順序は以下のとおりです。
1 月の空の間を検索して返します。
パターン の空の間を検索して返します。
パターン に予約されている部屋を更新します。
1 月に予約されている部屋を更新します。
一時的に、Pat がこの部屋を予約していましたが、Jan の更新操作が最後に実行されたため、ドキュメントはゲストとして"Jan"
を実行していることに注意してください。
競合条件がない例
複合操作 を使用して競合状態を回避し、常にユーザーに正しいメッセージを提供できるようにしましょう。
suspend fun bookARoomSafe(guestName: String) { val update = Updates.combine( Updates.set(HotelRoom::reserved.name, true), Updates.set(HotelRoom::guest.name, guestName) ) val filter = Filters.eq("reserved", false) val myRoom = hotelCollection.findOneAndUpdate(filter, update) if (myRoom == null) { println("Sorry, we are booked, $guestName") return } val myRoomName = myRoom.room println("You got the $myRoomName, $guestName") }
Jan と Pat の 2 人の別々の利用者が、この方法で同時に部屋を予約しようとしているとします。
Jan には次の出力が表示されます。
You got the Blue Room, Jan
そして、Pat は次の出力を確認します。
Sorry, we are booked, Pat
データベースを確認すると、次のことが表示されます。
{"_id": 1, "guest": "Jan", "room": "Blue Room", "reserved": false}
Pat は正しいメッセージを取得しました。 予約が取得されなかったため、
MongoDB インスタンスの観点から発生したイベントの順序は以下のとおりです。
1 月 日の空の間を見つけて予約します。
パターン 用の空のスペースを見つけて予約してください。
残りのスペースがない場合、
null
が返されます。
重要
書込みロック (write lock)
MongoDB インスタンスは、複合操作の実行中、変更しているドキュメントに対して書込みロック (write lock) を配置します。
Updates
クラスの詳細については、 アップデート ビルダに関するガイド をご覧ください。
Filters
クラスの詳細については、 フィルター ビルダに関するガイド をご覧ください。
findOneAndUpdate()
メソッドの詳細については、 MongoCollection クラスの API ドキュメントを参照してください。