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];
Related
I have a CoreData (SQLite) datamodel in xcode like so:
Friends Table
email
name
username
belongsToGroups
Groups Table
title
peopleInGroup
So the belongsToGroups and peopleInGroups is a many-to-many relationship with each other, both represented by NSSet in the code.
What do I use to query the NSSet for people in my groups and vice versa? (I'm new to CoreData)
With coredata, you can do it simple. Assume we have one object on Groups Table ( group), you want to get all friends belong to group, you can do:
[group. peopleInGroup allObjects]
For more detail:
Get group via title
NSError* error = nil;
NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
NSPredicate *predicate;
NSEntityDescription *entity;
NSArray *fetchedObjects;
Group* group;
entity = [NSEntityDescription
entityForName:[NSString stringWithFormat:#"%#",[Group class]]
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:#"(title like[c] \"%#\")" ,title]];
[fetchRequest setPredicate:predicate];
fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects.count > 0) {
group = [fetchedObjects lastObject];
}
return group;
}
Get all friend of group
NSMutableArray* friends = [NSMutableArray alloc] init];
[friends addObjectsFromArray:[group. peopleInGroup allObjects]];
I'm trying to modify a simple Core Data fetch request for contacts to only look for contacts with a relationship with a certain tag. Contact and Tag are both entities with a many-to-many relationship.
I understand with Core Data I can do this by first fetching the Tag object, and then calling tag.contact, but I don't want to do it this way as the rest of the code is dependent on the fact that the fetchResultsController returns Contact objects, not Tag objects.
If I were to do relational databasing, I could do a simple cross-table query and find all contacts with a certain tag. Is there a simple way I can replicate this via Core Data?
-(NSFetchedResultsController *) fetchedResultsController {
//if fetch controller already exists
if(_fetchedResultsController != nil) {
return _fetchedResultsController;
}
//create a new fetch request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Contact"
inManagedObjectContext:[self managedObjectContext]];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"lastName"
ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
//instantiate the fetch controller with the fetch request and sort by last name into sections
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[self managedObjectContext] sectionNameKeyPath:nil cacheName:nil];
//declare delegate of fetch controller as self
_fetchedResultsController.delegate = self;
NSLog(#"fetchResultsController Created");
return _fetchedResultsController;
}
Use NSPredicate.
Lets say you have related Contacts with Tag by name tags and tag entity has property name.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY tags.name = [cd] %#", #"sales"];
[fetchRequest setPredicate:predicate];
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).
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:.
I have a NSFetchRequest (executing in a NSFetchedResultsController) for the following data:
Person (one-to-many) Encounter
Encounter has an int field "type". In my tableView, I want to show:
Person A - Encounter of type 1
Person A - Encounter of type 2
Person B - Encounter of type 1
etc. That is, for a Person, I only want one Encounter of each type. Is there a way to do that in a Core Data query? I could do this with code to filter the result of an NSFetchRequest, but then I couldn't use NSFetchedResultsController.
[EDIT]
Here's the code I'm currently trying. The result is several Encounters with the same type for one Person.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// entity
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Encounter" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// return distinct
NSDictionary *entityProperties = [entity propertiesByName];
[fetchRequest setPropertiesToFetch:[NSArray arrayWithObjects:
[entityProperties objectForKey:#"person"],
[entityProperties objectForKey:#"type"],
nil]];
[fetchRequest setReturnsDistinctResults:YES];
[fetchRequest setResultType:NSDictionaryResultType];
NSError *error;
NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
for (NSDictionary *d in fetchedObjects) {
NSLog(#"NSDictionary = %#", d);
}
[fetchRequest release];
I am assuming you want to return just one record for each type of Encounter even if a Person has more than one of any given type. You can accomplish this by adjusting your NSFetchRequest accordingly:
[fetchRequest setReturnsDistinctValues:YES];