-
-
Save fatbobman/8e10c7a811c6cd384b943d1a25206b7b to your computer and use it in GitHub Desktop.
how to combine predicate for SwiftData
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import FoundationKit | |
import SwiftData | |
extension Memo { | |
public static func predicateFor(_ filter: MemoPredicate) -> Predicate<Memo>? { | |
var result: Predicate<Memo>? | |
switch filter { | |
case .filterAllMemo: | |
result = Predicate<Memo>(Self.Expressions.allMemos) | |
case .filterAllGlobalMemo: | |
result = Predicate<Memo>(Self.Expressions.allGlobalMemos) | |
case let .filterAllMemoOfRootNote(noteID): | |
result = Predicate<Memo>(Self.Expressions.memosOfRootNote(noteID)) | |
case .filterMemoWithImage: | |
result = Predicate<Memo>(Self.Expressions.memoWithImage) | |
case .filterMemoWithStar: | |
result = Predicate<Memo>(Self.Expressions.memosWithStar) | |
case let .filterMemoContainsKeyword(keyword): | |
result = Predicate<Memo>(Self.Expressions.memosContainersKeyword(keyword)) | |
} | |
return result | |
} | |
} | |
public enum MemoPredicate: Sendable { | |
/// 所有的 Memo | |
case filterAllMemo | |
/// 所有的没有关联 ItemData 的 Memo | |
case filterAllGlobalMemo | |
/// 指定根笔记下的全部 Memo,这种类型的 Memo 会与 ItemData 相关联,且不包含图片 | |
case filterAllMemoOfRootNote(noteID: PersistentIdentifier) | |
/// 包含图片的 Memo | |
case filterMemoWithImage | |
/// 设置了星标的 memo | |
case filterMemoWithStar | |
/// 内容包含特定关键词的 Meo | |
case filterMemoContainsKeyword(keyword: String) | |
} | |
extension Memo { | |
public static func sortDescriptorFor(_ option: MemoSortingOption) -> [SortDescriptor<Memo>] { | |
switch option { | |
case .orderByCreateTimestamp: | |
return [SortDescriptor(\Memo.createTimestamp, order: .reverse)] | |
case .empty: | |
return [] | |
} | |
} | |
} | |
public enum MemoSortingOption { | |
/// 根据创建时间排序( 降序 ) | |
case orderByCreateTimestamp | |
/// 不设定排序 | |
case empty | |
} | |
extension Memo { | |
public static func fetchDescriptorForQuery(_ filters: [MemoPredicate]) -> FetchDescriptor<Memo> { | |
/// 处理 predicate | |
let predicate = combinePredicate(filters) | |
return FetchDescriptor<Memo>(predicate: predicate, sortBy: Memo.sortDescriptorFor(.orderByCreateTimestamp)) | |
} | |
} | |
extension Memo { | |
/// 生成合成后的 Predicate | |
static func combinePredicate(_ filters: [MemoPredicate]) -> Predicate<Memo> { | |
func buildConjunction(lhs: some StandardPredicateExpression<Bool>, rhs: some StandardPredicateExpression<Bool>) -> any StandardPredicateExpression<Bool> { | |
PredicateExpressions.Conjunction(lhs: lhs, rhs: rhs) | |
} | |
return Predicate<Memo>({ memo in | |
var conditions: [any StandardPredicateExpression<Bool>] = [] | |
for filter in filters { | |
switch filter { | |
case .filterAllMemo: | |
conditions.append(Self.Expressions.allMemos(memo)) | |
case .filterAllGlobalMemo: | |
conditions.append(Self.Expressions.allGlobalMemos(memo)) | |
case let .filterAllMemoOfRootNote(noteID): | |
conditions.append(Self.Expressions.memosOfRootNote(noteID)(memo)) | |
case .filterMemoWithImage: | |
conditions.append(Self.Expressions.memoWithImage(memo)) | |
case .filterMemoWithStar: | |
conditions.append(Self.Expressions.memosWithStar(memo)) | |
case let .filterMemoContainsKeyword(keyword): | |
conditions.append(Self.Expressions.memosContainersKeyword(keyword)(memo)) | |
} | |
} | |
guard let first = conditions.first else { | |
return PredicateExpressions.Value(true) | |
} | |
let closure: (any StandardPredicateExpression<Bool>, any StandardPredicateExpression<Bool>) -> any StandardPredicateExpression<Bool> = { | |
buildConjunction(lhs: $0, rhs: $1) | |
} | |
return conditions.dropFirst().reduce(first, closure) | |
}) | |
} | |
} | |
extension Memo { | |
enum Expressions { | |
/// 获取全部的 Memo | |
static let allMemos = { (_: PredicateExpressions.Variable<Memo>) in | |
PredicateExpressions.Value(true) | |
} | |
static let allGlobalMemos = { (memo: PredicateExpressions.Variable<Memo>) in | |
PredicateExpressions.build_NilCoalesce( | |
lhs: PredicateExpressions.build_flatMap( | |
PredicateExpressions.build_KeyPath( | |
root: PredicateExpressions.build_Arg(memo), | |
keyPath: \.itemData | |
) | |
) { _ in | |
PredicateExpressions.build_Arg( | |
false | |
) | |
}, | |
rhs: PredicateExpressions.build_Arg( | |
true | |
) | |
) | |
} | |
static let memosContainersKeyword = { (keyword: String) in { (memo: PredicateExpressions.Variable<Memo>) in | |
PredicateExpressions.build_NilCoalesce( | |
lhs: PredicateExpressions.build_flatMap( | |
PredicateExpressions.build_KeyPath( | |
root: PredicateExpressions.build_Arg(memo), | |
keyPath: \.content | |
) | |
) { content in | |
PredicateExpressions.build_localizedStandardContains( | |
PredicateExpressions.build_Arg(content), | |
PredicateExpressions.build_Arg(keyword) | |
) | |
}, | |
rhs: PredicateExpressions.build_Arg( | |
false | |
) | |
) | |
} | |
} | |
static let memosWithStar = { (memo: PredicateExpressions.Variable<Memo>) in | |
PredicateExpressions.build_KeyPath( | |
root: PredicateExpressions.build_Arg(memo), | |
keyPath: \.star | |
) | |
} | |
// swiftlint:disable line_length | |
static let memosOfRootNote: (PersistentIdentifier) -> (PredicateExpressions.Variable<Memo>) -> PredicateExpressions.NilCoalesce<PredicateExpressions.OptionalFlatMap<PredicateExpressions.KeyPath<PredicateExpressions.Variable<Memo>, SchemaV1.ItemData?>, SchemaV1.ItemData, PredicateExpressions.OptionalFlatMap<PredicateExpressions.KeyPath<PredicateExpressions.Variable<SchemaV1.ItemData>, SchemaV1.Item?>, SchemaV1.Item, PredicateExpressions.OptionalFlatMap<PredicateExpressions.KeyPath<PredicateExpressions.Variable<SchemaV1.Item>, SchemaV1.Note?>, SchemaV1.Note, PredicateExpressions.Disjunction<PredicateExpressions.Equal<PredicateExpressions.KeyPath<PredicateExpressions.Variable<SchemaV1.Note>, PersistentIdentifier>, PredicateExpressions.Value<PersistentIdentifier>>, PredicateExpressions.Equal<PredicateExpressions.OptionalFlatMap<PredicateExpressions.KeyPath<PredicateExpressions.Variable<SchemaV1.Note>, SchemaV1.Note?>, SchemaV1.Note, PredicateExpressions.KeyPath<PredicateExpressions.Variable<SchemaV1.Note>, PersistentIdentifier>, PersistentIdentifier>, PredicateExpressions.Value<PersistentIdentifier?>>>, Bool>, Bool>, Bool>, PredicateExpressions.Value<Bool>> = { (noteID: PersistentIdentifier) in { (memo: PredicateExpressions.Variable<Memo>) in | |
PredicateExpressions.build_NilCoalesce( | |
lhs: PredicateExpressions.build_flatMap( | |
PredicateExpressions.build_KeyPath( | |
root: PredicateExpressions.build_Arg(memo), | |
keyPath: \.itemData | |
) | |
) { itemData in | |
PredicateExpressions.build_flatMap( | |
PredicateExpressions.build_KeyPath( | |
root: PredicateExpressions.build_Arg(itemData), | |
keyPath: \.item | |
) | |
) { item in | |
PredicateExpressions.build_flatMap( | |
PredicateExpressions.build_KeyPath( | |
root: PredicateExpressions.build_Arg(item), | |
keyPath: \.note | |
) | |
) { note in | |
PredicateExpressions.build_Disjunction( | |
lhs: PredicateExpressions.build_Equal( | |
lhs: PredicateExpressions.build_KeyPath( | |
root: PredicateExpressions.build_Arg(note), | |
keyPath: \.persistentModelID | |
), | |
rhs: PredicateExpressions.build_Arg(noteID) | |
), | |
rhs: PredicateExpressions.build_Equal( | |
lhs: PredicateExpressions.build_flatMap( | |
PredicateExpressions.build_KeyPath( | |
root: PredicateExpressions.build_Arg(note), | |
keyPath: \.parent | |
) | |
) { | |
PredicateExpressions.build_KeyPath( | |
root: PredicateExpressions.build_Arg($0), | |
keyPath: \.persistentModelID | |
) | |
}, | |
rhs: PredicateExpressions.build_Arg(noteID) | |
) | |
) | |
} | |
} | |
}, | |
rhs: PredicateExpressions.build_Arg( | |
false | |
) | |
) | |
} | |
} | |
// swiftlint:enable line_length | |
static let memoWithImage = { (memo: PredicateExpressions.Variable<Memo>) in | |
PredicateExpressions.build_KeyPath( | |
root: PredicateExpressions.build_Arg(memo), | |
keyPath: \.hasImages | |
) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment