Searching CoreData relationships - objective-c

I'm about to pull my hair out trying to figure out why this isn't working.
I have two entities:
Quote
Customer
A Quote has a one-to-one relationship property with a Customer called simply 'customer'. The Customer has a CoreData objectID (obviously). I am trying to search through all the Quote's and return the one's that have a specific Customer associated with it based off the Customer objectID. Reading through all the tutorials I've managed to get this but I keep getting a crash:
+ (void)fetchQuotesForCustomerID:(NSManagedObjectID*)objectID results:(void(^)(NSError* error, NSArray *fetchedResults))completion {
NSManagedObjectContext *context = [[QuoteGenerator sharedGenerator] managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Quote"
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"customer.objectID == %#", objectID];
[fetchRequest setPredicate:predicate];
NSError *error;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (error) {
NSLog(#"fetch error = %#", [error localizedDescription]);
completion(error, nil);
} else {
NSLog(#"fetch count = %d", fetchedObjects.count);
completion(nil, fetchedObjects);
}
}
Output error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'keypath customer.objectID not found in entity <NSSQLEntity Quote id=13>'
Is the predicated setup wrong? Reading through the documentation is says that you can use dot syntax to access properties in the predicate.
Please help...

Turns out a lack of sleep and #Gary lead me to the right answer.
I should have had a to-many relationship from customer to Quote.
When comparing an entities NSManagedObjectID property you don't have to explicitly state it. So the following modification to the NSPredicate fixed my issue.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY
customer == %#", objectID];

You shouldn't need to do a fetch at all, core data should generate a quotes member on your customer object that will return an NSSet of all the related quote objects.

Related

Core Data NSFetchRequest within specific object using NSPredicate

I have a core data entity, "Entity 1" it has a one to many relationship lets call it "entityRelationship" to another entity "Entity 2".
I'd like to be able to perform a NSFetchRequest for use with a NSFetchResultsController to return the list of "Entity 2" objects for a specific "Entity 1" object.
I have the "Entity 1" stored out as it's own variable, but i can't seem to find the correct way to set up an NSPredicate to return the objects:
Here's my code:
NSFetchedResultsController *fetchedEvents;
NSFetchRequest *fetchRequest;
NSError *error = nil;
fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Entity2"];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"Entity2 IN self = %#",entity1Object]];
[fetchRequest setSortDescriptors:#[]];// no sort descriptors
fetchedEvents = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:theManagedObjectContext sectionNameKeyPath:nil cacheName:nil];
[fetchedEvents performFetch:&error];
if (error) {
NSLog(#"Unable to perform fetch.");
NSLog(#"%#, %#", error, error.localizedDescription);
}
return fetchedEvents;
This crashes with the following error:
** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unable to parse the format string "NSMDEvents IN self = %#"'
Am i doing something wrong? Or is this the incorrect way to go about returning entities with relationships?
Since you have entity1Object and the defined relationship, you can retrieve the Entity2 objects directly from there
NSSet *entity2Objects = [entity1Object valueForKey:#"entityRelationship"];
An extra fetch is not needed.
But if you really need the fetch define a reverse relationship and use a property with an unique value.
For example let's assume that entity1 are clubs and entity2 are their members and you want to get all members for a specific club use this predicate:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Member"];
[NSPredicate predicateWithFormat:#"club.name == %#", currentClub.name];
The literal club in the predicate is the reverse relationship object.
Or translated to your example
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Entity2"];
[NSPredicate predicateWithFormat:#"entity1.property == %#", entity1Object.property];
Trying the suggested code (thanks vadian) kept causing my app to crash with various errors regarding keys not existing etc, this turned out to be down to a relationship issue.
"Entity2" was inheriting from another entity (had its parent Entity field set in the Data Model Inspector)"Entity 0". However the relationship between "Entity1" was between itself and "Entity0" not "Entity2".
So after a rejig of the core data model "Entity2" had a relationship added (lets call it "EntityEvents") between itself and "Entity1". Now using the following code i was able to select the specific events from the current object:
NSFetchedResultsController *fetchedEvents;
NSFetchRequest *fetchRequest;
NSError *error = nil;
fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Entity2"];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"EntityEvents == %#",Entity1]];
[fetchRequest setSortDescriptors:#[]];// no sort descriptors
fetchedEvents = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:theManagedObjectContext sectionNameKeyPath:nil cacheName:nil];
[fetchedEvents performFetch:&error];
if (error) {
NSLog(#"Unable to perform fetch.");
NSLog(#"%#, %#", error, error.localizedDescription);
}
return fetchedEvents;

cannot bind array controller to a nested one to many relationship

I have 3 entities in my data model which are connected as follow:
User<-->>Performance<-->>Trials
Meaning that every user has several performances, in each he/she goes under several trials.
For each entity, I have one table view and one array controller object. For all, I have bound their Managed Object Context parameter to App Delegate.
Then I bound PerformanceArrayController to UserArrayController Content Set (Controller Key: selection and performances relationship). And the same for TrialArrayController: I bound it to PerformanceArrayController (on selection) and trials relationship.
I have no problem in binding single columns of User table view and performance table view to entities attributes. but when I want to do the same for trial table view, first I don't get autocompletion and second when I write the name of the attributes manually, I get a gray exclamation mark. and only the first trial is saved this way but not the rest of them.
Here is my function for inserting into Trial:
- (void) insertIntoTrial: (NSString *) result
{
NSManagedObjectContext *context = [self managedObjectContext];
NSError *error;
Trial *trial = [NSEntityDescription insertNewObjectForEntityForName:#"Trial" inManagedObjectContext:context];
trial.result = result;
trial.time = [NSNumber numberWithDouble:[cueTimestamp timeElapsedInSeconds]];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Performance" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"user.fName==%#", userName]];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (Performance *performance in fetchedObjects) {
[performance addTrialsObject:trial];
trial.performance = performance;
}
if (![context save:&error]) {
NSLog(#"couldn't save Trial info: %#", [error localizedDescription]);
}
}
Thanks in advance,

Core Data: NSPredicate with managed objects

I am creating an iOS 5 app using Core Data.
I have two entities Item and Category there is a many-to-many relationship between these two entities, i.e. an Item belongs to many categories and a Category has many items.
What I am trying to do is, given a category, I want to know all the categories the items of that category is associated with. For that I am trying to execute the following request:
NSEntityDescription *catEntity = [NSEntityDescription entityForName:#"Category" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = catEntity;
request.predicate = [NSPredicate predicateWithFormat:#"items IN %#",[category.items allObjects]];
request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES]];
NSError *error = nil;
NSArray *categories = [context executeFetchRequest:request error:&error];
But when executing this I get the following error:
'NSInvalidArgumentException', reason: 'unimplemented SQL generation for predicate : (items IN {<Item: 0x89b9c50> (entity: Item; id: 0x89be960 <x-coredata://A5DE0832-95F1-460C-9F40-202A10E16BDC/Item/p17> ; data: {
attributes = (
);
categories = (
"0x89be540 <x-coredata://A5DE0832-95F1-460C-9F40-202A10E16BDC/Category/p28>",
"0x89be680 <x-coredata://A5DE0832-95F1-460C-9F40-202A10E16BDC/Category/p29>"
);
name = "Some Item";
})})'
What am I doing wrong here?
Try this, I'm not sure but it might work:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY items IN %#", [category.items allObjects]];

Check if a COMPLEX object exists in Core Data table?

I have the following tables in Core Data:
Cinema (movies)
Movie (title, times)
DateTime (startTime, endTime)
Basically there are several cinemas, each cinema can have several movies, also each movie can be shown in different cinemas, lastly each movie can have several show times(DateTime)
when I want to insert a movie into the database, I know its title and show time(startDate, endDate), I need to know if the movie is already in database, so what I do is:
- (BOOL)movieExistsInDB:(NSString *)title StartDate:(NSDate *)startDate EndDate:(NSDate *)endDate {
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
NSEntityDescription *entity =
[NSEntityDescription entityForName:#"Movie"
inManagedObjectContext:self.context];
[request setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"title LIKE %# AND timetable.startDate == %# AND timetable.endDate == %#",
title,
startDate,
endDate];
[request setPredicate:predicate];
NSError *error = nil;
NSUInteger count = [context countForFetchRequest:request error:&error];
if (!error) {
return count > 0;
}
return NO;
}
it does not seem to work, so what is the problem?
Thanks!
edit:
error message:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'to-many key not allowed here'
I think one of your attributes you are looking for is a one-to-many relation. So core data see's a NSSet instead of an NSObject. If you want to query through NSSet's you need "ANY" in your Fetchrequest. For instance:
...AND ANY timetable.startDate ...
If you want to know whether or not there's a Movie entity for a given title, you don't need the dates in your predicate.

Core Data & Generating Model Entities

Standard newbie question. I've created a data model for an iOS application. I am able to create, update and delete entities within the model from various views by using the NSEntityDescription object.
Say if I had a mutable array of objects returned from a fetch request. How can I loop through each one when I do not have a generated object definition from the entity model? By generated object definition I mean, a header and body class definition of the entity described in the data model package.
All CoreData entities derive from NSManagedObject and all the database data from those can be accessed via key value encoding. The minimum you need to know can be gained from the Model. You don't necessarily require the headers.
For example an entity PersonEntity which has a relationship to NameEntity with attribute firstname
NSArray *results = [managedObjectContext queryEntityForName:#"PersonEntity" predicateFormat:nil argumentArray:nil];
for(NSManagedObject *object in results)
{
NSString *name = [object valueForKeyPath:#"nameobject.firstname";
[self doSomething:name];
}
queryEntityForName is my own category. You might find it useful.
#implementation NSManagedObjectContext(VMQueryAdditions)
-(NSArray *)queryEntityForName:(NSString *)name predicateFormat:(NSString *)pstring argumentArray:(NSArray *)arr
{
NSEntityDescription *entity = [NSEntityDescription entityForName:name inManagedObjectContext:self];
NSFetchRequest *fetch = [[[NSFetchRequest alloc] init] autorelease];
[fetch setEntity:entity];
NSPredicate *pred;
if(pstring)
{
if(arr) pred = [NSPredicate predicateWithFormat:pstring argumentArray:arr];
else pred = [NSPredicate predicateWithFormat:pstring];
[fetch setPredicate:pred];
}
NSError *error = nil;
NSArray *results = [self executeFetchRequest:fetch error:&error];
if (error) {
NSLog(#"MOC Fetch - Unresolved error %#, %#", error, [error userInfo]);
return [NSArray array];
}
return results;
}
#end