AbuZubair
AbuZubair

Reputation: 1234

Retrieving count of a core data relation

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:

  1. All cars which are 'red'.
  2. Return only the 'Year' and 'Color' attributes of the matching car.
  3. Return a count of how many people are driving this car (i.e the size of the NSSet of People inside each resulting Car).

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

Answers (2)

eik
eik

Reputation: 4686

  1. Return only 'red' cars:

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"color LIKE 'red'"];
    
  2. 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];
    
  3. 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"]];
    
  4. 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];
    
  5. 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

Martin R
Martin R

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

Related Questions