MongoDB のクエリ - Java SDK
項目一覧
- ユースケース
- 前提条件
- プロジェクトを設定する
- プロジェクトを設定する
- MongoDB Atlas Service クラスターのリンク
- Realm 依存関係のインポート
- MongoDB コレクション ハンドルのインスタンス化
- サンプルデータ
- ドキュメントの作成
- 単一ドキュメントのインサート
- 複数のドキュメントの挿入
- ドキュメントを読む
- 単一ドキュメントの検索
- 複数ドキュメントの検索
- コレクション内のドキュメントをカウント
- Update Documents
- 単一ドキュメントの更新
- 複数のドキュメントの更新
- ドキュメントをアップサートする
- Delete Documents
- 単一ドキュメントの削除
- 複数のドキュメントの削除
- 変更の監視
- コレクションの変更の監視
- フィルターでコレクションの変更を監視
- コレクション内のドキュメントの集計
- フィルター ドキュメント
- グループ ドキュメント
- プロジェクト ドキュメント フィールド
- ドキュメントへのフィールドの追加
- Unwind Array Values
Realm Java SDK のMongoClientとクエリ APIを使用すると、MongoDB Atlas に保存されているデータを Android アプリケーションから直接クエリできます。 Atlas App Servicesは、ログインしたユーザーまたは各ドキュメントの内容に基づいて結果を安全に取得するためのコレクションにデータ アクセスルールを提供します。
次のアクションにより、Realm SDK を使用して Android アプリケーションからリンクされた MongoDB Atlas クラスターにアクセスできるようになります。
注意
このページで説明されている各操作では、クエリを使用して、操作が実行されるコレクション内の特定のドキュメントを照合します。 フィルターがコレクション内の複数のドキュメントに一致する場合、ソートパラメーターを指定しない限り、ドキュメントは不特定の順序で返されます。 つまり、 findOne()
、 updateOne()
、またはdeleteOne()
関数のいずれかに並べ替えを指定しないと、操作はクエリに一致する任意のドキュメントと一致する可能性があります。 ソートについて詳しくは、 cursor.sort() を参照してください。
ユースケース
MongoDB データソースをクエリする理由はさまざまあります。 Atlas Device Sync を介してクライアント内のデータを操作することは、必ずしも現実的ではないか、可能な限りありません。 次の場合には、MongoDB をクエリすることをお勧めします。
データセットが大きいか、クライアント デバイスにデータセット全体のロードに対する制約がある
Realm でモデル化されていないドキュメントを取得している
アプリは厳密なスキーマを持たないコレクションにアクセスする必要があります
非 Realm サービスは、アクセスしたいコレクションを生成します。
網羅的なものではありませんが、これらは MongoDB を直接クエリする一般的なユースケースです。
前提条件
Android アプリケーションから MongoDB をクエリする前に、App Services App で MongoDB Data Access を設定する必要があります。 バックエンド アプリを設定して Realm SDK クエリ Atlas を使用できるようにする方法については、App Services ドキュメントの「 MongoDB データアクセスの設定 」を参照してください。
プロジェクトを設定する
プロジェクトを設定する
Realm Java SDK のインストールガイドの手順に従います。
MongoDB Atlas Service クラスターのリンク
「 MongoDB データソースのリンク 」ガイドの手順に従います。 サービスに意味のある名前を割り当てます。この名前は、Realm SDK を使用してクラスターに接続するために必要になります。
Realm 依存関係のインポート
リモート MongoDB コレクションに対する CRUD 操作には、次の 1 つ以上のimport
ステートメントを使用します。
// Base Realm Packages import io.realm.mongodb.App; import io.realm.mongodb.AppConfiguration; // Realm Authentication Packages import io.realm.mongodb.User; import io.realm.mongodb.Credentials; // MongoDB Service Packages import io.realm.mongodb.mongo.MongoClient; import io.realm.mongodb.mongo.MongoDatabase; import io.realm.mongodb.mongo.MongoCollection; // Utility Packages import org.bson.Document;
// Base Realm Packages import io.realm.mongodb.App import io.realm.mongodb.AppConfiguration // Realm Authentication Packages import io.realm.mongodb.User import io.realm.mongodb.Credentials // MongoDB Service Packages import io.realm.mongodb.mongo.MongoClient import io.realm.mongodb.mongo.MongoDatabase import io.realm.mongodb.mongo.MongoCollection // Utility Packages import org.bson.Document
MongoDB コレクション ハンドルのインスタンス化
MongoDB のインスタンスに接続するには、MongoDB コレクションへのアクセス権を持つユーザーが必要です。 このようなユーザーとしてアプリケーションにログインし、次のコードを使用してローカル MongoDB コレクション ハンドルをインスタンス化します。
User user = app.currentUser(); MongoClient mongoClient = user.getMongoClient("mongodb-atlas"); MongoDatabase mongoDatabase = mongoClient.getDatabase("plant-data-database"); // registry to handle POJOs (Plain Old Java Objects) CodecRegistry pojoCodecRegistry = fromRegistries(AppConfiguration.DEFAULT_BSON_CODEC_REGISTRY, fromProviders(PojoCodecProvider.builder().automatic(true).build())); MongoCollection<Plant> mongoCollection = mongoDatabase.getCollection( "plant-data-collection", Plant.class).withCodecRegistry(pojoCodecRegistry); Log.v("EXAMPLE", "Successfully instantiated the MongoDB collection handle");
val user = app.currentUser() val mongoClient = user!!.getMongoClient("mongodb-atlas") val mongoDatabase = mongoClient.getDatabase("plant-data-database") // registry to handle POJOs (Plain Old Java Objects) val pojoCodecRegistry = CodecRegistries.fromRegistries( AppConfiguration.DEFAULT_BSON_CODEC_REGISTRY, CodecRegistries.fromProviders( PojoCodecProvider.builder().automatic(true).build())) val mongoCollection = mongoDatabase.getCollection( "plant-data-collection", Plant::class.java).withCodecRegistry(pojoCodecRegistry) Log.v("EXAMPLE", "Successfully instantiated the MongoDB collection handle")
注意
MongoDB でのカスタム クラスの使用
MongoDB で組み込みのDocument
クラス以外のクラスを使用するには、 MongoCollection
インスタンスにコーデックを追加できます。 上記の例では、Plain Old Java Object(POJO)をサポートするためにPojoCodecProvider
を追加しています。 カスタム オブジェクトをサポートするには 2 つのコーデック プロバイダーが必要です。
デフォルトのコーデック プロバイダーは、組み込み Java 型(
AppConfiguration.DEFAULT_BSON_CODEC_REGISTRY
からアクセス)をサポートします。PojoCodecProvider
は、POJO クラスをサポートするための新しいコーデックを自動的に作成します。
SDK は、要求されたクラスのコーデックを返すまで、レジストリを順番に確認します。 そのため、デフォルトのコーデック レジストリを最初に一覧表示する必要があります。また、 PojoCodecProvider
はほぼすべてのクラスのコーデックを提供できるため、常に最後の CodecProvider である必要があります。
サンプルデータ
次の例では、植物店舗のチェーンの在庫を記述する MongoDB コレクションで動作しています。 店舗で販売されるさまざまなプランを説明するドキュメントの次のコレクションについて考えてみましょう。
import org.bson.codecs.pojo.annotations.BsonProperty; import org.bson.types.ObjectId; public class Plant { private ObjectId id; private String name; private String sunlight; private String color; private String type; private String partition; // empty constructor required for MongoDB Data Access POJO codec compatibility public Plant() {} public Plant(ObjectId id, String name, String sunlight, String color, String type, String partition) { this.id = id; this.name = name; this.sunlight = sunlight; this.color = color; this.type = type; this.partition = partition; } public ObjectId getId() { return id; } public void setId(ObjectId id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSunlight() { return sunlight; } public void setSunlight(String sunlight) { this.sunlight = sunlight; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getPartition() { return partition; } public void setPartition(String partition) { this.partition = partition; } public String toString() { return "Plant [id=" + id + ", name=" + name + ", sunlight=" + sunlight + ", color=" + color + ", type=" + type + ", partition=" + partition + "]"; } }
User user = app.currentUser(); MongoClient mongoClient = user.getMongoClient("mongodb-atlas"); MongoDatabase mongoDatabase = mongoClient.getDatabase("plant-data-database"); // registry to handle POJOs (Plain Old Java Objects) CodecRegistry pojoCodecRegistry = fromRegistries(AppConfiguration.DEFAULT_BSON_CODEC_REGISTRY, fromProviders(PojoCodecProvider.builder().automatic(true).build())); MongoCollection<Plant> mongoCollection = mongoDatabase.getCollection( "plant-data-collection", Plant.class).withCodecRegistry(pojoCodecRegistry); mongoCollection.insertMany(Arrays.asList( new Plant(new ObjectId(), "venus flytrap", "full", "white", "perennial", "Store 42"), new Plant(new ObjectId(), "sweet basil", "partial", "green", "annual", "Store 42"), new Plant(new ObjectId(), "thai basil", "partial", "green", "perennial", "Store 42"), new Plant(new ObjectId(), "helianthus", "full", "yellow", "annual", "Store 42"), new Plant(new ObjectId(), "petunia", "full", "purple", "annual", "Store 47"))); Log.v("EXAMPLE", "Successfully inserted the sample data.");
import org.bson.codecs.pojo.annotations.BsonProperty import org.bson.types.ObjectId open class Plant(val id : ObjectId = ObjectId(), var name : String? = null, var sunlight : String? = null, var color : String? = null, var type : String? = null, "_partition") // specify that this is a field-level annotation ( var partition : String? = null) { override fun toString(): String { return "Plant [id=$id, name=$name, sunlight=$sunlight, color=$color, type=$type, partition=$partition]" } }
val user = app.currentUser() val mongoClient = user!!.getMongoClient("mongodb-atlas") val mongoDatabase = mongoClient.getDatabase("plant-data-database") // registry to handle POJOs (Plain Old Java Objects) val pojoCodecRegistry = CodecRegistries.fromRegistries( AppConfiguration.DEFAULT_BSON_CODEC_REGISTRY, CodecRegistries.fromProviders( PojoCodecProvider.builder().automatic(true).build())) val mongoCollection = mongoDatabase.getCollection( "plant-data-collection", Plant::class.java).withCodecRegistry(pojoCodecRegistry) mongoCollection.insertMany( listOf( Plant( ObjectId(), "venus flytrap", "full", "white", "perennial", "Store 42" ), Plant( ObjectId(), "sweet basil", "partial", "green", "annual", "Store 42" ), Plant( ObjectId(), "thai basil", "partial", "green", "perennial", "Store 42" ), Plant( ObjectId(), "helianthus", "full", "yellow", "annual", "Store 42" ), Plant( ObjectId(), "petunia", "full", "purple", "annual", "Store 47" ) ) ) Log.v("EXAMPLE", "Successfully Successfully inserted the sample data.")
ドキュメントの作成
これらのコード スニペットは、モバイル アプリケーションから MongoDB コレクションに 1 つ以上のドキュメントを挿入する方法を示しています。 挿入操作は、MongoDB に追加するドキュメントを引数として受け取り、操作の実行結果を含むオブジェクトに解決されるRealmResultTaskを返します。
単一ドキュメントのインサート
collection.insertOne() を使用して単一のドキュメントを挿入できます。
次のスニペットは、「 Ops Manager 」の植物を説明する単一のドキュメントを、店舗のグループで販売するプランを説明するドキュメントのコレクションに挿入します。
Plant plant = new Plant( new ObjectId(), "lily of the valley", "full", "white", "perennial", "Store 47"); mongoCollection.insertOne(plant).getAsync(task -> { if (task.isSuccess()) { Log.v("EXAMPLE", "successfully inserted a document with id: " + task.get().getInsertedId()); } else { Log.e("EXAMPLE", "failed to insert documents with: " + task.getError().getErrorMessage()); } });
val plant = Plant( ObjectId(), "lily of the valley", "full", "white", "perennial", "Store 47" ) mongoCollection?.insertOne(plant)?.getAsync { task -> if (task.isSuccess) { Log.v( "EXAMPLE", "successfully inserted a document with id: ${task.get().insertedId}" ) } else { Log.e("EXAMPLE", "failed to insert documents with: ${task.error}") } }
このスニペットを実行すると、次のような出力が生成されます。
V/EXAMPLE: successfully inserted a document with id: BsonObjectId{value=5f19...}
複数のドキュメントの挿入
collection.insertMany() を使用して、複数のドキュメントを同時に挿入できます。
次のスニペットは、プランを説明する 3 つのドキュメントを、店舗のグループで販売するプランを説明するドキュメントのコレクションに挿入します。
List<Plant> plants = Arrays.asList( new Plant(new ObjectId(), "rhubarb", "full", "red", "perennial", "Store 47"), new Plant(new ObjectId(), "wisteria lilac", "partial", "purple", "perennial", "Store 42"), new Plant(new ObjectId(), "daffodil", "full", "yellow", "perennial", "Store 42")); mongoCollection.insertMany(plants).getAsync(task -> { if (task.isSuccess()) { int insertedCount = task.get().getInsertedIds().size(); Log.v("EXAMPLE", "successfully inserted " + insertedCount + " documents into the collection."); } else { Log.e("EXAMPLE", "failed to insert documents with: ", task.getError()); } });
val plants = listOf( Plant( ObjectId(), "rhubarb", "full", "red", "perennial", "Store 47" ), Plant( ObjectId(), "wisteria lilac", "partial", "purple", "perennial", "Store 42" ), Plant( ObjectId(), "daffodil", "full", "yellow", "perennial", "Store 42" ) ) mongoCollection.insertMany(plants).getAsync { task -> if (task.isSuccess) { val insertedCount = task.get().insertedIds.size Log.v( "EXAMPLE", "successfully inserted $insertedCount documents into the collection." ) } else { Log.e("EXAMPLE", "failed to insert documents with: ${task.error}") } }
このスニペットを実行すると、次のような出力が生成されます。
V/EXAMPLE: successfully inserted 3 documents into the collection.
ドキュメントを読む
これらのコード スニペットは、 モバイルアプリケーションからMongoDBコレクションに保存されているデータを読み取る方法を示しています。読み取り操作では、 クエリ を使用して、データベースから返されるドキュメントを指定します。読み取り操作は Task を返します は、単一の一致したドキュメント(findOne()
long
count()
の場合)、 数値( の場合)、または一致したドキュメントのコレクションを走査できるイテレータ(find()
の場合)のいずれかに解決されます。 の)。
単一ドキュメントの検索
collection.findOne() を使用して単一のドキュメントを検索できます。
次のスニペットは、植物ドキュメントのtype
フィールドに string 値「perennial」が含まれるグループで販売されている植物を説明するドキュメントのコレクションから 1 つのドキュメントを検索します。
Document queryFilter = new Document("type", "perennial"); mongoCollection.findOne(queryFilter).getAsync(task -> { if (task.isSuccess()) { Plant result = task.get(); Log.v("EXAMPLE", "successfully found a document: " + result); } else { Log.e("EXAMPLE", "failed to find document with: ", task.getError()); } });
val queryFilter = Document("type", "perennial") mongoCollection.findOne(queryFilter) .getAsync { task -> if (task.isSuccess) { val result = task.get() Log.v("EXAMPLE", "successfully found a document: $result") } else { Log.e("EXAMPLE", "failed to find document with: ${task.error}") } }
このスニペットを実行すると、次のような出力が生成されます。
V/EXAMPLE: successfully found a document: Plant [id=5f18..., name=venus flytrap, sunlight=full, color=white, type=perennial, partition=Store 42]
複数ドキュメントの検索
collection.find() を使用して複数のドキュメントを見つけることができます。
次のスニペットは、値が「 Store 42 _partition
である という名前のフィールドを含む店舗のグループに販売されているプランを説明するドキュメントのコレクション内のすべてのドキュメントを検索します。
Document queryFilter = new Document("_partition", "Store 42"); RealmResultTask<MongoCursor<Plant>> findTask = mongoCollection.find(queryFilter).iterator(); findTask.getAsync(task -> { if (task.isSuccess()) { MongoCursor<Plant> results = task.get(); Log.v("EXAMPLE", "successfully found all plants for Store 42:"); while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()); } } else { Log.e("EXAMPLE", "failed to find documents with: ", task.getError()); } });
val queryFilter = Document("_partition", "Store 42") val findTask = mongoCollection.find(queryFilter).iterator() findTask.getAsync { task -> if (task.isSuccess) { val results = task.get() Log.v("EXAMPLE", "successfully found all plants for Store 42:") while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()) } } else { Log.e("EXAMPLE", "failed to find documents with: ${task.error}") } }
このスニペットを実行すると、次のような出力が生成されます。
V/EXAMPLE: successfully found all plants for Store 42: V/EXAMPLE: Plant [id=5f18..., name=venus flytrap, sunlight=full, color=white, type=perennial, partition=Store 42] V/EXAMPLE: Plant [id=5f18..., name=sweet basil, sunlight=partial, color=green, type=annual, partition=Store 42] V/EXAMPLE: Plant [id=5f18..., name=thai basil, sunlight=partial, color=green, type=perennial, partition=Store 42] V/EXAMPLE: Plant [id=5f18..., name=helianthus, sunlight=full, color=yellow, type=annual, partition=Store 42]
コレクション内のドキュメントをカウント
collection.count()を使用してコレクション内のドキュメントをカウントできます。 どのドキュメントをカウントするかを決定するために、任意のクエリを指定できます。 クエリを指定しない場合、アクションはコレクション内のすべてのドキュメントをカウントします。
次のスニペットは、店舗のグループで販売されるプランを説明するドキュメントのコレクション内のドキュメントの数をカウントします。
mongoCollection.count().getAsync(task -> { if (task.isSuccess()) { long count = task.get(); Log.v("EXAMPLE", "successfully counted, number of documents in the collection: " + count); } else { Log.e("EXAMPLE", "failed to count documents with: ", task.getError()); } });
mongoCollection.count().getAsync { task -> if (task.isSuccess) { val count = task.get() Log.v("EXAMPLE", "successfully counted, number of documents in the collection: $count") } else { Log.e("EXAMPLE", "failed to count documents with: ${task.error}") } }
このスニペットを実行すると、次のような出力が生成されます。
V/EXAMPLE: successfully counted, number of documents in the collection: 5
Update Documents
これらのコード スニペットは、モバイル アプリケーションから MongoDB コレクションに保存されているデータをアップデートする方法を示しています。 更新操作では、クエリを使用して更新するドキュメントを指定し、更新演算子を使用してクエリに一致するドキュメントをミューテーションする方法を記述します。 更新操作は タスク を返します は、操作の実行結果を含むオブジェクトに解決されます。
単一ドキュメントの更新
collection.updateOne() を使用して単一のドキュメントを更新できます。
次のスニペットは、 店舗 のグループで販売されるプランを説明するドキュメントのコレクション内の 1 つのドキュメントを更新します。 この操作は、 name
フィールドに値「ペナルティ」が含まれているドキュメントをクエリし、最初に一致したドキュメントのsunlight
フィールドの値を「部分的」に変更します。
Document queryFilter = new Document("name", "petunia"); Document updateDocument = new Document("$set", new Document("sunlight", "partial")); mongoCollection.updateOne(queryFilter, updateDocument).getAsync(task -> { if (task.isSuccess()) { long count = task.get().getModifiedCount(); if (count == 1) { Log.v("EXAMPLE", "successfully updated a document."); } else { Log.v("EXAMPLE", "did not update a document."); } } else { Log.e("EXAMPLE", "failed to update document with: ", task.getError()); } });
val queryFilter = Document("name", "petunia") val updateDocument = Document("\$set", Document("sunlight", "partial")) mongoCollection.updateOne(queryFilter, updateDocument).getAsync { task -> if (task.isSuccess) { val count = task.get().modifiedCount if (count == 1L) { Log.v("EXAMPLE", "successfully updated a document.") } else { Log.v("EXAMPLE", "did not update a document.") } } else { Log.e("EXAMPLE", "failed to update document with: ${task.error}") } }
このスニペットを実行すると、次のような出力が生成されます。
V/EXAMPLE: successfully updated a document.
複数のドキュメントの更新
コレクション.updateMany() を使用して複数のドキュメントを更新できます。
次のスニペットは、 店舗 のグループで販売されるプランを説明するドキュメントのコレクション内の複数のドキュメントを更新します。 この操作は、 _partition
フィールドに値「 47が含まれているドキュメントをクエリし、一致する各ドキュメントの_partition
フィールドの値を「 51を保存」に変更します。
Document queryFilter = new Document("_partition", "Store 47"); Document updateDocument = new Document("$set", new Document("_partition", "Store 51")); mongoCollection.updateMany(queryFilter, updateDocument).getAsync(task -> { if (task.isSuccess()) { long count = task.get().getModifiedCount(); if (count != 0) { Log.v("EXAMPLE", "successfully updated " + count + " documents."); } else { Log.v("EXAMPLE", "did not update any documents."); } } else { Log.e("EXAMPLE", "failed to update documents with: ", task.getError()); } });
val queryFilter = Document("_partition", "Store 47") val updateDocument = Document("\$set", Document("_partition", "Store 51")) mongoCollection.updateMany(queryFilter, updateDocument).getAsync { task -> if (task.isSuccess) { val count = task.get().modifiedCount if (count != 0L) { Log.v("EXAMPLE", "successfully updated $count documents.") } else { Log.v("EXAMPLE", "did not update any documents.") } } else { Log.e("EXAMPLE", "failed to update documents with: ${task.error}") } }
このスニペットを実行すると、次のような出力が生成されます。
V/EXAMPLE: successfully updated 2 documents.
ドキュメントをアップサートする
アップデート操作がコレクション内のどのドキュメントにも一致しない場合は、 upsert
オプションをtrue
に設定することで、アップデート クエリに一致する新しいドキュメントを 1 つ自動的に挿入できます。
次のスニペットは、店舗のグループで販売されているプランを説明するドキュメントのコレクション内のドキュメントを更新するか、クエリに一致するドキュメントがない場合は新しいドキュメントを挿入します。 この操作は、次のドキュメントをクエリします。
sunlight
フィールドの値は「Full」type
フィールドの値は「perennial」color
フィールドの値は「green」_partition
フィールドの値が「Store 47」である
このスニペットはupsert
オプションをtrue
に設定しているため、クエリに一致するドキュメントがない場合、MongoDB はクエリと指定された更新の両方を含む新しいドキュメントを作成します。
Document queryFilter = new Document("sunlight", "full") .append("type", "perennial") .append("color", "green") .append("_partition", "Store 47"); Document updateDocument = new Document("$set", new Document("name", "sweet basil")); UpdateOptions updateOptions = new UpdateOptions().upsert(true); mongoCollection.updateOne(queryFilter, updateDocument, updateOptions).getAsync(task -> { if (task.isSuccess()) { if(task.get().getUpsertedId() != null) { Log.v("EXAMPLE", "successfully upserted a document with id " + task.get().getUpsertedId()); } else { Log.v("EXAMPLE", "successfully updated a document."); } } else { Log.e("EXAMPLE", "failed to update or insert document with: ", task.getError()); } });
val queryFilter = Document("sunlight", "full") .append("type", "perennial") .append("color", "green") .append("_partition", "Store 47") val updateDocument = Document("\$set", Document("name", "sweet basil")) val updateOptions = UpdateOptions().upsert(true) mongoCollection.updateOne(queryFilter, updateDocument, updateOptions) .getAsync { task -> if (task.isSuccess) { if (task.get().upsertedId != null) { Log.v("EXAMPLE", "successfully upserted a document with id ${task.get().upsertedId}") } else { Log.v("EXAMPLE", "successfully updated a document.") } } else { Log.e("EXAMPLE", "failed to update or insert document with: ${task.error}") } }
このスニペットを実行すると、次のような出力が生成されます。
V/EXAMPLE: successfully upserted a document with id: BsonObjectId{value=5f19...}
Delete Documents
これらのコード スニペットは、MongoDB コレクションに保存されているドキュメントをモバイル アプリケーションから削除する方法を示しています。 削除操作はクエリを使用して、削除するドキュメントを指定し、 タスク を返す は、操作の実行結果を含むオブジェクトに解決されます。
単一ドキュメントの削除
collection.deleteOne() を使用して、コレクションから 1 つのドキュメントを削除できます。
次のスニペットは、 店舗のグループで販売されるプランを説明するドキュメントのコレクション内の 1 つのドキュメントを削除します。 この操作は、 color
フィールドの値が「green」であるドキュメントをクエリし、クエリに一致する最初のドキュメントを削除します。
Document queryFilter = new Document("color", "green"); mongoCollection.deleteOne(queryFilter).getAsync(task -> { if (task.isSuccess()) { long count = task.get().getDeletedCount(); if (count == 1) { Log.v("EXAMPLE", "successfully deleted a document."); } else { Log.v("EXAMPLE", "did not delete a document."); } } else { Log.e("EXAMPLE", "failed to delete document with: ", task.getError()); } });
val queryFilter = Document("color", "green") mongoCollection.deleteOne(queryFilter).getAsync { task -> if (task.isSuccess) { val count = task.get().deletedCount if (count == 1L) { Log.v("EXAMPLE", "successfully deleted a document.") } else { Log.v("EXAMPLE", "did not delete a document.") } } else { Log.e("EXAMPLE", "failed to delete document with: ${task.error}") } }
このスニペットを実行すると、次のような出力が生成されます。
V/EXAMPLE: successfully deleted a document.
複数のドキュメントの削除
collection.deleteMany() を使用して、コレクションから複数の項目を削除できます。
次のスニペットは、 sunlight
フィールド値が「 full 」と「 」のtype
フィールド値の両方を含むクエリに一致する店舗のグループに販売されているプランを説明するドキュメントのコレクション内のすべてのドキュメントを削除します。 ".
Document queryFilter = new Document("sunlight", "full") .append("type", "annual"); mongoCollection.deleteMany(queryFilter).getAsync(task -> { if (task.isSuccess()) { long count = task.get().getDeletedCount(); if (count != 0) { Log.v("EXAMPLE", "successfully deleted " + count + " documents."); } else { Log.v("EXAMPLE", "did not delete any documents."); } } else { Log.e("EXAMPLE", "failed to delete documents with: ", task.getError()); } });
val queryFilter = Document("sunlight", "full").append("type", "annual") mongoCollection.deleteMany(queryFilter).getAsync { task -> if (task.isSuccess) { val count = task.get().deletedCount if (count != 0L) { Log.v("EXAMPLE", "successfully deleted $count documents.") } else { Log.v("EXAMPLE", "did not delete any documents.") } } else { Log.e("EXAMPLE", "failed to delete documents with: ${task.error}") } }
このスニペットを実行すると、次のような出力が生成されます。
V/EXAMPLE: succcessfully deleted 2 documents.
変更の監視
これらのコード スニペットは、コレクションに対する監視操作を構成して実行する方法を示しています。
重要
サーバーレスの制限事項
データソースが Atlas サーバーレスインスタンスの場合、変更を監視することはできません。 MongoDB サーバーレスは現在、変更をリッスンするために監視対象コレクションで使用される 変更ストリーム をサポートしていません。
コレクションの変更の監視
collection.watch()またはcollection.watchAsync( ) を呼び出すと、コレクションに加えられた変更のストリームを開くことができます。 監視対象オブジェクトのオブジェクト ID を 変数の引数として渡すことで、コレクション内の特定のドキュメントに対する変更を監視できます。
次のスニペットは、 plants
コレクション内のドキュメントに対する変更を監視します。
RealmEventStreamAsyncTask<Plant> watcher = mongoCollection.watchAsync(); watcher.get(result -> { if (result.isSuccess()) { Log.v("EXAMPLE", "Event type: " + result.get().getOperationType() + " full document: " + result.get().getFullDocument()); } else { Log.e("EXAMPLE", "failed to subscribe to changes in the collection with : ", result.getError()); } }); Plant triffid = new Plant( new ObjectId(), "triffid", "low", "green", "perennial", "Store 47"); mongoCollection.insertOne(triffid).getAsync(task -> { if (task.isSuccess()) { BsonObjectId insertedId = task.get().getInsertedId().asObjectId(); Log.v("EXAMPLE", "successfully inserted a document with id " + insertedId); } else { Log.e("EXAMPLE", "failed to insert document with: ", task.getError()); } });
val watcher = mongoCollection.watchAsync() watcher[{ result -> if (result.isSuccess) { Log.v("EXAMPLE", "Event type: ${result.get().operationType} full document: ${result.get().fullDocument}") } else { Log.e("EXAMPLE", "failed to subscribe to changes in the collection with : ${result.error}") } }] val triffid = Plant( ObjectId(), "triffid", "low", "green", "perennial", "Store 47" ) mongoCollection.insertOne(triffid).getAsync { task -> if (task.isSuccess) { val insertedId = task.get().insertedId.asObjectId() Log.v("EXAMPLE", "successfully inserted a document with id $insertedId") } else { Log.e("EXAMPLE", "failed to insert document with: ${task.error}") } }
このスニペットを実行すると、次のような出力が生成されます。
V/EXAMPLE: successfully inserted a document with id BsonObjectId{value=5f6bb...} V/EXAMPLE: Event type: INSERT full document: Plant [id=5f6bb..., name=triffid, sunlight=low, color=green, type=perennial, partition=Store 47]
フィルターでコレクションの変更を監視
collection.watchWithFilter()またはcollection.watchWithFilterAsync( ) を呼び出すと、特定の条件を満たすコレクション内のドキュメントに加えられた変更のストリームを開くことができます。 どちらのメソッドも、コレクションの監視中に発生する各 データベース イベント を処理するために $match 演算子 のクエリとして使用される またはDocument
BsonDocument
パラメーターを受け入れます。
次のスニペットは、 plants
コレクション内のドキュメントに対する変更を監視しますが、「 Store 42 」という名前のパーティションに属するドキュメントに対応するイベントについては、提供されているコールバックのみをトリガーします。
RealmEventStreamAsyncTask<Plant> watcher = mongoCollection .watchWithFilterAsync(new Document("fullDocument._partition", "Store 42")); watcher.get(result -> { if (result.isSuccess()) { Log.v("EXAMPLE", "Event type: " + result.get().getOperationType() + " full document: " + result.get().getFullDocument()); } else { Log.e("EXAMPLE", "failed to subscribe to filtered changes in the collection with : ", result.getError()); } }); List<Plant> plants = Arrays.asList( new Plant( new ObjectId(), "triffid", "low", "green", "perennial", "Store 47"), new Plant( new ObjectId(), "venomous tentacula", "low", "brown", "annual", "Store 42" )); mongoCollection.insertMany(plants).getAsync(task -> { if (task.isSuccess()) { int insertedCount = task.get().getInsertedIds().size(); Log.v("EXAMPLE", "successfully inserted " + insertedCount + " documents into the collection."); } else { Log.e("EXAMPLE", "failed to insert documents with: ", task.getError()); } });
val watcher = mongoCollection .watchWithFilterAsync(Document("fullDocument._partition", "Store 42")) watcher[{ result -> if (result.isSuccess) { Log.v("EXAMPLE", "Event type: ${result.get().operationType} full document: ${result.get().fullDocument}") } else { Log.e("EXAMPLE", "failed to subscribe to filtered changes in the collection with : ${result.error}") } }] val plants = listOf( Plant( ObjectId(), "triffid", "low", "green", "perennial", "Store 47" ), Plant( ObjectId(), "venomous tentacula", "low", "brown", "annual", "Store 42" ) ) mongoCollection.insertMany(plants).getAsync { task -> if (task.isSuccess) { val insertedCount = task.get().insertedIds.size Log.v("EXAMPLE", "successfully inserted $insertedCount documents into the collection.") } else { Log.e("EXAMPLE", "failed to insert documents with: ${task.error}") } }
このスニペットを実行すると、次のような出力が生成されます。
V/EXAMPLE: successfully inserted 2 documents into the collection V/EXAMPLE: Event type: INSERT full document: Plant [id=5f6bb..., name=venomous tentacula, sunlight=low, color=brown, type=annual, partition=Store 42]
コレクション内のドキュメントの集計
集計操作は、集計パイプラインと呼ばれる一連のデータ集計ステージを通じてコレクション内のすべてのドキュメントを実行します。 集計を使用すると、ドキュメントのフィルタリングと変換、関連するドキュメントのグループに関するサマリー データの収集、その他の複雑なデータ操作が可能になります。
collection.aggregate() を使用して集計パイプラインを実行できます。
集計操作は、集計ステージのリストを入力として受け入れ、 タスク を返します パイプラインによって処理されたドキュメントのコレクションに解決されます。
フィルター ドキュメント
$matchステージを使用して、Query APIクエリフィルターに従ってドキュメントをフィルタリングできます。
{ "$match": { "<Field Name>": <Query Expression>, ... } }
例
次の$match
ステージでは、ドキュメントをフィルタリングして、 type
フィールドの値が「perennial」と等しいドキュメントのみを含めます。
// create an aggregation pipeline List<Document> pipeline = Arrays.asList( new Document("$match", new Document("type", new Document("$eq", "perennial")))); // query mongodb using the pipeline RealmResultTask<MongoCursor<Document>> aggregationTask = mongoCollection.aggregate(pipeline).iterator(); // handle success or failure of the query aggregationTask.getAsync(task -> { if (task.isSuccess()) { MongoCursor<Document> results = task.get(); // iterate over and print the results to the log Log.v("EXAMPLE", "successfully aggregated the plants. Results:"); while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()); } } else { Log.e("EXAMPLE", "failed to aggregate documents with: ", task.getError()); } });
// create an aggregation pipeline val pipeline = listOf( Document("\$match", Document("type", Document("\$eq", "perennial") ) ) ) // query mongodb using the pipeline val aggregationTask = mongoCollection.aggregate(pipeline).iterator() // handle success or failure of the query aggregationTask.getAsync { task: App.Result<MongoCursor<Document>> -> if (task.isSuccess) { val results = task.get() // iterate over and print the results to the log Log.v("EXAMPLE", "successfully aggregated the plants. Results:") while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()) } } else { Log.e("EXAMPLE", "failed to aggregate documents with: ${task.error}") } }
グループ ドキュメント
$groupステージを使用して、1 つ以上のドキュメントのサマリーデータを集計できます。 MongoDB は、 $group
ステージの_id
フィールド値で定義された式に基づいてドキュメントをグループ化します。 フィールド名の前に$
を付けることで、特定のドキュメント フィールドを参照できます。
次のスニペットは、 plants
コレクション内のすべてのドキュメントをtype
値でグループ化し、各タイプの数を集計します。
// create an aggregation pipeline List<Document> pipeline = Arrays.asList( new Document("$group", new Document("_id", "$type") .append("totalCount", new Document("$sum", 1)))); // query mongodb using the pipeline RealmResultTask<MongoCursor<Document>> aggregationTask = mongoCollection.aggregate(pipeline).iterator(); // handle success or failure of the query aggregationTask.getAsync(task -> { if (task.isSuccess()) { MongoCursor<Document> results = task.get(); // iterate over and print the results to the log Log.v("EXAMPLE", "successfully aggregated the plants. Results:"); while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()); } } else { Log.e("EXAMPLE", "failed to aggregate documents with: ", task.getError()); } });
// create an aggregation pipeline val pipeline = listOf( Document("\$group", Document("_id", "\$type") .append("totalCount", Document("\$sum", 1)) ) ) // query mongodb using the pipeline val aggregationTask = mongoCollection.aggregate(pipeline).iterator() // handle success or failure of the query aggregationTask.getAsync { task: App.Result<MongoCursor<Document>> -> if (task.isSuccess) { val results = task.get() // iterate over and print the results to the log Log.v("EXAMPLE", "successfully aggregated the plants. Results:") while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()) } } else { Log.e("EXAMPLE", "failed to aggregate documents with: ${task.error}") } }
プロジェクト ドキュメント フィールド
$projectステージを使用して、ドキュメントの特定のフィールドを含めたり省略したりできます。 さらに、集計演算子を使用して新しいフィールドを計算することもできます。 プロジェクションは、次の 2 つの方法で機能します。
値により 1 のフィールドが明示的に含められます。 これには、指定されていないすべてのフィールドを暗黙的に除外するという副作用があります。
値が 0 のフィールドを暗黙的に除外します。これには、指定されていないすべてのフィールドが暗黙的に含まれるという副作用があります。
これらの 2 つのプロジェクション方法は相互に排他的です。つまり、フィールドを明示的に含める場合は、フィールドを明示的に除外することはできません。また、その逆も同様です。
注意
_id
フィールドは特別なケースで、明示的に指定されない限り、すべてのクエリに常に含まれます。 このため、 0
値を持つ_id
フィールドを除外しながら、同時に_partition
などの他のフィールドを1
とともに含めることができます。 _id
フィールドを除外するという特別なケースのみ、1 つの$project
ステージで除外と包含の両方が許可されます。
{ "$project": { "<Field Name>": <0 | 1 | Expression>, ... } }
例
次の$project
ステージでは、 _id
フィールドが省略され、 name
フィールドが含まれ、 storeNumber
という名前の新しいフィールドが作成されます。 storeNumber
は 2 つの集計演算子を使用して生成されます。
$split
は、_partition
値をスペース文字で囲む 2 つのstringセグメントに分割します。 たとえば、この方法で分割された値「Store 42」は、「store」と「42」の 2 つの要素を含む配列を返します。$arrayElemAt
は、2 番目の引数に基づいて配列から特定の要素を選択します。 この場合、値1
は、0
の配列インデックス以降、$split
演算子によって生成された配列から 2 番目の要素を選択します。 たとえば、この操作に渡される値 ["store", "42"] は "42" の値を返します。
// create an aggregation pipeline List<Document> pipeline = Arrays.asList( new Document("$project", new Document("_id", 0) .append("name", 1) .append("storeNumber", new Document("$arrayElemAt", Arrays.asList( new Document("$split", Arrays.asList("$_partition", " ")), 1))))); // query mongodb using the pipeline RealmResultTask<MongoCursor<Document>> aggregationTask = mongoCollection.aggregate(pipeline).iterator(); // handle success or failure of the query aggregationTask.getAsync(task -> { if (task.isSuccess()) { MongoCursor<Document> results = task.get(); // iterate over and print the results to the log Log.v("EXAMPLE", "successfully aggregated the plants. Results:"); while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()); } } else { Log.e("EXAMPLE", "failed to aggregate documents with: ", task.getError()); } });
// create an aggregation pipeline val pipeline = listOf( Document("\$project", Document("_id", 0) .append("name", 1) .append("storeNumber", Document("\$arrayElemAt", listOf( Document("\$split", listOf( "\$_partition", " " ) ), 1 ) ) ) ) ) // query mongodb using the pipeline val aggregationTask = mongoCollection.aggregate(pipeline).iterator() // handle success or failure of the query aggregationTask.getAsync { task: App.Result<MongoCursor<Document>> -> if (task.isSuccess) { val results = task.get() // iterate over and print the results to the log Log.v("EXAMPLE", "successfully aggregated the plants. Results:") while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()) } } else { Log.e("EXAMPLE", "failed to aggregate documents with: ${task.error}") } }
ドキュメントへのフィールドの追加
$addFieldsステージを使用して、集計演算子を使用して計算された値を持つ新しいフィールドを追加できます。
注意
$addFields
は$projectに似ていますが、フィールドを含めたり省略したりすることはできません。
例
次の$addFields
ステージでは、 storeNumber
という名前の新しいフィールドが作成されます。値は_partition
フィールドの値を変換する 2 つの集計演算子の出力です。
// create an aggregation pipeline List<Document> pipeline = Arrays.asList( new Document("$addFields", new Document("storeNumber", new Document("$arrayElemAt", Arrays.asList( new Document("$split", Arrays.asList( "$_partition", " ")), 1))))); // query mongodb using the pipeline RealmResultTask<MongoCursor<Document>> aggregationTask = mongoCollection.aggregate(pipeline).iterator(); // handle success or failure of the query aggregationTask.getAsync(task -> { if (task.isSuccess()) { MongoCursor<Document> results = task.get(); // iterate over and print the results to the log Log.v("EXAMPLE", "successfully aggregated the plants. Results:"); while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()); } } else { Log.e("EXAMPLE", "failed to aggregate documents with: ", task.getError()); } });
// create an aggregation pipeline val pipeline = listOf( Document("\$addFields", Document("storeNumber", Document("\$arrayElemAt", listOf( Document("\$split", listOf( "\$_partition", " " ) ), 1 ) ) ) ) ) // query mongodb using the pipeline val aggregationTask = mongoCollection.aggregate(pipeline).iterator() // handle success or failure of the query aggregationTask.getAsync { task: App.Result<MongoCursor<Document>> -> if (task.isSuccess) { val results = task.get() // iterate over and print the results to the log Log.v("EXAMPLE", "successfully aggregated the plants. Results:") while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()) } } else { Log.e("EXAMPLE", "failed to aggregate documents with: ${task.error}") } }
Unwind Array Values
$unwindステージを使用して、配列を含む単一のドキュメントを、その配列の個々の値を含む複数のドキュメントに変換できます。 配列フィールドを展開すると、MongoDB は配列フィールドの要素ごとに各ドキュメントを 1 回コピーしますが、コピーごとに配列値を配列要素に置き換えます。
{ $unwind: { path: <Array Field Path>, includeArrayIndex: <string>, preserveNullAndEmptyArrays: <boolean> } }
例
次の$unwind
ステージでは、各ドキュメントのitems
配列の要素ごとに新しいドキュメントが作成されます。 また、元の配列内の要素の位置インデックスを指定するitemIndex
というフィールドも新しいドキュメントに追加されます。
// create an aggregation pipeline List<Document> pipeline = Arrays.asList( new Document("$unwind", new Document("path", "$items") .append("includeArrayIndex", "itemIndex"))); // query mongodb using the pipeline RealmResultTask<MongoCursor<Document>> aggregationTask = mongoCollection.aggregate(pipeline).iterator(); // handle success or failure of the query aggregationTask.getAsync(task -> { if (task.isSuccess()) { MongoCursor<Document> results = task.get(); // iterate over and print the results to the log Log.v("EXAMPLE", "successfully aggregated the plants. Results:"); while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()); } } else { Log.e("EXAMPLE", "failed to aggregate documents with: ", task.getError()); } });
// create an aggregation pipeline val pipeline = listOf( Document("\$unwind", Document("path", "\$items") .append("includeArrayIndex", "itemIndex")) ) // query mongodb using the pipeline val aggregationTask = mongoCollection.aggregate(pipeline).iterator() // handle success or failure of the query aggregationTask.getAsync { task: App.Result<MongoCursor<Document>> -> if (task.isSuccess) { val results = task.get() // iterate over and print the results to the log Log.v("EXAMPLE", "successfully aggregated the plants. Results:") while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()) } } else { Log.e("EXAMPLE", "failed to aggregate documents with: ${task.error}") } }
の購入に関するコレクションの次のドキュメントを検討します。
{ _id: 123, customerId: 24601, items: [ { name: "Baseball", quantity: 5 }, { name: "Baseball Mitt", quantity: 1 }, { name: "Baseball Bat", quantity: 1 }, ] }
このドキュメントに例の$unwind
ステージを適用すると、ステージは次の 3 つのドキュメントを出力します。
{ _id: 123, customerId: 24601, itemIndex: 0, items: { name: "Baseball", quantity: 5 } }, { _id: 123, customerId: 24601, itemIndex: 1, items: { name: "Baseball Mitt", quantity: 1 } }, { _id: 123, customerId: 24601, itemIndex: 2, items: { name: "Baseball Bat", quantity: 1 } }