Docs 菜单
Docs 主页
/ /
Atlas Device SDKs
/ / /

读取 Realm 对象 - Kotlin SDK

在此页面上

  • 读取操作
  • 冻结的结果和实时结果
  • 查找数据库对象
  • 查找对象的最新版本
  • 查询某种类型的所有对象
  • 查询单个对象
  • 按属性过滤
  • 按主键过滤
  • 按嵌入式对象属性过滤
  • 按 RealmAny(混合)属性过滤
  • 按重新映射的属性过滤
  • 按全文搜索 (FTS) 属性过滤
  • 按集合(列表、集、字典)属性过滤
  • 按关系属性过滤
  • 查询地理空间数据
  • 对结果进行排序和限制
  • 聚合结果
  • 使用流程迭代结果

本页介绍如何使用 Atlas Device SDK for Kotlin 查询和延迟读取数据库中持久保存的对象。在处理大型数据集和复杂查询时,这种延迟评估可以提高代码效率和性能。

读取操作包括查询数据库对象,然后在准备好访问结果时运行查询。对于同步数据库和非同步数据库,读取操作的语法是相同的。

所有查询都基于对象类型。您可以查询持久保存到数据库并且类型包含在数据库模式中的任何对象,包括嵌入式对象

使用 SDK 的查询构建器 RealmQuery构建查询,并将对象类型作为类型参数传递。 您可以查询 RealmMutableRealm实例、 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)

在您准备好访问数据并处理返回的结果时,运行查询:

请注意,任何检索的结果实际上并不会在内存中保留匹配的数据库对象。相反,数据库使用直接引用或指针。结果集合或流程中的数据库对象引用匹配的对象,这些对象直接映射到数据库文件中的数据。这也意味着,您可以通过查询结果直接遍历对象的关系图。

例子

运行查询

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()

要查找数据库中存储的对象,请执行以下操作:

  1. 将对象类型作为类型参数传递给 query()。对象类型必须已包含在数据库模式中。

  2. (可选)传递任何查询条件以进一步优化结果:

    • 指定过滤器以仅返回满足条件的对象。如果未指定过滤器,SDK 将返回指定类型的所有对象。

      您可以通过将额外的query()方法附加到RealmQuery来链接筛选器。

    • 指定结果的排序顺序。由于数据库是无序的,如果您不包含排序顺序,SDK 无法保证查询以任何特定顺序返回对象。

  3. 使用以下任一方法执行查询:

    • find()用于同步查询。 返回结果集合。

    • asFlow()用于异步查询。 订阅Flow项结果更改。

    提示

    对于大型数据集,首选 asFlow()

    find() 在从中调用它的线程上运行同步查询。因此,在用户界面线程上或可能延迟用户界面线程的逻辑中,避免对大型数据集使用 find()

    首选 asFlow() 以防止对性能或用户界面造成不利影响。

  4. 处理结果。对象可能是冻结或实时的,具体取决于您运行的查询类型。

例子

读取操作

// 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()

要查询特定类型的所有对象,请将 RealmObjectEmbeddedRealmObject 对象类型作为类型参数传递给 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()

您还可以通过父对象查询嵌入式对象。有关更多信息,请参阅本页上的按嵌入式对象属性过滤一节。

提示

在找到嵌入式对象后,您可以使用 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 属性。

在以下示例中,我们查询一个名为 favoriteThingRealmAny 属性,以查找一只最喜欢的东西类型为 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}")
}
}
}
}

在获取当前存储的值后,您可以像该类型的其他值一样处理该值。

注意

ByteCharIntLongShort 值在内部转换为 int64_t 值。在对这些类型的 RealmAny 值进行比较、排序或聚合时,请记住这一点。

如果数据模型包含重新映射的属性名称,您可以按代码中使用的 Kotlin 属性名称和数据库中持久保存的重新映射属性名称进行过滤。

在以下示例中,Frog 对象在代码中具有一个名为 species 的属性,该属性重新映射到数据库中的 latin_name

@PersistedName("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)

1.11.0 版本的更改内容:支持前缀通配符搜索

如果数据模型包含全文搜索 (FTS) 索引属性,您可以使用 TEXT 谓词按该属性进行过滤。分词器使用以下规则将查询中的单词转换为词元:

  • 词汇单元只能包含 ASCII 和 Latin-1 补充(西方语言)中的字符。所有其他字符均被视为空格。

  • 由连字符 (-) 拆分的单词将拆分为两个词元。例如,full-text 拆分为 fulltext

  • 词汇单元(token)不区分变音符号和大小写。

您可以搜索整个单词或短语,或使用以下字符限制搜索结果:

  • 在单词前加上 - 字符以排除该单词的结果。例如, fiction -science 将包含 fiction 的所有搜索结果,并排除包含单词 science 的搜索结果。

  • 在 Kotlin SDK 版本 1.11.0 和更高版本中,您可以通过在单词末尾添加 * 字符来指定前缀。例如,fict* 将包括 fictionfictitious 的所有搜索结果。(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 的内置集合函数,对结果进行过滤、排序和遍历。

您可以像查询和遍历 Kotlin列表 一样查询和遍历 RealmList 属性。

在以下示例中,我们查询一个名为 favoritePondsRealmList 属性:

// 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")
}
}

您可以像查询和遍历 Kotlin 集 一样查询和遍历 RealmSet 属性。

在以下示例中,我们查询一个名为 favoriteSnacksRealmSet 属性:

// 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")
}

您可以像查询和遍历 Kotlin 映射 一样查询和遍历 RealmDictionary 属性。

在以下示例中,我们查询一个名为 favoritePondsByForestRealmDictionary 属性,该属性将一个 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 对象类型具有一个名为 favoritePondPond 类型属性:

// 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}")
}

一对多关系属性是另一种对象类型的集合(RealmListRealmSet)。您可以像任何其他集合属性一样对关系属性进行过滤和遍历。

在以下示例中,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 查询中使用重新映射的类名称。

@PersistedName(name = "Blog_Author")
class User : RealmObject {
@PrimaryKey
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

查询地理空间数据:

  1. 创建一个包含嵌入式地理空间数据的对象。

  2. 定义地理空间形状以设置查询边界。

  3. 使用 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
查询 GeoCircle 示例
点击放大

为了确保按预期方式返回结果,您可以使用 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()

提示

订阅 Flow 以侦听变化

从查询中生成 Flow 后,您可以注册通知处理程序以侦听对 ResultsChanges 的更改。有关更多信息,请参阅响应更改 — Kotlin SDK

后退

创建