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.