查询

Query 是一个用于 Core Data 的流畅式查询构建器。

安装

安装通过 Swift Package Manager 完成。将此仓库的 URL 粘贴到 Xcode 中,或将此行添加到您的 Package.swift

.package(url: "https://github.com/lightyear/Query", from: "1.0.0")

用法

查询 Core Data 管理对象上下文涉及大量样板代码。此包提供了一个新的 Query<T> 类型,它是一个流畅式、可链式调用的查询构建器

class Entity: NSManagedObject { ... }

try Entity.query(in: someManagedObjectContext)
    .where("quantity == 1")
    .order(by: "date")
    .all()
// result is [Entity] or a thrown error

如果您的托管对象子类也遵循 Queryable 协议并提供嵌套的 Attribute 类型,则可以使用 .where().order() 的其他重载,这些重载接受可以由编译器检查的属性,而不是字符串

extension Entity: Queryable {
    enum Attribute {
        case quantity, date
    }
}

try Entity.query(in: someManagedObjectContext)
    .where([.quantity: 1])
    .order(by: .date)
    .all()
// result is [Entity] or a thrown error

(SR-5220 Expose API to retrieve string representation of KeyPath 是支持类似功能的更好方法。如果我们最终实现了它,Queryable 将会被弃用。)

谓词

基本的过滤谓词是 .where()。它接受与 NSPredicate 相同的参数,或者,如果托管对象子类遵循 Queryable,则接受另一个版本,该版本接受由属性键控的 Dictionary

where("quantity == 1")
where("quantity == %d", 1)
where("name == %@", "Alice")
where("%K == %@", "name", "Alice")
where([.quantity: 1])
where([.name: "Alice"])

链式调用 .where() 会使用 AND 组合

where("quantity == 1").where("name == %@", "Alice")
// same as where("quantity == 1 AND name == %@", "Alice")

还有 .or()

where("quantity == 1").or("name == %@", "Alice")
// same as where("quantity == 1 OR name == %@", "Alice")

当与 .where() 结合使用时,.or() 会将之前的所有内容视为包裹在一组括号中。

where("quantity == 1").where("name == %@", "Alice").or("name == %@", "Bob")
// same as where("(quantity == 1 AND name == %@) OR name == %@", "Alice", "Bob")

.where().or() 的字典形式支持值和范围周围的不等式包装器

where([.quantity: lt(1)])  // same as where("quantity < 1")
where([.quantity: 1...3])  // same as where("quantity BETWEEN (1,3)")

排序

添加一个或多个排序描述符通过 order() 完成

order(by: "department").order(by: "name")

默认排序顺序是升序,但降序也是一个选项

order(by: "quantity", .descending)

字符串值也可以不区分大小写地排序,或使用本地化比较进行排序

order(by: "name", .ascending, .localizedCaseInsensitive)
order(by: "name", .ascending, .localizedStandard)

获取结果对象或计数

所有匹配的结果(如果指定了排序)都通过调用 .all() 获取。如果您只想获取第一个匹配结果,请使用 .first()。如果您只关心匹配对象的数量,请使用 .count()

如果您的托管对象子类遵循 Queryable,您使用不带不等式或 .or() 条件的类型化 Attribute 版本的 .where().firstOrInsert() 将获取第一个匹配项,或者将一个新实例插入到上下文中,并将其属性设置为查询条件。在初始值不确定的情况下,它会抛出 QueryError.tooComplex 错误。

获取底层 fetch 请求

如果您需要与当前查询匹配的真实 NSFetchRequest,您可以从 fetchRequest 属性中获取它。这对于进一步自定义 fetch 请求或将其传递给 NSFetchedResultsController 非常有用。

Identifiable 托管对象

也遵循 Identifiable 的托管对象子类获得了一些额外的查询功能。如果上下文中存在具有提供的 ID 的对象,则 exists(_:) 返回 true

fetch(_:) 返回具有提供的 ID 的实例,如果上下文中不存在具有该 ID 的对象,则返回 nil

fetchOrInsert(_:) 返回具有提供的 ID 的实例,或者,如果尚不存在具有该 ID 的对象,则返回一个 id 属性设置为该 ID 的新实例。