NSPredicate and Core Data: retrieve objects among relationships - ios

I have a Core Data model structure like the following:
Product <-->> OrderProduct where the relationship (from Product to OrderProduct) is called productOrders while the inverse is called product
Order <-->> OrderProduct where the relationship is called orderProducts while the inverse is called order
Client <-->> Order where the relationship is called orders while the inverse is called client
Within an UIViewController I'm using a NSFetchRequest associated with the following predicate:
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"(ANY productOrders.order.client.code == %#)", clientCode];
The predicate works well. I'm able to retrieve products for one (or more) orders that are associated with a specific client.
Now I have to add another step. Find the last order (ordering by date) for a specific client. I've tried the following predicate but it doesn't work:
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"(ANY productOrders.order.client.code == %# AND productOrders.order.#max.orderDate)", clientCode];
where orderDate is of type NSDate.
Do I have to use a SUBQUERY? How can I achieve this? Thank you in advance.

You can use a sortDescriptor on your fetchrequest (fetchrequest takes an array of sortdescriptors).
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"productOrders.order.orderDate" ascending:NO];
[request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
edit: see NeverBe comment

I was able to fix the issue passing the date externally. In other words, I first calculated the last order date for a specific client and then I passed it to the interested element (the one that implement the request).
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"ANY productOrders.order.client.code == %# AND productOrders.order.orderDate == %#", clientCode, lastDate];
I don't know if could be correct but it works.
Hope it helps.

Related

Calling method on object in NSPredicate with Core Data

I have a 'position' entity with a to-many relationship to 'employee's. I want to use NSPredicate to do something like this
[position.employees containsObject:meEmployee]
There is a call to perform NSPredicate with block, but sadly it can't be used with CoreData.
How can I do this?
If you are fetching position entities with any employees matching meEmployee then you can use a predicate like this:
NSPredicate *predicate = [NSPredicate predicateWithFornat:#"ANY employees == %#", meEmployee];
But if the inverse relationship for employees is position, you can achieve the same with:
meEmployee.position

Data Retrieval With three entities without using manual looping

I have three entities.
I need to retrieve all the lectures that teach a certain student.
So far the solution i was able to come up is
first to use a subquery to retrieve all the courses the student takes by
NSPredicate *predicate = [NSPredicate
predicateWithFormat:#"SUBQUERY(students, $student, $ student.name ==
'student two').#count > 0"];
I execute the fetched request to obtain fetchedObjects.
Next i compare all the lecture objects with fetchedObjects. The lecture who conducts a course in fetchedObjects is the person who teaches the student in question.
Is there a much neater method of doing this without doing a comparison by hand ?
I mean can i do it using predicates alone?
To fetch all lecturers that have a course with the given student, use the predicate
[NSPredicate predicateWithFormat:#"SUBQUERY(courses, $c, ANY $c.students.name == %#).#count > 0", studentName]
Remark: In your first example (fetch all courses for a student), you don't need a SUBQUERY:
[NSPredicate predicateWithFormat:#"ANY students.name == %#", studentName]
Do you want to retrieve "Course" entity or "Lecturer" entities ?
for Course you could try this predicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"students.name = %#",NAME];
now you have Course entities and you can get Lecturers too,
for Lecturers you could try
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY courses.#unionOfArrays.students.name = %#",NAME];

CoreData - Fetch request with relationship object

please confirm if i understand that right...
Imagine there is an entity 'person' and an entity 'credit_card'.
So one 'person' can have many 'credit_card's.
The person entity has the attributes: name: STRING and age: INT
and relationship: creditcards (to many) inverse
and the credit card entity has: card_number: INT and valid_date: DATE and relationship: card_user (to one) inverse
In my code i have a specific person (ManagedObject) called f.e. Person *currentUser. If i now want to get all credit cards of this specific person with a specific 'valid_date' i would create a fetch request (for Entity 'credit_card') with following predicates:
NSPredicate *predicate1 = [NSPredicate predicateWithFormat:
#"valid_date == <NSDate object>"];
NSPredicate *predicate2 = [NSPredicate predicateWithFormat:
#"ANY card_user like currentUser"];
This predicate works well for me, but is that the right way? Should i really ask for: "ANY relationship name like ManagedObject" ?
If I'm understanding what you want correctly, all you need to do is use the creditcards property on your Person *currentUser and filter it down:
NSSet *setOfCreditCards = currentUser.creditcards;
NSPredicate *filter = [NSPredicate predicateWithFormat: #"valid_date == %#", date];
NSSet *cardsWithValidDates = [setOfCreditCards filteredSetUsingPredicate:filter];
The reason you tell CoreData about relationships is to avoid making another query from scratch.
You do not need to use the ANY key word if you have set up you core data model correctly. Specifically Credit cards need to have a person relationship back to the person object (you should get a warning if you didn't do this). Then you could combine both predicates into one like this
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"person == %# AND valid_date == %#", currentUser, valid_date];

NSPredicate filter to-Many with child object property

I have what I thought was a simple problem. I am attempting to filter some core data where I have a Parent object which has a to-many relationship with a child object and that child object has a string id. I want to get all the parent objects where no child object has a specific id.
I have tried !(ANY... LIKE) as well as !(ANY..==) and NONE with like and == and ALL children.id != otherid
My querying looks like:
NSFetchRequest* fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Parent"];
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"NONE children.id LIKE %#",otherID];
[fetchRequest setPredicate: predicate];
NSError* error;
NSArray* allParents = [[DataManager context] executeFetchRequest:fetchRequest error:&error];
//sanity check the predicate
for (Parent* p in allParents) {
for (Child* c in p.children) {
if([c.id isEqualToString:otherID]){
NSLog(#"PREDICATE FAIL!");
}
}
}
Am I missing something with NSPredicate? Is this type of filtering allowed for CoreData? Better solution?
I found a similar question although not easily apparent. It turns out the answer is the tricky SUBQUERY. Here is what led me on the chase:
NSPredicate Aggregate Operations with NONE
and an more open explanation about SUBQUERY here:
http://funwithobjc.tumblr.com/post/2726166818/what-the-heck-is-subquery
The resulting predicate is:
//in children get a $child and group all the $child objects together
//where the ids match, if that groups count is 0 we know
//the parent has no child with that id
[NSPredicate predicateWithFormat:
#"SUBQUERY(children, $child, $child.id == %#).#count == 0",objectId];

How to create one fetchRequest for multiple entities?

Update to the question below:
We were able to query the child entity using the Father entity fetch request by using fatherChild relationship. Sample query is as follows:
NSPredicate *myPredicate = [NSPredicate predicateWithFormat:#"fatherChild.name LIKE 'fx'"];
Now what we are trying to do is to use the predicate above but another another condition where we want to find child for a given fathers name. We used the following code
NSPredicate *myPredicate = [NSPredicate predicateWithFormat:#"name LIKE 'john' AND ANY fatherChild.name in 'fx'"];
But the program crashed with the exception:
[__NSCFString countByEnumeratingWithState:objects:count:]: unrecognized selector sent to instance 0xad49c40
Reading through examples we see we can use subquery but not sure what the syntax would be for our case where we have a entity with one to many relationship. Any help would be appreciated?
Thanks.
Question:
We have a data model with three entities: Father, Mother and Child. Please see the image for reference.
An example Query request we have on the father entity is given below :
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Father"
inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
NSString *attributeName = #"name";
NSPredicate *myPredicate = [NSPredicate predicateWithFormat:#"%K like %#",
attributeName, searchField.text];
We have similar query request for Mother and Child entities. What we would like to do is to create one query to combine Father and Mother entities. For example we want to search on Father's name =Mike and Mothers name =Jen in a single query. How do we do it?
Thanks for your response.
We found out that we can solve the problem using a SUBQUERY.
NSPredicate *myPredicate = [NSPredicate predicateWithFormat:#"name LIKE %# AND fatherMother.name LIKE %# AND (0!=SUBQUERY(fatherChild,$varChild,$varChild.name=%#).#count)",dad.text, mom.text, baby.text];
Here fatherMother and fatherChild are names of relationships.
dad.text, mom.text and baby.text are UITextField values.

Resources