【问题标题】:Using NSPredicate with Core Data for deep relationships使用 NSPredicate 和 Core Data 建立深层关系
【发布时间】:2011-01-03 01:13:34
【问题描述】:

我有一个 NSArrayController companiesController 绑定到顶级核心数据实体 Companies

一个Company 有很多Department,一个Department 有很多Employee;这些由一对多关系表示,departmentsemployees

基于Employeesalary 属性,我认为我可以动态地执行此操作,以便在调用UI 的方法中根据薪水进行过滤:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY departments.employees.salary < %@", [NSNumber numberWithInt:23000]];
[companiesController setFilterPredicate:predicate];

唉,这给了我错误:-[NSCFSet compare:]: unrecognized selector sent to instance

【问题讨论】:

    标签: objective-c core-data nspredicate cocoa-design-patterns


    【解决方案1】:

    在这种情况下不允许使用多对多键。

    相反,您可以执行以下操作:

    1. 通过将“过滤器”标志(布尔)属性添加到部门实体来修改数据模型。
    2. 创建一个方法:获取所有部门对象,将满足谓词后半部分条件的部门的过滤标志设置为 YES,将其他部门的过滤标志设置为 NO,然后保存。
    3. 在公司谓词中使用过滤器标志。

    代码更改(第 3 步):

        //NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY departments.employees.salary < %@", [NSNumber numberWithInt:23000]];
        [self setDeptFilter:23000];
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY depts.filter == YES"];
        [companiesController setFilterPredicate:predicate];
    

    以及新方法(步骤2):

    - (void)setDeptFilter:(NSUInteger)salary {
        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    
        NSEntityDescription *entity = [NSEntityDescription entityForName:@"Department" inManagedObjectContext:self.managedObjectContext];
        [fetchRequest setEntity:entity];
    
        NSError *error = nil;
    
        // fetch all Department objects
        NSArray *array = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
    
        [fetchRequest release];
    
        if (error) {
            NSLog(@"Error fetching Departments %@, %@", error, [error userInfo]);
            abort();
        }
    
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY emps.salary < %@",[NSNumber numberWithInteger:salary]];
        NSArray *filterArray = [array filteredArrayUsingPredicate:predicate];
    
        // set filter flag to YES for the departments that meet the criteria
        for (Department *dep in filterArray) {
            dep.filter = [NSNumber numberWithBool:YES];
        }
    
        NSMutableArray *diffArray = [array mutableCopy];
        [diffArray removeObjectsInArray:filterArray];
    
        // set filter flag to NO for the departments that do NOT meet the criteria
        for (Department *dep in diffArray) {
            dep.filter = [NSNumber numberWithBool:NO];
        }
    
        [diffArray release];
    
        // save
        if ([self.managedObjectContext hasChanges] && ![self.managedObjectContext save:&error]) {
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        } 
    }
    

    【讨论】:

    • 谢谢,这行得通。似乎是一种解决方法,但我猜这只是 Core Data 的问题。
    • 我认为这与 Core Data 如何将 NSPredicate 转换为 SQLite 语句有关。语法不明确:您是指特定部门中的 ANY 还是 ALL Employee(s)。另外,我是一个 SQL 新手,但我的猜测是,即使您的原始谓词可以在一个简单的 SQL 语句中完成,它也需要一个潜在的大连接,Apple 可能认为这对于单个提取来说内存或性能过于密集。跨度>
    • 另外,我在这方面花了很长时间,所以如果您投赞成票,我们将不胜感激:-)。
    【解决方案2】:

    您也可以使用子查询来做到这一点。

    获取所有部门。 “of”关系是公司与多部门的逆关系:

    -(void)printDepartmentsWithSalaryHigherThan:(int)salary inContext:(NSManagedObjectContext *)context {    
        NSFetchRequest *request = [[NSFetchRequest alloc ]init];
        request.entity = [NSEntityDescription entityForName:@"Department" inManagedObjectContext:context];
        request.predicate = [NSPredicate predicateWithFormat:@"SUBQUERY(employees, $emp, $emp.salary > %@ ).@count > 0", [NSNumber numberWithInt:salary]];
    
        for(Department *dep in [context executeFetchRequest:request error:nil]){
            NSLog(@"Department: %@", dep.depName);
            NSLog(@"in Company: %@", dep.of.compName);
        }
        [request release];
    }
    

    或者,如果您有更多公司,并且只希望公司的员工薪水“高于”某个金额。基于子查询结果的子查询

    -(void)printCompaniesWithHigherSalaryThan:(int)salary inContext:(NSManagedObjectContext *)context {
        NSFetchRequest *request = [[NSFetchRequest alloc ]init];
        request.entity = [NSEntityDescription entityForName:@"Company" inManagedObjectContext:context];
        request.predicate = [NSPredicate predicateWithFormat:@"SUBQUERY(departments, $dep, SUBQUERY($dep.employees,$emp,$emp.salary > %@).@count > 0 ).@count > 0", [NSNumber numberWithInt:salary]];
    
        for(Company *c in [context executeFetchRequest:request error:nil]){
            NSLog(@"Company: %@", c.compName);
        }
        [request release];
    }
    

    【讨论】:

    • 这是否适用于 Mac OS 和 iOS 的 SQLite 商店?来自 Apple 文档(来自 iOS 5.0 库:Core Data Programming Guide > Persistent Store Features > Fetch Predicates and Sort Descriptors - Mac OS 可能不同):“您可以在 SQLite 商店中使用的谓词有额外的限制:您不一定将“任意”SQL 查询转换为谓词。”
    • 我遇到过的最好的真实世界谓词子查询示例
    • +1。我遇到了类似的问题,数据模型有点复杂。 EntityA--->> EntityB > Entity C。虽然 Gerry 的解决方案可以工作,但这需要对数据模型进行一些更改,我觉得使用 SubQueries 会更优雅一些。我使用子查询来做到这一点。直到现在才使用它们 :) 感谢您的精彩提示。我认为这应该被接受为替代答案!
    • 第二个 SUBQUERY(...SUBQUERY(... 示例对我不起作用。可能只是做错了什么,但我将第一部分更改为“SUBQUERY(departments.employees, $emp , $emp.salary > %@).@count > 0" 并且它通过了 ;) ,所以 +1
    猜你喜欢
    • 1970-01-01
    • 2013-02-20
    • 2011-10-15
    • 1970-01-01
    • 2021-12-17
    • 1970-01-01
    • 1970-01-01
    • 2011-10-04
    • 1970-01-01
    相关资源
    最近更新 更多