How to use Core Data relationship? - ios

In my app I have 3 related entities. Athlete, Exercise, and Exercise Score.
Athlete has a to-many relationship with Exercise. It's inverse is whosExercise.
Exercise has a to-many relationship with exercise score. It's inverse is whichExercise.
I want to perform a fetch request where I get all the exercise scores for an athlete. How would I get that? Do I need another relationship between Athlete and Exercise Scores, or is that redundant? If it is, how would I use exercise as a predicate for my request?

If Exercise has a relationship to Athlete, then you should be able to do something like:
[NSPredicate predicateWithFormat:#"SELF.athlete = #%", currentAthlete];
and then just:
[Exercise fetchByPredicate:currentAthletePredicate];
Should be it.

If I clearly understand, you need to select all ExerciseScore for Athlete, but Athlete does not have direct relationship with ExerciseScore table.
SLQ Query for this may looks like :
Select *
from ExerciseScore
where IDScore in (select IDScore
from Exercise
where IDExercise in ( select IDExercise
from Athlete
)
)
But in Core Data you can't operate SLQ Queries.
Try this approach.
1.Fetch all Exercise for Athlete:
NSError *error;
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"Exercise"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.athlete = #%", currentAthlete];
request.predicate=predicate;
NSArray *athleteExercises = [self.managedObjectContext executeFetchRequest:request error:&error];
2.Iterate throughout array of Exercises to get all Exercise score for each Exercise.
NSMutableArray *allScores = [NSMutableArray arrayWithCapacity:0];;
for (Exercise *exercise in athleteExercises) {
if ([exercise.scores count]>0) {
[allScores addObjectsFromArray:[exercise.scores allObjects]; //exercise.scores must be NSSet type
}
}
allScores array now contain All ExerciseScore objects for particular Athlete.

Predicates can have key paths:
#"whichExercise.whosExercise = %#",athlete
Or, if you have the athlete already, don't do a fetch request, just get the scores via the relationship properties.

Related

CoreData relationship between objects

I have two kind of objects that I retrieve from a server:
1) Companies
2) Categories
I am updating the data from the server each time that the app is become active(deleting the old data and inserting the new data). From my database Categories and Companies are connected between them with the category_id.
My question here is can I connect with a relationship each company with a category using the category_id?
I assume that means that each Company has a relationship with only one particular Category.
You want to add categoryId to your Category entity, and save the ID when you add the category.
Your models would include a number of other attributes, but here is how the relationships would be setup.
And, the relationships in the inspector...
Of course, you should specify any other options as you deem necessary.
Now, you set the category relationship of a Company entity by searching for the Category based on CategoryId, and then simply setting the category relationship. Core data will automatically take care of the inverse relationship.
Now, given any Category, you can know all the Company objects that are in that category with the `categories relationship, and given any Company you can know which Category it is in.
To fetch a Category by ID, you could do something like this...
- (NSManagedObject*)categoryWithCategoryId:(NSString*)categoryId
inMOC:(NSManagedObjectContext*)moc
error:(NSError **)error {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Category"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"categoryId = %#", categoryId];
fetchRequest.fetchLimit = 1;
return [[moc executeFetchRequest:fetchRequest error:error] firstObject];
}
And to set the relationship, if you have xcode/mogenerator generated subclasses, just set the relationship property.
NSError *error = nil;
Category *category = [self categoryWithCategoryId:categoryId inMOC:moc error:&error];
if (category) {
// If you have generated subclasses...
company.category = category;
// Otherwise, you can do...
[company setValue:category forKey:#"category"];
}

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

Core data many to many relationship query

Suppose two entities: EntityA and EntityB, connected by many to many relationship. Suppose the relation attributes are relatedEntityBs and relatedEntityAs respectively. And I have an NSArray of EntityB instances. I want to find all EntityA instances which have in relation all instances that are in nsarray. That is if the array is [entityB1,.....,entityBn]. Find all EntityA instances, which are related with entityB1,wntityB2 till entityBn. How can I write this kind of predicate?
This predicate should do it:
NSMutablArray *predicateArray = [NSMutableArray array];
for (int i=0;i<entityBArray.count;i++){
NSPredicate *subPredicate = [NSPredicate predicateWithFormat:#"ANY relatedEntityBs contains %#",[entityBArray objectAtIndex:i];
[predicateArray addObject:subPredicate] ;
}
NSCompoundPredicate *daddyPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:predicateArray];
Basically each sub predicate checks if relatedEntityB contains a single object of the array and finally the daddyPredicate combines them all in with OR.

NSPredicate and Core Data: retrieve objects among relationships

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.

Resources