核心数据 NSPredicate 按数组中的项目过滤项目集
Core Data NSPredicate filtering set of items by items in array
所以我有一个与 GameGenre 具有一对多关系的 SavedGames 实体。每个游戏可以有多个流派。我的关系如下所示。现在,我希望从一系列流派名称中按流派名称进行过滤。
有些游戏可能只有单一类型,例如“运动”,而其他游戏可能包含多种类型,例如“运动”、“模拟”。我想获取任何游戏,其中可能包含数组内的任何类型名称。因此,例如,如果数组仅包含“Sport”。如果 gameA 有“Sport”,gameB 有“Sport”、“Simulation”,gameC 有“Simulation”、“Racing”,我想 return gameA 和 gameB。
我尝试了几个谓词,到目前为止只成功地 return 将单个游戏对象设置为“运动”类型,但还 return 我知道包含的其他对象“运动”。我如何配置我的谓词以获得我正在寻找的东西?到目前为止我已经尝试过:
NSPredicate(format: "ANY genreType.name IN %@", argumentArray: [genres])
NSPredicate(format: "SUBQUERY(%K, $genre, ANY $genre.name IN %@) .@count > 0" , #keyPath(SavedGames.genreType), genres)
NSPredicate(format: "SUBQUERY(%K, $genre, $genre.name = %@) .@count > 0", #keyPath(SavedGames.genreType), genres)
var predicateArr : [NSPredicate] = []
for genre in genres {
let predicate = NSPredicate(format: "ANY %K.name = %@",argumentArray: [#keyPath(SavedGames.genreType), genre])
predicateArr.append(predicate)
}
let compoundArr = NSCompoundPredicate(orPredicateWithSubpredicates: predicateArr)
这是我用来获取游戏的方法:
func fetchGame<T: NSManagedObject>(_ objectType: T.Type, sortBy: String? ,platformID: Int?, selectedGenres: [String]?, selectedPlatforms: [Int]?) -> [T]{
var entityName = String(describing: objectType)
var fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: entityName)
var filterPredicate : NSPredicate?
var sortAscending : NSSortDescriptor?
var platforms : [Int] = []
var genres : [String] = []
if let platform = selectedPlatforms{
platforms = platform
}
if let genre = selectedGenres {
genres = genre
}
if let sortKey = sortBy {
sortAscending = NSSortDescriptor(key: sortKey, ascending: true)
} else {
sortAscending = NSSortDescriptor(key: "title", ascending: true)
}
print("selectedPlatform is", platforms)
print("selectedGenre is", genres)
switch (platforms.isEmpty, genres.isEmpty) {
case (true, true):
// both arrays are empty so we don't filter anything and return all SavedGame objects
print("both arrays are empty so we don't filter anything and return all SavedGame objects")
filterPredicate = NSPredicate(value: true)
case (true, false):
// only filter genres
print("only filter genres")
filterPredicate = NSPredicate(format: "SUBQUERY(%K, $genre, $genre.name IN %@).@count > 0", #keyPath(SavedGames.genreType), genres)
case (false, true):
// only filter platforms
print("only filter platforms")
filterPredicate = NSPredicate(format: "%K in %@", argumentArray: [#keyPath(SavedGames.platformID), platforms])
case (false, false):
// filter both genres and platforms
print("filter both genres and platforms")
filterPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [
NSPredicate(format: "ANY genreType.name == %@", argumentArray: [genres]),
NSPredicate(format: "%K IN %@", argumentArray: [#keyPath(SavedGames.platformID), platforms])
])
}
print("filterPredicate", filterPredicate)
fetchRequest.predicate = filterPredicate
fetchRequest.sortDescriptors = [sortAscending!]
do {
let fetchedObjects = try context.fetch(fetchRequest) as? [T]
return fetchedObjects ?? [T]()
} catch {
print("error")
return [T]()
}
}
问题在于反向关系是一对一的:如果您只有一个“运动”类型,并将其分配给多个 SavedGames,则只会保留最后一个 - link其他 SavedGames 将被删除。所以你的谓词工作正常。
所以我有一个与 GameGenre 具有一对多关系的 SavedGames 实体。每个游戏可以有多个流派。我的关系如下所示。现在,我希望从一系列流派名称中按流派名称进行过滤。
有些游戏可能只有单一类型,例如“运动”,而其他游戏可能包含多种类型,例如“运动”、“模拟”。我想获取任何游戏,其中可能包含数组内的任何类型名称。因此,例如,如果数组仅包含“Sport”。如果 gameA 有“Sport”,gameB 有“Sport”、“Simulation”,gameC 有“Simulation”、“Racing”,我想 return gameA 和 gameB。
我尝试了几个谓词,到目前为止只成功地 return 将单个游戏对象设置为“运动”类型,但还 return 我知道包含的其他对象“运动”。我如何配置我的谓词以获得我正在寻找的东西?到目前为止我已经尝试过:
NSPredicate(format: "ANY genreType.name IN %@", argumentArray: [genres])
NSPredicate(format: "SUBQUERY(%K, $genre, ANY $genre.name IN %@) .@count > 0" , #keyPath(SavedGames.genreType), genres)
NSPredicate(format: "SUBQUERY(%K, $genre, $genre.name = %@) .@count > 0", #keyPath(SavedGames.genreType), genres)
var predicateArr : [NSPredicate] = []
for genre in genres {
let predicate = NSPredicate(format: "ANY %K.name = %@",argumentArray: [#keyPath(SavedGames.genreType), genre])
predicateArr.append(predicate)
}
let compoundArr = NSCompoundPredicate(orPredicateWithSubpredicates: predicateArr)
这是我用来获取游戏的方法:
func fetchGame<T: NSManagedObject>(_ objectType: T.Type, sortBy: String? ,platformID: Int?, selectedGenres: [String]?, selectedPlatforms: [Int]?) -> [T]{
var entityName = String(describing: objectType)
var fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: entityName)
var filterPredicate : NSPredicate?
var sortAscending : NSSortDescriptor?
var platforms : [Int] = []
var genres : [String] = []
if let platform = selectedPlatforms{
platforms = platform
}
if let genre = selectedGenres {
genres = genre
}
if let sortKey = sortBy {
sortAscending = NSSortDescriptor(key: sortKey, ascending: true)
} else {
sortAscending = NSSortDescriptor(key: "title", ascending: true)
}
print("selectedPlatform is", platforms)
print("selectedGenre is", genres)
switch (platforms.isEmpty, genres.isEmpty) {
case (true, true):
// both arrays are empty so we don't filter anything and return all SavedGame objects
print("both arrays are empty so we don't filter anything and return all SavedGame objects")
filterPredicate = NSPredicate(value: true)
case (true, false):
// only filter genres
print("only filter genres")
filterPredicate = NSPredicate(format: "SUBQUERY(%K, $genre, $genre.name IN %@).@count > 0", #keyPath(SavedGames.genreType), genres)
case (false, true):
// only filter platforms
print("only filter platforms")
filterPredicate = NSPredicate(format: "%K in %@", argumentArray: [#keyPath(SavedGames.platformID), platforms])
case (false, false):
// filter both genres and platforms
print("filter both genres and platforms")
filterPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [
NSPredicate(format: "ANY genreType.name == %@", argumentArray: [genres]),
NSPredicate(format: "%K IN %@", argumentArray: [#keyPath(SavedGames.platformID), platforms])
])
}
print("filterPredicate", filterPredicate)
fetchRequest.predicate = filterPredicate
fetchRequest.sortDescriptors = [sortAscending!]
do {
let fetchedObjects = try context.fetch(fetchRequest) as? [T]
return fetchedObjects ?? [T]()
} catch {
print("error")
return [T]()
}
}
问题在于反向关系是一对一的:如果您只有一个“运动”类型,并将其分配给多个 SavedGames,则只会保留最后一个 - link其他 SavedGames 将被删除。所以你的谓词工作正常。