在 SwiftData 中组合谓词

问题描述 投票:0回答:1

我正在尝试将类型的多个谓词与 and/or 组合起来。 以前使用 CoreData 和 NSPredicate 我只会这样做:

let predicate = NSPredicate(value: true)
let predicate2 = NSPredicate(value: false)
let combinedPred = NSCompoundPredicate(type: .or, subpredicates: [predicate, predicate2])

有没有类似的方法可以使用 SwiftData 和 #Predicate 来做到这一点? 如果没有,我如何实现一种预先创建部分条件并稍后将它们组合到谓词中的方法?

我发现将其作为表达式执行的唯一方法是这样的,但这将使我的谓词有数百行长

let includeOnlyFavorites = true
#Predicate { includeOnlyFavorites ? $0.isFavorite : true }

背景:

我正在开发一个应用程序,允许用户使用快捷操作保存和查询项目。这些项目使用 SwiftData 存储并使用 EntityPropertyQuery

进行查询

Apple 是这样实现查询属性的:

static var properties = QueryProperties {
    Property(\BookEntity.$title) {
        EqualToComparator { NSPredicate(format: "title = %@", $0) }
        ContainsComparator { NSPredicate(format: "title CONTAINS %@", $0) }
    }
}

然后将谓词与

NSCompoundPredicate
组合起来。


尝试过但失败了:

返回布尔值的闭包:

let isFavorite = { (item: Item) in item.isFavorite }
let predicate = #Predicate<Item> { isFavorite($0) }
  • 不起作用,因为谓词不允许全局函数
  • 我还尝试使用
    evaluate(Item) -> Bool
    方法创建一个对象 IsFavoriteFilter 但我也无法使用它

我还认为我可以在另一个谓词中使用 StandardPredicateExpression,因为在文档中它写道:

“组成谓词一部分的组件表达式,并且受标准谓词类型支持。” 但没有关于这种类型的进一步解释

swift nspredicate predicate swift-data
1个回答
0
投票

哇。这很难,但我找到了解决问题的方法:

如果我们使用

PredicateExpression
而不是谓词,我们稍后可以构建如下谓词:

let expression = PredicateExpressions.Value(true)

let predicate = Predicate<String>({ input in
    expression
})

下一步是注入输入。 我选择只创建一个接受变量并返回表达式的闭包(因为接受表达式的初始化程序不提供值,而是提供变量)

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)
})

模型示例

Swift 数据模型:

@Model
class Book {
    @Attribute
    var title: String
    
    @Attribute
    var lastReadDate: Date
    
    init(title: String, lastReadDate: Date) {
        self.title = title
        self.lastReadDate = lastReadDate
    }
}

为我们要使用的表达式创建闭包。

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)
    )
}

实际过滤:

// 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
    )
})
© www.soinside.com 2019 - 2024. All rights reserved.