I am having trouble creating an NSPredicate object based on my data model configuration.
I have 2 entity's one called Bob and one called Fred. Bob has a many to many relationship called 'hasFreds' (the reverse is hasBobs) with Fred and Fred has a property hasEaten of type BOOL.
I want to fetch a list of Bob's where every Bob has at least one Fred with hasEaten=YES. If there are no Freds with hasEaten=YES, I want to return all Bobs with no Freds.
This is what I have so far and it doesn't quite work (it doesn't meet the "If there are no Freds with hasEaten=YES, I want to return all Bobs with no Fred" condition):
predicate1 = [NSPredicate predicateWithFormat:
@"(SUBQUERY(hasFreds, $fred, $fred.hasEaten = %@).@count > 0)"
,@YES];
fr = [NSFetchRequest fetchRequestWithEntityName:@"Bob"];
fr.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:NO selector:@selector(compare:)]];
fr.predicate = predicate1;
NSFetchedResultsController *fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fr managedObjectContext:[[self appDelegate] managedObjectContext] sectionNameKeyPath:nil cacheName:nil];
fetchedResultsController.delegate = self;
I think that from a concept point of view your conditions are not really one query. You want to make two queries, one based on the outcome of the other one. This is not really what SQL queries, let alone object graph predicates, were conceived for.
Thus, I believe the simplest and most intuitive solution should be to have two versions of your fetched results controller.
You create the other version by changing the predicate dynamically. (I am using this pattern a lot e.g. for searching.) Because a subquery anyway results in more than one trip to the persistent store, you could just make one efficient fetch to evaluate the condition before setting the predicate for your fetched results controller.
After setting your predicate, remove it if necessary:
if ([context countForFetchRequest:request] == 0) {
request.predicate = nil;
}
// continue creating your FRC with the request
Note that countForFetchRequest
is very efficient and takes up no memory.
To update,
self.fetchedResultsController = nil;
[self.tableView reloadData]; // or collection view
If you need to take advantage of the FRC's cache feature, a solution with two separate FRCs following the above pattern would also be feasible.