thesuffering
thesuffering

Reputation: 121

Core Data NSPredicate filtering set of items by items in array

So I have a SavedGames entity which has a one-to-many relationship to GameGenre. Each game can have multiple genres. My relationship looks like as follows below. For now, im looking to filter by genre name from an array of genre names.

Some games may have single genres such as "Sport" and others may contain multiple such as "Sport", "Simulation". Id like to fetch any games which may include any genre names which are inside of an array. So, for example, if the array contains only "Sport". If gameA has "Sport", gameB has "Sport", "Simulation", and gameC has "Simulation", "Racing" I would like it to return gameA and gameB.

Ive tried several predicates, and so far have only been successful in returning a single game object a genre of "Sport" but its not also returning other objects which i know contain "Sport". How can i configure my predicate to get what I am looking for? So far I have tried:

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)

Relationship

Here is the method im using to fetch the games:

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

Upvotes: 1

Views: 482

Answers (1)

pbasdf
pbasdf

Reputation: 21536

The problem is that the inverse relationship is to-one: if you have only one "Sport" genre, and assign it to several SavedGames, it will only be the last that is retained - the link to other SavedGames will be removed. So your predicate is working fine.

Upvotes: 2

Related Questions