ios: NSFetchRequest and filtering - ios

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

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

iOS Core Data group by and return all the objects with all the property

I'm new to Core Data and trying to do the following thing:
I want to fetch all the objects in the persistent store and group by an attribute called day. Meanwhile, all the objects should be sorted by an attribute called startTime.
Here is what I do:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Meeting"];
// Add Sort Descriptors
[fetchRequest setSortDescriptors:#[[NSSortDescriptor sortDescriptorWithKey:#"startTime" ascending:YES]]];
NSEntityDescription* entity = [NSEntityDescription entityForName:#"Meeting" inManagedObjectContext:self.managedObjectContext];
NSAttributeDescription* meetingDay = [entity.attributesByName objectForKey:#"day"];
[fetchRequest setPropertiesToFetch:[NSArray arrayWithObjects:meetingDay, nil]];
[fetchRequest setPropertiesToGroupBy:[NSArray arrayWithObject:meetingDay]];
[fetchRequest setResultType:NSDictionaryResultType];
NSError *error = nil;
NSArray *results = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
But for results, what I got is a dictionary, which the key is 'day' and value is the day string.
How can I get all other attributes, basically I mean the whole objects?
Or do I need to fetch all the objects and do the group by myself?
Thanks!
Delete these lines from your fetch code...
[fetchRequest setPropertiesToFetch:[NSArray arrayWithObjects:meetingDay, nil]];
[fetchRequest setResultType:NSDictionaryResultType];
The first limits your data set to one single entity attribute, yet you are wanting the entire entity.
The second specifies an NSDictionary result set, which as far as I understand from your question is unnecessary in this instance. You are seeking an NSArray of entities, not an NSArray containing an NSDictionary of entity attributes.

Filter NSFetchedResultsController to get only objects with some relationship

I have two entities, A and B, and the following relationships:
A -> B - To many
B -> A - To one
In other words: A can have zero or more B and B can have only one A.
I want to use NSFetchedResultsController to show my A entries in a table view, but i want to filter the results by A -> B relationship.
To do so, i have a UISegmentedControl, if the user taps the first segment i want to show only the A entries that have at least one relationship with B, and if the second segment is tapped i want to show only the entries with no relationships with B.
I'm using CoreData's NSManagedObject, so my A object has a NSSet property with all B entries in a relationships with A.
This is how i'm instantiating my NSFetchedResultsController:
NSManagedObjectContext *context = self.managedObjectContext;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:"A" inManagedObjectContext:self.managedObjectContext];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:descriptorKey ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
[fetchRequest setEntity:entity];
NSFetchedResultsController *controller = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest
managedObjectContext:context
sectionNameKeyPath:controllerKey
cacheName:nil];
NSError *error;
BOOL success = [controller performFetch:&error];
if (success) {
return controller;
}
This code get all A entries, how can i make that filter?
You need to add a predicate to your fetch request:
e.g.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"B.#count == 0"];
[fetchRequest setPredicate:predicate];
This will filter As that don't have any related B objects.
As #Abizern mentioned in comments, you need to add a NSPredicate to your NSFetchedResultsController. The predicate would be something like:
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"b == %#", myVarReferenceToB]];
If you only have a unique identifier in B (lets call it identifier) instead of an object reference you could write it as:
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"b.identifier == %#", myVarReferenceToBIdentifier]];
This will produce your filter.
Every time the user changes the segmented control you will need to re-build the fetch or you will need to keep one NSFetchedResultsController around per segment.

NSFetchRequest canot fetch entity present in context

I have a problem where I am inserting entities A in a context. Right after I insert all of the entities A I execute a fetch request on the context:
NSEntityDescription* entity = [NSEntityDescription entityForName:#"A" inManagedObjectContext:ctx];
NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:entity];
NSError* fetchError;
NSArray* results = [ctx executeFetchRequest:fetchRequest error:&fetchError];
This code above is able to get all of them and I can see them in the NSLog...
Right after on the same context I try to fetch all entities A that have age = 5, like this:
(I assign "age" to the attirbute and a NSString #"5" to the value)
NSEntityDescription* entity = [NSEntityDescription entityForName:#"A" inManagedObjectContext:ctx];
NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:entity];
NSPredicate *filter = [NSPredicate predicateWithFormat:#"%K == %#",attr,value];
[fetchRequest setPredicate:filter];
NSError* fetchError;
NSArray* results = [ctx executeFetchRequest:fetchRequest error:&fetchError];
THE PROBLEM IS that even though the NSLog shows an entity A with age 5 this last fetch request returns always null! Nothing.
Any suggestions? Why fetching all of them shows entities with age 5 and filtering for age 5 only does not work right after on the same context?
Thanks
Try setting age to an instance of NSNumber initalized to 5 rather than the NSString #"5".

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

Resources