NSFetchedResultsController predicate NOT filtering part of it (using ANY, IN and BETWEEN in the same fechRequest's predicate) - core-data

I've searched a lot here and couldn't find a solution to my situation:
I have a TableViewController that uses NSFetchedResultsController to display data from CoreData.
The model has an entity "Places" that has a to-many relationship called "Types" (and an inverse one, also to-many relationship).
In a first TableViewController I display the objects from entity "types" (each place can belong to more than one type, and one type can have more than one place). When the user taps on a row it calls a new TableViewController that will show objects from entity "Places" related to "Types" using a NSFetchedResultsController.
I know I could just use:
NSSet = [aType valueForKey:#"Places"];
However, I really want to use the NSFetchedResultsController and all its benefits.
Well, at the NSFetchedResultsController accessor method I was able to recreate this relationship by using:
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Places"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSDictionary *types = [NSDictionary dictionaryWithObjectsAndKeys:self.theTypes, #"TYPES", nil];
NSPredicate *predicateAny = [NSPredicate
predicateWithFormat:#"ANY types IN $TYPES"];
NSPredicate *predicate = [predicateAny predicateWithSubstitutionVariables:types];
This code works fine as it returns "Places" that are related to the "Types" I want (those were hold inside the property theTypes). I use a property (theTypes), in the TableViewController to hold all "Types" objects one selected at the original tableViewController.
The problem is that the entity "Places" has a property named "distance" that I also need to use as a filter inside the NSPredicate, like this:
NSNumber *radious = [NSNumber numberWithDouble:10000.00];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"distance BETWEEN {0 , %#}", radious];
NSDictionary *types = [NSDictionary dictionaryWithObjectsAndKeys:self.theTypes, #"TYPES", nil];
NSPredicate *predicateAny = [NSPredicate
predicateWithFormat:#"ANY types IN $TYPES"];
NSPredicate *predicate = [predicateAny predicateWithSubstitutionVariables:types];
NSPredicate *thePred = [NSCompoundPredicate andPredicateWithSubpredicates:
[NSArray arrayWithObjects:predicate, pred, nil]];
[fetchRequest setPredicate:thePred];
Here's where the problem occurs: the "distance" filter appears to be simply ignored by the predicate at the fetchRequest. The resulted fetch always has Places that don't match the #"distance BETWEEN {0 , %#} clause.
Can anyone help me figure out what I am missing here?
Thanks a lot!
Daniel

This predicate:
NSPredicate *predicateAny = [NSPredicate
predicateWithFormat:#"ANY types IN $TYPES",
radious];
Doesn't make sense because you don't use the radious variable in this predicate. Not sure if that is the source of the problem but you should clean it up anyway.
This predicate:
NSPredicate *pred = [NSPredicate predicateWithFormat:#"distance BETWEEN {0 , %#}", radious];
... could probably be:
NSPredicate *pred = [NSPredicate predicateWithFormat:#"distance <=%#", radious];
... unless you have the possiblity of a negative radious value. It will be much faster that way.

Related

Is a subquery needed for my nspredicate to work properly?

I have an array of custom objects that have beds (1,2,3), fireplace (yes or no), den (yes or no) and ceiling heights (9-11,11-14,14-16). I need to allow filtering based on any/all/none of the items being selected to filter by. So a user may want to see 1 & 2 beds, den, fireplace and 9-11 foot ceilings. Or just 1 & 2 beds. My current predicate works for some of these. But it doesn't match all - only some. I am thinking I need a subquery. How to create a nested(?) subquery based on an array of filters?
Right now, the user selects buttons and those are matched against Filters and I use those to create my predicate.
Current predicate
Filters is an array of keys and predicate strings like 'beds, 1' and 'ceilings, 9-11'
`NSMutableArray *subPredicates = [NSMutableArray array];
for (Filter*fil in filters) {
NSPredicate *unitsPredicate = [NSPredicate predicateWithFormat:#"%K == %#", fil.key, fil.predicate];
[subPredicates addObject:bedsPredicate];
}
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:subPredicates];
NSLog(#"homes: %#", [searchArray filteredArrayUsingPredicate:predicate]);
NSArray *ar = [searchArray filteredArrayUsingPredicate:predicate];
I'd like to allow someone to pick any of the criteria and return appropriate data.
Subqueries are used with to-many relationships. If you want to filter multiple values then the class of fil.predicate should be an array (or set) of values. The predicate format is %K IN %#, for example
for (Filter*fil in filters) {
NSPredicate *unitsPredicate;
if ([fil.predicate isKindOfClass:[NSArray class]])
unitsPredicate = [NSPredicate predicateWithFormat:#"%K IN %#", fil.key, fil.predicate];
else
unitsPredicate = [NSPredicate predicateWithFormat:#"%K == %#", fil.key, fil.predicate];
[subPredicates addObject:bedsPredicate];
}
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:subPredicates];
See Aggregate Operations in Predicate Programming Guide.

CoreData: NSPredicate based on relationships

I'm working on a recipe book right now using Core Data. It's the first time I'm using CoreData and it's working so far, but I'm having some trouble using it in the iPad's split view.
Here's my object model:
http://i621.photobucket.com/albums/tt295/Naosu_FFXI/ObjectModel.png
In my app, steps and ingredients are shown in two tables in the detail view. When I have one recipe, it works as expected. However, the NSFetchedResultsControllers for both tables pulls all the information regardless of what recipe you select. So using an NSPredicate seems to be the most obvious choice.
Here is my code snippet for the NSPredicate:
filteredIngredientsArray = [[NSMutableArray alloc] init];
.... snip ....
//---------- Filtering the ingredients by name ----------
NSError *error = nil;
NSPredicate *ingredientsPredicate = [NSPredicate predicateWithFormat:#"recipe.recipeName == '%#'", selectedName];
[fetchRequest setPredicate:ingredientsPredicate];
NSLog(#"Filtering the INGREDIENTS with this: %#", selectedName);
NSArray *loadedIngredients = [_managedObjectContext executeFetchRequest:fetchRequest error:&error];
filteredIngredientsArray = [[NSMutableArray alloc] initWithArray:loadedIngredients];
[self.ingredientTable reloadData];
When I use this, my tables don't get filled period.... so it's definitively working, but not working as I want it to. My NSLog shows that the value of the selectedName variable is the name of the recipe the user taps on.
This has been driving me up the wall and it really bothers me, so any feedback/help would be appreciated.
Remove the single quotes around %# in the predicate format:
[NSPredicate predicateWithFormat:#"recipe.recipeName == %#", selectedName]

CoreData - NSPredicate results if relationship has data

I have a many-to-many relationship between tables, and I populate a tableView with Activities.
For that i user a simple NSPredicate like this:
request.predicate = [NSPredicate predicateWithFormat:#"deleted == %#", [NSNumber numberWithBool:NO]];
How can I do to show only the Activities that has Members attached to it?
I think that in the NSPredicate I have to do some count so that only the Activities with count > 0 are returned. Is that so?
How?
(i'm newbie in coredata...)
Thanks,
RL
You need to add a subquery to your predicate acting on the CompanyActivity entity as follows:
[[NSPredicate predicateWithFormat:#"deleted == %#" && (0 >= SUBQUERY(Members, $sub, $sub.deleted == %#).#count)", [NSNumber numberWithBool:NO] [NSNumber numberWithBool:NO]];
The first part of the predicate returns objects which have not been deleted, the second one related to the subquery will take care of retrieving all those CompanyActivity objects whose Members have not been deleted.

Predicate Problem when fetching objects in a To-Many relationship

I have a simple situation where I have two entities related with Many-To-Many relationship.
Two objects, Alarms and Tags. When I want to fetch all the Alarms associated with a given Tag, I tried this:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%# IN tags", theTag];
What I get is all Alarms, not just those related to the Tag.
However, trying this the other way around works:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF in %#", theTag.alarms];
For complicated reasons having to do with code reuse, I really need the first one to work. Any help would be much appreciated! Thanks!
If you have a Tag object, then you can get all of its alarms by doing:
NSSet *alarms = [theTag alarms];
If for some bizarre reason you have to do this with a fetch request (which you shouldn't), your predicate should be:
NSPredicate *p = [NSPredicate predicateWithFormat:#"tags CONTAINS %#", theTag];

Core Data fetches based on properties of 'ordered' relationships

My app has smart folder like functionality: a predicate is setup with a NSPredicateEditor and used to fill the folder with a fetch request.
The entity used in the search has a to-many relationship. The relationship is ordered, in the sense that an index is stored in the destination entity for sorting purposes.
My problem is that I would like to build in a rule based on the last values in the ordered relationship, but I can't figure out how to build a predicate to do this, because the relationship is not an array. Core data doesn't actually know about the order.
I have a readonly property on the class that returns the ordered items, but this doesn't seem to help with the fetch request because the property is not available in the core data store.
The only option I can think of is to de-normalize and store the last items in the relationship ordered in a separate property. Is that the only solution?
Well, assuming I have understood the problem correctly, I'd do it like this. Lets say you've got two entities, TopEntity has a (NSString *)name property and a to-many relationship to MyEntity which has a (NSString *)data property and (NSInteger)order property.
Lets say you want the TopEntity objects which match a given string, and whose MyEntity orders are satisfy a certain condition, then you can do it with two predicates and an NSFetchRequest like so....
NSManagedObjectContext *context = [self managedObjectContext];
// Create some top level entities
TopEntity *aTop = [TopEntity insertInManagedObjectContext:context];
aTop.name = #"This is Your Name";
TopEntity *bTop = [TopEntity insertInManagedObjectContext:context];
bTop.name = #"This aint a Name";
TopEntity *cTop = [TopEntity insertInManagedObjectContext:context];
cTop.name = #"This is My Name";
// Add some data
NSInteger i, len = 30;
for(i=0; i<len; i++) {
// Create a new object
MyEntity *entity = [MyEntity insertInManagedObjectContext:context];
entity.orderValue = i;
entity.data = [NSString stringWithFormat:#"This is some data: %d", i];
if(i < 10) {
[aTop addObjectsObject:entity];
[entity addTopObject:aTop];
} else if (i < 20) {
[bTop addObjectsObject:entity];
[entity addTopObject:bTop];
} else {
[cTop addObjectsObject:entity];
[entity addTopObject:cTop];
}
}
// Save the context
NSError *error = nil;
[context save:&error];
// A predicate to match against the top objects
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name BEGINSWITH %#", #"This is"];
// A predicate to match against the to-many objects
NSPredicate *secondPredicate = [NSPredicate predicateWithFormat:#"ANY objects.order < %d", 5];
NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
[fetch setEntity:[NSEntityDescription entityForName:#"TopEntity" inManagedObjectContext:context]];
[fetch setPredicate:predicate];
NSArray *result = [[context executeFetchRequest:fetch error:&error] filteredArrayUsingPredicate:secondPredicate];
for(TopEntity *entity in result) {
NSLog(#"entity name: %#", entity.name);
}
So, essentially you can just wrap the results of your fetch request with another predicate and use the ANY keyword.
I've got no idea how efficient that is, but it works for this case. Running the above will output "This is Your Name" i.e. it matches the first TopEntity.
I don't think there's a way to limit to n results in a predicate, only at the fetch request level.
Aside from referencing the last n items in a relationship as you mentioned, you might try a boolean attribute "lastN" and flip them on/off when you curate the order of the list (say, during user-initiated sort or drag-and-drop reordering).
Alternatively, you could create a separate fetch request for each searched thing that sorts by your sort key, ordered descending, and is limited (via -setFetchLimit: ) to n results.
Tracking this as a relationship or an attribute is somewhat "messy" whereas the fetch limit is more expensive (because of multiple round trips). If your reordering is done by one-off user actions, it might be better performance-wise to use the relationship or attribute approach since the work is amortized rather than done all at once in a series of fetches. I haven't found a better way myself and will follow this one closely. :-)

Resources