NSPredicate to-many relationships - ios

How can I find all the categories that have subcategories with more than 1 items?
I am using the following code, but I get "multiple to-many keys not allowed here".
Do I need to use a SUBQUERY?
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Categories"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY subcategories.items > 0"];
[request setPredicate:predicate];
Help! :)

Based on my experience, you should change the predicate format string in
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY subcategories.items > 0"];
from
#"ANY subcategories.items > 0"
into
#"subcategories.items.#count > 0"
Note that the ANY keyword needs to go as well, or you'll be querying the property the wrong way.

In your NSPredicate you should use subcategories.items.#count >0 instead.
Sorr, that would work only if categories-subcategories wer one - to one.
Really,what you should do, is delete all the SubCategories wich have no items, I think.
Than tou should just check if there are any subcategories and it would mean that they are not empty.
subcategories.#count >0

You can only use one to-many relationship in a SQL backend. This will do it, but you have to add the "back-link" into an ordered set (to preserve order if you use sorting - and uniqueness because you may get multiple subcategories pointing back to the same category). Not ideal, but not bad, considering the limitations.
// Search for all subcategories with item count > 0
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Subcategory"];
request.predicate = [NSPredicate predicateWithFormat:#"items.#count > 0"];
NSError *error = nil;
// They should have an inverse relationship to their category...
// We have to iterate and insert them in to a set, but there is no additional
// disk access.
NSMutableOrderedSet *results = [NSMutableOrderedSet orderedSet];
[[self.projectDatabase.managedObjectContext executeFetchRequest:request error:&error] enumerateObjectsUsingBlock:^(Subcategory *obj, NSUInteger idx, BOOL *stop) {
[results addObject:obj.category];
}];
EDIT
Unfortunately, I don't think this is possible with the SQL store. First, let me stress that I am not anywhere close to an SQL expert, and I usually try to design my CoreData stores so that these types of queries are not necessary. However, the following SUBQUERY works with an InMemory store, so it's a somewhat properly formatted subquery.
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"SUBQUERY(subcategories, $s, $s.items.#count > 0).#count > 0"];
However, that predicate gives an error when using an SQL store. The docs do say there are a fair number of limitations when using predicates with SQL stores, so I'm neither surprised, nor inclined to spend too much more time researching this.
Keypath containing KVC aggregate where there shouldn't be one; failed
to handle $s.items.#count

Related

Core Data predicate : unimplemented SQL generation for predicate

Basically I got 3 entities in my data model : Brand, Model and Trim.
A brand has a one-to-many relationship with Model called "models". (one brand have multiple models but a model has only one brand)
A model has a many-to-many relationship with Trim called "trims". (a model can have multiple trims, and a trim can have multiple models)
Having an array of trims objects, I would like to get all the brands having a model which "contains" at least one trim contained inside this array.
So here is my predicate for the fetch request :
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"Brand"];
[NSPredicate predicateWithFormat:#"ANY models.trims IN %#", arrayOfTrims];
And here is the error I'm getting :
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'unimplemented SQL generation for predicate : (ANY models.trims IN {<Trim: 0x8e60340> (entity: Trim; id: 0x8e62f10 <x-coredata://BCD28560-AED5-4409-9A35-1925001773E6/Trim/p8>
I'm kinda new to Core Data and I have no idea what I'm doing wrong.
Hope someone can help,
Thanks a lot.
"ANY" in a Core Data predicate works only for a single to-many relationship.
Since your query involves two to-many relationships, you have to use a SUBQUERY:
[NSPredicate predicateWithFormat:#"SUBQUERY(models, $m, ANY $m.trims IN %#).#count > 0",
arrayOfTrims];
This exception is also raised if one of the predicates uses a column (i.e. field) name that does not exist. Totally misleading error message...
When you use a predicate in a CoreData operation, the predicate gets translated into SQL. That translation is not possible for all NSPredicate operations, you've hit one that isn't. My suggestion would be something along the lines of:
NSMutableArray* predicates = [NSMutableArray new];
for(NSString* trim in arrayOfTrims)
{
[predicates addObject:[NSPredicate predicateWithFormat:#"%# IN models.trims", trim]];
}
NSPredicate* predicate = [NSCompoundPredicate orPredicateWithSubpredicates:predicates];
The keyword IN can be used but you cannot apply ANY at the same time as that does not make sense when you turn it into SQL.
The predicate you are most likely looking for is:
[NSPredicate predicateWithFormat:#"models.trims IN %#", arrayOfTrims];
But that isn't going to work in this case either because you are going across a relationship. So what you need to do is reverse the whole thing:
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Model"];
[request setPredicate:[NSPredicate predicateWithFormat:#"trims in %#", arrayOfTrims]];
NSError *error = nil;
NSArray *modelArray = [moc executeFetchRequest:request error:&error];
if (!modelArray) {
NSLog(#"Error: %#\n%#", [error localizedDescription], [error userInfo]);
}
NSArray parentObjectArray = [modelArray valueForKey:#"${PARENT_RELATIONSHIP_NAME}"];
Basically you are fetching the child objects to satisfy your ANY and then using KVC to retrieve the parent objects that you care about.
In my case I got this error because I made a silly mistake and didn't have the argument in the right data type.
I was trying to do:
let firstPredicate = NSPredicate(format: "firstName BEGINSWITH[cd] %#", firstNameField.stringValue)
and forgot to put the ".stringValue."

NSpredicate "'NSInvalidArgumentException', reason: 'keypath SELF.SIZE not found in entity <NSSQLEntity Files id=1>'"

I have a problem I can't figure out so I'm hoping in some help :)
I have a dore data db with the metadata of a filesystem. I want to retrieve the number of files > 10 kbytes, so I'm using a predicate statement. Unfortunately the app crashes with:
'NSInvalidArgumentException', reason: 'keypath SELF.SIZE not found in entity '
Here the code:
// Initialize NSFetchrequest and define entity
NSError *error;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *file = [NSEntityDescription entityForName:#"Files" inManagedObjectContext:appDelegate.managedObjectContext];
[request setEntity:file];
// Query on Number of files between 0 and 10000 bytes
NSNumber * tenk = #10000;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"any self.size < %#", tenk];
[request setPredicate:predicate];
fetchResult = [appDelegate.managedObjectContext executeFetchRequest:request error:&error];
int primaBar = fetchResult.count;
NSLog(#"Files smaller than 10k: %d :",primaBar);
The code works if instead of comparing NSnumbers I compare strings (e.g. I'm able to retrieve the number of files with extension "pdf" with the predicate #"any self.extension contains %#",extension ) so I believe that I'm doing something wrong accessing the NSnumber self.size.
Thanks in advance for any hint.
dom
Mistery Solved.
-From the documentation:
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Predicates/Articles/pSyntax.html
...all the way to the bottom you can read:
/*
Reserved Words
The following words are reserved:
AND, OR, IN, NOT, ALL, ANY, SOME, NONE, LIKE, CASEINSENSITIVE, CI, MATCHES, CONTAINS, BEGINSWITH, ENDSWITH, BETWEEN, NULL, NIL, SELF, TRUE, YES, FALSE, NO, FIRST, LAST, SIZE, ANYKEY, SUBQUERY, CAST, TRUEPREDICATE, FALSEPREDICATE
.
*/
You can see that size is a reserved work, and this did interfere with the execution of the predicate.
At the end escaping it with ' ' like this:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"any self.'size' < %#" , tenk];
made my query work.
I'm new to IOS programming, but this was a tricky one (learning to code in the hardest way...).
Thanks for the brainstorming !
dom
Actually this is syntax.
[NSPredicate predicateWithFormat:#"%K < %#",
attributeName, attributeValue];
So you can Try this.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%K < %#",#"\size" ,tenk];
I think, Here size is attribute name.
Update:
see Identifiers concept. I've updated my answer accordingly.
SELF refers to the current object being used.
use this
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"size < %#", tenk];
Note: this works only if there is a database attribute named size which stores the size of each file.
try NSPredicate *predicate = [NSPredicate predicateWithFormat:#"size < %#", tenk];

How to add two separate unrelated predicates to a single fetchrequest in NSFetchedResultsController

I have a simple app which has a Table View controller performing a NSFetchedResultsController based fetchRequest and my fetchRequest has, or needs to have two separate predicates that are completely independent.
One is for searching in the searchBar and one is for displaying information in the actual table view.
When I have both active, only one works.
Here's the predicates:
if ([self.personSearchBar.text length] > 0)
{
NSPredicate *personPredicate = [NSPredicate predicateWithFormat:#"ANY name CONTAINS[c] %#", self.personSearchBar.text];
[fetchRequest setPredicate:personPredicate];
}
NSPredicate *p = [NSPredicate predicateWithFormat:#"transactions.whoBy.#count !=0"];
[fetchRequest setPredicate:p];
So as you can see, there are two independent predicates, but both need to be active and working because in this state right now, the searching does not actually work but if I comment out the NSPredicate *p, it does.
Should I be using compound predicates and if so, how would I string these independent predicates together?
Use an NSCompoundPredicate to combine them. Add each of your predicates to an array as you determine that you need them. Then use andPredicateWithSubpredicates: or orPredicateWithSubpredicates: depending on whether you require both or either of the predicates to apply to all of the results.

How to write a Core Data predicate for objects which are related to an object in a to-many relationship

EntityA has a to-many relationship to EntityB (and EntityB has a to-one relationship to EntityA). I have an array of EntityBs (or more accurately, I have an NSArray that contains instances of NSManagedObject which represent EntityB). I want to create an NSFetchRequest that will fetch all EntityAs that have a relationship to at least one of the EntityBs in the array. How do I write the predicate for this fetch request?
The following works, but I think it is sub-optimal; it's hard to grok and I'm sure there must be a better way of expressing this:
NSArray *entityBs = ...;
NSMutableArray *containsEntityBSubPredicates = [NSMutableArray new];
for (NSManagedObject *entityB in entityBs) {
[containsEntityBSubPredicates addObject:[NSPredicate predicateWithFormat:#"%# IN entityBs", entityB]];
}
NSPredicate *containsEntityBsPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:containsEntityBSubPredicates];
I've also tried this, but it doesn't work:
NSArray *entityBs = ...;
NSPredicate *containsEntityBsPredicate = [NSPredicate predicateWithFormat:#"ANY %# in entityBs", entityBs];
Am I missing a simpler solution?
You were almost there with your predicate, just switch the parameters:
[NSPredicate predicateWithFormat:#"ANY entityBs in %#", entityBArray];
Look at Apple's example code with IN here for further explanation.

NSPredicate 'The left hand side for an ALL or ANY operator must be either an NSArray or NSSet'

Not totally sure why this isn't working now, i thought it had been working previously. Does anyone see an issue with this FetchRequest construction?
- (NSArray *)entriesForDate:(NSDate *)date {
NSFetchRequest *request = [[NSFetchRequest alloc]initWithEntityName:#"Entry"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY addedOn.unique like %#", [T3Utility identifierForDate:date]];
request.predicate = predicate;
NSError *error = nil;
NSArray *matches = [self.database.managedObjectContext executeFetchRequest:request error:&error];
return matches;
}
Again, i'm 99% sure that this code has been working until recently, so im thinking perhaps there's something else going on in my code somewhere . . .but when i run it through the debugger this is where is hangs. Here is my error:
The left hand side for an ALL or ANY operator must be either an NSArray or NSSet
Any ideas?
thanks!
It seems addedOn is not a to-many relationship. Make sure you use the correct names of your attributes / relationships in the predicate. Also, note that you cannot use a to-one relationship in a "ANY" predicate.

Resources