Reputation: 5389
Lets say I have 2 core data entities. A Student entity and a Class entity. For this example, a student is in exactly one class and a class can have many students.
Let's also suppose that these are big classes. That is, there are 1000's of students in each class.
The Class entity looks like:
NSString* name
NSArray* students
and the Student entity:
NSString* name
Class* class
NSString* grade
In my UI, I want to display a class. I want to show the number of students that have a certain grade. I.e. for a class of 1000 students, 50 have A's, 500 have B's 200 have C's... and so on.
Whats the most efficient way to get these counts from Core Data?
I'm current;y using an NSFetchedResultsController and passing it a fetch request for a group by + count query. I'm using the NSFetchedResultsController so that the UI updates as the number of students who have a certain grade changes. It's pretty slow though.
Is there a way to get this information without querying? I.e. as students are added to classes or their grades are changed, a property on the Class is updated with all the grade counts. So that when I want the counts, their are just a property of the Class and no query is needed.
Is it possible?
Upvotes: 3
Views: 6125
Reputation: 61870
Very simplest way in swift.
let request = NSFetchRequest(entityName: "BWCoreDataNotification")
request.predicate = NSPredicate(format: "type = 'purchase_reward'")
var error: NSError?
let numberOfNotifications = NSManagedObjectContext.MR_defaultContext().countForFetchRequest(request, error: &error)
Upvotes: 1
Reputation: 6011
You can try this fetch request:
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"Student"];
[request setResultType:NSDictionaryResultType];
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath:@"grade"];
NSExpression *countExpression = [NSExpression expressionForFunction:@"count:" arguments:[NSArray arrayWithObject:keyPathExpression]];
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName:@"gradeTypeCount"];
[expressionDescription setExpression:countExpression];
[expressionDescription setExpressionResultType:NSInteger64AttributeType];
[request setPropertiesToFetch:@[@"grade",expressionDescription]];
[request setPropertiesToGroupBy:@[@"grade"]];
[request setPredicate:[NSPredicate predicateWithFormat:@"theClass == %@",cls.objectID]];
This will result in an array of dictionaries holding the desired values.
The problem here will be that your FRC would not be able to track changes for those results. and therefore no reason to use a FRC in the first place
Even so, this is a very lightweight request and you could either issue it at a given time interval and update your UI.
(say 2 sec, and since you are not dealing with managed objects any more you can do the entire process in the background and dump your results to the main thread as you please)
Or listent to changes on your main context and if a student element changed, you refetch the counts again.
This will avoid fetching any Student
elements all together (at the described cost).
Upvotes: 9
Reputation: 1639
You can use following example to get count in given condition. You don't need to fetch data for counting.You can use countForFetchRequest
method to count the object efficiently .
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:entityName inManagedObjectContext:moc]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:
@"grade=='A'"];
[request setPredicate:predicate];
NSError *err;
NSUInteger count = [moc countForFetchRequest:request error:&err];
if(count == NSNotFound) {
//Handle error
}
[request release];
Upvotes: 5