ios NSFetchRequest for two related tables - ios

my program has a sqlite database with two related tables. One called "Rank" and other one called "Requirement"
I want to fetch all rows from the "Requirement" table that has a relationship with the specific row in a "Rank" table. Following is my code, it grabs the whole table, but I get the specified rows only according to the above mentioned rule.
-(NSArray *) getAllRequirementsForTheRank:(Rank *) rank
{
NSError *error;
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init]autorelease];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Requirement" inManagedObjectContext:self.context];
[fetchRequest setEntity:entity];
NSPredicate *searchType = [NSPredicate predicateWithFormat:#"Rank = %#", rank];
[fetchRequest setPredicate:searchType];
NSArray *scoutRequirementArray = [self.context executeFetchRequest:fetchRequest error:&error];
for (Requirement *r in scoutRequirementArray)
{
NSLog(#"Requirementttt : %# :", r.requirementName);
}
return scoutRequirementArray;
}

If you have the relationship modelled in core data, just get the linked objects from the relationship property. You don't need another fetch request. rank.requirements will give you an NSSet of everything you need. (I'm assuming names for your object and properties here).

Related

Using NSPredicate and Joins

I have 2 tables: Users and Comments like so:
Users
user_id (PK)
name
Comments
comment_id (PK)
user_id
comment
I want to get a list of all users and their comments.
How is this done using NSPredicate?
Thanks.
As far as I understood your question, you are using core-data.
First of all, you have to make a small change in your managed object model.
Entity
Users
Attributes
user_id
name
Entity
Comments
Attributes
comment_id
comment
Relationships
Relationship - - - Desination - - - Inverse
user - - - Users - - - NO Inverse
Now , whenever you enter the data for Comments object, associate a user relationship with it as
comment.user=theUser
where theUser is a 'Users' object which you can get from 'Users' table by comparing user_id, for that you can use a simple method as by calling the following method for the userID that you got from comment data
theUser= [self isUserAlreadyExistsWithUserID:userID];
where the method definition could be like in Users NSManagedObject subclass
+(NSManagedObject*)isUserAlreadyExistsWithUserID uniqueValue: (id)uniqueValue
{
NSManagedObjectContext* context=[self managedObjectContext];
NSError*error=nil;
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Users" inManagedObjectContext:context];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"user_id = %#",uniqueValue];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
fetchRequest.entity = entity;
[fetchRequest setPredicate:predicate];
NSArray *objects = [context executeFetchRequest:fetchRequest error:&error];
if(objects && objects.count>0)
{
return [objects objectAtIndex:0];
}
else
{
return nil;
}
}
Now, to fetch comments for particular user in comments NSManagedObject subClass:
+(NSFetchedResultsController*)getCommentsForUser:(Users*)user
{
NSString* cacheName=#"Root";
[NSFetchedResultsController deleteCacheWithName:cacheName];
NSManagedObjectContext* context=[self managedObjectContext];
NSEntityDescription *entity = [NSEntityDescription entityForName:NSStringFromClass([self class]) inManagedObjectContext:context];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:entity];
NSPredicate* predicate=[NSPredicate predicateWithFormat:#"user=%#",user];
fetchRequest setPredicate:predicate];
NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:context sectionNameKeyPath:nil
cacheName:cacheName];
return theFetchedResultsController;
}
Hope this helps.
I think, you need this query:
SELECT Users.*,Comments.comment_id,Comments.comment FROM Users LEFT JOIN Comments ON Users.user_id = Comments.user_id;
This will return list of all users along with their comments. You can modify it, as per your specific requirements.
Are you using CoreData? if yes then use try the following code:
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Users" inManagedObjectContext:self.managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
[request setRelationshipKeyPathsForPrefetching:[NSArray arrayWithObjects:#"Comments",nil]];
[request setIncludesSubentities:YES];
NSArray* returnArray = [self.managedObjectContext executeFetchRequest:request error:&error];
if([returnArray count] > 0)
{
Users* user = [returnArray objectAtIndex:0];
NSLog(#"%# %# %#", user.user_id, user.name, user.userComments.comment);
}
Also, CoreData uses relationships instead of foreignKeys. Therefore, to get this working you need to setup an inverse relationship userComments (many to one) from Comments to Users.
It is better not to use NSPredicate when making a query. You can simply write a JOIN query. As Apple's documentation also states that the NSPredicate class is used to define logical conditions used to constrain a search either for a fetch or for in-memory filtering. Also it will be easier to use Key-Value Coding. Here is a good explaination to how it can be used. Lets assume you have NSSet of Comments. You can do something like :
[Comments valueForKeyPath:#"users.user_id"];
But, if you really want to use NSPredicate following is the possible way:
NSFetchRequest *fetchReq = [[NSFetchRequest alloc] initWithEntityName:#"Users"];
NSError *error;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(user_id == %#)",Comments.user_id];
[fetchReq setPredicate:predicate];
NSArray *result = [self.managedObjectContext executeFetchRequest:fetchReq error:&error];

Trying to retrieve an Entity using an attribute of the Entity's relationship object as a search criteria in Core Data

I am using Core Data in my application where I am trying to retrieve entities whose relationship objects attribute match my criteria. Unfortunately I am stuck here, because I am passing the id for the relationship object, but I am getting an error saying that the criteria I am passing is not being recognized as an attribute of the entity I am querying.
This is true, because my criteria is actually an attribute for the relationship object, and not an attribute of the entity that I am querying itself. How do I achieve this?
Here is my code:
// Create fetch request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:myEntityName inManagedObjectContext:context];
[fetchRequest setEntity:entity];
// Create predicate
NSPredicate *pred = [NSPredicate predicateWithFormat:#"relationshipObjectId == %#", relationshipObjectId];//This is where I am having trouble
[fetchRequest setPredicate:pred];
NSArray *items = [context executeFetchRequest:fetchRequest error:&error];
if ([items count]>0) {
return items[0];
} else {
return nil;
}
Can anyone see what it is I am doing wrong?
To find all related objects whose attribute matches a certain value, you would
use a predicate like
[NSPredicate predicateWithFormat:#"rel.attr == %#", value]
where "rel" is the name of the relationship, and "attr" is the name
of the attribute that should match the value.

Reassigning sorted NSManagedObjects to the NSSet

I'm just getting started with Core Data and am not sure how this works. I basically have a Person entity and an alarm entity. Each person can have many alarms. What I want is to go to a detailViewController of the person object and see their alarms. Because NSSet isn't sorted, I have a method to return the alarms sorted like so:
- (NSArray *)sortedTimes {
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Alarm" inManagedObjectContext:self.managedObjectContext];
[request setEntity:entity];
NSSortDescriptor *timeDescriptor = [[NSSortDescriptor alloc] initWithKey:#"time" ascending:YES selector:#selector(compare:)];
[request setSortDescriptors:#[timeDescriptor]];
NSError *error = nil;
NSArray *objects = [self.managedObjectContext executeFetchRequest:request error:&error];
// Can I do this???
//self.person.alarms = [NSSet setWithArray:objects];
// for (NSManagedObject *obj in objects) {
// NSDate *date = [obj valueForKey:#"time"];
// NSLog(#"date: %#", [date description]);
// }
return objects;
}
What I'm wondering is, in the line self.person.alarms = [NSSet setWithArray:objects]; is that ok? I guess I'm not sure as to what actually is happening. My executeFetchRequest returns an array of the objects I want. Can I just go ahead and assign it to the person entity's alarm property? I wasn't sure if there was a relationship from Person->Alarm that I should not be mucking with, or if something like this is perfectly legal. Thanks!
First of all, your fetch request returns all alarms, not only the alarms of self.person. You have to add an predicate to the fetch request:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"person = %#", self.person];
[request setPredicate:predicate];
(assuming that person is the inverse relationship from the Alarm entity to the Person entity). But you don't really need a fetch request to get the sorted alarms of a person. A more direct way is
NSArray *objects = [[self.person.alarms allObjects]
sortedArrayUsingDescriptors:#[timeDescriptor]];
Now to your question: The statement
self.person.alarms = [NSSet setWithArray:objects];
just re-assigns the same set of alarms to the person. This effectively does not change anything, because it is the same set. In particular, it does not guarantee that self.person.alarms will now be sorted by time.
Remark: It you want to display a table view with the alarms of a person, you can also use a NSFetchedResultsController (FRC) as table view data source. The advantage of using a FRC is that the table view is automatically updated if objects are inserted, removed or updated.
Have a look at the NSFetchedResultsController and NSFetchedResultsControllerDelegate documentation which contains all the required code templates.

Core Data how to retrieve a column from an entity

I am using Core Data for my ios app and I am wondering how would I go about in retrieving an entire column from an entity table? For example I am soly interested in grabbing the primary key from my table.
In sql i would just do Select name from MYTABLE.
I think you could do it this way :
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"--table--" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:nil];
NSMutableArray *elementsFromColumn = [[NSMutableArray alloc] init];
for (NSManagedObject *fetchedObject in fetchedObjects) {
[elementsFromColumn addObject:[fetchedObject valueForKey:#"--column--"]];
}
So you have all the elements from a specific column of your table.
Hope it's what you're looking for :)
Look at the documentation for NSFetchRequest. You can ask it to return dictionaries containing specific properties only - this is about as close as you will get. The methods of interest are setResultType: and setPropertiesToFetch:.

Core-Data complex countForFetchRequest

Suppose i have a one to many relationship between two model entities Entity One and Entity Many, and Entity One has a name field,
Entity One (1) --- (m) Entity Many
I want to check if an Entity One exist with name equal to "one" and has associated Entity Many records. Can i perform this check using countForFetchRequest: method? How? I'm not very good very predicates.
Ended up doing this, and seems to be working:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
fetchRequest.entity = [NSEntityDescription entityForName:#"EntityA" inManagedObjectContext:context];
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"name == %# AND relationshipRecords.#count != 0", aName];
NSError *error = nil;
NSUInteger numberOfRecords = [context countForFetchRequest:fetchRequest error:&error];
[fetchRequest release];

Resources