dman
dman

Reputation: 146

Complex NSPredicate traversing multiple Entities and relationship types

I'm having a tough time solving this predicate issue since the database structure is a bit complex. I have the following database structure, or at least what is of concern for this question:

PUBLISHER<< --- >>PUBLICIST<<--BOOK<<--->AUTHOR<<-->>AGENTS

I tried the following predicate that I have used when I traversed relationships in the past, but not to this degree. I should mention that I have an NSArray with agent names that I want to query against the database to determine a list of publishing houses the agent works with:

NSArray *agentNames = [NSArray arrayWithObjects:Dan, Hunter, Sloan, Jackson];

NSFetchRequest *request = [[NSFetchRequest alloc] init];

request.entity = [NSEntityDescription entityForName:@"Publisher" inManagedObjectContext:context];
request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor
             sortDescriptorWithKey:@"publisherName" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)] ];
request.predicate = [NSPredicate predicateWithFormat:@"ANY pubHouse.publicist.assignedBook.authorRep.agentName IN %@", [agentNames valueForKey:@"agentName"]];
request.fetchBatchSize = 20;

    NSFetchedResultsController *frc = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];

When I run the previous predicate I get the following warning:

 *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'unimplemented SQL generation for predicate : ANY pubHouse.publicist.assignedBook.authorRep.agentName IN

I believe the predicate breaks when I travel the Book Entity relationship to the Author Entity and then onto the Agent Entity. At this point suggestions would help. Thanks

Upvotes: 0

Views: 1675

Answers (3)

TechZen
TechZen

Reputation: 64428

Your problem is that the predicate parser has no clue what set ANY should be applied to.

With this data model:

PUBLISHER<< --- >>PUBLICIST<<-->BOOK<<--->AUTHOR<<-->>AGENTS

… your keypath:

pubHouse.publicist.assignedBook.authorRep.agentName

… in terms of objects and set of the relationship looks something like:

object.set.object.object.set

So, that is two sets that the ANY could apply to.

You could try to build a subquery to handle the predicate but if you have to transverse that many relationships, your fetch will involve a big chunk of your data and will be very, very slow (assuming you get it work in the first place.)

Usually when you end up with a convoluted predicate like this it indicates that you are approaching the problem from the wrong end. In this case, it would be easier to start with a simple predicate like:

NSArray *agentNames = [NSArray arrayWithObjects:Dan, Hunter, Sloan, Jackson];
request.entity = [NSEntityDescription entityForName:@"Agent" inManagedObjectContext:context];
request.predicate = [NSPredicate predicateWithFormat:@"agentName IN %@", agentNames];

Then you would walk the relationship keypath of:

authors.books.publicist.publishers 

… to find all the related publishers.

I think that you will have trouble no matter what you do because having more than one to-many-to-many relationship e.g.

PUBLISHER<<--->>PUBLICIST

… increases the complexity of predicates and relationship walks exponentially. Usually, in such a case, you may need an additional entity to more thoroughly model one of the relationships. That usually reduces the complexity of the data model itself which simplifies fetches and walks.

Upvotes: 6

Mundi
Mundi

Reputation: 80273

Perhaps NSArray has a problem with valueForKey:. I have seen a solution that uses NSCompoundPredicate adding the array items in a loop.

BTW, in your chain of multiple relationships, aren't you missing your authors?

Upvotes: 0

Mundi
Mundi

Reputation: 80273

Just wrap the IN clause of your predicate string in parentheses:

@"ANY (pubHouse.publicist.assignedBook.authorRep.agentName IN %@)"

Would be glad to know if it works.

Upvotes: -1

Related Questions