Основные данные NSPredicate набор фильтрации элементов по элементам в массиве

#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:

Проблема в том, что обратная зависимость-к одному: если у вас есть только один «Спортивный» жанр и вы назначаете его нескольким сохраненным играм, он будет сохранен только последним — ссылка на другие сохраненные игры будет удалена. Так что ваш предикат работает нормально.