NSPredicate with OR returning error - ios

I have a problem with the following predicate:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(creatorUserRecordID == %#) OR (toUser == %#)", userId, userId];
When I use it in query CKQuery *query = [[CKQuery alloc] initWithRecordType:#"Message" predicate:predicate]; I have an error that says: 'CKException', reason: 'Unexpected expression'.
When I use these two seperately like this:
NSPredicate *predicate1 = [NSPredicate predicateWithFormat:#"(creatorUserRecordID == %#)", userId];
NSPredicate *predicate2 = [NSPredicate predicateWithFormat:#"(toUser == %#)", userId];
And then performing query with one of these predicates it works fine. I also tried using NSCompoundPredicate but the result is always the same... Any ideas?

The documentation for CKQuery lists all of the valid predicate syntax. Oddly, under "Basic compound predicates" it lists NOT, AND, and &&. OR and || are not listed and apparently are not supported for CloudKit query predicates.

Related

NSPredicate with ANY and "=nil" doesn't work

This condition works
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"ANY region.beacons.major = %d",rangedBeacon.major.intValue];
But this not
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"ANY region.beacons.minor = nil"];
beacons is a list inside region object. Major and minor have type NSNumber
You may try this:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY region.beacons.minor = $minor"];
predicate = [predicate predicateWithSubstitutionVariables:
[NSDictionary dictionaryWithObject:[NSNull null] forKey: #"minor"]];
I found solution
Because of ANY and "=nil" can't be in the same condition (explanation is here Core Data, NSPredicate, ANY key.path == nil)? solution of this problem looks like:
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"SUBQUERY(region.beacons, $x, $x.minor == nil)"];

Filtering values with NSNumber == nil in NSPredicate

I'm trying select values by property with type NSNumber, if property==intValue or property==nil.
But this code ignores values with property==nil.
What the point?
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"(ANY region.beacons.major == %d OR ANY region.beacons.major = nil)
AND (ANY region.beacons.minor == %d OR ANY region.beacons.minor = nil)",
rangedBeacon.major.intValue, rangedBeacon.minor.intValue];
UPD: The most simple doesn't work too:
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"ANY region.beacons.minor = nil"];
UPD 2: Probably problem because of ANY+nil
According to this answer Core Data, NSPredicate, ANY key.path == nil ANY select only NOTNULL values.
Simply test for one of the properties of minor; for example:
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"region.beacons.minor.length = 0"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"region.beacons.minor.stringValue = nil"];
EDIT I recognized, that you should remove the ANY
If minor is nil, any call would return in nil as well, or 0 for numeric types.
For one thing, I notice you're using the assignment operator = instead of equality == while comparing with nil.
Also, try comparing with the Singleton NSNull instance [NSNull null].

NSPredicate with Multiple Conditions

I am trying to create a NSPredicate with multiple conditions. I've found several solutions, but none of them appear to be working with my method. The best looking one I've found is below.
This is my single predicate method, and it works just fine:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name contains[c] %#",
searchText];
filteredBusinesses = [businesses filteredArrayUsingPredicate:predicate];
Here is my edited version with multiple conditions. I'm not sure whats going wrong. Any ideas?
NSPredicate *p1 = [NSPredicate predicateWithFormat:#"name contains[c] %#", searchText];
NSPredicate *p2 = [NSPredicate predicateWithFormat:#"businessArea contains[c] %#",
searchText];
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:#[p1, p2]];
filteredBusinesses = [businesses filteredArrayUsingPredicate:predicate];
You can try this
NSPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:#[p1, p2]];
Addition to #Nikunj's answer, you can also use NSCompoundPredicate for your AND operations like this.
Obj-C - AND
NSPredicate *predicate1 = [NSPredicate predicateWithFormat:#"X == 1"];
NSPredicate *predicate2 = [NSPredicate predicateWithFormat:#"X == 2"];
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:#[predicate1, predicate2]];
Swift - AND
let predicate1:NSPredicate = NSPredicate(format: "X == 1")
let predicate2:NSPredicate = NSPredicate(format: "Y == 2")
let predicate:NSPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [predicate1,predicate2] )
Swift 3 - AND
let predicate1 = NSPredicate(format: "X == 1")
let predicate2 = NSPredicate(format: "Y == 2")
let predicateCompound = NSCompoundPredicate.init(type: .and, subpredicates: [predicate1,predicate2])
Nothing looks wrong in the code you posted, which means the error is probably coming from when you evaluate the predicate by filtering the array.
Since the first predicate works, the problem lies with the businessArea key path.
Filtering the array would throw an exception if:
There's an object in the array that doesn't have a businessArea value (as in, it's not an object that has a -businessArea method)
The object does have a businessArea value, but the value is neither an NSString nor nil
NSCompoundPredicate received a facelift. Just in case someone's looking for a Swift 5+ version:
AND
let predicatesAND = NSCompoundPredicate(andPredicateWithSubpredicates:[pred1,pred2])
OR
let predicatesOR = NSCompoundPredicate(orPredicateWithSubpredicates:[pred1,pred2])
First off, I suspect you may have a flaw in your two basic predicates. Judging by your object name "businesses", it is a collection i.e NSArray, NSSet or NSDictionay of items. In that case, "name contains[c] 'mmm'" has no meaning when evaluated for the whole collection, and should crash. It is missing a collection operator (ANY, ALL, SOME) , e.g. "ANY name contains[c] 'mmm'".
Ah... now I see... you're using the predicate to filter an NSArray, which means the predicate is executed on each item separately. So your predicates are alright.
Now... the simplest way to combine your predicates is at the very format-string level! what's wrong with
NSPredicate *p1 = [NSPredicate predicateWithFormat:#"name contains[c] %# AND businessArea contains[c] %#", nameToSearch, areaToSearch];
If you insist on doing it the hard way, than building your compound NSPredicate should have worked too - only you employed the same searchText to both conditions, which may caused the predicate to never match.
My advice to you, place a breakpoint on the line evaluating the filtered array and stop just before the filtering, and in the debugger print the predicate description, using the debugger command po p1 you will see there what is the final form of your predicate, and will be able to determine if you built it correctly.

NSPredicate too many arguments. Hard to read it

I just faced with some issue that is connected to clear code. I use predicates in many modules of my projects. When I work with core data the NSPredicates are really helpful for retrieving filtered object.
But the predicating is good for logic but not for code readability as I use it.
So let's see on this example:
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"(ANY option.playerzoneID == %d) \
AND (ANY option.gameId == %#) \
AND (team.teamID == %#) \
AND (league.leagueID == %#) \",
zoneIdType,
SELECTED_GAME.gameID,
SELECTED_TEAM.teamID,
SELECTED_LEAGUE.leagueID];
When I look on this predicate I am little confused about this structure even in case that this predicate I have written one week ago.
Any suggestion how to make this code more readable?
I think it will be better to have something like this:
[predicate setParametr:#"gameID == %#", SELECTED_GAME.gameID];
[predicate setParametr:#"league.leagueID == %#", SELECTED_LEAGUE.leagueID];
You can explore the following:
+ (NSPredicate *)andPredicateWithSubpredicates:(NSArray *)subpredicates
+ (NSPredicate *)orPredicateWithSubpredicates:(NSArray *)subpredicates
+ (NSPredicate *)notPredicateWithSubpredicate:(NSArray *)subpredicate
With this you can actually form short predicates and join them together with arrays. So, you don't exactly get what you wished for, but its a near shot.
NSPredicate *predicate1 = [NSPredicate predicateWithFormat:#"(ANY option.playerzoneID == %d)", zoneIdType];
NSPredicate *predicate2 = [NSPredicate predicateWithFormat:#"(ANY option.gameId == %d)", SELECTED_GAME.gameID];
NSPredicate *predicate3 = [NSPredicate predicateWithFormat:#"(ANY team.teamID == %#)", SELECTED_TEAM.teamID];
NSCompoundPredicate *compoundANDPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:#[predicate1, predicate2, predicate3]];
Read more about these here:http://nshipster.com/nspredicate/
Edit: Using OR Predicates along with AND Predicates:
Let's assume: you have this predicate to add as OR:
NSPredicate *predicate4 = [NSPredicate predicateWithFormat:#"(ANY team.teamName == %#)", SELECTED_TEAM.teamName];
So, you can group your AND and OR predicates into one compound predicate (compoundANDPredicate as shown above) and then use
+ (NSPredicate *)orPredicateWithSubpredicates:(NSArray *)subpredicates
So it becomes:
[NSCompoundPredicate orPredicateWithSubpredicates:#[compoundANDPredicate, predicate4]];

NSPredicate find an array inside db

I have the DB that contains an
NSString *Number
NSSet *Values
I can easily get all the results based on the string Number with the following:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"Number LIKE[c] %#", numberValue];
NSArray *result = [DB MR_findAllWithPredicate:predicate inContext:_context];
If instead of Number I want to pass an Array of values, how can I find in db all the results that contain the given array (NSSet)?
NSPredicate *predicate = [NSPredicate predicateWithFormat: ????
NSArray *result = [DB MR_findAllWithPredicate:predicate inContext:_context];
UPDATE
Based on Igoris answer :
[NSPredicate predicateWithFormat:#“(Values.#count == %d) AND (SUBQUERY(Values, $x, $x IN %#).#count == %d)", allValues.count, allValues, allValues.count];
This seem to work but it doesn t return all the values in result.
Just use IN:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"Number IN[c] %#", numberValues];
Where numberValues is NSArray containing numbers you are looking for
UPDATE:
After data structure clarifications I came up with the following predicate to get objects:
[NSPredicate predicateWithFormat:#“(Values.#count == %d) AND (SUBQUERY(Values, $x, $x IN %#).#count == %d)", allValues.count, allValues, allValues.count];
How about ANY and NONE?
[NSPredicate predicateWithFormat:#"ANY number == %#", numberValues];

Resources