Skip to content
This repository has been archived by the owner on Jun 21, 2023. It is now read-only.

Remove “in” expression workaround #183

Merged
merged 3 commits into from
Mar 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions platform/darwin/docs/guides/For Style Authors.md.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ In style specification | Method, function, or predicate type | Format string syn
`at` | `objectFrom:withIndex:` | `array[n]`
`get` | `+[NSExpression expressionForKeyPath:]` | Key path
`has` | `mgl_does:have:` | `mgl_does:have:(self, 'key')`
`in` | `NSInPredicateOperatorType` | `needle IN haystack` or `haystack CONTAINS needle` or `ANY haystack = needle`
`length` | `count:` | `count({1, 2, 2, 3, 4, 7, 9})`
`!` | `NSNotPredicateType` | `NOT (p0 OR … OR pn)`
`!=` | `NSNotEqualToPredicateOperatorType` | `key != value`
Expand Down
11 changes: 10 additions & 1 deletion platform/darwin/docs/guides/Predicates and Expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ The following aggregate operators are supported:
`NSInPredicateOperatorType` | `key IN { 'iOS', 'macOS', 'tvOS', 'watchOS' }`
`NSContainsPredicateOperatorType` | `{ 'iOS', 'macOS', 'tvOS', 'watchOS' } CONTAINS key`

The following combinations of comparison operators and modifiers are supported:

`NSComparisonPredicateModifier` | `NSPredicateOperatorType` | Format string syntax
--------------------------------|-------------------------------------|---------------------
`NSAllPredicateModifier` | `NSNotEqualToPredicateOperatorType` | `ALL haystack != needle`
`NSAnyPredicateModifier` | `NSEqualToPredicateOperatorType` | `ANY haystack = needle`<br />`SOME haystack = needle`

The following comparison predicate options are supported for comparison and
aggregate operators that are used in the predicate:

Expand All @@ -65,7 +72,9 @@ aggregate operators that are used in the predicate:

Other comparison predicate options are unsupported, namely `l`
(for locale sensitivity) and `n` (for normalization). A comparison is
locale-sensitive as long as it is case- or diacritic-insensitive.
locale-sensitive as long as it is case- or diacritic-insensitive. Comparison
predicate options are not supported in conjunction with comparison modifiers
like `ALL` and `ANY`.

### Operands

Expand Down
72 changes: 64 additions & 8 deletions platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,52 @@ - (NSString *)mgl_keyPath {
@implementation NSComparisonPredicate (MGLExpressionAdditions)

- (id)mgl_jsonExpressionObject {
switch (self.comparisonPredicateModifier) {
case NSDirectPredicateModifier:
break;

case NSAllPredicateModifier:
// “ALL x != y” is logically equivalent to “NOT y IN x”.
if (self.predicateOperatorType == NSNotEqualToPredicateOperatorType) {
// https://github.com/mapbox/mapbox-gl-js/issues/9339
if (self.options) {
[NSException raise:NSInvalidArgumentException format:@"NSComparisonPredicateOptions not supported for “ALL … !=” comparisons."];
}

NSPredicate *directPredicate = [NSComparisonPredicate predicateWithLeftExpression:self.rightExpression
rightExpression:self.leftExpression
modifier:NSDirectPredicateModifier
type:NSInPredicateOperatorType
options:self.options];
NSPredicate *invertedPredicate = [NSCompoundPredicate notPredicateWithSubpredicate:directPredicate];
return invertedPredicate.mgl_jsonExpressionObject;
} else {
[NSException raise:NSInvalidArgumentException format:@"“ALL” is only supported for the “!=” operator."];
}

case NSAnyPredicateModifier:
// “ANY x = y” is logically equivalent to “y IN x”.
if (self.predicateOperatorType == NSEqualToPredicateOperatorType) {
// https://github.com/mapbox/mapbox-gl-js/issues/9339
if (self.options) {
[NSException raise:NSInvalidArgumentException format:@"NSComparisonPredicateOptions not supported for “ANY … =” comparisons."];
}

NSPredicate *directPredicate = [NSComparisonPredicate predicateWithLeftExpression:self.rightExpression
rightExpression:self.leftExpression
modifier:NSDirectPredicateModifier
type:NSInPredicateOperatorType
options:self.options];
return directPredicate.mgl_jsonExpressionObject;
} else {
[NSException raise:NSInvalidArgumentException format:@"“ANY” or “SOME” is only supported for the “=” operator."];
}

default:
[NSException raise:NSInvalidArgumentException
format:@"NSComparisonPredicateModifier:%lu is not supported.", (unsigned long)self.comparisonPredicateModifier];
}

NSString *op;
switch (self.predicateOperatorType) {
case NSLessThanPredicateOperatorType:
Expand Down Expand Up @@ -115,14 +161,24 @@ - (id)mgl_jsonExpressionObject {
return @[op, leftHandPredicate.mgl_jsonExpressionObject, rightHandPredicate.mgl_jsonExpressionObject];
}
case NSInPredicateOperatorType: {

NSExpression *matchExpression = [NSExpression expressionForFunction:@"MGL_MATCH"
arguments:@[self.leftExpression,
self.rightExpression,
[NSExpression expressionForConstantValue:@YES],
[NSExpression expressionForConstantValue:@NO]]];

return matchExpression.mgl_jsonExpressionObject;
// An “in” expression comparing two string literals is unfortunately
// misinterpreted as a legacy “in” filter due to ambiguity. Wrap one
// argument in a “literal” expression to force an expression.
// https://github.com/mapbox/mapbox-gl-js/issues/9373#issuecomment-594537077
if (self.leftExpression.expressionType == NSConstantValueExpressionType &&
self.rightExpression.expressionType == NSConstantValueExpressionType &&
[self.leftExpression.constantValue isKindOfClass:[NSString class]] &&
[self.rightExpression.constantValue isKindOfClass:[NSString class]]) {
NSExpression *rightLiteralExpression = [NSExpression expressionWithFormat:@"MGL_FUNCTION('literal', %@)", self.rightExpression];
NSPredicate *literalPredicate = [NSComparisonPredicate predicateWithLeftExpression:self.leftExpression
rightExpression:rightLiteralExpression
modifier:NSDirectPredicateModifier
type:NSInPredicateOperatorType
options:self.options];
return literalPredicate.mgl_jsonExpressionObject;
}
op = @"in";
break;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏🏼 this is simple

}
case NSContainsPredicateOperatorType: {
NSPredicate *inPredicate = [NSComparisonPredicate predicateWithLeftExpression:self.rightExpression
Expand Down
1 change: 1 addition & 0 deletions platform/darwin/src/NSPredicate+MGLAdditions.mm
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ @implementation NSPredicate (MGLAdditions)
@"<=": @(NSLessThanOrEqualToPredicateOperatorType),
@">": @(NSGreaterThanPredicateOperatorType),
@">=": @(NSGreaterThanOrEqualToPredicateOperatorType),
@"in": @(NSInPredicateOperatorType),
};

+ (instancetype)predicateWithMGLJSONObject:(id)object {
Expand Down
Loading