The base for many of my SELECT-ish queries when querying by exact match is one generic method I created in some category methods on NSManagedObjectContext:
-(NSArray*)entityName:(NSString*)entityName findManyByRelation:(NSDictionary*)dict { // TODO handle dict nil and emptyness NSMutableArray *arr = [[NSMutableArray alloc] initWithCapacity:dict.count]; for(NSString *key in dict) { NSExpression *left = [NSExpression expressionForKeyPath:key]; NSExpression *right = [NSExpression expressionForConstantValue:[dict objectForKey:key]]; NSComparisonPredicate *cp = [[NSComparisonPredicate alloc] initWithLeftExpression:left rightExpression:right modifier:NSDirectPredicateModifier type:NSEqualToPredicateOperatorType options:0]; [arr addObject:cp]; [cp release]; } NSPredicate *pred = nil; if(arr.count == 1) pred = [[arr objectAtIndex:0] retain]; // why do I have to retain here? else pred = [[NSCompoundPredicate alloc] initWithType:NSAndPredicateType subpredicates:arr]; NSFetchRequest *fr = [[NSFetchRequest alloc] init]; fr.entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:self]; fr.predicate = pred; [arr release]; [pred release]; // NSLog(@"predicate effective: %@", fr.predicate); NSError *err = nil; NSArray *ps = [self executeFetchRequest:fr error:&err]; if(err != nil) [NSException raise:@"DB Error" format:@"Fetch problem %@", fr.predicate.predicateFormat]; [fr release]; return ps; }
For example it serves under the hood of it’s sibling helpers
-(NSManagedObject*)entityName:(NSString*)entityName findByPrimaryKey:(NSDictionary*)dict { NSArray *ps = [self entityName:entityName findManyByRelation:dict]; if(ps == nil || ps.count == 0) return nil; if (ps.count == 1) return [ps objectAtIndex:0]; [NSException raise:@"DB Error" format:@"Multiple hits for %@", dict]; return (NSManagedObject*)1/0; }
and
-(NSManagedObject*)entityName:(NSString*)entityName selectOrInsertWithKey:(NSDictionary*)dict { NSManagedObject *o = [self entityName:entityName findByPrimaryKey:dict]; if(o == nil) { NSEntityDescription *ed = [NSEntityDescription entityForName:entityName inManagedObjectContext:self]; o = [[NSManagedObject alloc] initWithEntity:ed insertIntoManagedObjectContext:self]; for(NSString *key in dict) [o setValue:[dict objectForKey:key] forKey:key]; [o autorelease]; } return o; }
Those methods again are called by custom convenience wrappers in the model classes loosely following the ActiveRecord Pattern.