How to do Core Data queries through a relationship? - objective-c

I'm messing around with Core Data, and I am sure I am missing something obvious, because I cannot find an example that at all resembles what I am trying to do.
Let's say I'm playing around with a DVD database. I have two entities. A Movie (title, year, rating, and a relationship to Actor) and Actor (name, sex, picture).
Getting all the Movies is easy. It's just:
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Winery"
inManagedObjectContext:self.managedObjectContext];
Getting all the Movies with "Kill" in the title is easy, I just add a NSPredicate:
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"name LIKE[c] "*\"Kill\"*""];
But Core Data seems to abstract out the id fields for the managed objects... so how to I query against an attribute that is an object (or: query against a relationship)?
In other words, assuming that I already have the Actor object I am concerned with ([Object id 1 - 'Chuck Norris'] for instance), what is the Predicate format for "Give me all movies starring [Object id 1 - 'Chuck Norris']"?

Assuming that there is a one-to-many inverse relationship between the Actor and the Movie entities, you can just get the entity for Chuck Norris the same way you would get any specific entity, and then access the array of Movie entities attached to the relationship on the Actor entity.
// Obviously you should do proper error checking here... but for this example
// we'll assume that everything actually exists in the database and returns
// exactly what we expect.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Actor" inManagedObjectContext:self.managedObjectContext];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name LIKE[c] 'Chuck Norris'"];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
[request setPredicate:predicate];
// You need to have imported the interface for your actor entity somewhere
// before here...
NSError *error = nil;
YourActorObject *chuck = (YourActorObject*) [[self.managedObjectContext executeFetchRequest:request error:&error] objectAtIndex:0];
// Now just get the set as defined on your actor entity...
NSSet *moviesWithChuck = chuck.movies;
As a note, this example obviously assumes 10.5 using properties, but you can do the same thing in 10.4 using accessor methods.

Or you can use another predicate:
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Actor" inManagedObjectContext:self.managedObjectContext];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name = %#",#"Chuck Norris"]
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
[request setPredicate:predicate];
YourActorObject *chuck = [[self.managedObjectContext executeFetchRequest:request error:nil] objectAtIndex:0];
[request release];
NSSet *moviesWithChuck = chuck.movies;

Related

Fetch inverse-relationship, rather than NSSet in Core Data

I'm new to Core Data.
In the Data Model I have the entities:
Song
Playlist
This is the relationship:
Every Song belongs to no, one or multiple Playlist's
The I've added the relationship to the model like this.
I also made the inverse To-Many Relationship.
Now my question.
The Playlist's now have a NSSet with all the Songs.
So I can access them via playlist.songs.
I can fetch any specific Song using a NSPredicate:
- (NSArray *)fetchWithEntity:(NSString *)entity
predicate:(NSPredicate *)predicate
sortDescriptors:(NSArray *)sortDescriptors
error:(NSError **)error {
NSManagedObjectContext *moc = [self managedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription
entityForName:entity
inManagedObjectContext:moc];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
[request setPredicate:predicate];
[request setSortDescriptors:sortDescriptors];
NSArray *fetchResult = [moc executeFetchRequest:request error:error];
return fetchResult;
}
However, I'd like to be possible to filter the Songs of a specific playlist too, rather than having to filter the NSSet with an NSPredicate, because it's simply faster.
How can I do this?
The get songs of a specific playlist, just add "playlist = %#" to the predicate used for fetching Song objects, e.g.
[NSPredicate predicateWithFormat:#"playlist = %# AND title CONTAINS[cd] %#",
thePlaylist, songTitle]
Or, if the playlist is given by a title:
[NSPredicate predicateWithFormat:#"playlist.title = %# AND title CONTAINS[cd] %#",
playlistTitle, songTitle]
EDIT If there is a many-to-many relationship between Songs and Playlist, you can define playlists as to-many relationship from Song to Playlist, as inverse relationship to songs. Then the following predicate should work:
[NSPredicate predicateWithFormat:#"ANY playlist = %# AND title CONTAINS[cd] %#",
thePlaylist, songTitle]

NSPredicate to retrieve all objects whose attribute in an 1:n relationship is not NIL

I would like to build the NSPredicate for a Core Data query which should retrieve all IBEstPeriod managed objects whose 1:n relationship estType.consHistory.consType <> NIL *.
Unfortunately I have not found any clue on how such a NSPredicate should look like. Do you have any idea or suggestion?
Thank you!
Use "ANY" for to-many-relationships in predicates:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:#"IBEstPeriod" inManagedObjectContext:context]];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"ANY estType.consHistory.consType != nil"]];
NSArray *fetchResult = [managedObjectContext executeFetchRequest:fetchRequest error:nil];

Core Data- NSPredicate and to-many relationship

I have a one to many relationship between Games and Players. There is a relationship on Games to Players called players.
Games <-->> Players
The Games entity has a relationship called players.
Players has an attributed named 'order'
I am unable to use a predicate to query on the relationship. Here is the code I am using:
-(NSArray *)returnPlayerLastAtBat: (int)rosterNo
{
NSError *error;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Games" inManagedObjectContext:managedObjectContext];
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"(finished = '%d') and (players.order = '%d')",0,rosterNo];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:predicate];
NSArray* result = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];
return result;
}
Not sure what I am doing wrong- I am able to work with predicated throughout my project- but this one has been stuck.
Take a look at this SO question. And as the mvds mentionned, remove the quotes around the %d.

Core Data Programmatically Adding a to-many Relationship

I have an entity Item, and an entity Type (that has an attribute "Name") in a to-many relationship with Item. (Ie: Item: Brown Table, related to Type with Name "Coffee Table").
I've programmatically added new Items fine, using, for example:
[newItem setValue:([nameTextField stringValue]) forKey:#"Name"];
[newItem setValue:(costNumber) forKey:#"Cost"];
[newItem setValue:(priceNumber) forKey:#"Price"];
I've been searching for hours but can't find something that works for me adding a relationship to the new item. I'm using a NSPopUpButton to choose the Type of the item, and have tried methods like selectedItem, selectedTag, and selectedCell. I'm trying to get values from my "typeArray", which is filled as follows:
NSFetchRequest *fetchRequest2 = [[NSFetchRequest alloc] init];
NSEntityDescription *entity2 = [NSEntityDescription entityForName:#"Type"
inManagedObjectContext:managedObjectContext];
[fetchRequest2 setEntity:entity2];
NSError *error = nil;
typeArray = [managedObjectContext executeFetchRequest:fetchRequest2 error:&error];
if (typeArray == nil) {
NSLog(#"ERROR");
}
[fetchRequest2 release];
I'm not sure if the following is along the right lines:
NSManagedObject *selectedType = [typeArray objectAtIndex:[typePopUpButton selectedTag]];
But then I have no option for selectedType to add something like "addObject"..
Any help appreciated, thank you.
This is what I ended up using:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Type"
inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSError *error = nil;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"Name like %#", [typePopUpButton titleOfSelectedItem]];
[fetchRequest setPredicate:predicate];
NSArray *typeSet = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (typeSet == nil) {
NSLog(#"ERROR");
}
[fetchRequest release];
NSManagedObject *typeObject = [typeSet objectAtIndex:0];
[typeObject addItemsObject:newItem];
Basically, the object needs to be fetched so that a relationship can be made between the two items, and the predicate is based on the typePopUpButton's titleOfSelectedItem method.
I ensure to only select one object with the method [objectAtIndex:0].
It does bring up a warning though: NSManagedObject may not respond to 'addItemsObject'.
However this does work for me.

Fetch Relationship Objects

CoreData beginner
I have a simple problem with CoreData. My model has two entities, now called A and B. Entity A has a to many relationship of B entities, which has a inverse relationship to entity A.
I'm retrieving entities A with this code:
NSManagedObjectContext *context = [self managedObjectContext];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"A"
inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"name"
ascending:YES];
[request setSortDescriptors:[NSArray arrayWithObject:descriptor]];
NSError *error = nil;
NSArray *items = [context executeFetchRequest:request error:&error];
if (error) /* ... */;
for (id item in items)
{
/* ... */
}
[request release];
[descriptor release];
Now I'd like to retrieve, inside that loop, an array of all the objects B pointed by the relationship of A. How can I achieve this? Should I create another fetch request or there is a more practical way?
I've searched StackOverflow and found similar questions, but too vague sometimes.
NSFetchRequest has an instance method on it called -setRelationshipKeyPathsForPrefetching:.
This method takes an array of key names that will be used to prefetch any objects defined in relationships with those key paths. Consider your example, updated with the new code:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
NSString *relationshipKeyPath = #"bObjects"; // Set this to the name of the relationship on "A" that points to the "B" objects;
NSArray *keyPaths = [NSArray arrayWithObject:relationshipKeyPath];
[request setRelationshipKeyPathsForPrefetching:keyPaths];
Now once you complete your fetch request, all of those relationship objects should be faulted in and ready to go.