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".
Related
I am looking to create a predicate that can check the TypeID of the first 5 objects in a Core Data relationship.
Here is what I am trying, but it doesn't work:
int num = 5;
NSMutableArray *predicates = [[NSMutableArray alloc] init];
for (int i = 0; i < num; i++) {
[predicates addObject:[NSPredicate predicateWithFormat:#"SELF IN %# AND logs[%i].TypeID == 3", items, i]];
}
This gives the error:
error: SQLCore dispatchRequest: exception handling request:
< NSSQLFetchRequestContext: 0x281837aa0 > , Unsupported function
expression logs[0].TypeID with userInfo of (null) CoreData:
error: SQLCore dispatchRequest: exception handling request:
< NSSQLFetchRequestContext: 0x281837aa0 > , Unsupported function
expression logs[0].TypeID with userInfo of (null)
I realize I am probably doing this wrong, so is there a different way that I could be doing this using NSPredicate?
if you want only fetch 5. Set fetchLimit to 5. Maybe the following code can not run right away, but the principle is same. You can add a property or get function to assign the "logs.TypeID" like -(Int)myID {return logs[0].TypeID;} then "SELF IN %# AND myID == 3" would solve the problem.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Entity name" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF IN %# AND logs.TypeID == 3", items];
[fetchRequest setPredicate:predicate];
fetchRequest.fetchLimit = 5;
NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
}
I’m not sure I understand the questions, but I don’t think it’s possible to do what you asked, at least not as worded. It doesn’t seem like it would be possible to inspect only five objects in the relationship and stop there, returning any number of matches or none at all, even if there are more in the database.
However, and I think this may be what you were getting at, it is possible to find five objects that are both in the items set and have at least one log with a typeID == 3. It can be done similar to what E.Coms proposed, except you need a subquery to handle the relationship. (I am assuming logs is a to-many relationship, either one-to-many or many-to-many).
Note that the following code has not been tested:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Entity name" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF IN %# AND SUBQUERY(logs, $log, $log.typeID == 3).#count != 0", items];
[fetchRequest setPredicate:predicate];
fetchRequest.fetchLimit = 5;
NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
}
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];
I am a new bid in iOS development. I am using NSManagedObject of Core Data to perform Insert and Fetch operations. It works perfectly fine. But the problem is, I want to fetch only some selected records (where condition in MySQL) from the table.
For e.g. "select name from users where city='Pune'";
I found NSPredicate to fetch filtered data. But it gives all the data in array and not just the selected one. For e.g. if result for above query is:
Anu
Then the NSPredicate result will give:
fname = Anu
lname = Padhye
city = Pune
id = 3
Is there a way to only fetch selected record/s in iOS Objective-c? Following is the code snippet I am using for NSManagedObject:
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"User"];
valueData = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
NSEntityDescription *productEntity=[NSEntityDescription entityForName:#"User" inManagedObjectContext:managedObjectContext];
NSFetchRequest *fetch=[[NSFetchRequest alloc] init];
[fetch setEntity:productEntity];
NSPredicate *p=[NSPredicate predicateWithFormat:#"id == %d", 3];
[fetch setPredicate:p];
//... add sorts if you want them
NSError *fetchError;
NSArray *fetchedProducts=[valueData filteredArrayUsingPredicate:p];
Try this:
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"User"];
request.predicate = [NSPredicate predicateWithFormat:#"city == %# && id == %d", #"Pune", 3];
request.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"id" ascending:YES]];
NSArray *results = [managedObjectContext executeFetchRequest:request error:nil];
Results array should now contain all records who have Pune as their city with the id of 3.
I am trying to request distinct results using my NSFetchedResult. I tried to use the answer posted here: What class is returned when requesting distinct NSManagedObject property instances from Core-Data? to use the NSMangedObjectID in order to get the actual NSManagedObject. Here is the NSFetchedResult code:
//Unique fetch request
NSFetchRequest *uniqueFetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"EntityName"];
[uniqueFetchRequest setPropertiesToFetch:#[#"name"]];
[uniqueFetchRequest setReturnsDistinctResults:YES];
[uniqueFetchRequest setResultType:NSDictionaryResultType];
[uniqueFetchRequest setPredicate:predicate];
[uniqueFetchRequest setSortDescriptors:sortDescriptors];
uniqueResults = [[NSArray alloc] initWithArray:[context executeFetchRequest:uniqueFetchRequest error:&error]];
NSManagedObjectID *mid = uniqueResults[0][#"name"];
EntityData *card = (EntityData *)[context objectWithID:mid];
However, [uniqueResults[0][#"name"] is returning a NSString with the actual name of the NSManagedObject it found rather than something like 0x8b7b120 <x-coredata://C7E53293-94C7-444D-8162-167B1D66A961/Hair/p33> (which is what was returned in the link I posted). Because it is now a NSString, [context objectWithID:mid] crashes and gives me the following error: [__NSCFString persistentStore]: unrecognized selector sent to instance 0xc47b170
What am I doing wrong here?
Take a look at the collection operators
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"EntityName"];
[fetchRequest setPredicate:predicate];
[fetchRequest setSortDescriptors:sortDescriptors];
results = [context executeFetchRequest:fetchRequest error:&error];
uniqueResults = [results valueForKeyPath:#"#distinctUnionOfObjects.name"];
Your mistake here is trying to treat your name (NSString property) as a relationship.
Your request will return all distinct name attribute values (existing for the EntityName entity) in an array of dictionaries.
If 2 (or more) objects could have the same name, which object ID do you expect to get back?
If you want to segment your data based on a property value, I would suggest you use a NSFetchedResultsController and specify the sectionNameKeyPath: to match your needs.
Edit:
If your OK with unstable results you could try:
NSFetchRequest* r = [NSFetchRequest fetchRequestWithEntityName:#"Ingredient"];
r.resultType = NSDictionaryResultType;
r.propertiesToGroupBy = #[#"name"];
NSExpressionDescription* objectIdDesc = [[NSExpressionDescription alloc] init];
objectIdDesc.name = #"objectID";
objectIdDesc.expression = [NSExpression expressionForEvaluatedObject];
objectIdDesc.expressionResultType = NSObjectIDAttributeType;
r.propertiesToFetch = #[objectIdDesc,#"name"];
NSArray* res = [context executeFetchRequest:r error:nil];
But I see no use for this kind of fetch
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];