web-dev-qa-db-fra.com

Combinaison des conditions "ET" et "OU" dans NSPredicate

De nouveau besoin d’aide pour la construction de mon NSPredicates :(

Category
{
   name:string
   subs<-->>SubCategory
}

SubCategory
{
   name:string
   numbervalue:NSNumber
}

Je voudrais savoir comment créer un prédicat avecETetOU.

Par exemple, je voudrais renvoyer chaque catégorie portant le nom == "nom_catégorie" comportant également une sous-catégorie avec une valeur numérique correspondant à "valeurA"OU"valeurB".

J'ai essayé toutes les combinaisons possibles de constructeurs de prédicats que je pouvais inventer, mais je ne peux tout simplement pas le faire fonctionner.

C'est ma meilleure tentative jusqu'ici.

NSPredicate *predicate=[NSPredicate predicateWithFormat@"(name== %@) AND (ANY subs.numbervalue==%@ OR ANY subs.numbervalue==%@)", @"aname", value1, value2];

Modifier 1

Deuxième essai. Le filtrage sur la 'numbervalue' ne fonctionne toujours pas.

NSNumber valueA=[NSNumber numberWithInt:20];
NSNumber valueA=[NSNumber numberWithInt:90];
NSArray *arr=[NSArray arrayWithObject:valueA,valueB,nil];

predicate=[NSPredicate predicateWithFormat:@"(name==%@)AND(ANY subs.numbervalue IN %@)",@"aname",arr];

Modifier 2

J'ai essayé de filtrer uniquement par numberValue et j'ai omis le nom.

1) utiliser cette option renvoie le jeu complet, même si un seul élément du jeu a la valeur. 

predicate=[NSPredicate predicateWithFormat:@"(ANY subs.numbervalue IN %@)",arr];

2) L'utilisation de cette méthode entraîne le même problème, chaque élément de l'ensemble étant renvoyé, même si un seul d'entre eux correspond.

predicate=[NSPredicate predicateWithFormat:@"((SUBQUERY(subs, $x, $x.numbervalue == %@ or $x.numbervalue == %@).@count > 0)", value1, value2];

Également utiliser la version la plus simple entraîne le même problème ... Je ne l’avais pas remarqué plus tôt.

NSPredicate *predicate=[NSPredicate predicateWithFormat@"(ANY subs.numbervalue==%@ ,valueA];

Donc, je pense avoir mon problème réduit. 

La catégorie est une entité ... La sous-catégorie est une entité.

La catégorie a une relation un à plusieurs avec la sous-catégorie et est représentée en tant que NSSet de sous-catégorie.

Chaque sous-catégorie a un attribut nommé numbervalue.

Modifier 3

Pour tester j'ai 2 catégories . Les deux catégories ont 10 sous-marins, chacun avec une valeur numérique de 1 -10;

En utilisant ce code, les deux catégories sont retournées avec les 10 sous-marins pour chacune.

Le résultat attendu est les deux catégories retournées, chacune avec seulement 2 sous-marins.

J'ai tracé tous mes objets et les données sont correctes. Le problème est que je ne peux pas retourner une catégorie qui a filtré les sous-marins.

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSManagedObjectContext *context=[[DataSource sharedDataSource]managedObjectContext];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Category" inManagedObjectContext:context];
[fetchRequest setEntity:entity];

[fetchRequest setFetchBatchSize:20];

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];

NSPredicate *predicate;
NSNumber *a=[NSNumber numberWithFloat:1];
NSNumber *b=[NSNumber numberWithFloat:2];
predicate=[NSPredicate predicateWithFormat:@"(SUBQUERY(subs,$x,$x.numbervalue==%@ or $x.numbervalue==%@).@count> 0)",a,b];

[fetchRequest setPredicate:predicate];
[fetchRequest setSortDescriptors:sortDescriptors];

NSError *error = nil;
[NSFetchedResultsContoller deleteCacheWithName:nil];

NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:@"name" cacheName:@"Master"];
aFetchedResultsController.delegate = self;

self.fetchedResultsController = aFetchedResultsController;

if (![self.fetchedResultsController performFetch:&error]) {

   NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
   abort();
}
35
dubbeat

Je vais faire ce qui suit en utilisant une SUBQUERY.

[NSPredicate predicateWithFormat@"(name == %@) AND (SUBQUERY(subs, $x, $x.numbervalue == %@ or $x.numbervalue == %@).@count > 0)", @"aname", value1, value2];

Essayez et faites le moi savoir.

Une autre solution pourrait consister à saisir la collection entière subs, puis à filtrer le jeu extrait avec un prédicat pour vérifier si les éléments correspondent à value1 ou value2.

J'espère que ça aide.

Modifier

Si vous suivez Eimantas suggestion, assurez-vous de créer le bon tableau:

NSNumber valueA = [NSNumber numberWithInt:20];
NSNumber valueB = [NSNumber numberWithInt:90];
NSArray *arr = [NSArray arrayWithObjects:valueA, valueB, nil];
33
Lorenzo B

Outre la réponse de @ Stuart, vous pouvez utiliser NSCompoundPredicate pour vos opérations OR & AND comme ceci.

Obj-C - OR

// OR Condition //

NSPredicate *predicate1 = [NSPredicate predicateWithFormat:@"X == 1"];
NSPredicate *predicate2 = [NSPredicate predicateWithFormat:@"X == 2"];
NSPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:@[predicate1, predicate2]];

Obj-C - ET

NSPredicate *predicate1 = [NSPredicate predicateWithFormat:@"X == 1"];
NSPredicate *predicate2 = [NSPredicate predicateWithFormat:@"X == 2"];
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[predicate1, predicate2]];

Swift - OR

let predicate1:NSPredicate = NSPredicate(format: "X == 1")
let predicate2:NSPredicate = NSPredicate(format: "Y == 2")
let predicate:NSPredicate  = NSCompoundPredicate(orPredicateWithSubpredicates: [predicate1,predicate2] )

Swift - ET

let predicate1:NSPredicate = NSPredicate(format: "X == 1")
let predicate2:NSPredicate = NSPredicate(format: "Y == 2")
let predicate:NSPredicate  = NSCompoundPredicate(andPredicateWithSubpredicates: [predicate1,predicate2] )

Swift 3 - OR

    let predicate1 = NSPredicate(format: "X == 1")
    let predicate2 = NSPredicate(format: "Y == 2")
    let predicateCompound = NSCompoundPredicate.init(type: .or, subpredicates: [predicate1,predicate2])

Swift 3 - ET

    let predicate1 = NSPredicate(format: "X == 1")
    let predicate2 = NSPredicate(format: "Y == 2")
    let predicateCompound = NSCompoundPredicate.init(type: .and, subpredicates: [predicate1,predicate2])
64
Onur Var

Une autre option consiste à utiliser un NSCompoundPredicate (note etPredicateWithSubpredicates font un AND mais il y a quelques options à utiliser, cela devrait être beaucoup plus efficace que deux recherches) 

https://developer.Apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSCompoundPredicate_Class/index.html#//Apple_ref/occ/clm/NSCompoundPredicate/andPredicateWithSubpredicates :)

1
Stuart P.

Voici comment vous pouvez le faire:

Swift 4.x - ET

        let p0 = NSPredicate(format: "X == 1")
        let p1 = NSPredicate(format: "Y == 2")
        fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [p0, p1])

Swift 4.x - OR

        let p0 = NSPredicate(format: "X == 1")
        let p1 = NSPredicate(format: "Y == 2")
        fetchRequest.predicate = NSCompoundPredicate(orPredicateWithSubpredicates: [p0, p1])

Swift 4.x - PAS

        let p0 = NSPredicate(format: "X == 1")
        let p1 = NSPredicate(format: "Y == 2")
        fetchRequest.predicate = NSCompoundPredicate(notPredicateWithSubpredicates: [p0, p1])

Exemple

Voici un exemple tiré de l'un de mes codes, dans lequel je recherche l'uid et la synchronisation:

        let p0 = NSPredicate(format: self.uid + " = '" + uid + "'")
        let p1 = NSPredicate(format: self.sync + " = %@", NSNumber(value: true as Bool))
        fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [p0, p1])
0
zeeshan