Core Data NSPredicate 按数组中的项目过滤项目集

Posted

技术标签:

【中文标题】Core Data NSPredicate 按数组中的项目过滤项目集【英文标题】:Core Data NSPredicate filtering set of items by items in array 【发布时间】:2021-07-25 17:27:37 【问题描述】:

所以我有一个 SavedGames 实体,它与 GameGenre 具有一对多的关系。每个游戏都可以有多种类型。我的关系如下所示。目前,我希望从流派名称数组中按流派名称进行过滤。

有些游戏可能只有单一类型,例如“运动”,而其他游戏可能包含多个类型,例如“运动”、“模拟”。我想获取任何可能包含数组内任何类型名称的游戏。因此,例如,如果数组仅包含“Sport”。如果gameA有“Sport”,gameB有“Sport”、“Simulation”,而gameC有“Simulation”、“Racing”,我希望它返回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]()
        
    
        
        
    

【问题讨论】:

试试NSPredicate(format: "SUBQUERY(%K, $genre, $genre.name IN %@).@count &gt; 0", #keyPath(SavedGames.genreType), genres) 感谢您的回复,尝试使用类型“运动”并返回一个结果:包含“模拟器”和“运动”类型的 SavedGames 对象,但它没有返回包含的项目只是“运动”或包含“赛车”、“运动”和“冒险”的项目 NSPredicate(format: "ANY genreType.name IN %@", argumentArray: [genres]) 应该可以工作。你尝试过其他类型吗?你如何使用谓词? 你怎么知道其他项目包含“运动”类型?反比关系是一对一的问题:如果您只有一个“体育”类型,并将其分配给多个 SavedGames,则只会保留最后一个 - 与其他 SavedGames 的链接将被删除。跨度> @pbasdf 就是这样。将反向关系从一对一更改为对多现在返回预期的对象。如果您可以添加作为答案,我可以接受它作为解决方案。 【参考方案1】:

问题在于反向关系是一对一的:如果您只有一个“运动”类型,并将其分配给多个 SavedGames,它将只保留最后一个 - 与其他 SavedGames 的链接将被删除.所以你的谓词工作正常。

【讨论】:

以上是关于Core Data NSPredicate 按数组中的项目过滤项目集的主要内容,如果未能解决你的问题,请参考以下文章

如何通过 NSPredicate 过滤 Core Data 托管对象?

使用 NSPredicate 遍历多个 Core Data 对象

NSPredicate 按数组中包含的第一个字母过滤

Core Data NSPredicate 过滤结果

使用 NSPredicate 从 Core Data 访问对象

Core Data 嵌套数组计数