NSFetchRequest - Fetch entity from relationship - ios

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"];

Related

Core Data: How to fetch Entities based on an attribute of a related entity

This is what the pertinent part of my object graph looks like:
[Anime] <->> [AnimeName #string #type]
So an Anime object has many AnimeName objects that contain a string and the type of name they are. Now to implement a "Search by Name" feature, I would need a predicate that matches all Anime entities where any name contains the search string. What I have tried so far is this:
NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName:#"Anime"];
[fetch setPredicate:[NSPredicate predicateWithFormat:#"names.string == %#", searchString]];
on an NSFetchRequest of Anime entities, this however gives the following error:
"NSInvalidArgumentException", "to-many key not allowed here"
Which makes perfect sense to me, and I could simply work around that by using this:
NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName:#"AnimeName"];
[fetch setPredicate:[NSPredicate predicateWithFormat:#"string == %#", searchString]];
and then getting the relationship to the Anime entity from each returned object, but then how do I plug that into an NSFetchedResultsController for my UITableView?
Please help if anyone knows a solution.
For to-many relationships, use "ANY":
NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName:#"Anime"];
[fetch setPredicate:[NSPredicate predicateWithFormat:#"ANY names.string == %#", searchString]];

Core Data predicate : unimplemented SQL generation for predicate

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."

Fetch Data According To Specific Entity

I am using magical record to fetch data from core data, using the code:
- (NSFetchedResultsController *)fetchedResultsController {
if (!_fetchedResultsController ) {
_fetchedResultsController = [TagItem MR_fetchAllGroupedBy:nil withPredicate:nil sortedBy:#"name" ascending:YES delegate:self];
}
return _fetchedResultsController;
}
The problem is, it is populating a table view controller that is pushed to by another table view controller, so basically a user selects an option which takes the person to a list of data, but the data within the option is specific to that option. So what I want to do is fetch only the data that is assigned to that option, rather than simply displaying all the data available. My method above only allows me to obtain all the data, rather than selecting specific data according to the option. The data within the option is given the objectId of the option itself as an entity and thus provides a relationship between the two.
So my question is, how do I go about fetching data according to a specific entity, using magical record?
UPDATE
I have tried setting the predicate to a specific value just to test if it works, thinking it would work the same as just working with core data alone, but I just get an error.
- (NSFetchedResultsController *)fetchedResultsController {
if (!_fetchedResultsController ) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name = test"];
_fetchedResultsController = [TagItem MR_fetchAllGroupedBy:nil withPredicate:predicate sortedBy:#"name" ascending:YES delegate:self];
}
return _fetchedResultsController;
}
The error is as below, even though I know for certain there is a data item with the name test stored, nonetheless it doesn't seem to work.
'NSInvalidArgumentException', reason: 'Unable to generate SQL for predicate (name == test) (problem on RHS)'
That's what the withPredicate parameter is for:
NSPredicate *predicate = ... // predicate that filters the items to display
_fetchedResultsController = [TagItem MR_fetchAllGroupedBy:nil withPredicate:predicate ...];
For example, if TagItem has a to-one relationship person to Person, then
Person *person = ...; // person selected in the first view controller
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"person = %#", person];
_fetchedResultsController = [TagItem MR_fetchAllGroupedBy:nil withPredicate:predicate ...];
would fetch all items related to that person.

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.

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