Reputation: 2864
I am looking for a predicate to fetch all managed objects of type Entity
whose values are duplicated in a property sessionId
, where all groups' ("groups", meaning managed objects whose sessionId
's are equal) contents' flags in a property processed
is set to YES. This can be done (slowly), but I am looking for an efficient one liner for this. Thanks
This is the slow way:
NSFetchRequest *request = [NSEntityDescription entityForName:@"Entity"
inManagedObjectContext:context];
NSArray *all = [context executeFetchRequest:request error:nil];
NSArray *sessionIds = [all valueForKeyPath:@"@distinctUnionOfObjects.sessionId"];
NSMutableArray *objects = [NSMutableArray array];
for (NSString *sessionId in sessionIds) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"sessionId == %@", sessionId];
NSArray *inSession = [all filteredArrayUsingPredicate:predicate];
for(id obj in inSession) {
if(![obj valueForKey:@"processed"]) continue;
}
[objects arrayByAddingObjectsFromArray:processed];
}
NSLog(@"%@", objects);
Upvotes: 0
Views: 189
Reputation: 21536
Since booleans are stored as 0s and 1s, a group where all rows have processed = YES
will have average(processed) = 1
. Hence you can use NSFetchRequest
's propertiesToGroupBy
and havingPredicate
to get the sessionId
s that meet your criteria. A second fetch is then required to get the Entity
objects with any of those sessionId
s:
NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName:@"Entity"];
fetch.resultType = NSDictionaryResultType;
fetch.propertiesToFetch = @[@"sessionId"];
fetch.propertiesToGroupBy = @[@"sessionId"];
fetch.havingPredicate = [NSPredicate predicateWithFormat: @"average:(processed) == 1"];
NSArray *resultsArray = [context executeFetchRequest:fetch error:nil];
NSArray *sessionIdArray = [resultsArray valueForKeyPath:@"sessionId"];
NSFetchRequest *newFetch = [NSFetchRequest fetchRequestWithEntityName:@"Entity"];
newFetch.predicate = [NSPredicate predicateWithFormat:@"name IN %@",sessionIdArray];
NSArray *finalResults = [context executeFetchRequest:newFetch error:nil];
NSLog(@"Final results, %@", finalResults);
Sorry it's not a one-liner. And I leave it to you to determine whether it's any quicker than your own code.
EDIT
To do it all in one fetch, use NSFetchRequestExpression
in place of the intermediate arrays:
NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName:@"Entity"];
fetch.resultType = NSDictionaryResultType;
fetch.propertiesToFetch = @[@"sessionId"];
fetch.propertiesToGroupBy = @[@"sessionId"];
fetch.havingPredicate = [NSPredicate predicateWithFormat: @"average:(processed) == 1"];
NSExpression *fetchExpression = [NSFetchRequestExpression expressionForFetch:[NSExpression expressionForConstantValue:fetch] context:[NSExpression expressionForConstantValue:context] countOnly:false];
NSFetchRequest *newFetch = [NSFetchRequest fetchRequestWithEntityName:@"Entity"];
newFetch.predicate = [NSPredicate predicateWithFormat:@"sessionId IN %@",fetchExpression];
NSArray *finalResults = [context executeFetchRequest:newFetch error:nil];
NSLog(@"Final results, %@", finalResults);
Note that on my (admittedly trivial) test setup this actually ran more slowly than the two-fetch solution.
FYI, if you use the SQLDebug build setting to examine the SQL that is generated, it looks something like this:
SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZSESSIONID, t0.ZPROCESSED FROM ZENTITY t0 WHERE t0.ZSESSIONID IN (SELECT n1_t0.ZSESSIONID FROM ZENTITY n1_t0 GROUP BY n1_t0.ZSESSIONID HAVING avg( n1_t0.ZPROCESSED) = ? )
Upvotes: 1