NSFetchedResultController complex sort case - ios

I have following data model:
[Agency] <--->> [AgencyRating] <<---> [RatingType]
There are numbers of rating types (ex. overall, branding, design, programming, etc). Each agency can have all these rating types (ex. agency FooBar has 1050 points in design and 700 points in branding).
Tables structures:
Agency: name:string
RatingType: name:string
AgencyRating: agency_ref: relationship to agency, ratingType_ref: relationship to rating type, value: Integer.
I have split view controller. Left side contains available rating types. When user picks any rating type, I'd like to show sorted by rating value agencies on the right side.
I'd like to achieve it with NSFetchedResultController. I understand how to get rating values to display it in cells, but I don't understand how to make a sort descriptor.
Currently I have:
Controller:
_contentFetchController = [Agency MR_fetchAllGroupedBy:nil withPredicate:[NSPredicate predicateWithValue:YES] sortedBy:#"name" ascending:YES];
Cell:
- (void) configureForAgency:(Agency *) agency ratingType:(RatingType *) ratingType forIndexPath:(NSIndexPath *) path {
self.positionLabel.text = [NSString stringWithFormat: #"%d", (int)path.row + 1];
UIImage *image = [UIImage imageWithContentsOfFile:agency.logoPath];
if (image) {
self.logoImageView.image = image;
}
self.nameLabel.text = agency.name;
// HERE IS VALUE OF RATING
self.ratingValueLabel.text = [[agency ratingForRatingType:ratingType] stringValue];
}
As you can see: I sort by name, but I need to sort by rating value, based on selected rating type. Any ideas?

In order to do that you need to fetch Agencies, that have an associated AgencyRating with a selected rating type and sort everything by rating value.
I assume that rating type is ratingType, rating value is ratingValue and AgencyRating association is agencyRatings. I also assume that each agency can have only one agency rating of each rating type (if not, you will have to sort by the SUM of all agency ratings of selected type).
Judging by the MR_ prefix, I guess you are using MagicalRecord, which I'm not familiar with, but here's how you can do that with plain NSFetchRequest and NSFetchedResultsController.
First you will need to set up a NSFetchRequest:
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Agency"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"ANY agencyRatings.ratingType = %#", ratingType];
fetchRequest.sortDescriptors = #[ [NSSortDescriptor sortDescriptorWithKey:#"agencyRatings.ratingValue" ascending:YES] ];
Then you can set up the NSFetchedResultsController as you would normally do providing it with the fetchRequest you just created.
Every time the user changes the ratingType you will need to change the fetchRequest's predicate and call performFetch on the fetchedResultsController.
I guess you need to adjust this a little bit to fit your case, but the idea is to to have a predicate and a sort descriptor that are pointing to a related table.

Related

iOS Sorting by Parse relation size

I have some User objects on my Parse backend with a relation with "Comment" objects. I want to query and sort by the users with the highest number of objects in the previous relation, however I am not sure how to go about doing this.
Any tips?
Thanks
I assume after parsing your response you are left with an array of objects with a property comments which is again an array of objects, something like this:
// NSLog(#"%#", users);
(
{
comments = (
{
date = "2016-01-13T02:04:52+00:00";
content = "nice comment, bro";
}
);
userName = "louise.lausebär";
}
)
Sorting that by count of comments is super simple with NSSortDescriptor and KVO Collection Operators:
NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:#"comments.#count" ascending:YES];
[array sortUsingDescriptors:#[descriptor]];

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

How to get data from unrelated entities using Core Data?

I have these three entities which are not related. How can i get the salary of an employee. I want something like this :
Select C.salary FROM Employee A, Department B, Salaries C Where A.id=B.empid AND B.id=C.deptid AND A.id=12
i need to do the same above operation in core data.
As Martin suggested, the easy way is to set up the needed relationships and traverse them.
But if you don't have the permission to alter the model, you need to work and filter managed in memory object you own once retrieved. In other words, you need to set up NSFetchRequests with the correct NSPredicates.
For example, to retrieve the department for a given employee.
NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:#"Department"];
request.predicate = [NSPredicate predicateWithFormat:#"empid", #(12)];
NSError *error = nil;
NSArray *departments = [moc executeFetchRequest:request error:&error];
if(departments) {
// do a NSLog for departments here to see what you have
// here you can access deptid for each department you retrieve
// and with that value run another request against Salaries
// e.g. NSManagedObject* department = [departments lastObject];
// NSNumber* deptid = [department valueForKey:#"deptid"]
} else {
// handle error here
}
Other part is left as excersise ;)

How to use Core Data relationship?

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.

Nested core data fetch

I have a core data DB with multiple entities. There is a parent entity called "Person" which has many "Friends", "Friends" have many "Activities", and "Activities" have "Types" ("Types has a to-many relationship to "Activities"). What I'm trying to achieve is filtering all the "Person" entities by "Types". A user would be tapping on a "Type" and then I would refresh my table and filter the "Person" entities that are displayed by the "Types" that are associated with them.
Currently I'm thinking I have to use a compound predicate but I'm completely sure how to go about it. So far all I've done is printed out the values I've wanted by looping through my fetchedObjects like so:
NSArray *persons = self.fetchedResultsController.fetchedObjects;
for (JIPerson *person in persons) {
JIFriend *friend = person.friends.anyObject;
JIActivity *activity = friend.activities.anyObject;
JIType *type = activity.type;
NSLog(#"%#", type.name);
}
This prints out the values correctly, but I need to filter my table using these values. How can I achieve that?
Seems like I got it. Using NSPredicate you can traverse a deep relationship like this using dot notation. My implementation went as follows:
- (void)filterPersonByType:(NSString *)typeName {
NSPredicate *typePredicate = [NSPredicate predicateWithFormat:#"ANY friends.activities.type.name CONTAINS[cd]%#", typeName];
}
Yes, you can use dot notation with NSPredicate. You might want to have the type be an actual type, though, that would be cleaner than just doing string comparisons.

Resources