Database stucture image.
Get country with predicate active = 1 in debuger watch condition where zactive = 1 it's ok.
In next view controller get sub-objects country.regions but request in debuger got only relationships condition. How send there active = 1 condition for sub-objects? And same way for region.items.
Got some ideas:
Filter result from country.regions but query will remain the same
Build relationships myself
You can create a fetch request with a predicate, and you can use the country object in your predicate. Example:
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Region"];
request.predicate = [NSPredicate predicateWithFormat:
#"country = %# AND active = 1", country];
NSError *error;
NSArray *activeRegions = [managedObjectContext executeFetchRequest:request
error:&error];
Related
I have a NSManagedObject with related objects. The relation is described by a keyPath.
Now I want to display these related objects in a table view. Of course I could just take the NSSet with these objects as a data source, but I'd prefer to refetch the objects with a NSFetchedResultsController to benefit from its features.
How can I create a predicate that describes these objects?
To display the related objects of a given object with a fetched results controller,
you would use the inverse relationship in the predicate. For example:
To display the children related to a given parent, use a fetched results controller
with the following fetch request:
Parent *theParent = ...;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Child"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"parent = %#", theParent];
[request setPredicate:predicate];
For nested relationships, just use the inverse relationships in inverted order. Example:
To display the streets of a given country:
Country *theCountry = ...;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Street"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"city.country = %#", theCountry];
[request setPredicate:predicate];
Thanks Martin, you gave me important information.
To generically get the key path I have found the following implementation:
// assume to have a valid key path and object
NSString *keyPath;
NSManagedObject *myObject;
NSArray *keys = [keyPath componentsSeparatedByString:#"."];
NSEntityDescription *entity = myObject.entity;
NSMutableArray *inverseKeys = [NSMutableArray arrayWithCapacity:keys.count];
// for the predicate we will need to know if we're dealing with a to-many-relation
BOOL isToMany = NO;
for (NSString *key in keys) {
NSRelationshipDescription *inverseRelation = [[[entity relationshipsByName] valueForKey:key] inverseRelationship];
// to-many on multiple hops is not supported.
if (isToMany) {
NSLog(#"ERROR: Cannot create a valid inverse relation for: %#. Hint: to-many on multiple hops is not supported.", keyPath);
return nil;
}
isToMany = inverseRelation.isToMany;
NSString *inverseKey = [inverseRelation name];
[inverseKeys insertObject:inverseKey atIndex:0];
}
NSString *inverseKeyPath = [inverseKeys componentsJoinedByString:#"."];
// now I can construct the predicate
if (isToMany) {
predicate = [NSPredicate predicateWithFormat:#"ANY %K = %#", inverseKeyPath, self.dataObject];
}
else {
predicate = [NSPredicate predicateWithFormat:#"%K = %#", inverseKeyPath, self.dataObject];
}
Update: I changed the predicate format so that it also supports many-to-many relations.
Update 2 This is getting more complex: I need to check my inverse relation if it is to-many and use different predicates. I updated the code example above.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"city.country = '%#'", theCountry];
You miss ' ' in predicateWithFormat string. Now it work.
I have a method for destroying outdated NSManagedObjects called messages:
+ (void)syncMessagesByIDs:(NSArray *)messageIDs toGroup:(Group *)group context:(NSManagedObjectContext *)context
{
// This is getting all the Messages that still have a relationship to this Group but
// are not in our passed in set of messages and therefor should be removed
// from the Group.
NSPredicate *searchTerm = [NSPredicate predicateWithFormat:#"(%# IN %K) && NOT (%K in %#)", group, #"groups", #"id_number", messageIDs];
NSArray *results = [Message fetchManyWithPredicate:searchTerm context:context];
for (Message *message in results) {
DLog(#"message.groups %d", message.groups.count);
[message removeGroupsObject:group];
if (message.groups.count == 0) {
[context deleteObject:message];
}
}
}
+ (NSArray *)fetchManyWithPredicate:(NSPredicate *)predicate context:(NSManagedObjectContext *)context
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
fetchRequest.entity = [NSEntityDescription entityForName:NSStringFromClass(self) inManagedObjectContext:context];
fetchRequest.predicate = predicate;
fetchRequest.returnsObjectsAsFaults = NO;
NSArray *results = nil;
NSError *error = nil;
results = [context executeFetchRequest:fetchRequest error:&error];
if (error) {
// TODO: Handle error.
}
[fetchRequest release];
return results;
}
Oddly, I have results returned for my fetch. This implies that there are a set of messages that have a relationship to the group which I used for the query BUT do not have an id_number in the array of messageIDs.
However, when I print message.groups from the console I get 0x1f83c740 as the address of the group that matches the group in my query, but the group in my query has an address of 0x1fe99db0, two different addresses.
Because the addresses of the group in my query and the group in the relationship are different my call to [message removeGroupsObject:group] does nothing.
Why could this be happening?
More Info:
If it helps, this call is made in a background thread with a context of NSPrivateQueueConcurrencyType.
The following code also returns a group with a different address than both of the other groups previously shown.
searchTerm = [NSPredicate predicateWithFormat:#"SELF == %#", group];
NSArray *groupResults = [RDGroup fetchManyWithPredicate:searchTerm context:context];
DLog(#"groupsResults: %#", groupResults);
I'm getting the same group with a different address every time.
Wups! I looked down the call stack and just realized my Group entity was never created in the thread of the context I'm using for the fetch request. I needed to pass in the objectID of the Group and create a new Group in the correct context.
I'm curious to why an exception wasn't thrown for accessing an entity outside of the context that owns it though.
Hope this helps anyone else in the future!
I have an NSArray containing NSDictionary objects.
I need to filter the objects based on a selection of values, so i have created an array of the values i want to filter by and using a predicatewithformat and feeding it the array of objects.
This is kind of working, but weirdly in situations where i know i should be getting an empty array returned i am getting a single object, that shouldn't be there.
I have logged out the value of the array of filter values, and i can clearly see that it contains a key which corresponds to the id_str of the object, so it shouldn't be returned.
Below is the code i am using, any pointers of where i am going wrong would be very vert helpful!
//Create new fetch request
NSFetchRequest *request = [[NSFetchRequest alloc] init];
//Set new predicate to only fetch tweets that have been favourited
NSPredicate *filterFavourite = [NSPredicate predicateWithFormat:#"favouriteTweet == 'YES'"];
//Setup the Request
[request setEntity:[NSEntityDescription entityForName:#"Tweet" inManagedObjectContext:_managedObjectContext]];
//Assign the predicate to the fetch request
[request setPredicate:filterFavourite];
NSError *error = nil;
//Create an array from the returned objects
NSArray *favouriteTweets = [_managedObjectContext executeFetchRequest:request error:&error];
NSAssert2(favouriteTweets != nil && error == nil, #"Error fetching events: %#\n%#", [error localizedDescription], [error userInfo]);
//Create a new array containing just the tweet ID's we are looking for
NSArray *favouriteTweetsID = [favouriteTweets valueForKey:#"tweetID"];
NSLog(#"%#", favouriteTweetsID);
//Create a new predicate which will take our array of tweet ID's
NSPredicate *filterFavouritsearchPredicate = [NSPredicate predicateWithFormat:#"(id_str != %#)" argumentArray:favouriteTweetsID];
//Filter our array of tweet dictionary objects using the array of tweet id's we created
NSArray *filteredTweets = [self.timelineStatuses filteredArrayUsingPredicate:filterFavouritsearchPredicate];
//Send those tweets out to be processed and added to core data
[self processNewTweets:filteredTweets];
NSLog(#"Update Favoutited Tweets: %#", filteredTweets);
This is probably not doing what you intent:
[NSPredicate predicateWithFormat:#"(id_str != %#)" argumentArray:favouriteTweetsID];
It is equivalent to
[NSPredicate predicateWithFormat:#"(id_str != %#)", id1, id2, ... idN];
where id1, id2, ..., idN are the elements of favouriteTweetsID.
The format string has only one format specifier,
so that everything but
the first element is ignored and you just have
[NSPredicate predicateWithFormat:#"(id_str != %#)", id1];
If you want to filter all objects where id_str is not equal to any of the array elements,
use
[NSPredicate predicateWithFormat:#"NOT id_str IN %#", favouriteTweetsID];
So here's the deal:
// A. Inserting
Item *item = (Item *)[NSEntityDescription insertNewObjectForEntityForName:#"Item" inManagedObjectContext:managedObjectContext];
NSError *error = nil;
[managedObjectContext save:&error];
..
[item setItemID:#"15"];
[managedObjectContext save:&error];
NSLog(#"Error: %#", error); // outputs (null)
// B. Fetching all records
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"Item"];
request.returnsObjectsAsFaults = NO;
NSArray *allItems = [managedObjectContext executeFetchRequest:request error:nil];
NSLog(#"All Items: %#", allItems);
Now, this outputs a huge list, containing the previously inserted item:
"<Item: 0x7eb7bc0> (entity: Item; id: 0x7eb71c0 <x-coredata://BC6EB71C-47C0-4445-905D-7D42E6FC611B/Item/p2> ; data: {\n itemID = 15;\n})"
So far so good, but I want to check whether this particular item does exist (I know it may sound strange in this context, but it really makes sense here). However, the predicate I'm using fails (and I don't see why):
// C. Fetching a single record
NSFetchRequest *singleRequest = [[NSFetchRequest alloc] initWithEntityName:#"Item"];
singleRequest.predicate = [NSPredicate predicateWithFormat:#"itemID == %#", #"15"];
NSError *error = nil;
NSArray *results = [managedObjectContext executeFetchRequest:singleRequest error:&error];
NSLog(#"Error: %#", error); // outputs (null) again
NSLog(#"Results: %#", results); // outputs () ...
I don't really understand how to "fix" this.
Here are some other facts:
Using persistent SQLite store with CoreData (pretty much default configuration, not even relationships, just plain key-value in 3 tables).
The itemIDs always are strings
When reopening the app, the second code block, does return an item (= the item inserted in the previous run). Could it be that save: writes to disk asynchronously, and that the NSPredicate only filters items wrote to disk?
Part A happens in a different method, but on the same thread as B and C. C is directly below B and both are placed in the same method.
If you're comparing strings, try this :
#"itemID LIKE %#"
Have a read of this, the section titled 'String Comparisons"
Okay got it. I used #synthesize instead of #dynamic in the particular model's .m-file. Didn't know it would be such a big problem .. :)
For some reason, updating the SQLite-database goes wrong when using #synthesize ..
I am creating an iOS 5 app using Core Data.
I have two entities Item and Category there is a many-to-many relationship between these two entities, i.e. an Item belongs to many categories and a Category has many items.
What I am trying to do is, given a category, I want to know all the categories the items of that category is associated with. For that I am trying to execute the following request:
NSEntityDescription *catEntity = [NSEntityDescription entityForName:#"Category" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = catEntity;
request.predicate = [NSPredicate predicateWithFormat:#"items IN %#",[category.items allObjects]];
request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES]];
NSError *error = nil;
NSArray *categories = [context executeFetchRequest:request error:&error];
But when executing this I get the following error:
'NSInvalidArgumentException', reason: 'unimplemented SQL generation for predicate : (items IN {<Item: 0x89b9c50> (entity: Item; id: 0x89be960 <x-coredata://A5DE0832-95F1-460C-9F40-202A10E16BDC/Item/p17> ; data: {
attributes = (
);
categories = (
"0x89be540 <x-coredata://A5DE0832-95F1-460C-9F40-202A10E16BDC/Category/p28>",
"0x89be680 <x-coredata://A5DE0832-95F1-460C-9F40-202A10E16BDC/Category/p29>"
);
name = "Some Item";
})})'
What am I doing wrong here?
Try this, I'm not sure but it might work:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY items IN %#", [category.items allObjects]];