#swift #core-data #nspredicate
Вопрос:
Итак, у меня есть объект SavedGames, который имеет отношение «один ко многим» к GameGenre. Каждая игра может иметь несколько жанров. Мои отношения выглядят следующим образом ниже. На данный момент я ищу фильтр по названию жанра из массива названий жанров.
Некоторые игры могут иметь один жанр, такой как «Спорт», а другие могут содержать несколько, таких как «Спорт», «Симуляция». Я хотел бы получить любые игры, которые могут включать в себя любые названия жанров, которые находятся внутри массива. Так, например, если массив содержит только «Спорт». Если в gameA есть «Спорт», в gameB есть «Спорт», «Симуляция», а в GAMeC есть «Симуляция», «Гонки», я бы хотел, чтобы он вернул gameA и gameB.
Я попробовал несколько предикатов, и до сих пор мне удавалось вернуть только один игровой объект в жанре «Спорт», но он также не возвращал другие объекты, которые, как я знаю, содержат «Спорт». Как я могу настроить свой предикат, чтобы получить то, что я ищу? До сих пор я пытался:
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]()
}
}
Комментарии:
1. Попробуй
NSPredicate(format: "SUBQUERY(%K, $genre, $genre.name IN %@).@count > 0", #keyPath(SavedGames.genreType), genres)
2. Спасибо за ответ, попробовал это с жанром «Спорт», и он возвращает один результат: сохраненный объект игры, содержащий жанры «Симулятор» и «Спорт», но он не вернул элементы, содержащие только «Спорт» или элемент, содержащий «Гонки», «Спорт» и «Приключения».
3.
NSPredicate(format: "ANY genreType.name IN %@", argumentArray: [genres])
должно сработать. Пробовали ли вы другие жанры? Как вы используете предикат?4. Откуда вы знаете, что другие предметы содержат жанр «Спорт»? Проблема в том, что обратная зависимость-к одному: если у вас есть только один «Спортивный» жанр и вы назначаете его нескольким сохраненным играм, он будет сохранен только последним — ссылка на другие сохраненные игры будет удалена.
5. @pbasdf вот и все. Изменение обратной зависимости от-один ко-многим теперь возвращает ожидаемые объекты. Если вы можете добавить в качестве ответа, я могу принять это как решение.
Ответ №1:
Проблема в том, что обратная зависимость-к одному: если у вас есть только один «Спортивный» жанр и вы назначаете его нескольким сохраненным играм, он будет сохранен только последним — ссылка на другие сохраненные игры будет удалена. Так что ваш предикат работает нормально.