开发者

core data predicate and expression madness

This is one for core data experts, I guess. Maybe this is just beyond the bounds of what it's supposed to... anyway:

on iOS, using sqlite persistent store.

I have entities like so: A<-->>B<<-->C

B has an attribute 'v' which is a float, and another attribute 'd' which is a date. B has relationship 'a' which is single to A, and 'c' which is single to C.

I can calculate the average of all B.v where B.a == someA, code is below. But, what I really want to do is calculate the average of a开发者_高级运维ll B.v where B.a == someA and where B is the last B for a C, ordered by B.d. So the average function would 'pick up' only one B for each C (the highest-dated one). Any ideas?

// create the fetch request
NSFetchRequest * request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:@"B" inManagedObjectContext:managedObjectContext]];

// create the expression
NSExpression * keyPathExpression = [NSExpression expressionForKeyPath:@"v"];
NSString * aggregationFunction = @"average:";
NSExpression * aggregationExpression = [NSExpression expressionForFunction:aggregationFunction arguments:[NSArray arrayWithObject:keyPathExpression]];
NSExpressionDescription * expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName:@"aggregateValue"];
[expressionDescription setExpression:aggregationExpression];
[expressionDescription setExpressionResultType:NSDecimalAttributeType];
[request setPropertiesToFetch:[NSArray arrayWithObject:expressionDescription]];

NSPredicate * predicate = [NSPredicate predicateWithFormat:@"a == %@", someA];
[request setPredicate:predicate];

[request setResultType:NSDictionaryResultType];

// Execute the fetch.
NSError * error = nil;
    NSArray * objects = [managedObjectContext executeFetchRequest:request error:&error];


When you use setPropertiesToFetch: you are telling the fetch to ignore all other properties. This means your predicate of @"a == %@" which is keyed on the a attribute, is going to be ignored.

What you probably want to do in this case is to convert the @"a == %@" predicate into an NSSubqueryExpressionType equality expression and then make that the first expression in the array passed to setPropertiesToFetch:.

That will direct the fetch to first find all B objects that match @"a == %@" and then to run the v expression against that set of objects.

However, when you find yourself having to create convoluted predicates, that is usually an indication that your data model is poorly designed. Quite often, you end up with complex predicates because you are trying to create logical relationships within a predicate instead of modeling them in the data model.

Further, as a rule, when you have a particular object in a hand e.g. someAand/or someC then you don't do a fetch at all but instead you walk the relationships. Why fetch all B objects related to the someA object when you can just use someA.bObjects?

I think your data model really looks like this:

A<-->>B<<-->C

… and your looking for an intersection of B objects between the sets of someA.bs and someC.bs. So, the first thing to do is to get the intersection of the sets:

NSMutableSet *secSet=[[NSMutableSet setWithSet:someA.bs] intersectSet:someC.bs];

… now all you have to do is find the B object with the most recent date:

BClass *someB=[secSet valueForKeyPath:@"@max.dateAttribute"];

… and you're done. Iterate the process to find the intersections between multiple A and C objects.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜