Realm オブジェクトの読み取り - Kotlin SDK
項目一覧
このページでは、 Kotlin 用の Atlas Device SDK を使用してデータベースに保存されているオブジェクトをクエリし、遅延して読み取る方法について説明します。 この遅延評価により、大規模なデータセットや複雑なクエリを処理する際にコードの効率とパフォーマンスが向上します。
読み取り操作
読み取り操作は、データベース オブジェクトをクエリし、結果にアクセスする準備ができたときにクエリを実行することで構成されます。 読み取り操作の構文は、同期されたデータベースと同期されていないデータベースで同じです。
すべてのクエリはオブジェクトタイプに基づいています。 埋め込みオブジェクトを含む、データベースに永続し、そのタイプがデータベース スキーマに含まれる任意のオブジェクトをクエリできます。
SDK のクエリ ビルダ RealmQueryを使用してクエリを構築し、オブジェクトタイプを型パラメータとして渡します。 Realm
またはMutableRealm
インスタンス、 RealmResults
コレクション、またはRealmList
コレクション上のオブジェクトをクエリできます。
基本のRealmQuery
は、指定された型のすべてのオブジェクトを返します。
// Finds all objects of type <T> .query<T>()
追加のフィルターと条件(たとえば、並べ替え、集計、結果の制限など)を使用してクエリを絞り込むことができます。 RQL ( RQL )は string ベースのクエリ言語で、SDK によって提供される組み込みのKotlin拡張関数とヘルパー メソッドと組み合わせて、1 つ以上のプロパティで結果を絞り込むことができます。
// Finds all objects of type <T> that meet the filter conditions // and returns them in the specified sort order .query<T>(filter).sort(sort)
追加の query() メソッドを使用して、クエリを 連鎖 させることもできます。追加された各query()
はAND
クエリ条件として機能します。 また、SDK の遅延評価により、連続するクエリにデータベースへの個別のトリップは必要ありません。
// Finds all objects of type <T> that meet the first filter conditions // AND the subsequent filter conditions .query<T>(filter).query(filter)
データにアクセスして返された結果を操作する準備ができたら、クエリを実行します。
find() を使用して 同期クエリ を実行します。 SDK は、クエリ条件に一致するすべてのデータベースオブジェクトを表す RealmResults コレクションを遅延して返します。一般に、他の Kotlinコレクション と同様に、結果コレクションを操作できます。
非同期クエリ を実行するには、 asFlow() を使用します。SDK は Kotlin コルーチン フロー を遅延サブスクライブ : を収集して反復処理したり、変更をリッスンしたりできます。
MutableRealm.query
でasFlow()
を呼び出すことはできません。
検索された結果では、メモリ内に一致するデータベース オブジェクトが実際には保持されていないことに注意してください。 代わりに、データベースは直接参照、またはポインターを使用します。 結果コレクションまたはフロー内のデータベース オブジェクトは、データベース ファイル内のデータに直接マップされる一致したオブジェクトを参照します。 つまり、クエリの結果からオブジェクトの関係のグラフを直接走査できることも意味します。
例
クエリを実行する
val queryAllFrogs = realm.query<Frog>() val queryAllLiveFrogs = this.query<Frog>() // this: MutableRealm // Calling 'find()' on the query returns a RealmResults collection // Can be called on a `Realm.query()` or `MutableRealm.query()` val allFrogs: RealmResults<Frog> = queryAllFrogs.find() val allLiveFrogs: RealmResults<Frog> = queryAllLiveFrogs.find() // Calling 'asFlow()' on the query returns a ResultsChange Flow // Can ONLY be called on a `Realm.query()` val allFrogsFlow: Flow<ResultsChange<Frog>> = queryAllFrogs.asFlow()
凍結された結果とライブ結果
常にライブ結果を返す他の Atlas Device SDK とは異なり、 Kotlin SDK の結果は固定またはライブ化できます。 Kotlin SDK の固定アーキテクチャの詳細については、「 凍結されたアーキテクチャ - Kotlin SDK 」を参照してください。
固定された結果にアクセスするには、 Realmで クエリを実行します。 凍結された結果は変更されず、データベースの最新の変更を反映していません。 Realm.query()
には書込みトランザクションは必要ありません。
ライブ結果にアクセスするには、書込みトランザクション (write transaction) のMutableRealmインスタンスに対してクエリを実行します。 MutableRealm
はデータベースの書込み可能な状態を表し、書込みトランザクションからのみアクセスできます。 MutableRealm .クエリの結果はライブですが、呼び出しスレッドでのみ有効であり、書込みトランザクションが完了すると固定されます。 書込みトランザクションとMutableRealm
へのアクセスの詳細については、「 書込みトランザクション 」を参照してください。
MutableRealm.find latest()を呼び出して、固定された結果からライブ オブジェクトにアクセスすることもできます。 詳細については、このページの「オブジェクトの最新バージョンの検索」セクションを参照してください。
例
ライブ結果へのアクセス
// 'Realm.query()' results are always frozen val frozenResults = realm.query<Frog>("age > $0", 50).find() // 'MutableRealm.query()' results are live within the current write transaction realm.write { // this: MutableRealm val liveResults = this.query<Frog>("age > $0", 50).find()
データベース オブジェクトの検索
データベース内に保存されているオブジェクトを検索するには、次の手順に従います。
オブジェクトタイプを型パラメーターとしてquery()に渡します。 オブジェクトタイプはデータベース スキーマにすでに含まれている必要があります。
オプションで、結果をさらに絞り込むために任意のクエリ条件を渡します。
条件を満たすオブジェクトのみを返すフィルターを指定します。 フィルターを指定しない場合、SDK は指定されたタイプのすべてのオブジェクトを返します。
RealmQuery
に追加のquery()メソッドを追加することで、フィルターを連鎖させることができます。結果のソート順序を指定します。 データベースは順序付けられていないため、ソート順序を含めない場合、SDK はクエリが特定の順序でオブジェクトを返すことを保証できません。
次のいずれかを使用してクエリを実行します。
Tip
大規模なデータセットでは asFlow() を使用する
find()
は、呼び出されたスレッドで同期クエリを実行します。 そのため、UI スレッド上の大規模なデータセット、または UI スレッドを遅延させる可能性のあるロジックでは、find()
を使用しないでください。悪パフォーマンスや UI への影響を防ぐには、
asFlow()
を使用することを優先します。結果を操作します。 実行したクエリの種類に応じて、オブジェクトは固定されているか保存されている場合があります。
例
読み取り操作
// Pass the object type as <T> parameter and filter by property val findFrogs = realm.query<Frog>("age > 1") // Chain another query filter .query("owner == $0 AND name CONTAINS $1", "Jim Henson", "K") // Sort results by property .sort("age", Sort.ASCENDING) // Run the query .find() // ... work with the results
オブジェクトの最新バージョンの検索
SDK の固定アーキテクチャにより、常に最新バージョンのオブジェクトまたはコレクションを操作しているわけではありません。
データベースの最新の変更を反映しているオブジェクトまたはコレクションのバージョンを取得するには、 MutableRealm
インスタンスからfindLast()を呼び出します。 MutableRealm.query()
と同様に、結果はライブですが、呼び出し元のスレッドでのみ有効であり、書込みトランザクションが完了すると固定されます。
次の例では、既存のfrozenFrogs
クエリのRealmResults
をfindLatest()
に渡して、コレクションの最新のライブ コピーを取得します。 次に、書込みトランザクション (write transaction) 内の有効なオブジェクトを変更します。
// Open a write transaction to access the MutableRealm realm.write { // this: MutableRealm for (frog in frozenFrogs) { // Call 'findLatest()' on an earlier query's frozen results // to return the latest live objects that you can modify // within the current write transaction findLatest(frog)?.also { liveFrog -> copyToRealm(liveFrog.apply { age += 1 }) println(liveFrog.name + " is now " + liveFrog.age + " years old") } } }
Tip
isFrozen()メソッドを使用して、オブジェクトが固定されているかどうかを確認できます。
val isFrozen = frog.isFrozen()
一定型のすべてのオブジェクトのクエリ
特定の型のすべてのオブジェクトをクエリするには、 RealmObject
またはEmbeddedRealmObject
オブジェクトタイプを型パラメータとしてquery()にクエリ引数なしで渡します。 SDK は、指定されたタイプのすべてのオブジェクトを返します。
注意
非対称オブジェクトは読み取れません
非対称オブジェクトは、データベースに永続しない特殊な書込み専用オブジェクトであるため、読み取りできません。 アプリケーションで非対称オブジェクトを使用する方法の詳細については、「 Atlas へのデータのストリーム - Kotlin SDK 」を参照してください。
次の例では、タイプFrog
のすべてのRealmObject
オブジェクトをクエリします。
// Query all Frog objects in the database val queryAllFrogs = realm.query<Frog>() val allFrogs = queryAllFrogs.find()
次の例では、タイプEmbeddedAddress
のすべてのEmbeddedRealmObject
オブジェクトをクエリします。
val queryAllEmbeddedAddresses = realm.query<EmbeddedAddress>() val allEmbeddedAddresses = queryAllEmbeddedAddresses.find()
また、親オブジェクトを介して埋め込みオブジェクトをクエリすることもできます。 詳細については、このページの「埋め込みオブジェクト プロパティによるフィルタリング」セクションを参照してください。
Tip
埋め込みオブジェクトが見つかったら、埋め込みRealmObject.parent()メソッドを使用してその親にアクセスできます。
val getParent = embeddedObject.parent<Contact>()
単一オブジェクトのクエリ
特定のオブジェクトタイプの単一のオブジェクトを検索するには、クエリでfirst()を呼び出します。 クエリを実行すると、SDK は条件に一致する最初のオブジェクトまたはnull
を返します。
次の例では、 Frog
オブジェクトタイプをクエリし、最初のオブジェクトを返します。
val querySingleFrog = realm.query<Frog>().first() val singleFrog = querySingleFrog.find() if (singleFrog != null) { println("${singleFrog.name} is a frog.") } else { println("No frogs found.") }
プロパティでフィルタリング
データベースに永続するオブジェクトタイプの任意のプロパティでクエリをフィルタリングできます。 これには、ドット表記を使用して参照できる子プロパティが含まれます。
プロパティでフィルタリングするには、 RQL ( RQL )フィルターと演算子を渡し、 Kotlinの組み込み拡張メソッドまたは SDK の便宜メソッドを使用するか、 組み合わせを使用します。 現在サポートされているすべてのRQL演算子と構文の詳細については、 RQLに関する参考ドキュメントを参照してください。
次の例では、 Frog
オブジェクトタイプをクエリし、 name
プロパティでフィルタリングします。
val filterByProperty = realm.query<Frog>("name == $0", "Kermit") val frogsNamedKermit = filterByProperty.find()
プライマリキーによるフィルタリング
プライマリキーは、データベース内のオブジェクトの一意の識別子であるため、特定のオブジェクトをクエリするのに役立ちます。
特定のプライマリキーでフィルタリングするには、オブジェクトタイプを type パラメーターとして渡し、プライマリキー フィールドで目的の値をクエリします。
次の例では、 Frog
オブジェクトをクエリし、プライマリキー プロパティ_id
でフィルタリングします。
val filterByPrimaryKey = realm.query<Frog>("_id == $0", PRIMARY_KEY_VALUE) val findPrimaryKey = filterByPrimaryKey.find().first()
Tip
Device Sync では常に _id がプライマリキーとして使用されます
Atlas Device Sync を使用している場合は、いつでもプライマリキー フィールド_id
でクエリを実行できます。 これは、Device Sync データモデルでは、オブジェクトが_id
という名前のプライマリキーを持っている必要があるためです。 詳細については、「 Device Sync によるデータのモデル化 - Kotlin SDK 」を参照してください。
埋め込みオブジェクト プロパティによるフィルタリング
埋め込みオブジェクトは、単一の特定の親オブジェクト内でネストされたデータとして機能します。 埋め込みオブジェクトを直接、またはその親オブジェクトのプロパティとしてクエリできます。 埋め込みオブジェクトを直接クエリする方法については、このページの「タイプのすべてのオブジェクトをクエリ」セクションを参照してください。
親オブジェクトから埋め込みオブジェクトを検索するには、親オブジェクトタイプを型パラメータとして渡し、ドット表記を使用して埋め込みオブジェクトプロパティでフィルタリングします。
次の例では、 address
という埋め込みオブジェクト プロパティを含むContact
親オブジェクトがあります。 埋め込みオブジェクトのaddress.street
プロパティに対してContact
オブジェクトタイプをクエリします。
// Use dot notation to access the embedded object properties as if it // were in a regular nested object val filterEmbeddedObjectProperty = realm.query<Contact>("address.street == '123 Pond St'") // You can also access properties nested within the embedded object val queryNestedProperty = realm.query<Contact>() .query("address.propertyOwner.name == $0", "Mr. Frog")
RealmAny(混合)プロパティによるフィルタリング
RealmAny(混合)プロパティは、特定の時点でサポートされているデータ型の 1 つを保持できる多態的な値を表します。 RealmAny
プロパティは、任意のプロパティと同じ方法でクエリできます。
次の例では、タイプInt
がお気に入りのコレクションを持つfログのfavoriteThing
と呼ばれるRealmAny
プロパティをクエリします。
val filterByRealmAnyInt = realm.query<Frog>("favoriteThing.@type == 'int'") val findFrog = filterByRealmAnyInt.find().first()
他のプロパティと違い、 RealmAny
プロパティを操作する前に、その保存値を抽出する必要があります。 値を抽出するには、保存された型の SDK の getter メソッドを使用します。 型に間違った getter を使用すると、SDK は例外をスローします。
ベストプラクティスとして、条件式を使用して、 RealmAny.type()に現在保存されている型を取得します。 型に基づいて値を抽出します。 getter メソッドの完全なリストについては、 RealmAny API リファレンス を参照してください。
次の例では、 RealmAny.asInt()を使用して 値を抽出します。 これは、返されたカドのお気に入りがInt
型の値であることがわかっているためです。
val frogsFavoriteThing = findFrog.favoriteThing // Int // Using the correct getter method returns the value val frogsFavoriteNumber = frogsFavoriteThing?.asInt() println("${findFrog.name} likes the number $frogsFavoriteNumber")
Tip
条件式による多形データの処理
条件付きwhen
式を使用して、特定のRealmAny
プロパティの可能な内部値クラスを処理します。
// Handle possible types with a 'when' statement frogsFavoriteThings.forEach { realmAny -> if (realmAny != null) { when (realmAny.type) { RealmAny.Type.INT -> { val intValue = realmAny.asInt() // Do something with intValue ... } RealmAny.Type.STRING -> { val stringValue = realmAny.asString() // Do something with stringValue ... } RealmAny.Type.OBJECT -> { val objectValue = realmAny.asRealmObject(Frog::class) // Do something with objectValue ... } // Handle other possible types... else -> { // Debug or perform a default action for unhandled types Log.d("Unhandled type: ${realmAny.type}") } } } }
現在保存されている値を取得したら、その型の別の値と同じ方法でその値を操作できます。
注意
Byte
、 Char
、 Int
、 Long
、 Short
の値は、内部的にint64_t
値に変換されます。 これらのタイプのRealmAny
値を比較、ソート、または集計するときは、この点に注意してください。
再マッピングされたプロパティでフィルタリング
データモデルに再マッピングされたプロパティ名が含まれている場合は、コードで使用される Kotlin プロパティ名と、 データベースに保存されている再マッピングされたプロパティ名の両方でフィルタリングできます。
次の例では、 Frog
オブジェクトには、 データベース内のlatin_name
に再マッピングされているコード内のspecies
という名前のプロパティがあります。
var species: String? = null // Remapped property
データベースでは、プロパティ名でフィルタリングして同じ結果を返すことができます。
val filterByKotlinName = realm.query<Frog>("species == $0", "Muppetarium Amphibius") val findSpecies = filterByKotlinName.find().first() val filterByRemappedName = realm.query<Frog>("latin_name == $0", "Muppetarium Amphibius") val find_latin_name = filterByRemappedName.find().first() // Both queries return the same object assertEquals(findSpecies, find_latin_name)
全文検索(FTS)プロパティでフィルタリング
バージョン 1.11.0 での変更: プレフィックス ワイルドカード検索のサポート
データモデルに全文検索(FTS)インデックス プロパティが含まれている場合は、 TEXT
述語を使用したプロパティでフィルタリングできます。 クエリ内の単語は、次のルールを使用して トークナイザ によってトークンに変換されます。
トークンは、ASCII と Atlas 1 の追加文字(西部言語)のみで構成できます。 他のすべての文字は空白と見なされます。
ハイフン(
-
)で区切られた単語は 2 つのトークンに分割されます。 たとえば、full-text
はfull
とtext
に分割されます。トークンは、発音区別符号を区別せず、大文字と小文字を区別しません。
単語またはフレーズ全体を検索することも、次の文字で結果を制限することもできます。
単語の前に
-
文字を付けて、単語の結果を除外します。 たとえば、fiction -science
にはfiction
のすべての検索結果が含まれ、science
という単語を含む検索結果は除外されます。Kotlin SDK バージョン 1.11.0 以降では、単語の末尾に
*
文字を配置することでプレフィックスを指定できます。 たとえば、fict*
にはfiction
とfictitious
のすべての検索結果が含まれます。 ( Kotlin SDK は現在、サフィックス検索をサポートしていません。)
SDK は、指定されたクエリに対して関連性ベースの一致ではなくブール値の一致を返します。
次の例では、 Frog
オブジェクトタイプにはphysicalDescription
という FTS インデックス プロパティがあり、これをフィルタリングしてさまざまなタイプの fログ を検索できます。
// Filter by FTS property value using 'TEXT' // Find all frogs with "green" in the physical description val onlyGreenFrogs = realm.query<Frog>("physicalDescription TEXT $0", "green").find() // Find all frogs with "green" but not "small" in the physical description val onlyBigGreenFrogs = realm.query<Frog>("physicalDescription TEXT $0", "green -small").find() // Find all frogs with "muppet-" and "rain-" in the physical description val muppetsInTheRain = realm.query<Frog>("physicalDescription TEXT $0", "muppet* rain*").find()
コレクション(リスト、セット、辞書)プロパティでフィルタリング
オブジェクトタイプの定義方法によっては、サポートされている次のコレクション タイプのいずれかとして定義されるプロパティがある場合があります。
RealmList
RealmSet
RealmDictionary
これらのコレクション プロパティは、RQL を使用して他のプロパティと同じ方法でクエリできます。 Kotlin に組み込まれているコレクション関数を使用して、結果をフィルタリング、ソート、反復処理することもできます。
RealmList
RealmList プロパティでは Kotlin List と同様にクエリと反復処理ができます。
次の例では、 favoritePonds
というRealmList
プロパティをクエリします。
// Find frogs with a favorite pond val allFrogs = query<Frog>().find() val frogsWithFavoritePond = allFrogs.query("favoritePonds.@size > $0", 0).find() // Check if the list contains a value for (frog in frogsWithFavoritePond) { val likesBigPond = frog.favoritePonds.any { pond -> pond.name == "Big Pond" } if (likesBigPond) { Log.v("${frog.name} likes Big Pond") } else { Log.v("${frog.name} does not like Big Pond") } }
RealmSet
RealmSet プロパティでは Kotlin Set と同様にクエリと反復処理ができます。
次の例では、 favoriteSnacks
というRealmSet
プロパティをクエリします。
// Find frogs with flies and crickets as a favorite snack val filterBySnackSet = query<RealmSet_Frog>("favoriteSnacks.name CONTAINS $0 AND favoriteSnacks.name CONTAINS $1", "Flies", "Crickets") val potentialFrogs = filterBySnackSet.find() // Check if the set contains a value val frogsThatLikeWorms = potentialFrogs.filter { frog -> val requiredSnacks = query<RealmSet_Snack>("name == $0", "Worms") frog.favoriteSnacks.contains(requiredSnacks.find().first()) } for (frog in frogsThatLikeWorms) { Log.v("${frog.name} likes both Flies, Worms, and Crickets") }
RealmDictionary
Kotlin マップ と同様に RealmDictionary プロパティをクエリして反復処理できます。
次の例では、 favoritePondsByForest
というRealmDictionary
プロパティをクエリします。このプロパティは、 String
キー(フォスト)をString
値(ロード)にマッピングします。
// Find frogs who have forests with favorite ponds val frogs = realm.query<Frog>().find() val frogsWithFavoritePonds = frogs.query("favoritePondsByForest.@count > $0", 1).find() val thisFrog = frogsWithFavoritePonds.first() // Iterate through the map and log each key-value pair for (forestName in thisFrog.favoritePondsByForest.keys) { val pondName = thisFrog.favoritePondsByForest[forestName] Log.v("Forest: $forestName, Pond: $pondName") } // Check if the dictionary contains a key if (thisFrog.favoritePondsByForest.containsKey("Hundred Acre Wood")) { Log.v("${thisFrog.name}'s favorite pond in Hundred Acre Wood is ${thisFrog.favoritePondsByForest["Hundred Acre Wood"]}") } // Check if the dictionary contains a value if (thisFrog.favoritePondsByForest.containsValue("Picnic Pond")) { Log.v("${thisFrog.name} lists Picnic Pond as a favorite pond") }
関係プロパティでフィルタリング
オブジェクトタイプの定義方法によっては、別のデータベース オブジェクトを参照するプロパティがある場合があります。 これは 1 対多、対多、または逆の関係にすることができます。
埋め込みオブジェクトを参照する関係プロパティによるフィルタリングの詳細については、このページの「埋め込みオブジェクト プロパティによるフィルタリング 」セクションを参照してください。
対 1 の関係
対 1 の関係プロパティは、別のオブジェクトタイプの単一のインスタンスにマップされます。 ドット表記を使用して、ネストされたオブジェクトと同様に、 関係 プロパティでフィルタリングできます。
次の例では、 Frog
オブジェクトタイプには、タイプPond
のfavoritePond
というプロパティがあります。
// Find frogs who have a favorite pond val allFrogs = query<Frog>().find() val frogsWithFavoritePond = allFrogs.query("favoritePond.@count == $0", 1).find() // Iterate through the results for (frog in frogsWithFavoritePond) { Log.v("${frog.name} likes ${frog.favoritePond?.name}") }
対多の関係
対多の関係プロパティは、別のオブジェクトタイプのコレクション( RealmListまたはRealmSet )です。 関係プロパティでは、他のコレクション プロパティと同様に、 でフィルタリングして反復処理できます。
次の例では、 Forest
オブジェクトタイプには、 nearbyPonds
というタイプのPond
RealmList
である というプロパティがあります。
// Find all forests with at least one nearby pond val allForests = query<Forest>().find() val forestsWithPonds = allForests.query("nearbyPonds.@count > $0", 0).find() // Iterate through the results for (forest in forestsWithPonds) { val bigPond = query<Pond>("name == $0", "Big Pond").find().first() if (forest.nearbyPonds.contains(bigPond)) { Log.v("${forest.name} has a nearby pond named ${bigPond.name}") } else { Log.v("${forest.name} does not have a big pond nearby") } }
逆の関係
1 対多の関係とは異なり、逆関係では親オブジェクトと子オブジェクト間のバックリンクが自動的に作成されます。 つまり、親と子の両方に対して常にクエリを実行できます。 RQL 固有の構文を使用してバックリンクをクエリすることもできます(詳細については、「 バックリンククエリ」を参照してください)。
次の例では、タイプUser
の親オブジェクトとタイプPost
の子オブジェクトと逆の関係があります。 親オブジェクトのUser.posts
関係(「ユーザーには多数の投稿がある」)と、逆のPost.user
関係(「投稿はユーザーに属する」)をクエリできます。
// Query the parent object val filterByUserName = query<User>("name == $0", "Kermit") val kermit = filterByUserName.find().first() // Use dot notation to access child objects val myFirstPost = kermit.posts[0] // Iterate through the backlink collection property kermit.posts.forEach { post -> Log.v("${kermit.name}'s Post: ${post.date} - ${post.title}") } // Filter posts through the parent's backlink property // using `@links.<ObjectType>.<PropertyName>` syntax val oldPostsByKermit = realm.query<Post>("date < $0", today) .query("@links.User.posts.name == $0", "Kermit") .find() // Query the child object to access the parent val post1 = query<Post>("title == $0", "Forest Life").find().first() val post2 = query<Post>("title == $0", "Top Ponds of the Year!").find().first() val parent = post1.user.first()
重要
再マッピングされたクラス名による逆関係のクエリ
逆関係プロパティが再マップされた(永続化)クラス名を持つオブジェクトタイプである場合は、未加工 RQL クエリで再マップされたクラス名を使用する必要があります。
class User : RealmObject { var _id: ObjectId = ObjectId() var name: String = "" var posts: RealmList<Post> = realmListOf() }
// Filter by the remapped object type name // using `@links.<RemappedObjectType>.<PropertyName>` syntax val postsByKermit = realm.query<Post>() .query("@links.Blog_Author.posts.name == $0", "Kermit") .find()
地理空間データのクエリ
バージョン 1.11.0 の新機能。
Kotlin SDK バージョン 1.11.0 以降では、地理空間データによるクエリをサポートする実験的な地理空間 API が追加されています。
注意
地理空間データを保持するには、データモデル内に GeoJSON 互換のカスタム埋め込みクラスを定義する必要があります。 要件の詳細については、「 永続的な地理空間データ 」を参照してください。
地理空間データは、カスタム埋め込みオブジェクトのcoordinates
プロパティに緯度と経度のペアとして保持されます。 地理空間クエリは、 coordinates
プロパティによって定義された点が定義された地理空間形状の境界内に含まれているかどうかを確認します。
SDK は次の地理空間の形状をサポートしています。
GeoCircle
: 中心GeoPoint
と半径Distance
によって定義GeoBox
: ボックスのエスケープとメンバーシップを表す 2 つのGeoPoint
座標によって定義されますGeoPolygon
: 閉じた形状を表す少なくとも 4 つのGeoPoint
座標のセットによって定義されます。 この形状には、定義された多角形の範囲内の排他的な境界を表す穴を含めることができます。
地理空間の形状とその定義方法について詳しくは、「地理空間データ - Kotlin SDK 」を参照してください。
地理空間データをクエリするには、次のようにします。
埋め込み地理空間データを含むオブジェクトを作成します。
地理空間の形状を定義して、クエリの境界を設定します。
GEOWITHIN
RQL 演算子を使用してクエリを実行します。 このメソッドは、GeoJSON 互換埋め込みオブジェクトのcoordinates
プロパティを受け取り、その点が定義された形状の境界内に含まれているかどうかを確認します。 地理空間の形状に関係なく、構文は同じです。
次の例では、埋め込まれたlocation
プロパティを使用して、 Company
オブジェクトに保存されている地理空間データをクエリします。 クエリ境界を設定するために 2 つのGeoCircle
オブジェクトを作成します。
val circle1 = GeoCircle.create( center = GeoPoint.create(47.8, -122.6), radius = Distance.fromKilometers(44.4) ) val circle2 = GeoCircle.create( center = GeoPoint.create(47.3, -121.9), radius = Distance.fromDegrees(0.25) )
次に、定義されたGeoCircle
境界内に含まれるlocation
を持つCompany
オブジェクトをクエリします。
val companiesInLargeCircle = realm.query<Company>("location GEOWITHIN $circle1").find() println("Companies in large circle: ${companiesInLargeCircle.size}") val companiesInSmallCircle = realm.query<Company>("location GEOWITHIN $circle2").find() println("Companies in small circle: ${companiesInSmallCircle.size}")
Companies in large circle: 1 Companies in small circle: 0
結果のソートと制限
結果が期待どおりに返されるようにするには、 RQL ソート、制限、個別の 演算子、次の SDK 便利メソッド、または両方の組み合わせを使用して、ソート順序と制限条件を指定できます。
重要
順序は重要
RQL メソッドまたは便宜的メソッドのどちらを使用するかにかかわらず、SDK はクエリに追加された順序で各リクエストを実行します。 これは返される結果に影響を与える可能性があります。 たとえば、クエリを制限する前にソートすると、制限してからソートすることとは異なる結果が返される可能性があります。
次の例では、便宜的メソッドのみ、RQL のみ、および両方の組み合わせを使用してソートと制限を行い、同じ結果を返します。
// Query for all frogs owned by Jim Henson, then: // 1. Sort results by age in descending order // 2. Limit results to only distinct names // 3. Limit results to only the first 2 objects val organizedWithMethods = realm.query<Frog>("owner == $0", "Jim Henson") .sort("age", Sort.DESCENDING) .distinct("name") .limit(2) .find() organizedWithMethods.forEach { frog -> Log.v("Method sort: ${frog.name} is ${frog.age}") } val organizedWithRql = realm.query<Frog>() .query("owner == $0 SORT(age DESC) DISTINCT(name) LIMIT(2)", "Jim Henson") .find() organizedWithRql.forEach { frog -> Log.v("RQL sort: ${frog.name} is ${frog.age}") } val organizedWithBoth = realm.query<Frog>() .query("owner == $0 SORT(age DESC)", "Jim Henson") .distinct("name") .limit(2) .find() organizedWithBoth.forEach { frog -> Log.v("Combined sort: ${frog.name} is ${frog.age}") }
Method sort: Kermit, Sr. is 100 Method sort: Kermit is 42 RQL sort: Kermit, Sr. is 100 RQL sort: Kermit is 42 Combined sort: Kermit, Sr. is 100 Combined sort: Kermit is 42
注意
stringソートと大文字と小文字を区別しないクエリは、「ラテン語の基本」、「ラテン語の追加」、「ラテン語の拡張 A」、および「ラテン語の拡張 B」の文字セットでのみサポートされます(UTF-8 範囲 0 ~ 591)。
結果を集計する
結果を集計することもできます。これにより、指定された数値プロパティまたはコレクションに基づいて、結果を単一の値に縮小します。 RQL 集計演算子、次のいずれかの便利なメソッド、または両方の組み合わせを使用できます。
次の例では、 Frog
オブジェクトタイプのage
プロパティを集計します。
val jimHensonsFrogs = realm.query<Frog>("owner == $0", "Jim Henson") // Find the total number of frogs owned by Jim Henson val numberOfJimsFrogs = jimHensonsFrogs.count().find() // Find the oldest frog owned by Jim Henson val maxAge = jimHensonsFrogs.max<Int>("age").find() val oldestFrog = jimHensonsFrogs.query("age == $0", maxAge).find().first()
フローを使用した結果の反復処理
Kotlin コルーチン フローを使用して結果を反復処理できます。
クエリ結果を非同期Flow
に変換するには、クエリでasFlow()を呼び出します。 SDK は、 Flow
flow.collection () を使用して反復処理できる 結果変更 を返します 。
次の例では、 Frog
オブジェクトのFlow
を反復処理します。
// Get a Flow of all frogs in the database val allFrogsQuery = realm.query<Frog>() val frogsFlow: Flow<ResultsChange<Frog>> = allFrogsQuery.asFlow() // Iterate through the Flow with 'collect()' val frogsObserver: Deferred<Unit> = async { frogsFlow.collect { results -> when (results) { is InitialResults<Frog> -> { for (frog in results.list) { Log.v("Frog: $frog") } } else -> { /* no-op */ } } } } // ... Later, cancel the Flow, so you can safely close the database frogsObserver.cancel() realm.close()
Tip
フローをサブスクライブして変更をリッスンする
クエリからFlow
を生成したら、通知ハンドラーを登録して、 ResultsChanges
への変更をリッスンできます。 詳細については、「変更に対応する - React Kotlin SDK 」を参照してください。