Reputation: 1234
I searched high and low but I couldn't find exactly what I was looking for. My question is similar to this, but slightly different:
Core Data - Count of Related Records
Let's say I have a Car entity which has a one to many relation with a Person entity. This means that the car could have multiple people driving it, but each person drives only one car.
I want to be able to execute only one predicate wherein I could achieve the following:
Is it possible to do all this with one query?
I know how to do this with multiple queries. I would just use setPropertiesToFetch
and use a filtered predicate to achieve 1 and 2 above. I would then perform another count query (countForFetchRequest
) on the Persons entity for every car to find how many Person(s) drive each car.
The key is the 3rd requirement above. I want to do everything in one predicate and I don't want to bring all of the Person entity objects into memory (performance) on the initial query. Furthermore it hurts to call another countForFetchRequest
query for each car.
What's the best way to do this?
Thanks!
Upvotes: 1
Views: 764
Reputation: 4686
Return only 'red' cars:
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"color LIKE 'red'"];
Return a count of how many people are driving this car:
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath:@"people"];
NSExpression *countExpression = [NSExpression expressionForFunction:@"count:"
arguments:@[keyPathExpression]];
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName:@"count"];
[expressionDescription setExpression:countExpression];
[expressionDescription setExpressionResultType:NSInteger32AttributeType];
Return only the 'year' and 'color' attributes (and the count):
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Car"
inManagedObjectContext:context];
NSDictionary *attributes = [entity attributesByName];
NSArray *properties = @[expressionDescription, attributes[@"year"], attributes[@"color"]];
Build and execute the fetch request:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
[request setResultType:NSDictionaryResultType];
[request setPropertiesToFetch:properties]; // return only count, year & color
[request setPredicate:predicate]; // return only red cars
NSError *error = nil;
NSArray *results = [context executeFetchRequest:request error:&error];
Process the results:
if (results) {
for (NSDictionary *result in results) {
NSLog(@"Year: %@", result[@"year"]);
NSLog(@"Color: %@", result[@"color"]);
NSLog(@"Drivers: %@", result[@"count"]);
}
}
else {
NSLog(@"Error: %@", error);
}
Upvotes: 3
Reputation: 540145
I cannot test this at the moment, but that should be possible by adding the following expression description to the "properties to fetch":
NSExpression *countExpression = [NSExpression expressionForFunction: @"count:" arguments: [NSArray arrayWithObject:[NSExpression expressionForKeyPath: @"drivers"]]];
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName: @"driversCount"];
[expressionDescription setExpression: countExpression];
[expressionDescription setExpressionResultType: NSInteger32AttributeType];
Upvotes: 3