複合演算子
Overview
このガイドでは、MongoDB Java ドライバーを使用して複合操作を実行する方法を学習します。
複合操作 は、1 つのアトミック操作として実行される読み取り操作と書込み操作で構成されます。 アトミック操作とは、完全に完了するか、まったく完了しない操作です。 アトミック操作は部分的には完了できません。
アトミック操作は、コード内の競合状態を回避するのに役立ちます。 コードの動作が制御できないイベントの順序に依存している場合、競合状態が発生します。
MongoDB は次の複合操作をサポートしています。
1 つのドキュメントを検索して更新
1 つのドキュメントを検索して置換
1 つのドキュメントを検索して削除
複数のドキュメントの読み取りと書き込みなど、より複雑なタスクをアトミックに実行する必要がある場合は、トランザクションを使用します。 トランザクションは MongoDB やその他のデータベースの機能であり、データベースコマンドの任意のシーケンスをアトミック操作として定義できます。
アトミック操作とアトミック性の詳細については、MongoDB マニュアルの「 アトミシティとトランザクション 」を参照してください。
トランザクションの詳細については、 トランザクション に関する MongoDB マニュアルのエントリを参照してください。
複合演算子の使用方法
このセクションでは、MongoDB Java ドライバーで各複合操作を使用する方法を示します。
次の例では、これら 2 つのサンプル ドキュメントを含むコレクションを使用します。
{"_id": 1, "food": "donut", "color": "green"} {"_id": 2, "food": "pear", "color": "yellow"}
注意
書き込みの前、後は?
デフォルトでは、各複合操作は、書込み操作の前の状態で見つかったドキュメントを返します。 複合操作に対応するオプション クラスを使用すると、書込み (write) 操作後に 状態で見つかったドキュメントを検索できます。 この構成の例については、以下の「 検索と置換 の例」を参照してください。
検索と更新
1 つのドキュメントを検索して更新するには、 MongoCollection
クラスの findOneAndUpdate()
メソッドを使用します。 findOneAndUpdate()
null
メソッドは見つかったドキュメントを返します。クエリに一致するドキュメントがない場合は メソッドは返します。
例
次の例では、 findOneAndUpdate()
メソッドを使用して、 color
フィールドが"green"
に設定されているドキュメントを検索し、そのドキュメントのfood
フィールドを"pizza"
に更新します。
この例では、 FindOneAndUpdateOptions
インスタンスを使用して次のオプションも指定しています。
プロジェクションを使用して、見つかったドキュメントから
_id
フィールドを除外します。クエリに一致するドキュメントがない場合に、クエリフィルターで指定されたドキュメントを挿入する アップサート を指定します。
MongoDB インスタンスで、この操作の最大実行時間を 5 秒に設定します。 操作に時間がかかる場合、
findOneAndUpdate()
メソッドはMongoExecutionTimeoutException
をスローします。
// <MongoCollection set up code here> // Creates a projection to exclude the "_id" field from the retrieved documents Bson projection = Projections.excludeId(); // Creates a filter to match documents with a "color" value of "green" Bson filter = Filters.eq("color", "green"); // Creates an update document to set the value of "food" to "pizza" Bson update = Updates.set("food", "pizza"); // Defines options that specify projected fields, permit an upsert and limit execution time FindOneAndUpdateOptions options = new FindOneAndUpdateOptions(). projection(projection). upsert(true). maxTime(5, TimeUnit.SECONDS); // Updates the first matching document with the content of the update document, applying the specified options Document result = collection.findOneAndUpdate(filter, update, options); // Prints the matched document in its state before the operation System.out.println(result.toJson());
上記のコードの出力は次のようになります。
{"food": "pizza", "color": "green"}
Projections
クラスの詳細については、 プロジェクション ビルダに関するガイドを参照してください。
アップサート操作の詳細については、 アップサート に関するガイドをご覧ください。
このセクションで説明されるメソッドとクラスの詳細については、次の API ドキュメントを参照してください。
検索と置換
1 つのドキュメントを検索して置き換えるには、 MongoCollection
クラスのfindOneAndReplace()
メソッドを使用します。 findOneAndReplace()
null
メソッドは見つかったドキュメントを返します。クエリに一致するドキュメントがない場合は メソッドは返します。
例
次の例では、 findOneAndReplace()
メソッドを使用して、 color
フィールドが"green"
に設定されているドキュメントを検索し、それを次のドキュメントに置き換えます。
{"music": "classical", "color": "green"}
この例ではまた、 FindOneAndReplaceOptions
インスタンスを使用して、置換操作後に返されるドキュメントが の状態であることを指定しています。
// <MongoCollection set up code here> // Creates instructions to replace the matching document with a new document Bson filter = Filters.eq("color", "green"); Document replace = new Document("music", "classical").append("color", "green"); // Defines options specifying that the operation should return a document in its post-operation state FindOneAndReplaceOptions options = new FindOneAndReplaceOptions(). returnDocument(ReturnDocument.AFTER); // Atomically finds and replaces the matching document and prints the replacement document Document result = collection.findOneAndReplace(filter, replace, options); System.out.println(result.toJson());
上記のコードの出力は次のようになります。
{"_id": 1, "music": "classical", "color": "green"}
このセクションで説明されるメソッドとクラスの詳細については、次の API ドキュメントを参照してください。
検索と削除
1 つのドキュメントを検索して削除するには、 MongoCollection
クラスのfindOneAndDelete()
メソッドを使用します。 findOneAndDelete()
null
メソッドは見つかったドキュメントを返します。クエリに一致するドキュメントがない場合は メソッドは返します。
例
次の例では、 findOneAndDelete()
メソッドを使用して、 _id
フィールドの 値が最も大きいドキュメントを検索して削除します。
この例では、 FindOneAndDeleteOptions
インスタンスを使用して、 _id
フィールドで降順の並べ替えを指定しています。
// <MongoCollection set up code here> Bson sort = Sorts.descending("_id"); // Creates an empty filter to match all documents in the collection Bson filter = Filters.empty(); // Defines options that specify a descending sort on the "_id" field FindOneAndDeleteOptions options = new FindOneAndDeleteOptions(). sort(sort); // Deletes the document containing the highest "_id" value and prints the deleted document Document result = collection.findOneAndDelete(filter, options); System.out.println(result.toJson());
上記のコードの出力は次のようになります。
{"_id": 2, "food": "pear", "color": "yellow"}
Sorts
クラスの詳細については、 ソート ビルダに関するガイド を参照してください。
このセクションで説明されるメソッドとクラスの詳細については、次の API ドキュメントを参照してください。
競合状態の回避
このセクションでは、2 つの例について説明します。 最初の例には競合状態が含まれており、2 番目の例では 複合操作 を使用して最初の例に存在する競合状態を回避しています。
両方の例では、1 つの部屋を持つプロパティを実行し、この部屋をゲスト チェックアウトするのに役立つ小さな Java プログラムがあるとします。
MongoDB 内の次のドキュメントは、この部屋を表しています。
{"_id": 1, "guest": null, "room": "Blue Room", "reserved": false}
競合状態の例
アプリがこのbookARoom
メソッドを使用して、部屋をゲスト用にチェックアウトするとします。
public void bookARoom() { // Creates a filter to match documents representing available rooms Bson filter = Filters.eq("reserved", false); // Retrieves a document that represents the first available room Document myRoom = this.collection.find(filter).first(); // Prints a message if no documents representing available rooms are found if (myRoom == null){ System.out.println("Sorry, we are booked " + this.guest); return; } String myRoomName = myRoom.getString("room"); // Prints a message that guest that successfully booked the room System.out.println("You got the " + myRoomName + " " + this.guest); // Creates an update document to mark a room as reserved Bson update = Updates.combine(Updates.set("reserved", true), Updates.set("guest", guest)); // Creates a filter that matches the "_id" field of the first available room Bson roomFilter = Filters.eq("_id", myRoom.get("_id", Integer.class)); // Updates the first matching document to mark it as reserved this.collection.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": true}
パッチは Atlas がお客様のインスタンスに表示されると、Jan は自分の部屋を占有することになります。 何が起こるでしたか。
MongoDB インスタンスの観点から発生したイベントの順序は以下のとおりです。
1 月の空の間を検索して返す
パッチの空のテーブルを検索して返します
パッチに予約されている部屋を更新する
1 月に予約されている部屋を更新する
一時的に、Pat がこの部屋を予約していましたが、Jan の更新操作が最後に実行されたため、ドキュメントはゲストとして"Jan"
を実行していることに注意してください。
競合条件がない例
複合操作 を使用して競合状態を回避し、常にユーザーに正しいメッセージを提供できるようにしましょう。
public void bookARoom(){ // Creates an update document to mark a room as reserved Bson update = Updates.combine(Updates.set("reserved", true), Updates.set("guest", guest)); // Creates a filter to match a document representing an available room Bson filter = Filters.eq("reserved", false); // Updates the first document that matches the filter to mark it as reserved Document myRoom = this.collection.findOneAndUpdate(filter, update); // Prints a message when there are no available rooms if (myRoom == null){ System.out.println("Sorry, we are booked " + this.guest); return; } // Prints the name of the guest that successfully booked the room String myRoomName = myRoom.getString("room"); System.out.println("You got the " + myRoomName + " " + this.guest); }
Jan と Pat の 2 人の別々の利用者が、この方法で同時に部屋を予約しようとしているとします。
Jan には次の出力が表示されます。
You got the Blue Room Jan
そして、Pat は次の出力を確認します。
Sorry, we are booked Pat
データベースを確認すると、次のことが表示されます。
{"_id":1, "guest":"Jan", "room":"Blue Room", "reserved":true}
Pat は正しいメッセージを取得しました。 予約が取得されなかったため、
MongoDB インスタンスの観点から発生したイベントの順序は以下のとおりです。
1 月 日の空の間を見つけて予約します。
パターン 用の空のスペースを見つけて予約してください。 残りのルームがないため、
null
が返されます。
重要
書込みロック (write lock)
MongoDB インスタンスは、複合操作の実行中、変更しているドキュメントに対して書込みロック (write lock) を配置します。
Updates
クラスの詳細については、 アップデート ビルダに関するガイド をご覧ください。
Filters
クラスの詳細については、 フィルター ビルダに関するガイド をご覧ください。
findOneAndUpdate()
メソッドの詳細については、 MongoCollection クラスの API ドキュメントを参照してください。