CRUD — 读取 — Flutter SDK
在此页面上
您可以通过查找、筛选和排序对象来读回数据库中存储的数据。
读取数据库通常包括以下步骤:
从数据库中获取特定类型的所有对象。
(可选)筛选结果。
查询操作返回 结果集合 。这些集合是实时的,这意味着它们始终包含关联查询的最新结果。
读取特征
围绕这三个关键读取特征设计应用的数据访问模式,以尽可能高效地读取数据。
结果不是副本
查询结果不是数据的副本。 修改查询结果会直接修改磁盘上的数据。 这种内存映射还意味着结果是实时的:也就是说,它们始终反映磁盘上的当前状态。
结果出现延迟
SDK 仅在您实际请求该查询的结果时运行查询。 这种延迟求值使您能够编写高性能代码来处理大型数据集和复杂查询。 您可以链接多个筛选器操作,而无需额外的工作来处理中间状态。
引用被保留
SDK 对象模型的优点之一是,SDK 会自动将对象的所有关系保留为直接引用。 这使您能够直接通过查询结果遍历关系图。
直接引用或指针允许您直接通过该引用访问相关对象的属性。
当您需要直接使用对象时,其他数据库通常会将对象从数据库存储复制到应用程序内存中。由于应用程序对象包含直接引用,因此您只有一个选项:将每个直接引用所引用的对象从数据库中复制出来(如果需要),或仅复制每个对象的外键并在该对象被访问的情况下使用该键来查询此对象。如果选择将引用的对象复制到应用程序内存中,则可能会为从未访问过的对象占用大量资源;但是,如果选择仅复制外键,引用的对象查找则可能会导致应用程序变慢。
SDK 使用零拷贝活动对象绕过了所有这些。 SDK 的对象访问器使用内存映射直接指向数据库存储,因此数据库中的对象与应用程序内存中的查询结果没有区别。 因此,您可以从任何查询结果中遍历整个数据库的直接引用。
限制查询结果
由于采用延迟求值,因此您不需要任何特殊机制来限制使用 SDK 的查询结果。 例如,如果您的查询匹配数千个对象,但您只想加载前十个对象,则只需访问collection的前十个元素即可。
分页
多亏了延迟求值,常见的分页任务变得非常简单。 例如,假设您有一个与数据库中数千个对象匹配的查询关联的collection。每页显示一百个对象。 要前进到任何页面,只需从与目标页面对应的索引开始访问结果collection中的元素即可。
读取对象
除非另有说明,否则本页上的示例将使用两种Realm 对象类型: Person
和Team
。
()class _Person { () late ObjectId id; late String name; late List<String> hobbies; } ()class _Team { () late ObjectId id; late String name; late List<_Person> crew; late RealmValue eventLog; }
通过主节点查找对象
final luke = realm.find<Person>(lukePrimaryKey);
查询所有对象
使用 Realm.all() 检索数据库中数据模型的所有对象的集合方法。
final people = realm.all<Person>();
查询相关对象
版本 1.8.0 中的新增内容。
如果数据模型包含引用其他对象的对象,则可以使用 getBacklinks () 方法。
此方法返回 结果集合 通过对一、对多或反向关系链接到给定对象的所有对象的数量。以下示例使用“关系”页面上定义的模型。
关系: 在本例中,我们有一个Realm
Bike
Person
对象模型,它与对象存在一对一关系。我们使用
getBacklinks()
方法查找通过owner
属性链接到指定人员的任何Bike
对象:// Persons have a to-one relationship with Bikes final person = realm.query<Person>("firstName == 'Anakin'").first; // Find all Bikes owned by a Person named 'Anakin' final allBikes = person.getBacklinks<Bike>('owner'); 多对多关系:在此示例中,我们有一个与
ScooterShop
对象具有对多关系的Scooter
对象模型。我们使用
getBacklinks()
方法查找通过scooters
列表属性链接到指定滑板车的任何ScooterShops
对象:// Scooters have a to-many relationship with ScooterShops final scooters = realm.query<Scooter>("name == 'Scooterbug'").first; // Find all ScooterShops with a Scooter named 'Scooterbug' final shops = scooters.getBacklinks<ScooterShop>('scooters'); 反向关系:在此示例中,我们有一个与
User
对象具有反向关系的Task
对象模型。我们使用
getBacklinks()
方法查找通过tasks
反向链接属性链接到指定任务的任何User
对象:// Tasks have an inverse relationship to Users final inCompleteTasks = realm.query<Task>("isComplete == false"); // Find all Users who have an incomplete Task for (final task in inCompleteTasks) { final ownersWithIncompleteTasks = task.getBacklinks<User>('tasks'); for (final user in ownersWithIncompleteTasks) { print("User ${user.username} has incomplete tasks."); } }
查询列表
您可以查询任何 RealmObjects 列表 或基元。
final config = Configuration.local([Person.schema, Team.schema]); final realm = Realm(config); final heroes = Team(ObjectId(), 'Millenium Falcon Crew', crew: [ Person(ObjectId(), 'Luke'), Person(ObjectId(), 'Leia'), Person(ObjectId(), 'Han'), Person(ObjectId(), 'Chewbacca') ]); realm.write(() => realm.add(heroes)); final lukeAndLeia = heroes.crew.query('name BEGINSWITH \$0', ['L']);
查询混合数据的嵌套集合
2.0.0版本新增。
在 Flutter SDK v 2.0.0及更高版本中, RealmValue属性可以包含混合数据的集合(列表或地图)。 这些集合可以嵌套在集合内,也可以包含其他混合数据的集合。
您可以使用与查询普通列表或字典集合相同的语法来查询这些内容。 请参阅有关支持的操作符和列表比较的更多信息,请参阅文档。
对于嵌套集合,还可以使用:
括号表示法,提供以下集合查询运算符:
[FIRST]
和 [LAST
]:匹配集合中的第一个或最后一个元素。[<int>]
:匹配特定索引处的元素。[*]
:匹配集合中的任何元素(此操作符假定该路径上的集合类型)。[SIZE]
:匹配集合长度。
@type
操作符,支持以下值:array
和list
:匹配列表集合。dictionary
和object
:匹配地图集合。collection
:匹配列表或地图集合。
realm.write(() { realm.addAll([ (Team(ObjectId(), 'Janitorial Staff', eventLog: RealmValue.from({ '1': { 'date': DateTime.utc(5622, 8, 18, 12, 30, 0), 'type': ['work_order', 'maintenance'], 'summary': 'leaking pipes in control room', 'priority': 'high', }, '2': { 'date': DateTime.utc(5622, 9, 18, 12, 30, 0), 'type': ['maintenance'], 'summary': 'trash compactor jammed', 'priority': 'low', 'comment': 'this is the second time this week' } }))), (Team(ObjectId(), 'IT', eventLog: RealmValue.from({ '1': { 'date': DateTime.utc(5622, 9, 20, 12, 30, 0), 'type': ['hardware', 'repair'], 'summary': 'lightsaber damage to server room', 'priority': 'high', } }))) ]); final teams = realm.all<Team>(); // Use bracket notation to query collection values at the specified path final teamsWithHighPriorityEvents = // Check any element at that path with [*] teams.query("eventLog[*].priority == 'high'"); print(teamsWithHighPriorityEvents.length); // prints `2` final teamsWithMaintenanceEvents = // Check for the first element at that path with [FIRST] teams.query("eventLog[*].type[FIRST] == 'maintenance'"); print(teamsWithMaintenanceEvents.length); // prints `1` final teamsWithMultipleEvents = // Check for collection at that path with matching elements // Note that the order must match unless you use ANY or ALL teams.query("eventLog[*].type[*] == {'maintenance', 'work_order'}"); print( teamsWithMultipleEvents.length); // prints `0` because order matters final teamsWithEventsAsLists = // Check the collection type with @type teams.query("eventLog[*].type.@type == 'list'"); print(teamsWithEventsAsLists.length); // prints `2` });
将列表或集转换为结果
您可以使用各自的方法将RealmList
或RealmSet
转换为RealmResults
的实例:
这些方法支持列表和RealmObjects
集合以及原始值。
final config = Configuration.local([Person.schema, Team.schema]); final realm = Realm(config); final heroes = Team(ObjectId(), 'Millenium Falcon Crew', crew: [ Person(ObjectId(), 'Luke', hobbies: [ 'Going to Tashi Station', 'Fixing the Moisture Vaporators' ]), Person(ObjectId(), 'Leia', hobbies: [ 'Going on diplomatic missions', 'Rescuing short stormtroopers' ]), Person(ObjectId(), 'Han', hobbies: ['Shooting first', 'Making fast Kessel Runs']), Person(ObjectId(), 'Chewbacca', hobbies: [ 'Fixing the Millenium Falcon', 'Tearing the arms off of droids' ]) ]); realm.write(() => realm.add(heroes)); // Converts the Team object's 'crew' List into a RealmResults<Person>. final heroesCrewAsResults = heroes.crew.asResults(); final luke = heroesCrewAsResults.query("name == 'Luke'").first; // Converts Luke's 'hobbies' list into a RealmResults<String> final lukeHobbiesAsResults = luke.hobbies.asResults();
筛选结果
使用RealmList
Realm.query() 过滤 以检索特定的对象段方法。 在query()
方法的参数中,使用 RQL 执行筛选。 RQL 是一种基于字符串的查询语言,可用于从数据库中检索对象。
final team = realm.query<Team>('name == \$0', ['Millennium Falcon Crew']).first; final humanCrewMembers = team.crew.query('name != \$0', ['Chewbacca']);
您可以在筛选器中使用可迭代参数。 例如:
final listOfNames = ['Luke', 'Leia']; final matchingRealmObjects = realm.query<Person>('name IN \$0', [listOfNames]);
筛选反向关系
您还可以使用@links.<Type>.<Property>
语法按反向关系进行筛选。 例如,过滤器可以根据引用它的User
对象的属性来匹配Task
对象:
// Filter Tasks through the User's backlink property // using `@links.<ObjectType>.<PropertyName>` syntax final jarjarsIncompleteTasks = realm.query<Task>( "ALL @links.User.tasks.username == 'jarjar_binks' AND isComplete == false"); final tasksForHan = realm.query<Task>("ALL @links.User.tasks.username == 'han'");
有关构建查询的详情,请参阅 Realm 查询语言参考文档。
使用全文搜索进行过滤
您可以使用 Realm Query Language (RQL) 来查询带有 Full Text Search 索引 (FTS) 注释的属性。要查询这些属性,请在查询中使用 TEXT
谓词。
在单词前加上 -
字符以排除该单词的结果。例如,-sheep wool
的搜索中将包括 wool
的所有搜索结果,但不包括 sheep
的搜索结果。
在 Flutter SDK 版本 1.6.0 及更高版本中,您可以通过在单词末尾添加*
字符来指定前缀。 例如, wo*
将包括wool
和woven
的所有搜索结果。 Flutter SDK 目前不支持后缀搜索。
在以下示例中,我们对Rug.pattern
和Rug.material
字段进行查询:
// Find rugs with a chevron pattern final chevronRugs = realm.query<Rug>("pattern TEXT \$0", ["chevron"]); // Find rugs with a wool material but not sheep wool final notSheepWoolRugs = realm.query<Rug>("material TEXT \$0", [" -sheep wool"]); // Find rugs with a material starting with "co-" final coRugs = realm.query<Rug>("material TEXT \$0", ["co*"]);
全文搜索分词器详细信息
全文搜索 (FTS) 索引支持:
布尔匹配词搜索,而不支持相关性搜索。
词汇单元(token)不区分变音符号和大小写。
词汇单元只能包含 ASCII 和 Latin-1 补充(西方语言)中的字符。
所有其他字符均被视为空格。由连字符 (-) 连接的单词(例如 full-text)会被分割为两个词汇单元。
有关 FTS 索引功能的更多信息,请参阅 RealmIndexType 的 API 参考。
对结果进行排序
在 query()
方法的参数中使用 Realm Query Language SORT() 操作符对查询结果进行排序。
请注意,您不能在 RQL SORT () 子句中使用参数化查询。 请改用字符串或字符串插值。
realm.write(() { realm.addAll([ Person(ObjectId(), 'Luke'), Person(ObjectId(), 'Leia'), Person(ObjectId(), 'Han'), Person(ObjectId(), 'Chewbacca') ]); }); final alphabetizedPeople = realm.query<Person>('TRUEPREDICATE SORT(name ASC)'); for (var person in alphabetizedPeople) { print(person.name); } // prints 'Chewbacca', 'Han', 'Leia', 'Luke'
限制结果
在 query()
方法的参数中使用 Realm Query Language LIMIT() 操作符来限制查询结果。
请注意,您不能在 RQL LIMIT() 子句中使用参数化查询。 请改用字符串或字符串插值。
realm.write(() { realm.addAll([ Person(ObjectId(), 'Luke'), Person(ObjectId(), 'Luke'), Person(ObjectId(), 'Luke'), Person(ObjectId(), 'Luke') ]); }); final limitedPeopleResults = realm.query<Person>('name == \$0 LIMIT(2)', ['Luke']); // prints `2` print(limitedPeopleResults.length);