问题
I have an NSArrayController, companiesController
bound to a top level Core Data entity, Companies
.
A Company
has many Department
's, and a Department
has many Employee
; these are represented by the 1-to-many relationships, departments
and employees
.
Based on the attribute salary
of an Employee
I thought I could dynamically do this for filtering based on salary inside a UI-called method:
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY departments.employees.salary < %@", [NSNumber numberWithInt:23000]];
[companiesController setFilterPredicate:predicate];
Alas, this gives me the error: -[NSCFSet compare:]: unrecognized selector sent to instance
.
回答1:
Multiple to-many keys are not allowed in this case.
Instead, you could do the following:
- Modify the data model by adding a "filter" flag (Boolean) attribute to the Department entity.
- Create a method to: fetch all the Department objects, set the filter flag to YES for the departments that meet the criteria of the second half of your predicate, set the filter flag to NO for the other departments, and save.
- Use the filter flag in the Company predicate.
Code changes (step 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];
And the new method (step 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();
}
}
回答2:
You could also do this using subqueries.
Get all departments. The 'of' relationship is the inverse of company to-many departments:
-(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];
}
Or, if you have more companies and just want the companies that have an employee with a salary 'higher than' some amount. A subquery based on the result of a subquery
-(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];
}
来源:https://stackoverflow.com/questions/2029962/using-nspredicate-with-core-data-for-deep-relationships