CoreData NSPredicate with relationship - ios

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"];

Related

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]];

NSFetchedResultsController with NSPredicate

I am using a NSFetchedResultsController to handle the data going in and out of my UITableView. It all works fine until I put a NSPredicate on it. I need to get all Thread objects that have a relationship with at least one message that is not expired or does not have a expiration_date set yet. So I use the following predicate.
NSPredicate *threadPredicate = [NSPredicate predicateWithFormat:#"ANY messages.expiration_date == null OR ANY messages.expiration_date > %#", [NSDate date]];
Problem is that this causes the NSFetchedResultsController to act very strange. If a thread gains a new message in its messages relationship it deletes the row. It also does not insert new ones.
I am 100% sure it is the NSPredicate causing these behaviors. Without it, everything works fine I just don't have some of the data I do not want filtered out.
Below is a picture of the talked about section of my data model. I will also include the code for my NSFetchedResultsController.
/**
* Returns a NSFetchedResultsController for the unified inbox
*
* #return Controller for fetched results
*/
- (NSFetchedResultsController *)resultsControllerForUnifiedInbox
{
NSFetchRequest *threadsRequest = [NSFetchRequest fetchRequestWithEntityName:#"Thread"];
NSEntityDescription *threadModel = [NSEntityDescription entityForName:#"Thread" inManagedObjectContext:_mainManagedObjectContext];
[threadsRequest setEntity:threadModel];
NSPredicate *threadPredicate = [NSPredicate predicateWithFormat:#"ANY messages.expiration_date == null OR ANY messages.expiration_date > %#", [NSDate date]];
[threadsRequest setPredicate:threadPredicate];
NSSortDescriptor *threadSort = [NSSortDescriptor sortDescriptorWithKey:#"date" ascending:NO];
[threadsRequest setSortDescriptors:[NSArray arrayWithObject:threadSort]];
[threadsRequest setFetchBatchSize:LiftFetchControllerAtrributeBatchSize];
NSFetchedResultsController *fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:threadsRequest
managedObjectContext:_mainManagedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
return fetchedResultsController;
}
I cannot explain the strange effects of the predicate that you observed, but I assume
that your predicate is not doing what you expect.
[NSPredicate predicateWithFormat:#"ANY messages.expiration_date == null OR ANY messages.expiration_date > %#", [NSDate date]];
finds all threads that have a message with expiration_date not set OR have a possible different message that is not expired. What you (probably) need instead is
[NSPredicate predicateWithFormat:#"SUBQUERY(messages, $m, $m.expiration_date == null OR $m.expiration_date > %#).#count > 0", [NSDate date]];

NSPredicate multiple ANY query with CoreData

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.

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"];

NSPredicate, get results with a subset of one-to-many relationship

I'mm working around with Core Data and NSFetchedResultsController.
My Data Model looks like this:
Product with one-to-many relationship called dataLines.
The dataLine entity has a property name theWeek.
I want to fetch all Product where dataLines.theWeek == someValue. This is easily done with a subquery. But this returns all dataLines. Is it possible to create a NSPredicate that returns the Product and a subset if dataLines only with the dataLines == someValue?
What you want to achieve could be reached in two ways:
using a SUBQUERY
[NSPredicate predicateWithFormat:#"SUBQUERY(dataLines, $x, $x.theWeek == %#).#count > 0)", [NSNumber numberWithInt:18]];
or the ANY modifier
[NSPredicate predicateWithFormat:#"ANY dataLines.theWeek == %#", [NSNumber numberWithInt:18]];
You can do also the following if you need to check against multiple values:
[NSPredicate predicateWithFormat:#"SUBQUERY(dataLines, $x, $x.theWeek == %# or $x.theWeek == %#).#count > 0)", [NSNumber numberWithInt:18], [NSNumber numberWithInt:19]];
The same can be applied to ANY modifier. ANY ... OR ANY ....
Maybe if you share some code we could help you.
P.S. I suppose you don't use scalar values and theWeek is a number.
Hope it helps.
You should fetch the dataLine property instead.
Assuming your Product and dataLine entity connected by relationship someRelation then you can try this code;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityWithName:#"dataLine" inManagedObjectContext:self.managedObjectContext]];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"dataLines.week == %#",theWeek]];
NSMutableArray *tmpProduct [[NSMutableArray init] alloc];
NSMutableArray *tmpArray = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
for (dataLine *theDataLine in tmpArray);
NSLog(#"%#",theDataLine.someRelation.name);
tmpProduct = theDataLine.someRelation.name;
then you can just call tmpProduct to call or display your product in table view
Create a fetch request for the 'Product' entity:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity: [NSEntityDescription entityForName:#"Product" ...]]
then create a predicate using the properties/attributes of Product with 'ANY':
[fetchRequest setPredicate:
[NSPredicate predicateWithFormat:#"ANY dataLines.theWeek == %#", <whatever week>]];
then execute the fetch to get an array of Product with at least one <whatever week>.
Generally see 'Fetching Managed Objects', NSPredicate and related documentation.

Resources