持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第11天,点击查看活动详情
Isar:用于 Futter 可跨平台的超快数据库
官方文档:Home | Isar Database
pub:isar | Dart Package (flutter-io.cn)
本文翻译自:Queries | Isar Database
译时版本: 3.0.2
查询
查询是如何查找匹配确定条件的记录,例如:
- 查找所有标星的联系人
- 查找联系人中不同的名
- 删除所有未定义姓的联系人
因为查询是在数据库上执行,不是在 Dart 中执行,所以它们相当快。当你灵活运用索引时,可以更进一步改善查询性能。以下的内容,你会学习到如何编写查询和如何使更高效。
有两种方法可以过滤记录:过滤和 Where 子句。先看一下过滤如何工作。
过滤
过滤易于使用和理解。根据属性的类型,它们具有不同的过滤操作可用,大部分都有可自解释的名称。
过滤通过为过滤目标集合中的每个对象评价表达式来工作。如果表达式的解答是 true ,Isar 会在结果中包含对象。过滤对结果排序没有影响。
我们将使用下面的模型用于后面的示例:
@Collection()
class Shoe {
int? id;
int? size;
late String model;
late bool isUnisex;
}
查询条件
根据字段的类型,有不同的条件可用。
| 条件 | 说明 |
|---|---|
.equalTo(value) | 匹配等于指定 value 的值 |
.between(lower, upper) | 匹配 lower 和 upper 之间的值 |
.greaterThan(bound) | 匹配大于 bound 的值 |
.lessThan(bound) | 匹配小于 bound 的值。 null 值会默认包含,因为 null 被认为小于任何值 |
.isNull() | 匹配 null 的值 |
.isNotNull() | 匹配所有非 null 的值。 |
.length() | 列表、字符串和链接长度查询,过滤列表或链接中的元素个数。 |
让我们假定数据库包含 4 双鞋子,它们的尺码是 39、40、46 和 未设定(null)尺码。除非你进行排序,值会返回按 id 排序后的结果。
isar.shoes.filter()
.sizeLessThan(40)
.findAll() // -> [39, null]
isar.shoes.filter()
.sizeLessThan(40, include: true)
.findAll() // -> [39, null, 40]
isar.shoes.filter()
.sizeBetween(39, 46, includeLower: false)
.findAll() // -> [40, 46]
逻辑操作符
可以使用以下的逻辑操作符组合操作逻辑。
| 操作符 | 说明 |
|---|---|
.and() | 如果左右表达式的结果都为 true ,则评估结果为 true 。 |
.or() | 如果左右表达式的其中一个结果为 true ,则评估结果为 true 。 |
.xor() | 如果只有一个表达式的结果为 true 则评估结果为 true 。 |
.not() | 之后的表达式结果取反。 |
.group() | 对条件进行分组和允许指定评估顺序。 |
如果想要查找所有尺码为 46 的鞋,可以使用下面的查询:
final result = await isar.shoes.filter()
.sizeEqualTo(46)
.findAll();
如果使用多于一个条件,可以用逻辑与 .and() 、逻辑或 .or() 、逻辑异或 .xor() 绑定多个过滤:
final result = await isar.shoes.filter()
.sizeEqualTo(46)
.and() // 可选。Filter隐式用逻辑与绑定。
.isUnisexEqualTo(true)
.findAll();
该查询等同于: size == 46 && isUnisex == true 。
也可以使用 .group() 对条件分组:
final result = await isar.shoes.filter()
.sizeBetween(43, 46)
.and()
.group((q) => q
.modelNameContains('Nike')
.or()
.isUnisexEqualTo(false)
)
.findAll()
该查询等同于 size >= 43 && size <= 46 && (modelName.contains('Nike') || isUnisex == false) 。
要对一个条件或分组取反,使用逻辑取反 .not() :
final result = await isar.shoes.filter()
.not().sizeEqualTo(46)
.and()
.not().isUnisexEqualTo(true)
.findAll();
该查询等同于 size != 46 && isUnisex != true 。
字符串条件
可以使用字符串操作符比较字符串的值。正则类的通配符在查询时有更多的弹性。
| 条件 | 说明 |
|---|---|
.startsWith(value) | 匹配以指定的 value 开始的值。 |
.contains(value) | 匹配包含指定的 value 的值。 |
.endsWith(value) | 匹配以指定的 value 结束的值。 |
.matches(wildcard) | 匹配符合 wildcard (通配符)模式的值。 |
大小写敏感
所有的字符串操作都有一个可选的 caseSensitive 参数,默认是 true(即默认大小写敏感)。
通配符:
通配符表达式 是一个字符串,它使用带有两个特殊通配符的普通字符:
*通配符匹配 0 个或多个任意字符?通配符匹配任意字符。例如,通配符字符串"d?g"匹配"dog"、"dig"、 和"dug"、 但不匹配"ding"、"dg"或"a dog"。
查询修饰符
有时候,基本一些条件或不同的值构建查询是必需的。Isar 带有非常强大的工具来构建条件式查询:
| 修饰符 | 说明 |
|---|---|
.optional(cond, qb) | 只在cond 是 true 时扩展查询。该修饰符几乎可用于查询的任何地方,例如根据条件排序或限定。 |
.anyOf(list, qb) | 为 values 中的每个值扩展查询,并使用逻辑或绑定条件。 |
.allOf(list, qb) | 为 values 中的每个值扩展查询,并使用逻辑与绑定条件。 |
本示例中,我们构建了一个带可选过滤的查找鞋子的方法:
Future<List<Shoe>> findShoes(Id? sizeFilter) {
return isar.shoes.filter()
.optional(
sizeFilter != null, // 只在 sizeFilter != null 时进行过滤。
(q) => q.sizeEqualTo(sizeFilter!),
).findAll();
}
如果要查找具有多种鞋码之一的所有鞋子,可以编写常规查询或使用 anyOf() 修饰符:
final shoes1 = await isar.shoes.filter()
.sizeEqualTo(38)
.or()
.sizeEqualTo(40)
.or()
.sizeEqualTo(42)
.findAll();
final shoes2 = await isar.shoes.filter()
.anyOf(
[38, 40, 42],
(q, int size) => q.sizeEqualTo(size)
).findAll();
// shoes1 == shoes2
想要构建动态查询时,查询修饰符特别有用。
列表
列表也能用在查询中使用。
class Tweet {
Id? id;
String? text;
List<String> hashtags = [];
}
可以基于列表长度查询:
final tweetsWithoutHashtags = await isar.tweets.filter()
.hashtagsIsEmpty()
.findAll();
final tweetsWithManyHashtags = await isar.tweets.filter()
.hashtagsLengthGreaterThan(5)
.findAll();
这些等于 Dart 代码的 tweets.where((t) => t.hashtags.isEmpty); 和 tweets.where((t) => t.hashtags.length > 5);。
也可以基于列表元素查询:
final flutterTweets = await isar.tweets.filter()
.hashtagsElementEqualTo('flutter')
.findAll();
这等于 Dart 代码 tweets.where((t) => t.hashtags.contains('flutter')); 。
嵌入对象
嵌入对象可使用相同的条件作为顶级对象来查询。 我们假定有下面的模型:
@collection
class Car {
Id? id;
Brand? brand;
}
@embedded
class Brand {
String? name;
String? country;
}
我们想查询所有品牌是 "BMW" 和 国家是 "Germany" 的汽车。
我们可以使用如下查询去做:
final germanCars = await isar.cars.filter()
.brand((q) => q
.nameEqualTo('BMW')
.and()
.countryEqualTo('Germany')
).findAll();
总要试着分组嵌套查询。 即使结果相同,上面的查询也比下面的查询更高效:
final germanCars = await isar.cars.filter()
.brand((q) => q.nameEqualTo('BMW'))
.and()
.brand((q) => q.countryEqualTo('Germany'))
.findAll();
链接
如果模型包含链接或反向链接 ,会基于链接对象过滤查询。
@Collection()
class Teacher {
int? id;
late String subject;
}
@Collection()
class Student {
int? id;
late String name;
final teachers = IsarLinks<Teacher>();
}
可以举个例子,查找有数学老师或英语老师的学生:
final result = await isar.students.filter()
.teachers((q) => q.subjectEqualTo('Math')
.or()
.subjectEqualTo('English')
).findAll()
如果至少有一个链接对象匹配条件,链接过滤的评价结果会为 true 。
我们查询一下所有没有老师的学生:
final result = await isar.students.filter().teachersLengthEqualTo(0).findAll();
或者下面的代替方案:
final result = await isar.students.filter().teachersIsEmpty().findAll();