[官文翻译]Futter超快数据库Isar - 概念 - 查询(中) - Where子句 & 排序 & 限定

451 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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 对象是执行查询中开销最高的部分,所以只加载需要的对象是个好主意。