I'm using Core Data to store simple entities that consist of a value and a timestamp. I'm looking to make a fetch request which returns the latest value added, as well as a running average of all values. This all seemed straightforward enough until I attempted to use an NSPredicate to filter the results to within a certain time period.
I'm using NSExpression's expressionForFunction:withArguments:
method to determine the average. By setting the launch flag -com.apple.CoreData.SQLDebug 1
, I can clearly see that only the latest value is adhering to my date predicate. The average calculating is instead being performed as a subquery, but not taking my date predicate into account:
SELECT (SELECT avg(t1.ZVALUE) FROM ZEVENT t1 WHERE t1.Z_ENT = ?), t0.ZVALUE FROM ZEVENT t0 WHERE ( t0.ZTIMESTAMP >= ? AND t0.Z_ENT = ?) ORDER BY t0.ZTIMESTAMP DESC
What's the most efficient (and scalable) way of determining the average value while still honouring my NSFetchRequest's predicate?
For reference, here is my code in it's entirety:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"timestamp" ascending:NO];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Reading" inManagedObjectContext:moc];
[request setEntity:entity];
[request setSortDescriptors:@[sortDescriptor]];
[request setResultType:NSDictionaryResultType];
// Filter by date where necessary
NSPredicate *datePredicate = [NSPredicate predicateWithFormat:@"(timestamp >= %@)", toDate];
[request setPredicate:datePredicate];
NSExpression *valueExpression = [NSExpression expressionForKeyPath:valueKey];
NSExpressionDescription *valueDescription = [[NSExpressionDescription alloc] init];
[valueDescription setName:@"value"];
[valueDescription setExpression:valueExpression];
[valueDescription setExpressionResultType:NSDoubleAttributeType];
NSExpression *avgExpression = [NSExpression expressionForFunction:@"average:" arguments:[NSArray arrayWithObject:valueExpression]];
NSExpressionDescription *avgDescription = [[NSExpressionDescription alloc] init];
[avgDescription setName:@"averageReading"];
[avgDescription setExpression:avgExpression];
[avgDescription setExpressionResultType:NSDoubleAttributeType];
[request setPropertiesToFetch:@[avgDescription, valueDescription]];