Utilizzando NSPredicate con Core Data per relazioni profonde
-
19-09-2019 - |
Domanda
Ho un NSArrayController, companiesController
legato ad un livello superiore un'entità Core Data, Companies
.
Un Company
ha molte Department
di, ed un Department
ha molti Employee
; questi sono rappresentati dalle relazioni 1-a-molti, departments
e employees
.
Sulla base all'attributo salary
di un Employee
ho pensato di dinamicamente farlo per filtro basato su stipendio all'interno di un metodo UI cd:
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY departments.employees.salary < %@", [NSNumber numberWithInt:23000]];
[companiesController setFilterPredicate:predicate];
Purtroppo, questo mi dà l'errore:. -[NSCFSet compare:]: unrecognized selector sent to instance
Soluzione
Multiple a-molti chiavi non sono accettati in questo caso.
Al contrario, si potrebbe procedere come segue:
- Modificare il modello di dati con l'aggiunta di un attributo "filtro" bandiera (booleano) per l'entità Department.
- Creare un metodo per: recuperare tutti gli oggetti Reparto, impostare il flag di filtro su SI per i dipartimenti che soddisfano i criteri della seconda metà del predicato, impostare il flag filtro per no per gli altri reparti, e risparmiare.
- Utilizza la bandiera del filtro nel predicato Società.
Le modifiche del codice (fase 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];
E il nuovo metodo (fase 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();
}
}
Altri suggerimenti
Si potrebbe anche fare questo utilizzando sottoquery.
Ottenere tutti i reparti. L' 'di' rapporto è l'inverso della società a-molti dipartimenti:
-(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];
}
In alternativa, se si dispone di più aziende e vogliono solo le aziende che hanno un impiegato con uno stipendio 'superiore' una certa quantità. Una subquery in base al risultato di una sottointerrogazione
-(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];
}