读取 Realm 对象 - Kotlin SDK
在此页面上
本页介绍如何使用 Atlas Device SDK for Kotlin 查询和延迟读取数据库中持久保存的对象。在处理大型数据集和复杂查询时,这种延迟评估可以提高代码效率和性能。
读取操作
读取操作包括查询数据库对象,然后在准备好访问结果时运行查询。对于同步数据库和非同步数据库,读取操作的语法是相同的。
所有查询都基于对象类型。您可以查询持久保存到数据库并且类型包含在数据库模式中的任何对象,包括嵌入式对象。
使用 SDK 的查询构建器 RealmQuery构建查询,并将对象类型作为类型参数传递。 您可以查询 Realm
或MutableRealm
实例、 RealmResults
集合或RealmList
集合上的对象。
基本 RealmQuery
返回指定类型的所有对象:
// Finds all objects of type <T> .query<T>()
您可以使用额外的过滤器和条件(例如,对结果进行排序、聚合和限制)以优化查询。您可以将 Realm Query Language (RQL)(一种基于字符串的查询语言)与内置 Kotlin 扩展函数和 SDK 提供的辅助方法结合使用,以根据一个或多个属性优化结果。
// 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()
都充当一个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 协程流 您可以收集并遍历或侦听是否有更改。不能 在
asFlow()
上调用MutableRealm.query
。
请注意,任何检索的结果实际上并不会在内存中保留匹配的数据库对象。相反,数据库使用直接引用或指针。结果集合或流程中的数据库对象引用匹配的对象,这些对象直接映射到数据库文件中的数据。这也意味着,您可以通过查询结果直接遍历对象的关系图。
例子
运行查询
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)。
要访问权限实时结果,请在写事务(write transaction)中对MutableRealm实例运行查询。 MutableRealm
表示数据库的可写状态,只能通过写事务(write transaction)访问。 来自MutableRealm 的结果。查询处于活动状态,但仅在调用线程上有效,并在写事务(write transaction)完成后冻结。 有关写入事务和访问MutableRealm
的更多信息,请参阅写事务。
您还可以调用 MutableRealm.findLatest(),以从冻结的结果中访问实时对象。有关更多信息,请参阅本页上的查找对象的最新版本一节。
例子
访问实时结果
// '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 将返回指定类型的所有对象。
您可以通过将额外的query()方法附加到
RealmQuery
来链接筛选器。指定结果的排序顺序。由于数据库是无序的,如果您不包含排序顺序,SDK 无法保证查询以任何特定顺序返回对象。
使用以下任一方法执行查询:
提示
对于大型数据集,首选 asFlow()
find()
在从中调用它的线程上运行同步查询。因此,在用户界面线程上或可能延迟用户界面线程的逻辑中,避免对大型数据集使用find()
。首选
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
实例调用findLatest() 。 与MutableRealm.query()
一样,结果是实时的,但仅在调用线程上有效,并在写事务完成后冻结。
在以下示例中,我们将现有 frozenFrogs
查询的 RealmResults
传递给 findLatest()
,以获取集合的最新实时副本。然后,我们在写事务中修改当前实时对象:
// 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") } } }
提示
您可以使用 isFrozen() 方法检查对象是否冻结。
val isFrozen = frog.isFrozen()
查询某种类型的所有对象
要查询特定类型的所有对象,请将 RealmObject
或 EmbeddedRealmObject
对象类型作为类型参数传递给 query(),而无需使用任何查询参数。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()
您还可以通过父对象查询嵌入式对象。有关更多信息,请参阅本页上的按嵌入式对象属性过滤一节。
提示
在找到嵌入式对象后,您可以使用 EmbeddedRealmObject.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.") }
按属性过滤
您可以按数据库中持久保存的对象类型中的任何属性过滤查询。这包括子属性,您可以使用点表示法引用这些属性。
要按属性进行过滤,您可以传递 Realm Query Language (RQL) 过滤器和操作符,使用 Kotlin 的内置扩展方法或 SDK 的便利方法,或者使用组合。有关当前支持的所有 RQL 操作符和语法的信息,请参阅 Realm Query Language 参考文档。
在以下示例中,我们查询 Frog
对象类型,并按 name
属性进行过滤:
val filterByProperty = realm.query<Frog>("name == $0", "Kermit") val frogsNamedKermit = filterByProperty.find()
按主键过滤
主键是数据库中对象的唯一标识符,这使得它们对查询特定对象很有用。
要按特定主键进行过滤,请将对象类型作为类型参数传递,并查询主键字段以获取所需的值。
在以下示例中,我们查询一个 Frog
对象,并按主键属性 _id
进行过滤:
val filterByPrimaryKey = realm.query<Frog>("_id == $0", PRIMARY_KEY_VALUE) val findPrimaryKey = filterByPrimaryKey.find().first()
提示
Device Sync 始终将 _id 作为主键
如果使用 Atlas Device Sync,您始终可以按主键字段 _id
进行查询。这是因为,Atlas Device Sync 数据模型要求对象具有名为 _id
的主键。有关更多信息,请参阅使用 Device Sync 对数据建模 — Kotlin SDK。
按嵌入式对象属性过滤
嵌入式对象是嵌套在单个特定父对象中的数据。您可以直接查询嵌入式对象,也可以将其作为父对象上的属性进行查询。有关直接查询嵌入式对象的信息,请参阅本页上的查询某种类型的所有对象一节。
要通过父对象查找嵌入式对象,请将父对象类型作为类型参数传递,并使用点表示法按嵌入式对象属性进行过滤。
在以下示例中,我们具有一个 Contact
父对象,其中包含名为 address
的嵌入式对象属性。我们针对嵌入式对象的 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(混合)属性表示一个多态值,可以在特定时刻保存它支持的任何一种数据类型。您可以像任何属性一样查询 RealmAny
属性。
在以下示例中,我们查询一个名为 favoriteThing
的 RealmAny
属性,以查找一只最喜欢的东西类型为 Int
的青蛙:
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()提取值 因为我们知道返回的 frog 最喜欢的东西是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")
提示
使用条件表达式处理多态性
使用条件 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
对象在代码中具有一个名为 species
的属性,该属性重新映射到数据库中的 latin_name
:
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 和 Latin-1 补充(西方语言)中的字符。所有其他字符均被视为空格。
由连字符 (
-
) 拆分的单词将拆分为两个词元。例如,full-text
拆分为full
和text
。词汇单元(token)不区分变音符号和大小写。
您可以搜索整个单词或短语,或使用以下字符限制搜索结果:
在单词前加上
-
字符以排除该单词的结果。例如,fiction -science
将包含fiction
的所有搜索结果,并排除包含单词science
的搜索结果。在 Kotlin SDK 版本 1.11.0 和更高版本中,您可以通过在单词末尾添加
*
字符来指定前缀。例如,fict*
将包括fiction
和fictitious
的所有搜索结果。(Kotlin SDK 当前不支持后缀搜索)。
SDK 返回指定查询的布尔值匹配项,而不是基于相关性的匹配项。
在以下示例中,Frog
对象类型具有一个名为 physicalDescription
的 FTS 索引属性,我们可以按该属性过滤以查找不同类型的青蛙:
// 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
您可以像查询和遍历 Kotlin列表 一样查询和遍历 RealmList 属性。
在以下示例中,我们查询一个名为 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
您可以像查询和遍历 Kotlin 集 一样查询和遍历 RealmSet 属性。
在以下示例中,我们查询一个名为 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
键 (forest) 映射到一个 String
值 (pond):
// 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") }
按关系属性过滤
根据您定义对象类型的方式,您可能具有引用另一个数据库对象的属性。这可能是一对一、一对多或反向关系。
有关按引用嵌入式对象的关系属性进行过滤的信息,请参阅本页上的按嵌入式对象属性过滤一节。
一对一关系
一对一关系属性映射到另一种对象类型的单个实例。您可以像嵌套对象一样使用点表示法按关系属性进行过滤。
在以下示例中,Frog
对象类型具有一个名为 favoritePond
的 Pond
类型属性:
// 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") } }
反向关系
与一对一和一对多关系不同,反向关系自动在父对象和子对象之间创建反向链接。这意味着,您始终可以查询父对象和子对象。您也可以使用 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
:由两个GeoPoint
坐标定义,它们表示框的西南角和东北角GeoPolygon
:由一组GeoPoint
坐标(至少 4 个)定义,它们表示封闭形状。该形状可能包含一些孔,它们表示定义的多边形范围内的排他边界。
有关地理空间形状以及如何定义它们的更多信息,请参阅地理空间数据 — Kotlin SDK。
查询地理空间数据:
创建一个包含嵌入式地理空间数据的对象。
定义地理空间形状以设置查询边界。
使用
GEOWITHIN
RQL 操作符进行查询。该方法使用 GeoJSON 兼容嵌入式对象的coordinates
属性,并检查该点是否包含在定义的形状的边界内。无论地理空间形状如何,语法都是相同的。
在以下示例中,我们希望通过嵌入式 location
属性查询 Company
对象中持久保存的地理空间数据。我们创建两个 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 Sort、Limit 和 Distinct 操作符、以下 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
注意
字符串排序和不区分大小写的查询仅支持“基本拉丁语”、“拉丁语补充”、“拉丁扩展 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 会返回 ResultsChange ,您可以使用Flow
flow.collect() 对其进行遍历。
在以下示例中,我们对 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()