NSPredicate, get results with a subset of one-to-many relationship - ios

I'mm working around with Core Data and NSFetchedResultsController.
My Data Model looks like this:
Product with one-to-many relationship called dataLines.
The dataLine entity has a property name theWeek.
I want to fetch all Product where dataLines.theWeek == someValue. This is easily done with a subquery. But this returns all dataLines. Is it possible to create a NSPredicate that returns the Product and a subset if dataLines only with the dataLines == someValue?

What you want to achieve could be reached in two ways:
using a SUBQUERY
[NSPredicate predicateWithFormat:#"SUBQUERY(dataLines, $x, $x.theWeek == %#).#count > 0)", [NSNumber numberWithInt:18]];
or the ANY modifier
[NSPredicate predicateWithFormat:#"ANY dataLines.theWeek == %#", [NSNumber numberWithInt:18]];
You can do also the following if you need to check against multiple values:
[NSPredicate predicateWithFormat:#"SUBQUERY(dataLines, $x, $x.theWeek == %# or $x.theWeek == %#).#count > 0)", [NSNumber numberWithInt:18], [NSNumber numberWithInt:19]];
The same can be applied to ANY modifier. ANY ... OR ANY ....
Maybe if you share some code we could help you.
P.S. I suppose you don't use scalar values and theWeek is a number.
Hope it helps.

You should fetch the dataLine property instead.
Assuming your Product and dataLine entity connected by relationship someRelation then you can try this code;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityWithName:#"dataLine" inManagedObjectContext:self.managedObjectContext]];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"dataLines.week == %#",theWeek]];
NSMutableArray *tmpProduct [[NSMutableArray init] alloc];
NSMutableArray *tmpArray = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
for (dataLine *theDataLine in tmpArray);
NSLog(#"%#",theDataLine.someRelation.name);
tmpProduct = theDataLine.someRelation.name;
then you can just call tmpProduct to call or display your product in table view

Create a fetch request for the 'Product' entity:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity: [NSEntityDescription entityForName:#"Product" ...]]
then create a predicate using the properties/attributes of Product with 'ANY':
[fetchRequest setPredicate:
[NSPredicate predicateWithFormat:#"ANY dataLines.theWeek == %#", <whatever week>]];
then execute the fetch to get an array of Product with at least one <whatever week>.
Generally see 'Fetching Managed Objects', NSPredicate and related documentation.

Related

objective c - fetch data (core data)

I have two tables Photo and Photographer.
Below is the structure of the tables.
I'm trying to fetch the name of all Photographers who have taken a photo called "Panda".
Here's my code but for some reason it's returning empty.
NSArray *matchingPhotographers;
NSFetchRequest *fetch = [[NSFetchRequest alloc]init];
NSEntityDescription *desc = [NSEntityDescription entityForName:#"Photographer" inManagedObjectContext:context];
[fetch setEntity:desc];
[fetch setPredicate:[NSPredicate predicateWithFormat:#"photo.name like[c] %#",#"Panda"]];
NSError *error;
matchingPhotographers = [context executeFetchRequest:fetch error:&error];
NSLog(#"Photographers: %#", matchingPhotographers);
Am I doing something wrong? The query should definitely return at least 1 photographer.
Thanks
Photo is a collection, so your predicated must be:
[fetch setPredicate:[NSPredicate predicateWithFormat:#"ANY photo.name like[c] %#",#"Panda"]];
This predicate finds "Photographers" which have at list one photo (among his photo's collection) with the name like "Panda".
photo is a to-many relationship so it's easier to use a subquery predicate to count the photos in the relationship which match your criteria:
#"SUBQUERY(photo, $p, $p.name ==[c] %#).#count > 0", #"Photo"
Note I'm also not using LIKE so it isn't a regex comparison.

NSPredicate core data, fetch some objects

I have an entity, ProductCart & Cart, with a to many relationship. ProductCart has an attribute called ordered, which determine if the product has been ordered. Now I want to fetch all the objects in ProductCart which has'nt been ordered. Have tried something like this but it does not work:
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"ProductCart"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"inCart == %# && !ordered == %#", cart, [self valueForKey:#"ordered"]];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"products.name" ascending:YES];
NSArray *sortDescriptors = #[sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
inCart fetches the relationship, ordered is giving me problems. How can I fix this?
I expect your predicate should be more like:
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"inCart == %# && ordered == NO", cart];
because you just want to test the state of ordered rather than trying to compare it to some other value.

Core Data fetching relationship objects

in my app i have two entities: Members and Lists. they both have a one-to-many relationships (member can have more than one list, list can have more than one member). now i want to fetch the lists belonging to a specific member. here is my code:
WSAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Lists" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:#"has_members contains[cd] %#", [self.currentMember valueForKey:#"name"]]];
[fetchRequest setPredicate:predicate];
NSError *error;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
// Handle the error.
NSLog(#"NO LISTS AVAILABLE IN REFRESH");
}
self.currentMember is a managed object of the user himself.
Note: member has name, (NSSet*) member_of_list
list has list_name, has-members
Problem: when i run the code it's breaking at the fetchedObjects array. i suspect that there is something wrong with the NSPredicate but i don't know where and how to fix it. can any one point out the problem?
First, the relationship you describe between Member (Calling an entity in a plural form is confusing) and List is many-to-many.
Second, instead of using CoreData's inherent object graph capabilities, you went and "rolled your own" relationship between the entities (you should use your interface builder to model a CoreData relationship between the two entities).
See HERE how to do that.
after you model your data, your predicate should look something like:
//Not tested
NSPredicate* p = [NSPredicate predicateWithFormat:#"ANY members = %#",self.currentMember];
DO NOT pass a formatted string to create the predicate, use NSPredicate formatting to substitute parameters or you will not be able to accomplish your goal (in most cases).

Fetchrequest with NSPredicate not returning right values

I'm having a strange problem with Core Data.
The problem is that I'm looking for objects with a property less than X and it isn't returning all the values that matches.
There is no cache and I'm not using //[fetchRequest setFetchBatchSize:50];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"self.level <= [cd] %#", [self.filters objectForKey:#"level"]];
I add it to a MutableArray of predicates and later I execute
NSPredicate *myPredicate = [NSCompoundPredicate andPredicateWithSubpredicates: subPredicates];
This one it's in a function that returns myPredicate called preparePredicates. For the moment there aren't more predicates, only one.
It's NSLog returns: level <=[cd] "30".
I have tried it also with intValue of the string and %i
The main function:
NSError* error;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"myEntity"
inManagedObjectContext:self.context];
[fetchRequest setEntity:entity];
NSPredicate* predicate = [self preparePredicates];
[fetchRequest setPredicate:predicate];
//[fetchRequest setFetchBatchSize:50];
NSArray *fetchedObjects = [self.context executeFetchRequest:fetchRequest error:&error];
NSLog (#"Error: %#", error);
NSLog(#"los fetchedobjects: %i",[fetchedObjects count]);
return fetchedObjects;
It doesn't return any error.
If I look at the results there isn't one having a level of 6, but there are others that matches (all are less than the specified). I know there is one with level 6.
If I open the .sqlite in SQLite Database Browser, I can see it there.
If I do with this program a
SELECT * FROM zmyentity WHERE zlevel <30;
it doesn't appear, same as in Core Data do. But if I do a
SELECT * FROM zmyentity WHERE zlevel == 6;
it appears.
I really don't know what is happening. Maybe I'm making a rookie mistake, so please point me in the right direction.
Thank you very much in advance.
Probably you have stored the level as a String attribute, because
"30" < "6"
when comparing strings. Choosing a numeric attribute type (Integer 16/32/64) should solve the problem. The corresponding level property in the myEntity class has then the NSNumber type.
You should also omit the [cd] modifier in the predicate, because that enforces a string comparison.
You predicate could then look like this:
[NSPredicate predicateWithFormat:#"self.level <= %d", [[self.filters objectForKey:#"level"] intValue]]

How do you construct the predicate for NSFetchRequest setHavingPredicate:?

For the life of me I can not seem to get this to work.
Assume our entity is an managed object with a status field and an order field.
How would I go about getting all orderedEntries having more than one order that are the same?
Please no answers telling me to just do a subquery with #count in the main predicate, since I know of that solution, the point of this post is to understand how to use the having predicate in core data, which would probably be faster than a subquery anyways. (unless you explain why I can not use a having clause)
The following code would return an array of dictionaries with the number of orders per order number. What I want is to be able to add a having clause to restrict my request to only return the dictionaries representing objects of those orders that have a count greater than 1.
Here is the code so far and my attempts at a having predicate:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"OrderedEntry"
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
[fetchRequest setResultType:NSDictionaryResultType];
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"(status == %#)",[NSNumber numberWithInt:EntryStatusAlive]];
[fetchRequest setPredicate:predicate];
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath: #"order"]; // Does not really matter
NSExpression *maxExpression = [NSExpression expressionForFunction: #"count:"
arguments: [NSArray arrayWithObject:keyPathExpression]];
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName: #"orderCount"];
[expressionDescription setExpression: maxExpression];
[expressionDescription setExpressionResultType: NSInteger32AttributeType];
[fetchRequest setPropertiesToFetch: [NSArray arrayWithObjects:expressionDescription,#"order",nil]];
[fetchRequest setPropertiesToGroupBy:[NSArray arrayWithObjects:#"order",nil]];
//[fetchRequest setHavingPredicate:[NSPredicate predicateWithFormat:#"#count > 1"]];
//[fetchRequest setHavingPredicate:[NSComparisonPredicate predicateWithLeftExpression:maxExpression rightExpression:[NSExpression expressionForConstantValue:[NSNumber numberWithInteger:1]] modifier:NSDirectPredicateModifier type:NSGreaterThanPredicateOperatorType options:NSCaseInsensitivePredicateOption]];
NSError *error;
NSArray * array = [context executeFetchRequest:fetchRequest error:&error];
I ended up going with this for anyone interested
-(BOOL)ordersAreSaneOnDay:(NSNumber*)dayNumber forUser:(User*)user inContext:(NSManagedObjectContext*)context {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"BasicEntry"
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
[fetchRequest setResultType:NSDictionaryResultType];
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"(status == %#) && ((type != %#) && (type != %#) && (dayNumber == %#)) && ((user == NIL) || (user == %#))",[NSNumber numberWithInt:EntryStatusAlive],[NSNumber numberWithInt:EntryTypeTask],[NSNumber numberWithInt:EntryTypeCompletedTask],dayNumber,user];
[fetchRequest setPredicate:predicate];
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath: #"order"]; // Does not really matter
NSExpression *maxExpression = [NSExpression expressionForFunction: #"count:"
arguments: [NSArray arrayWithObject:keyPathExpression]];
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName: #"orderCount"];
[expressionDescription setExpression: maxExpression];
[expressionDescription setExpressionResultType: NSInteger32AttributeType];
[fetchRequest setPropertiesToFetch: [NSArray arrayWithObjects:expressionDescription,#"order",nil]];
[expressionDescription release];
[fetchRequest setPropertiesToGroupBy:[NSArray arrayWithObjects:#"order",nil]];
//[fetchRequest setHavingPredicate:[NSPredicate predicateWithFormat:#"self.order.#count > 1"]];
//[fetchRequest setHavingPredicate:[NSComparisonPredicate predicateWithLeftExpression:maxExpression rightExpression:[NSExpression expressionForConstantValue:[NSNumber numberWithInteger:1]] modifier:NSDirectPredicateModifier type:NSGreaterThanPredicateOperatorType options:NSCaseInsensitivePredicateOption]];
NSError *error;
NSArray * array = [context executeFetchRequest:fetchRequest error:&error];
array = [array filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"orderCount > 1"]];
//NSLog(#"it worked %#",array);
[fetchRequest release];
if ([array count]) return FALSE;
return TRUE;
}
I've got this working using the following:
[fetchRequest setHavingPredicate:[NSPredicate predicateWithFormat:#"$orderCount > 1"]];
Use the name of your expressionDecription as variable $orderCount.
Alternatively you can use
NSExpression *countExpression = [NSExpression expressionForVariable:#"orderCount"];
[fetchRequest setHavingPredicate:[NSPredicate predicateWithFormat:#"%# > 1", countExpression]];
Firstly, whenever I try something analogous to what you're doing, I get an error that you can't pass a to-many relationship to setPropertiesToFetch:. The NSFetchRequest documentation backs this up: "The property descriptions may represent attributes, to-one relationships, or expressions." So that's problem #1.
Problem #2 is that it appears that you can't group by a to-many relationship either (this isn't made clear in the documentation, but you get the same error and it also makes sense).
Remove "order" from the properties to fetch. Group by an attribute. Modify your main predicate to only include those attributes you group by (or just remove it). Specify "order" in your having predicate.
[fetchRequest setPropertiesToGroupBy: #[ #"???" ]];
[fetchRequest setHavingPredicate: [NSPredicate predicateWithFormat: #"self.order.#count > 1"]];
Now you'll see the request will work, but the results probably weren't what you were expecting:
- NSArray
- NSDictionary { status = #"alive", orderCount = "4" }
- NSDictionary { status = #"alive", orderCount = "9" }
- NSDictionary { status = #"alive", orderCount = "2" }
- etc...
NSDictionaryResultType doesn't actually give you anything to identify those objects by - it just gives you the values.
So your next step is to get back IDs for those OrderedEntry objects. The trick is to include an expression which will give you back the NSManagedObjectID as a key in each dictionary.
I don't know if this will actually give you improved performance at all (over just AND-ing it in to the main predicate). In my experience, one of the best things you can do to improve fetching performance is to create singleton NSPredicates and use substitution variables to set up each fetch. Predicate parsing can be expensive.
Dictionary result types can be a pain. Usually just constructing a good predicate is better. I tend only to use them for expressions (i.e. performing statistic-type calculations on the graph which return a value). If you look at all the restrictions around properties to fetch and group by and the predicate restrictions, this seems to be what Apple intend it for. I'm not even sure it's possible to do what you want to do by grouping and using a having predicate - for example, what are you going to group by? If by status (which you need to group by to include in your predicate), won't that collapse all the OrderEntries and you won't get the separate objectIDs and orderCounts? It's best to stick to the main predicate for this, not grouping and having predicates.

Resources