开发者

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);
0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜