持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第12天,点击查看活动详情
Isar:用于 Futter 可跨平台的超快数据库
官方文档:Home | Isar Database
pub:isar | Dart Package (flutter-io.cn)
本文翻译自:
译时版本:3.0.2
查询
查询是如何查找匹配确定条件的记录,例如:
- 查找所有标星的联系人
- 查找联系人中不同的名
- 删除所有未定义姓的联系人
因为查询是在数据库上执行,不是在 Dart 中执行,所以它们相当快。当你灵活运用索引时,可以更进一步改善查询性能。以下的内容,你会学习到如何编写查询和如何使更高效。
Where 子句
Where 子句是一个非常强大的工具,但是要正确运用它们有些难度。
相对于过滤,Where 子句使用在 Schema 中定义的索引, 来检查查询条件。 查询索引比单独过滤每条记录快很多。
➡️ 学习更多: 索引
作为一个基本规则,你应该一直尝试尽可能地使用 Where 子句减少记录数,然后使用过滤对剩下的内容进行过滤。
可以使用逻辑或 绑定 Where 子句。
向鞋子集合中添加索引:
@Collection()
class Shoe with IsarObject {
int? id;
@Index()
int? size;
late String model;
@Index(composite: [CompositeIndex('size')])
late bool isUnisex;
}
这里有两个索引。size 上的索引允许我们使用如 .sizeEqualTo() 的 Where 子句。
isUnisex 上的复合索引允许 isUnisexSizeEqualTo() 之类的 Where 子句。
因为也可以使用索引的任意前缀,所以也可以使用 isUnisexEqualTo() 。
现在我们使用复合索引重写一下前面查找不分男女尺码为46的鞋子的查询。 该查询会比之前的快很多:
final result = isar.shoes.where()
.isUnisexSizeEqualTo(true, 46)
.findAll();
Where 子句还有两个超级力量:提供『自由的』排序和超快的去重操作。
绑定 Where 子句和过滤
还记得 shoes.filter() 查询吗?
实际上它只是 shoes.where().filter() 的快捷方式。可以(也应该)在相同类型的查询中同时绑定 Where 子句和过滤,这样可以结合两者的长处:
final result = isar.shoes.where()
.isUnisexEqualTo(true)
.filter()
.modelContains('Nike')
.findAll();
会首先应用 Where 子句减少用于过滤的记录数。然后过滤会应用于剩下的记录。
排序
执行查询时,可使用 .sortBy() 、 .sortByDesc() 、 .thenBy() 和 .thenByDesc() 方法来定义结果应该如何排序。
不使用索引,查找所有以 model 名升序和尺码降序的鞋子:
final sortedShoes = isar.shoes.filter()
.sortByModel()
.thenBySizeDesc()
.findAll();
排序大量的结果是高成本的操作。 尤其是因为排序在偏移和限定之前。 上面的排序方法永远不会使用索引。 幸运的是,可以再使用 Where 子句排序使查询如闪电般快速,即使需要排序百万级的对象。
Where 子句排序
如果在查询中使用 单个 索引,结果就已经按照索引排序了。这是件大事!
假设我们有 [43, 39, 48, 40, 42, 45] 的鞋子,然后想要查找所有大于等于 42 并且已经按照尺码排好序:
final bigShoes = isar.shoes.where()
.sizeGreaterThan(42) // 结果已按尺码排好序
.findAll(); // -> [43, 45, 48]
正如你所见,结果按照 size 索引排好序了。如果想反转顺序,可以将 sort 设置为 Sort.desc :
final bigShoesDesc = await isar.shoes.where(sort: Sort.desc)
.sizeGreaterThan(42)
.findAll(); // -> [48, 45, 43]
有时候你不想使用 Where 子句,但仍然想使用隐含的排序,可以使用 any Where 子句:
final shoes = await isar.shoes.where()
.anySize()
.findAll(); // -> [39, 40, 42, 43, 45, 48]
如果你使用复合索引,结果会用索引中的所有字段排序。
如果需要对结果排序,考虑使用索引达到该目的。特别是使用
offset()和limit()的时候。
有时候使用索引排序不太可能或者没太大作用。 对于这种情况,应用尽可能使用索引减少结果实体的数量。
唯一值
要使用唯一值返回实体,使用 distinct 谓词。例如,要查找出在 Isar 数据库中有多少不同的鞋子样式:
final shoes = await isar.shoes.filter()
.distinctByModel()
.findAll();
也可以使用链式的 distinct 条件,例如查找 样式-尺码 唯一组合的鞋子:
final shoes = await isar.shoes.filter()
.distinctByModel()
.distinctBySize()
.findAll();
只有每个唯一组合的第一个结果会返回,所以你可以使用 Where 子句和排序操作来控制它。
Where 子句 distinct
如果你有一个非唯一的索引,但是想要获取它的所有的唯一值。可以在前面的部分使用 distinctBy 操作,但是它在排序和过滤之后运行,所以在这儿有些超前。
如果只是使用单个 Where 子句,可以依靠索引执行取唯一值的处理来代替。
final shoes = await isar.shoes.where(distinct: true)
.anySize()
.findAll();
甚至可以使用多个 Where 子句用于排序和取唯一值。 唯一的限制是这些 Where 子句不能有重叠,也不能使用相同的索引。 对于正确排序,它们也需要应用于排序顺序。如果要依靠这个,必须非常谨慎!
Offset & Limit (偏移和限定)
通过限定查询结果的数量是个好主意,例如对于懒加载的列表视图。可以设定 limit() 来做到这一点:
final firstTenShoes = await isar.shoes.where()
.limit(10)
.findAll();
设置 offset() 也可以对查询结果进行分页。
final firstTenShoes = await isar.shoes.where()
.offset(20)
.limit(10)
.findAll();
由于初始化 Dart 对象是执行查询中开销最高的部分,所以只加载需要的对象是个好主意。