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:
1-(NSArray*)entityName:(NSString*)entityName findManyByRelation:(NSDictionary*)dict
2{
3// TODO handle dict nil and emptyness
4 NSMutableArray *arr = [[NSMutableArray alloc] initWithCapacity:dict.count];
5 for(NSString *key in dict)
6 {
7 NSExpression *left = [NSExpression expressionForKeyPath:key];
8 NSExpression *right = [NSExpression expressionForConstantValue:[dict objectForKey:key]];
9 NSComparisonPredicate *cp = [[NSComparisonPredicate alloc] initWithLeftExpression:left
10 rightExpression:right modifier:NSDirectPredicateModifier type:NSEqualToPredicateOperatorType options:0];
11 [arr addObject:cp];
12 [cp release];
13 }
14 NSPredicate *pred = nil;
15 if(arr.count == 1)
16 pred = [[arr objectAtIndex:0] retain]; // why do I have to retain here?
17 else
18 pred = [[NSCompoundPredicate alloc] initWithType:NSAndPredicateType subpredicates:arr];
19
20 NSFetchRequest *fr = [[NSFetchRequest alloc] init];
21 fr.entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:self];
22 fr.predicate = pred;
23 [arr release];
24 [pred release];
25// NSLog(@"predicate effective: %@", fr.predicate);
26
27 NSError *err = nil;
28 NSArray *ps = [self executeFetchRequest:fr error:&err];
29 if(err != nil)
30 [NSException raise:@"DB Error" format:@"Fetch problem %@", fr.predicate.predicateFormat];
31 [fr release];
32 return ps;
33}
For example it serves under the hood of it’s sibling helpers
1-(NSManagedObject*)entityName:(NSString*)entityName findByPrimaryKey:(NSDictionary*)dict
2{
3 NSArray *ps = [self entityName:entityName findManyByRelation:dict];
4 if(ps == nil || ps.count == 0)
5 return nil;
6 if (ps.count == 1)
7 return [ps objectAtIndex:0];
8 [NSException raise:@"DB Error" format:@"Multiple hits for %@", dict];
9 return (NSManagedObject*)1/0;
10}
and
1-(NSManagedObject*)entityName:(NSString*)entityName selectOrInsertWithKey:(NSDictionary*)dict
2{
3 NSManagedObject *o = [self entityName:entityName findByPrimaryKey:dict];
4 if(o == nil)
5 {
6 NSEntityDescription *ed = [NSEntityDescription entityForName:entityName inManagedObjectContext:self];
7 o = [[NSManagedObject alloc] initWithEntity:ed insertIntoManagedObjectContext:self];
8 for(NSString *key in dict)
9 [o setValue:[dict objectForKey:key] forKey:key];
10 [o autorelease];
11 }
12 return o;
13}
Those methods again are called by custom convenience wrappers in the model classes loosely following the ActiveRecord Pattern.