Core Data predicate : unimplemented SQL generation for predicate - ios

Basically I got 3 entities in my data model : Brand, Model and Trim.
A brand has a one-to-many relationship with Model called "models". (one brand have multiple models but a model has only one brand)
A model has a many-to-many relationship with Trim called "trims". (a model can have multiple trims, and a trim can have multiple models)
Having an array of trims objects, I would like to get all the brands having a model which "contains" at least one trim contained inside this array.
So here is my predicate for the fetch request :
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"Brand"];
[NSPredicate predicateWithFormat:#"ANY models.trims IN %#", arrayOfTrims];
And here is the error I'm getting :
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'unimplemented SQL generation for predicate : (ANY models.trims IN {<Trim: 0x8e60340> (entity: Trim; id: 0x8e62f10 <x-coredata://BCD28560-AED5-4409-9A35-1925001773E6/Trim/p8>
I'm kinda new to Core Data and I have no idea what I'm doing wrong.
Hope someone can help,
Thanks a lot.

"ANY" in a Core Data predicate works only for a single to-many relationship.
Since your query involves two to-many relationships, you have to use a SUBQUERY:
[NSPredicate predicateWithFormat:#"SUBQUERY(models, $m, ANY $m.trims IN %#).#count > 0",
arrayOfTrims];

This exception is also raised if one of the predicates uses a column (i.e. field) name that does not exist. Totally misleading error message...

When you use a predicate in a CoreData operation, the predicate gets translated into SQL. That translation is not possible for all NSPredicate operations, you've hit one that isn't. My suggestion would be something along the lines of:
NSMutableArray* predicates = [NSMutableArray new];
for(NSString* trim in arrayOfTrims)
{
[predicates addObject:[NSPredicate predicateWithFormat:#"%# IN models.trims", trim]];
}
NSPredicate* predicate = [NSCompoundPredicate orPredicateWithSubpredicates:predicates];

The keyword IN can be used but you cannot apply ANY at the same time as that does not make sense when you turn it into SQL.
The predicate you are most likely looking for is:
[NSPredicate predicateWithFormat:#"models.trims IN %#", arrayOfTrims];
But that isn't going to work in this case either because you are going across a relationship. So what you need to do is reverse the whole thing:
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Model"];
[request setPredicate:[NSPredicate predicateWithFormat:#"trims in %#", arrayOfTrims]];
NSError *error = nil;
NSArray *modelArray = [moc executeFetchRequest:request error:&error];
if (!modelArray) {
NSLog(#"Error: %#\n%#", [error localizedDescription], [error userInfo]);
}
NSArray parentObjectArray = [modelArray valueForKey:#"${PARENT_RELATIONSHIP_NAME}"];
Basically you are fetching the child objects to satisfy your ANY and then using KVC to retrieve the parent objects that you care about.

In my case I got this error because I made a silly mistake and didn't have the argument in the right data type.
I was trying to do:
let firstPredicate = NSPredicate(format: "firstName BEGINSWITH[cd] %#", firstNameField.stringValue)
and forgot to put the ".stringValue."

Related

NSFetchRequest - Fetch entity from relationship

I have two entities: Alarm and Appointment, which have 1-to-1 relationship.
Now, I would like to retrieve the alarm for a given appointment using NSFetchRequest.
I tried:
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:ALARM_ENTITY_NAME];
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"forAppointment.objectID = %#", [appointment objectID]];
which throws:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'keypath forAppointment.objectID not found in entity <NSSQLEntity Alarm id=1>'
Does anyone know how I can achieve this?
Simply use
[NSPredicate predicateWithFormat:#"forAppointment = %#", appointment]
Using the objectID is only necessary if appointment is from a different managed object
context. In that case
[NSPredicate predicateWithFormat:#"forAppointment = %#", [appointment objectID]]
should work. (The right-hand side of == %# can be a managed object or its object ID.
You don't have to specify "objectID" in the predicate itself.)
If you have a 1-1 relationship between the two (and if you've implemented NSManagedObject subclasses for your entities), there's no need for a fetch. Just walk the keypath:
Alarm *alarm = appointment.alarm;
Or, if you're working with primitive NSManagedObject
NSManagedObject *alarm = [appointment valueForKeyPath:#"alarm"];

How to write a Core Data predicate for objects which are related to an object in a to-many relationship

EntityA has a to-many relationship to EntityB (and EntityB has a to-one relationship to EntityA). I have an array of EntityBs (or more accurately, I have an NSArray that contains instances of NSManagedObject which represent EntityB). I want to create an NSFetchRequest that will fetch all EntityAs that have a relationship to at least one of the EntityBs in the array. How do I write the predicate for this fetch request?
The following works, but I think it is sub-optimal; it's hard to grok and I'm sure there must be a better way of expressing this:
NSArray *entityBs = ...;
NSMutableArray *containsEntityBSubPredicates = [NSMutableArray new];
for (NSManagedObject *entityB in entityBs) {
[containsEntityBSubPredicates addObject:[NSPredicate predicateWithFormat:#"%# IN entityBs", entityB]];
}
NSPredicate *containsEntityBsPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:containsEntityBSubPredicates];
I've also tried this, but it doesn't work:
NSArray *entityBs = ...;
NSPredicate *containsEntityBsPredicate = [NSPredicate predicateWithFormat:#"ANY %# in entityBs", entityBs];
Am I missing a simpler solution?
You were almost there with your predicate, just switch the parameters:
[NSPredicate predicateWithFormat:#"ANY entityBs in %#", entityBArray];
Look at Apple's example code with IN here for further explanation.

NSPredicate 'The left hand side for an ALL or ANY operator must be either an NSArray or NSSet'

Not totally sure why this isn't working now, i thought it had been working previously. Does anyone see an issue with this FetchRequest construction?
- (NSArray *)entriesForDate:(NSDate *)date {
NSFetchRequest *request = [[NSFetchRequest alloc]initWithEntityName:#"Entry"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY addedOn.unique like %#", [T3Utility identifierForDate:date]];
request.predicate = predicate;
NSError *error = nil;
NSArray *matches = [self.database.managedObjectContext executeFetchRequest:request error:&error];
return matches;
}
Again, i'm 99% sure that this code has been working until recently, so im thinking perhaps there's something else going on in my code somewhere . . .but when i run it through the debugger this is where is hangs. Here is my error:
The left hand side for an ALL or ANY operator must be either an NSArray or NSSet
Any ideas?
thanks!
It seems addedOn is not a to-many relationship. Make sure you use the correct names of your attributes / relationships in the predicate. Also, note that you cannot use a to-one relationship in a "ANY" predicate.

NSPredicate to-many relationships

How can I find all the categories that have subcategories with more than 1 items?
I am using the following code, but I get "multiple to-many keys not allowed here".
Do I need to use a SUBQUERY?
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Categories"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY subcategories.items > 0"];
[request setPredicate:predicate];
Help! :)
Based on my experience, you should change the predicate format string in
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY subcategories.items > 0"];
from
#"ANY subcategories.items > 0"
into
#"subcategories.items.#count > 0"
Note that the ANY keyword needs to go as well, or you'll be querying the property the wrong way.
In your NSPredicate you should use subcategories.items.#count >0 instead.
Sorr, that would work only if categories-subcategories wer one - to one.
Really,what you should do, is delete all the SubCategories wich have no items, I think.
Than tou should just check if there are any subcategories and it would mean that they are not empty.
subcategories.#count >0
You can only use one to-many relationship in a SQL backend. This will do it, but you have to add the "back-link" into an ordered set (to preserve order if you use sorting - and uniqueness because you may get multiple subcategories pointing back to the same category). Not ideal, but not bad, considering the limitations.
// Search for all subcategories with item count > 0
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Subcategory"];
request.predicate = [NSPredicate predicateWithFormat:#"items.#count > 0"];
NSError *error = nil;
// They should have an inverse relationship to their category...
// We have to iterate and insert them in to a set, but there is no additional
// disk access.
NSMutableOrderedSet *results = [NSMutableOrderedSet orderedSet];
[[self.projectDatabase.managedObjectContext executeFetchRequest:request error:&error] enumerateObjectsUsingBlock:^(Subcategory *obj, NSUInteger idx, BOOL *stop) {
[results addObject:obj.category];
}];
EDIT
Unfortunately, I don't think this is possible with the SQL store. First, let me stress that I am not anywhere close to an SQL expert, and I usually try to design my CoreData stores so that these types of queries are not necessary. However, the following SUBQUERY works with an InMemory store, so it's a somewhat properly formatted subquery.
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"SUBQUERY(subcategories, $s, $s.items.#count > 0).#count > 0"];
However, that predicate gives an error when using an SQL store. The docs do say there are a fair number of limitations when using predicates with SQL stores, so I'm neither surprised, nor inclined to spend too much more time researching this.
Keypath containing KVC aggregate where there shouldn't be one; failed
to handle $s.items.#count

Core-Data NSPredicate with multiple to-many conditions

Helo guys,
I have the following core-data database:
(Unfortunately I cannot embed the image, because of the spam prevention!)
Data base model image: http://snapplr.com/eqx9
I am trying to create a fetchRequest, which fetches all FC objects whose fcCollection.fcSets.trainingSet property is an object in my code BUT are not in trainingSet.memorizationSession.mistakeFCs. I want to use it in NSFetchedResultsController.
I have tried so many different things and they all didnt work (Unfortunately I didnt save what I tried, so I cannot post).
But I have tried SUBQUERIES and so on. My last approach is the following:
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
NSEntityDescription *entity = [NSEntityDescription entityForName: kFCMistakeEntityName
inManagedObjectContext: self.managedObjectContext];
[fetchRequest setEntity: entity];
NSPredicate *subPredicate = [NSPredicate predicateWithFormat:
#"memorizationSession.trainingSet == %#", _trainingSet];
[fetchRequest setPredicate: subPredicate];
NSArray *mistakeFCs = [self.managedObjectContext executeFetchRequest:fetchRequest error: nil];
predicate = [NSPredicate predicateWithFormat:
#"ANY fcCollection.fcSets.trainingSet == %# && NOT mistakeFC in %#", _trainingSet, mistakeFCs];
Unfortunately I am getting this error:
2011-05-18 22:26:55.643 testApp[9464:207] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'unimplemented SQL generation for predicate : (mistakeFC IN {FCMistake: empty FC; correctFC: 0, FCMistake: 55; correctFC: 9, FCMistake: empty FC; correctFC: 3, FCMistake: empty FC; correctFC: 9, FCMistake: empty FC; correctFC: 4})'
This must be possible somehow! Thanks in advance for helping!
Greetings, Kim
From the Predicate Programming Guide:
The Core Data SQL store supports only
one to-many operation per query;
therefore in any predicate sent to the
SQL store, there may be only one
operator (and one instance of that
operator) from ALL, ANY, and IN.
so if you are using the SQL datastore you can only represent one one-to-many relationship in your predicates. If you change to some other persistence store it seems to imply that this particular restriction is lifted.

Resources