Core Data fetching relationship objects - ios

in my app i have two entities: Members and Lists. they both have a one-to-many relationships (member can have more than one list, list can have more than one member). now i want to fetch the lists belonging to a specific member. here is my code:
WSAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Lists" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:#"has_members contains[cd] %#", [self.currentMember valueForKey:#"name"]]];
[fetchRequest setPredicate:predicate];
NSError *error;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
// Handle the error.
NSLog(#"NO LISTS AVAILABLE IN REFRESH");
}
self.currentMember is a managed object of the user himself.
Note: member has name, (NSSet*) member_of_list
list has list_name, has-members
Problem: when i run the code it's breaking at the fetchedObjects array. i suspect that there is something wrong with the NSPredicate but i don't know where and how to fix it. can any one point out the problem?

First, the relationship you describe between Member (Calling an entity in a plural form is confusing) and List is many-to-many.
Second, instead of using CoreData's inherent object graph capabilities, you went and "rolled your own" relationship between the entities (you should use your interface builder to model a CoreData relationship between the two entities).
See HERE how to do that.
after you model your data, your predicate should look something like:
//Not tested
NSPredicate* p = [NSPredicate predicateWithFormat:#"ANY members = %#",self.currentMember];
DO NOT pass a formatted string to create the predicate, use NSPredicate formatting to substitute parameters or you will not be able to accomplish your goal (in most cases).

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

NSPredicate fetch coredata

I have two linked types in my CoreData model.
FlightRecording and AHRSMessage
One FlightRecording links to many AHRSMessages.
I've been fetching each recording and iterating through its linked messages:
for (__weak id msgObj in rec.ahrsMessages) {
and have not been seeing the performance I'd like. As I'm fetching the actual recording objects and not the messages I don't believe I can set a batch size on the fetch so I was thinking i'm better off fetching the messages using a correct predicate format.
Assuming I have a NSManagedObjectID for my flight recording is there a quick way to do a predicate query on my AHRSMessage
I've gotten this far which isn't that far:
NSFetchRequest *msgFetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"AHRSMessage" inManagedObjectContext:context];
And then i wasn't sure what to do for my predicate. Do I have to match on an actual field such as:
"ANY flightRecordings = %#" or something like that?
Do I have to reference a specific field in flightRecording or is there a way to just match on the ID?
If "flightRecordings" is a to-many relationship from the AHRSMessage entity to
FlightRecording then this should work:
NSManagedObjectID *flightRecordingId = ...;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"AHRSMessage"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY flightRecordings = %#", flightRecordingId];
[fetchRequest setPredicate:predicate];
The argument of the "ANY flightRecordings = %#" predicate can be a FlightRecording object or a NSManagedObjectID of a FlightRecording object.

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.

ios NSFetchRequest for two related tables

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

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