an NSPredicate for selecting a row with maximum value by group
I'm using Core Data to store entities in the form
TrackerEntry
- username
- timestamp
I want to select the latest record for each user. Using sql it would be something like
SELECT MAX(timestamp) FROM Log GROUP BY username
Is there anyway to create 开发者_运维问答an NSPredicate to do this?
I would do it using NSExpression. This bit of code below won't work for you because you will have to group by username too, but it's a start for the best way of doing this without having to fetch everything. You want to perform the max and group in the db, not in memory - as it will be faster:
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
// Expression for the max
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath:@"timestamp"];
NSEntityDescription *entity = [NSEntityDescription entityForName:entityName
inManagedObjectContext:self.coreDataStack.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setResultType:NSDictionaryResultType];
NSExpression *valueSumExpression = [NSExpression expressionForFunction:@"max:" arguments:[NSArray arrayWithObject:keyPathExpression]];
NSExpressionDescription *expressionDescription = [[[NSExpressionDescription alloc] init] autorelease];
[expressionDescription setName:@"maxTimestamp"];
[expressionDescription setExpression:valueSumExpression];
[expressionDescription setExpressionResultType:NSDecimalAttributeType];
[fetchRequest setPropertiesToFetch:[NSArray arrayWithObject:expressionDescription]];
// Filter
//NSPredicate *pred = [NSPredicate predicateWithFormat:@" your predicate here"];
//fetchRequest.predicate = pred;
NSArray *results = [self.coreDataStack.managedObjectContext executeFetchRequest:fetchRequest error:nil];
if([results count] == 0) {
} else {
// [[results objectAtIndex:0] valueForKey:@"maxTimestamp"];
}
This worked for me, but it is a loop. First you have to get the unique names into an array.
NSFetchRequest *nameRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Log" inManagedObjectContext:self.managedObjectContext];
nameRequest.entity = entity;
nameRequest.resultType = NSDictionaryResultType;
[nameRequest setPropertiesToFetch:[NSArray arrayWithObject:[[entity propertiesByName] objectForKey:@"name"]]];
NSArray *allNames = [self.managedObjectContext executeFetchRequest:nameRequest error:nil];
names = [allNames valueForKeyPath:@"@distinctUnionOfObjects.name"];
[nameRequest release];
NSLog(@"%@", names);
Not very efficient or convenient. After all, Core Data is not a database but an object graph.
Then you can loop through these and fetch just the top one.
NSMutableArray *mutableResults = [NSMutableArray array];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = [NSEntityDescription entityForName:@"Log" inManagedObjectContext:self.managedObjectContext];
NSSortDescriptor *numberSort = [NSSortDescriptor sortDescriptorWithKey:@"number" ascending:NO];
request.sortDescriptors = [NSArray arrayWithObject:numberSort];
request.fetchLimit = 1;
for (NSString *s in names) {
NSPredicate *pred = [NSPredicate predicateWithFormat:@"name = %@", s];
request.predicate = pred;
NSArray *results = [self.managedObjectContext executeFetchRequest:request error:nil];
[mutableResults addObject:[results objectAtIndex:0]];
}
[request release];
NSLog(@"%@", mutableResults);
精彩评论