Docs Menu
Docs Home
/ / /
Kotlin コルーチン
/ /

複合演算子

項目一覧

  • 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(
@BsonId 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(
@BsonId 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(
@BsonId 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 月の空の間を検索して返します。

  2. パターン の空の間を検索して返します。

  3. パターン に予約されている部屋を更新します。

  4. 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. 1 月 日の空の間を見つけて予約します。

  2. パターン 用の空のスペースを見つけて予約してください。

  3. 残りのスペースがない場合、 nullが返されます。

重要

書込みロック (write lock)

MongoDB インスタンスは、複合操作の実行中、変更しているドキュメントに対して書込みロック (write lock) を配置します。

Updatesクラスの詳細については、 アップデート ビルダに関するガイド をご覧ください。

Filtersクラスの詳細については、 フィルター ビルダに関するガイド をご覧ください。

findOneAndUpdate()メソッドの詳細については、 MongoCollection クラスの API ドキュメントを参照してください。

戻る

クエリを指定する