Quick Question About NSFetchRequest and Relationship - iphone

In my Core Data model, I have an entity Session and Exercise.
Session has a to many relationship to Exercise (there is a one-one inverse relationship as well).
In my fetch, I am trying to find all Session object that are related to the current Exercise.
I am using the following code which isn't working.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat: #"exercise = %#", exercise.name]];
NSEntityDescription *sessionEntity = [NSEntityDescription entityForName:#"Session" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:sessionEntity];
NSError *error = nil;
NSArray *results = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
NSLog(#"Fetch error: %#", error);
self.sessionArray = results;
Here is my data model:

First, from the screenshot it seems that your relationship attribute of the Session entity is called exercises not exercise.
Also, it seems to me that it would work if you searched not the Session entity but the Exercise entity and then iterate through the resulting array to extract the sessions.
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:"name == %#", exerciseName]];
NSEntityDescription *exerciseEntity = [NSEntityDescription entityForName:#"Exercise" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:exerciseEntity];
...
NSArray *results = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
...
NSMutableArray *mutableSessionArray = [NSMutableArray array];
for (Exercise *ex in results) {
Session *session = [ex exercises];
if (session) [mutableSessionArray addObject:session];
}
self.sessionArray = [NSArray arrayWithArray:mutableSessionArray];
The relationship of the Exercise entity called exercises could be called session for clarity. I would recommend renaming it.
BTW, you can also write = instead of ==, they are equivalent, as far as I know. In the Predicate Programming Guide section on Predicate Format String Syntax it says:
=, ==
The left-hand expression is equal to the right-hand expression.

In your predicate you use =, == is the comparison operator. I think that's where you're going wrong.
Source: http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSPredicate_Class/Reference/NSPredicate.html

Related

NSFetchRequest for all children of a parent

How do I fetch all child entities of a parent?
I have a table populated by a parent entity in Core Data. When the user touches a cell I intend to show another table with all children of that parent.
How does the NSFetchRequest look like for this please?
Edit:
model is like this:
student>>dates [one to many, one student have many days]
So I want all dates for any given student (selected by touching in student table cell for that student), then populate dates table with dates for that student.
Thanks!
Assuming that the entity and the class names are Student and Date, and the reverse relationship for Date->Student is called student,
Student *aStudent = ...;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity: [NSEntityDescription entityForName: #"Date" inManagedObjectContext: [aStudent managedObjectContext]]];
[fetchRequest setPredicate: [NSPredicate predicateWithFormat: #"student == %#", aStudent]];
You don't need a separate fetch request for this. All of the objects from the to-many relationship (don't call them child entities, that is misleading and incorrect) are available by accessing the relationship from the student object - something like student.dates. This gives you an NSSet, you can sort it and turn it to an array if you need to.
Within your first table delegate, when you touch a specific cell, I'll inject the specific parent property to the second table controller. For example:
SecondController secondController = ... // alloc-init
secondController.studentToGrab = ...
where SecondController declaration has a studentToGrab property like the following:
#property (nonatomic, retain) Student* studentToGrab; // use strong with ARC, if non-ARC remember to release it
and in definition synthesize it.
Then in your second controller, within viewDidLoad method you could do:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"YourNameEntityForDate" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20];
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"student == %#", studentToGrab];
[fetchRequest setPredicate:predicate];
// you can also use a sortdescriptors to order dates...
NSError *error = nil;
NSArray *resultArray = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (error != nil) {
NSLog(#"Error: %#", [error localizedDescription]);
abort();
}
// use resultArray to populate something...
A remark when you deal with table you could also use NSFetchedResultController class. It has advantages when used for displaying data in tables.
If you have custom classes, you could traverse the generated relationship (return [student dates]). That will get you an unordered NSSet on iOS4, or, you can do it with a fetch request (note I use ARC so no releases/autoreleases here):
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Date"
inManagedObjectContext:moc];
[fetchRequest setEntity:entity];
NSMutableArray *predicates = [NSMutableArray arrayWithCapacity:3];
[predicates addObject:[NSPredicate predicateWithFormat:#"student == %#", aStudent]];
// You might add other predicates
[fetchRequest setPredicate:[NSCompoundPredicate andPredicateWithSubpredicates:predicates]];
// and if you want sorted results (why not, get the database to do it for you)
// sort by date to the top
NSArray *sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"dateAdded" ascending:NO]];
}
[fetchRequest setSortDescriptors:sortDescriptors];
NSError *error = nil;
NSArray *sorted = [moc executeFetchRequest:fetchRequest error:&error];
if (error) {
// Handle the error, do something useful
}
return sorted;

Help me to get the result based on condition

I have created a Users class based on NSManagedObject with following attributes (id,name,age etc).
I am using the core data model but i am not sure how to do the follwing...
Now i would like to know How can i get the user detail based on user id.
example: select * from users where id = 1
please help me out.
You should use NSPredicate class for executing SQL commands. The code:
NSManagedObjectContext *context = self.managedObjectContext; // specify your MOC object
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"users" inManagedObjectContext:context]; // specify your entity (table)
NSPredicate *predicate = [NSPredicate predicatewithFormat:#"id == %d",yourID]; // specify your condition (predicate)
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:predicate];
NSError *error = nil;
NSArray *array = [context executeFetchRequest:fetchRequest error:&error]; // execute
[entity release];
[predicate release];
[fetchRequest release];
if (array == nil) {
// Error: no objects returned
} else {
// Success: do whatever you want
}
Step 1: Alloc/init NSFetchRequest
You need to alloc/init a NSFetchRequest object if you want to execute queries.
Step 2: Select entity
If you want to specify select * from users ..., you should use NSEntityDescription:
NSEntityDescription *entity = [NSEntityDescription entityForName:#"users" inManagedObjectContext:context];
At the end you need to 'attach' your entity description to your NSFetchRequest object via:
[fetchRequest setEntity:entity];
Step 3: Condition
If you want to have a condition (e.g. ... where id = 1), you have to implement NSPredicate.
NSPredicate *predicate = [NSPredicate predicatewithFormat:#"id == %d",yourID];
yourID must be a number (e.g. 1, 2, 7 or 46).
And, again:
[fetchRequest setPredicate:predicate];
Step 4: Let's execute it!
NSArray *array = [context executeFetchRequest:fetchRequest error:&error];
All the records that meet the conditions will be returned as array of NSManagedObjects.
Step 5: Release objects
[entity release];
[predicate release];
[fetchRequest release];
Step 6: Do something
If there are no objects that meet the conditions, array object will be nil. You can check it and deal with the error via:
if (array == nil)
Check out Core Data Programming Guide for more info. :)

Retrieve single object from NSFetchRequest

I want to get a single object from my Core Data datastore, here is the code I have been using but it returns an array of objects. There must be a simpler and better way:
NSFetchRequest *request= [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Manufacturer" inManagedObjectContext:managedObjectContext];
NSPredicate *predicate =[NSPredicate predicateWithFormat:#"ManufacturerID==%#",[[mitems objectAtIndex:i] objectForKey:#"ManufacturerID"]];
[request setEntity:entity];
[request setPredicate:predicate];
NSError *error;
NSArray *entities = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
If your predicate always fetches more than one result:
refine the predicate - don't forget you can build predicates with logic like AND/OR, simple equality is easy but may not be selective enough in your case.
just select the result you want from the array, it's no big deal - although if this is possible it should also be possible to refine the predicate...
consider reorganizing your data model so that you can refine the predicate to get just one item back.
The fetch always returns an array, that is its definition. However it can be an array of one object.
It will always return an array but you can make it cleaner:
NSFetchRequest *request= [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Manufacturer" inManagedObjectContext:managedObjectContext];
NSPredicate *predicate =[NSPredicate predicateWithFormat:#"ManufacturerID==%#",[[mitems objectAtIndex:i] objectForKey:#"ManufacturerID"]];
[request setEntity:entity];
[request setPredicate:predicate];
NSError *error;
//Making a mutable copy here makes no sense. There is never a reason to make this mutable
//NSArray *entities = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
id manufacturer = [[managedObjectContext executeFetchRequest:request error:&error] lastObject];
request = nil;
NSAssert1(error && !manufacturer, #"Error fetching object: %#\n%#", [error localizedDescription], [error userInfo]);
-lastObject will return the last item in the array or nil if the array is empty. This keeps your code a little cleaner when you know there is going to be one object in the array or if you don't care what object you pull out of the array.
BTW, your property names should start with a lower case letter. I am surprised the compiler did not warn you about that.
Try fetchRequest.fetchLimit = 1. You still get an array, but with a maximum of 1 object.

How to determine number of objects in one-to-many relationship in CoreData

So, I've got a one-to-many relationship of Companies to Employees in CoreData (using a SQLite backend on iOS, if that's relevant). I want to create a predicate that only returns Companies that have 0 Employees associated with them. I could do it by getting all the Companies and iterating over them, but that would be (I assume) much slower.
Any ideas?
Thanks,
-Aaron
After trying #falconcreek's answer and getting an error (described in my comment on his answer), I did some googling and determined that the answer was
NSPredicate *noEmployeesPredicate = [NSPredicate predicateWithFormat:#"employees.#count == 0"];
Now everything works über efficiently. Thanks!
Assuming your Company -> Employee relationship is named "employees"
NSManagedObjectContext *moc = [self managedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Company" inManagedObjectContext:moc];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entityDescription];
// the following doesn't work
// NSPredicate *noEmployeesPredicate = [NSPredicate predicateWithFormat:#"employees = nil OR employees[SIZE] = 0"];
// use #count instead
NSPredicate *noEmployeesPredicate = [NSPredicate predicateWithFormat:#"employees = nil OR employees.#count == 0"];
[request setPredicate:predicate];
NSError *error = nil;
NSArray *array = [moc executeFetchRequest:request error:&error];
if (error)
{
// Deal with error...
}

How to Determine if a Table Contains Any Records with Core Data?

I haven't seen any other questions quite like this on here, but I'm hoping someone has some insight. I'm just starting to learn Core Data.
Basically, I have two methods and I want to choose which one to call with an if/else statement based on whether or not the "Contacts" table contains any records. Is there a way using core data to check if there are any records in a table?
The best way I've found so far is to set the fetchLimit to 1 and then check to see if anything returns.
[request setFetchLimit:1];
But I keep thinking there has to be a better/easier way. Anyone know or have a good reference I can look at?
Thanks a ton!
Yes, definitely there is a better method. Setup a fetch request as usual, but, instead of actually executing it, simply ask for the number of objects it would have returned if it had been passed to executeFetchRequest:error:
This can be done using
- (NSUInteger)countForFetchRequest:(NSFetchRequest *)request error:(NSError **)error;
Something like this:
- (int) numberOfContacts{
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSManagedObjectContext *managedObjectContext = yourManagedObjectContext;
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Contacts" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
NSError *error = nil;
NSUInteger count = [managedObjectContext countForFetchRequest:request error:&error];
[request release];
if (!error){
return count;
}
else
return -1;
}
It's not necessarily any better or easier, but you can look for a specific record and then create it if it doesn't exist like this:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Contact"
inManagedObjectContext:[self managedObjectContext]];
[fetchRequest setEntity:entity];
NSError *error;
// Filter based on a predicate
[fetchRequest setPredicate:
[NSPredicate predicateWithFormat:#"identifier == %#", #"1"]];
NSManagedObject *contact = [[managedObjectContext
executeFetchRequest:fetchRequest error:&error] lastObject];
// If the contact was not found
if (!contact)
{
// Create the contact
contact = [NSEntityDescription insertNewObjectForEntityForName:#"Contact"
inManagedObjectContext:managedObjectContext];
[contact setValue:[NSNumber numberWithInt:1] forKey:#"identifier"];
[managedObjectContext save:nil];
}
Marcus Zarra posted some code that demonstrates this in a feed reader app. Marcus is the Core Data master.