2010-01-08 5 views
11

J'ai un NSArrayController, companiesController lié à une entité de base de données de niveau supérieur, Companies.Utilisation de NSPredicate avec des données de base pour les relations profondes

Un Company a beaucoup de Department, et un Department a beaucoup Employee; ceux-ci sont représentés par les relations un-à-plusieurs, departments et employees.

Sur la base de l'attribut salary d'un Employee je pensais que je pouvais le faire de façon dynamique pour le filtrage basé sur le salaire à l'intérieur d'une méthode appelée UI-:

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

Hélas, cela me donne l'erreur: -[NSCFSet compare:]: unrecognized selector sent to instance.

Répondre

17

Plusieurs clés à plusieurs sont interdites dans ce cas.

Au lieu de cela, vous pouvez effectuer les opérations suivantes:

  1. Modifier le modèle de données en ajoutant un drapeau « filtre » (Boolean) attribut à l'entité Département.
  2. Créez une méthode pour: récupérer tous les objets Department, définir l'indicateur de filtre sur YES pour les départements répondant aux critères de la seconde moitié de votre prédicat, définir l'indicateur de filtre sur NO pour les autres départements et enregistrer.
  3. Utilisez l'indicateur de filtre dans le prédicat Société.

changements de code (étape 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]; 

et la nouvelle méthode (étape 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(); 
    } 
} 
+0

Merci, cela fonctionne. Cela ressemble à une solution de contournement, mais je suppose que ce sont les problèmes de Core Data. – raheel

+0

Je suppose que cela a quelque chose à voir avec la façon dont Core Data traduit le NSPredicate en instructions SQLite. La syntaxe est ambiguë: voulez-vous dire TOUT ou TOUT employé (s) dans un département particulier. En outre, je suis un novice SQL, mais je suppose que même si votre prédicat d'origine peut être fait dans une instruction SQL simple, cela nécessiterait une jointure potentiellement importante qu'Apple peut considérer comme trop gourmande en mémoire ou en performances pour un seul chargement. – gerry3

+0

Aussi, j'ai passé un certain temps sur ce sujet, donc un vote serait apprécié :-). – gerry3

10

Vous pouvez aussi le faire en utilisant les sous-requêtes.

Obtenez tous les ministères. Le « de » relation est l'inverse de la société vers plusieurs départements:

-(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 > %@)[email protected] > 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]; 
} 

Ou, si vous avez plus d'entreprises et veulent juste les entreprises qui ont un employé avec un salaire « supérieur à » une certaine quantité. Une sous-requête basée sur le résultat d'une sous-requête

-(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 > %@)[email protected] > 0)[email protected] > 0", [NSNumber numberWithInt:salary]]; 

    for(Company *c in [context executeFetchRequest:request error:nil]){ 
     NSLog(@"Company: %@", c.compName); 
    } 
    [request release]; 
} 
+0

Est-ce que cela fonctionne avec un magasin SQLite pour Mac OS et iOS? Dans la documentation Apple (de la bibliothèque iOS 5.0: Guide de programmation des données de base> Fonctions de stockage permanentes> Récupérer les prédicats et les descripteurs de tri - peut être différent pour Mac OS): "Il existe des contraintes supplémentaires sur les prédicats que vous pouvez utiliser avec le Banque SQLite: Vous ne pouvez pas nécessairement traduire les requêtes SQL "arbitraires" en prédicats." – Dalmazio

+0

Ne sait pas. Essayez-le :) – andershqst

+0

Meilleur exemple réel SubQuery de prédicat réel Je suis tombé encore –

Questions connexes