nOk*_*nOk 7 predicate nspredicate swift swift-data
我正在尝试使用 and/or 组合该类型的多个谓词。以前使用 CoreData 和 NSPredicate 我只会这样做:
let predicate = NSPredicate(value: true)
let predicate2 = NSPredicate(value: false)
let combinedPred = NSCompoundPredicate(type: .or, subpredicates: [predicate, predicate2])
Run Code Online (Sandbox Code Playgroud)
是否有类似的方法可以使用 SwiftData 和 #Predicate 来做到这一点?如果没有,我如何实现一种预先创建部分条件并稍后将它们组合到谓词中的方法?
我发现将其作为表达式执行此操作的唯一方法是这样的,但这将使我的谓词长数百行
let includeOnlyFavorites = true
#Predicate { includeOnlyFavorites ? $0.isFavorite : true }
Run Code Online (Sandbox Code Playgroud)
我正在开发一个应用程序,允许用户使用快捷操作保存和查询项目。这些项目使用 SwiftData 存储并使用 EntityPropertyQuery 查询
Apple 是这样实现 Query 属性的:
static var properties = QueryProperties {
Property(\BookEntity.$title) {
EqualToComparator { NSPredicate(format: "title = %@", $0) }
ContainsComparator { NSPredicate(format: "title CONTAINS %@", $0) }
}
}
Run Code Online (Sandbox Code Playgroud)
然后将谓词与 组合起来NSCompoundPredicate。
返回 Bool 的闭包:
let isFavorite = { (item: Item) in item.isFavorite }
let predicate = #Predicate<Item> { isFavorite($0) }
Run Code Online (Sandbox Code Playgroud)
evaluate(Item) -> Bool方法创建一个对象 IsFavouriteFilter 但我也无法使用它我还认为我也许可以在另一个谓词中使用 StandardPredicateExpression,因为在文档中它写道:
“组成谓词一部分的组件表达式,并且受标准谓词类型支持。 ”但没有对此类型的进一步解释
nOk*_*nOk 13
哇。这很难,但我找到了解决问题的方法:
如果我们使用PredicateExpression而不是谓词,我们可以稍后构建一个像这样的谓词:
let expression = PredicateExpressions.Value(true)
let predicate = Predicate<String>({ input in
expression
})
Run Code Online (Sandbox Code Playgroud)
下一步是注入输入。我选择只创建一个接受变量并返回表达式的闭包(因为接受表达式的初始化程序不提供值,而是提供变量)
let variableExp = { (variable: PredicateExpressions.Variable<String>) in
let value = PredicateExpressions.Value("Hello There")
return PredicateExpressions.Equal(
lhs: variable,
rhs: value
)
}
let variablePredicate = Predicate<String>({ input in
variableExp(input)
})
Run Code Online (Sandbox Code Playgroud)
斯威夫特数据模型:
@Model
class Book {
@Attribute
var title: String
@Attribute
var lastReadDate: Date
init(title: String, lastReadDate: Date) {
self.title = title
self.lastReadDate = lastReadDate
}
}
Run Code Online (Sandbox Code Playgroud)
为我们要使用的表达式创建闭包。
typealias BookVariable = PredicateExpressions.Variable<Book>
typealias BookKeyPath<T> = PredicateExpressions.KeyPath<BookVariable,T>
let dateEquals = { (input: BookKeyPath<Date>, _ value: Date) in
return PredicateExpressions.Equal(
lhs: input,
rhs: PredicateExpressions.Value(value)
)
}
Run Code Online (Sandbox Code Playgroud)
实际过滤:
// Do we want to filter by date at all?
let filterByDate = true
// The date we want to test against
let testDate = Date.now
let variablePredicate = Predicate<Book>({ input in
// Wrap values
let shouldFilterByDate = PredicateExpressions.Value(filterByDate)
let alwaysTrue = PredicateExpressions.Value(true)
// Create date Expression with testDate
let dateExp = dateEquals(BookKeyPath(root: input, keyPath: \Book.lastReadDate), testDate)
// Predicate that ,
// if shouldFilterByDate evaluates to true returns dateExp result
// otherwise returns expression that evaluates to true
return PredicateExpressions.build_Conditional(
shouldFilterByDate,
dateExp,
alwaysTrue
)
})
let descriptor = FetchDescriptor(predicate: variablePredicate)
modelContext.fetch(descriptor)
Run Code Online (Sandbox Code Playgroud)
以@orgtre 的答案为基础
TLDR:这个要点conjunction()实现disjunction()了两种方法Array<Predicate<T>>
错误和随后崩溃的原因是PredicateExpressions.Variable用于解析谓词输入。
这就是谓词变量解析的内部工作原理:
您创建的谓词看起来像这样(展开时):
let predicate = Foundation.Predicate<Person>({
PredicateExpressions.build_contains(
PredicateExpressions.build_KeyPath(
root: PredicateExpressions.build_Arg($0),
keyPath: \.name
),
PredicateExpressions.build_Arg("Luke")
)
})
Run Code Online (Sandbox Code Playgroud)
闭包采用 PredicateExpressions.Variable<Input>您需要将其作为参数传递给表达式的参数$0
该变量对于您创建的每个谓词都是唯一的,这意味着当您仅使用属性组合它们时predicate.expression,每个表达式都有一个不同的变量,从而导致错误unresolved Variable。
我创建了一个StandardPredicateExpression带有谓词和变量的自定义,并将在其评估方法中执行以下操作:
struct VariableWrappingExpression<T>: StandardPredicateExpression {
let predicate: Predicate<T>
let variable: PredicateExpressions.Variable<T>
func evaluate(_ bindings: PredicateBindings) throws -> Bool {
// resolve the variable
let value: T = try variable.evaluate(bindings)
// bind the variable of the predicate to this value
let innerBindings = bindings.binding(predicate.variable, to: value)
// evaluate the expression with those bindings
return try predicate.expression.evaluate(innerBindings)
}
}
Run Code Online (Sandbox Code Playgroud)
扩展@orgtre的出色工作,创建一个解决方案,该解决方案采用一系列谓词和用于组合它们的闭包
extension Predicate {
typealias Expression = any StandardPredicateExpression<Bool>
static func combining<T>(
_ predicates: [Predicate<T>],
nextPartialResult: (Expression, Expression) -> Expression
) -> Predicate<T> {
return Predicate<T>({ variable in
let expressions = predicates.map({
VariableWrappingExpression<T>(predicate: $0, variable: variable)
})
guard let first = expressions.first else {
return PredicateExpressions.Value(true)
}
let closure: (any StandardPredicateExpression<Bool>, any StandardPredicateExpression<Bool>) -> any StandardPredicateExpression<Bool> = {
nextPartialResult($0,$1)
}
return expressions.dropFirst().reduce(first, closure)
})
}
}
let compound = Predicate<Person>.combine([predicateA, predicateB]) {
func buildConjunction(lhs: some StandardPredicateExpression<Bool>, rhs: some StandardPredicateExpression<Bool>) -> any StandardPredicateExpression<Bool> {
PredicateExpressions.Conjunction(lhs: lhs, rhs: rhs)
}
return Predicate<T>.combining(self, nextPartialResult: {
buildConjunction(lhs: $0, rhs: $1)
})
}
Run Code Online (Sandbox Code Playgroud)
检查此要点以了解实施情况
| 归档时间: |
|
| 查看次数: |
1904 次 |
| 最近记录: |