NSPredicate multiple ANY query with CoreData - ios

I'm trying to find all instances of an object that contain a reference to a combination of separate objects in my object graph.
recommendation
may contain one or more of the following three objects:
damageType
areaDamaged
validVehicles
This structure is built from an import of an existing system's file format and I am unable to restructure the object graph.
I'm using an NSPredicate to find all recommendation objects that have a damageType matching a selected damage as follows:
NSFetchRequest *fetchRequestDamages = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([Recommendation class])];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY damageType == %#", _currentRecordedDamage.damageType];
But want the filter to return all Recommendations that have matches for a specific damageType, areaDamaged and validVehicle
I've tried
NSMutableArray *predicates = [[NSMutableArray alloc] initWithCapacity:2];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY damageType == %#", _currentRecordedDamage.damageType];
[predicates addObject:predicate];
NSPredicate *predicate2 = [NSPredicate predicateWithFormat:#"ANY areaDamaged == %#", _currentAreaDamaged];
[predicates addObject:predicate2];
NSPredicate *predicate3 = [NSPredicate predicateWithFormat:#"ANY validVehicles == %#", _currentVehicle];
[predicates addObject:predicate3];
fetchRequestDamages.predicate = [NSCompoundPredicate andPredicateWithSubpredicates:predicates];
fetchRequestDamages.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES]];
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequestDamages managedObjectContext:[RKManagedObjectStore defaultStore].mainQueueManagedObjectContext sectionNameKeyPath:nil cacheName:nil];
self.fetchedResultsController.delegate = self;
NSError *error;
[self.fetchedResultsController performFetch:&error];
int resultsFound = self.fetchedResultsController.fetchedObjects.count;
but it seems this returns the set of all objects satisfying any of the predicates - I'd like the set of objects that match all three.
I'm looking into using SUBQUERY but can't quite make sense of how to create this query?

Just combine the three predicates with "AND" to find the objects that match all of them:
NSArray *predicates = ... // your array of predicates
NSPredicate *finalPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:predicates];
[fetchRequestDamages setPredicate:finalPredicate];

Why not to use AND in one query?
Something like:
damage = %# AND damagePoints = %# AND damageCost = %#
and:
damageType IN %#
Where %# in the last code example should be an array/set or something else.

Related

How to check NSString type DB object is null?

I wanted to fetch only those users who do not have null lastname which of NSString type.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"USER" inManagedObjectContext:dbHandler.managedObjectContext];
NSMutableArray * predicateArray = [[NSMutableArray alloc] init];
NSPredicate * predicate1 = [NSPredicate predicateWithFormat:#"id = %#",abc];
[predicateArray addObject:predicate1];
NSPredicate * predicate2 = [NSPredicate predicateWithFormat:#"lastname != %#",nil];
[predicateArray addObject:predicate2];
NSCompoundPredicate * resultantPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:predicateArray];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:resultantPredicate];
NSError *fetchError = nil;
NSArray *result = [dbHandler.managedObjectContext executeFetchRequest:fetchRequest error:&fetchError];
Predicate2 is not working for some reason.After applying it no object is returned even if DB contains some Non-Null string values of lastname. Please tell what is wrong in the code or is there any way to fetch not-null string ONLY from core data .
You can use method which is described here.
Just replace
[NSPredicate predicateWithFormat:#"lastname != %#",nil]
with
[NSPredicate predicateWithFormat:#"lastname.length > 0"]
UPDATE
This predicates will return nil objects
[NSPredicate predicateWithFormat:#"lastname = NULL"];
[NSPredicate predicateWithFormat:#"lastname = NIL"];
This predicate will return empty strings
[NSPredicate predicateWithFormat:#"lastname = ''"];

CoreData: Using NSPredicate to filter one to many to many relationship (error to-many key not allowed here)

I have coreData model:
I'm trying to verify the schedule of the movie match the movie and theater but I'm getting the error "to-many key not allowed here"
Here is my code:
NSManagedObjectContext *moc = [self managedObjectContext];
NSEntityDescription *timeDescription = [ NSEntityDescription entityForName:#"Schedules" inManagedObjectContext:moc];
NSPredicate *timePredicate = [NSPredicate predicateWithFormat:#"showTimes <= %#", _movieTime];
NSPredicate *moviePredicate = [NSPredicate predicateWithFormat:#"ANY movie.nameOfMovie CONTAINS[cd] %#", _movie];
NSPredicate *theatersPredicate = [NSPredicate predicateWithFormat:#"movie.theaters.nameOfTheater == %#", _theaterName];
NSPredicate *compoundPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:#[timePredicate,moviePredicate,theatersPredicate]];
NSFetchRequest *request = [NSFetchRequest new];
request.predicate = compoundPredicate;
request.entity = timeDescription;
NSError *error = nil;
NSArray *result = [moc executeFetchRequest:request error:&error];
If I only use the following predicates:
NSPredicate *timePredicate = [NSPredicate predicateWithFormat:#"showTimes <= %#", _movieTime];
NSPredicate *moviePredicate = [NSPredicate predicateWithFormat:#"ANY movie.nameOfMovie CONTAINS[cd] %#", _movie];
I can get the match with the schedule and the movie but my questions is how can I get match for schedule and movies and theaters any of you knows what I'm doing wrong?
I'll really appreciate your help
You are getting the "to-many key not allowed here" error because, in your theatersPredicate "movie" is a to-many relationship. Compare with your moviePredicate, which works OK because it uses the "ANY" keyword.
So you might be tempted to just add "ANY" to your theatersPredicate. That will work (i.e. compile and run OK), but will not, I think, give you the results you are looking for: it will show those where "(ANY movies' moviename matches) and (ANY movies' nameOfTheatre matches)". But it could be a different Movie in each case.
What I think you want is "ANY movies where (moviename matches AND nameOfTheatre matches)". For that purpose, use SUBQUERY:
NSPredicate *timePredicate = [NSPredicate predicateWithFormat:#"showTimes <= %#", _movieTime];
NSPredicate *movieAndTheaterPredicate = [NSPredicate predicateWithFormat:#"SUBQUERY(movie, $x, $x.nameOfMovie CONTAINS[cd] %# AND $x.theaters.nameOfTheater == %#).#count > 0", _movie, _theaterName];
NSPredicate *compoundPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:#[timePredicate,movieAndTheaterPredicate]];

How to use a compound NSPredicate?

I would like to know how if at all to use a compound NSPredicate?
I have made an attempt as follows however the currentInstall array is exactly the same at the start as it is after the predicate has been applied.
NSArray *currentInstall = [coreDataController filterReadInstalls:selectedInstallID];
NSArray *tempArray = [currentInstalls filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"cHR == 0"]];
currentInstalls = [tempArray copy];
NSPredicate *predicateAreaString = [NSPredicate predicateWithFormat:#"area == %#", [myFilter objectForKey:#"area"]];
NSPredicate *predicateBString = [NSPredicate predicateWithFormat:#"stage == %#", [myFilter objectForKey:#"area2"]];
NSPredicate *predicateCString = [NSPredicate predicateWithFormat:#"partCode == %#", [myFilter objectForKey:#"area3"]];
NSPredicate *predicateDString = [NSPredicate predicateWithFormat:#"doorNo CONTAINS[cd] %#", [myFilter objectForKey:#"door"]];
NSPredicate *predicateEString = [NSPredicate predicateWithFormat:#"doorDesc CONTAINS[cd] %#", [myFilter objectForKey:#"doorDesc"]];
NSPredicate *compoundPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:#[predicateAreaString, predicateBString, predicateCString, predicateDString, predicateEString]];
NSMutableArray *filteredArray = [NSMutableArray arrayWithArray:[currentInstalls filteredArrayUsingPredicate:compoundPredicate]];
currentInstalls = [filteredArray mutableCopy];
There doesn't seem to be anything obviously wrong with the way you have implemented NSCompundPredicate. If you are not trying to And or Not predicates then I would say it is something wrong with your predicate formats and how they match the array you are filtering.
I would try to use just 2 of the predicates to create an NSCompundPredicate then get that working or see what is causing your issue. NSHipster also has some good info about NSPredicates.

predicate for fetching an entities' assosicate entities where associate entity's certain attribute has a certain string value

i have catalogues and categories entities in my coredata
they have a many-to-many relationship
i want to fetch specific categories where field categoryStatus="active" and involved in catalogue.categories
what is the proper predicate ?
or without a fetch > catalogue.categories where categoryStatus=#"active"
c# equivelant of aCatalogue.categories.Where(c=>c.categoryStatus == "active")
i try something like this but no luck
-(NSArray *) CategoryGetListWhereStatusActive4Catalogue:aCatalogue2DisplayCategories
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Category" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"([catalogues containsObject:%#) AND (categoryStatus == %#)" , aCatalogue2DisplayCategories, #"active"];
[fetchRequest setPredicate:predicate];
return [self performFetch:fetchRequest withContextObject:context];
}
Something like this?
[NSPredicate predicateWithFormat:#"(ANY catalogues == %#) AND (categoryStatus == %#)",
aCatalogue, #"active"]
I think this should work:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(SUBQUERY(catalogues, $catalogues, $catalogues == %#).#count>0) AND (categoryStatus == %#)" , aCatalogue2DisplayCategories, #"active"];
or
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(ANY catalogues = %#) AND (categoryStatus == %#)" , aCatalogue2DisplayCategories, #"active"];

CoreData NSPredicate with relationship

I have following CoreData objects Model
Now I am having issue in making a predicate with following conditions.
Fetch all those DBOpportunity WHERE
DBOpportunity.stateCode == 1
AND
DBOpportunity.invoiceDate >= GIVEN_DATE
AND
DBOpportunityLines.crmAccept == 1 OR DBOpportunityLines.crmAccept == 3
I have tried lots of examples and programming guide by the apple but can't able to achieve this.
opportunitylines is a to-many relationship, so there are multiple DBOpportunityLines objects for one DBOpportunity object. Assuming that the last condition
DBOpportunityLines.crmAccept == 1 OR DBOpportunityLines.crmAccept == 3
should hold for any of the related objects, you need a SUBQUERY:
NSDate *givenDate = ...;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"stateCode == 1 AND invoiceDate >= %# "
"AND SUBQUERY(opportunitylines, $x, $x.crmAccept == 1 OR $x.crmAccept == 3).#count > 0",
givenDate];
Remark: Unfortunately, the usage of SUBQUERY in predicates is poorly documented. There is one example in the NSExpression class reference. See also Quick Explanation of SUBQUERY in NSPredicate Expression.
The structure of your predicate is A && B && (C || D)
Setup your predicates
NSPredicate *aPredicate = [NSPredicate predicateWithFormat:#"stateCode == %d", value];
NSPredicate *bPredicate = [NSPredicate predicateWithFormat:#"invoiceDate >= %#", givenDate];
Similar do the cPredicate and dPredicate. Then first combine c and d with OR
NSArray *cdPredicateArray = #[cPredicate, dPredicate];
NSPredicate *cdPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:cdPredicateArray];
and then all of them with AND
NSArray *allPredicateArray = #[aPredicate, bPredicate, cdPredicate];
NSPredicate *allPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:allPredicateArray];
If I misunderstood your question and your structure is A && B && C || D Then you have to combine A, B and C first (with AND) and then combine that result with D (with OR).
You could also fetch your opportunityLines and then get the parent entities like this:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"opportunityLines" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSDate *yourDate = [NSDate date];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(crmAccept==1 OR crmaccept==3) AND opportunity.stateCode==1 AND opportunity.invoiceDate>=%#", yourDate];
[fetchRequest setPredicate:predicate];
NSError *error;
//So here you have your array of opportunitylines
NSArray *opportunityLines = [context executeFetchRequest:fetchRequest error:&error];
//And this is how you get your opportunity objects
NSArray *opportunities = [opportunityLines valueForKeyPath:#"#distinctUnionOfObjects.opportunity"];

Resources