[ios, macos] Removed in expression workaround

Removed the match-based in expression workaround in favor of real support for an in expression operator. Avoid interpreting “literal IN literal” predicates as legacy filters.
This commit is contained in:
Minh Nguyễn
2020-02-26 12:23:32 -08:00
committed by Minh Nguyễn
parent 363610c7df
commit bc6f434a88
5 changed files with 89 additions and 50 deletions

View File

@@ -115,14 +115,24 @@
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;
}
case NSContainsPredicateOperatorType: {
NSPredicate *inPredicate = [NSComparisonPredicate predicateWithLeftExpression:self.rightExpression

View File

@@ -54,6 +54,7 @@ static NSDictionary * const MGLPredicateOperatorTypesByJSONOperator = @{
@"<=": @(NSLessThanOrEqualToPredicateOperatorType),
@">": @(NSGreaterThanPredicateOperatorType),
@">=": @(NSGreaterThanOrEqualToPredicateOperatorType),
@"in": @(NSInPredicateOperatorType),
};
+ (instancetype)predicateWithMGLJSONObject:(id)object {

View File

@@ -216,76 +216,104 @@
mustRoundTrip:NO];
}
{
NSArray *expected = @[@"match", @[@"id"], @[@6, @5, @4, @3], @YES, @NO];
NSArray *expected = @[@"in", @[@"id"], @[@"literal", @[@6, @5, @4, @3]]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier IN { 6, 5, 4, 3}"];
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($featureIdentifier, { 3, 4, 5, 6 }, YES, NO) == YES"];
auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter;
NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter);
XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:expected], predicate);
[self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:expected]
mustRoundTrip:YES];
}
{
NSArray *expected = @[@"match", @[@"to-number", @[@"id"]], @[@3002970001, @3004140052, @3002950027, @3002970033], @YES, @NO];
NSArray *expected = @[@"in", @[@"to-number", @[@"id"]], @[@"literal", @[@3002970001, @3004140052, @3002950027, @3002970033]]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST($featureIdentifier, 'NSNumber') IN { 3002970001, 3004140052, 3002950027, 3002970033 }"];
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST($featureIdentifier, 'NSNumber'), { 3002950027, 3002970001, 3002970033, 3004140052 }, YES, NO) == YES"];
auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter;
NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter);
XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:expected], predicate);
[self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:expected]
mustRoundTrip:YES];
}
{
NSArray *expected = @[@"!", @[@"match", @[@"get", @"x"], @[@6, @5, @4, @3], @YES, @NO]];
NSArray *expected = @[@"!", @[@"in", @[@"get", @"x"], @[@"literal", @[@6, @5, @4, @3]]]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT x IN { 6, 5, 4, 3}"];
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"NOT MGL_MATCH(x, { 3, 4, 5, 6 }, YES, NO) == YES"];
auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter;
NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter);
XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:expected], predicate);
[self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:expected]
mustRoundTrip:YES];
}
{
NSArray *expected = @[@"match", @[@"get", @"a"], @[@"b", @"c"], @YES, @NO];
NSArray *expected = @[@"in", @[@"get", @"a"], @[@"literal", @[@"b", @"c"]]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a IN { 'b', 'c' }"];
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(a, { 'b', 'c' }, YES, NO) == YES"];
auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter;
NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter);
XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:expected], predicate);
[self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:expected]
mustRoundTrip:YES];
}
{
NSArray *expected = @[@"match", @[@"geometry-type"], @[@"LineString", @"Polygon"], @YES, @NO];
NSArray *expected = @[@"in", @[@"geometry-type"], @[@"literal", @[@"LineString", @"Polygon"]]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ IN %@", [NSExpression expressionForVariable:@"geometryType"], @[@"LineString", @"Polygon"]];
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($geometryType, { 'LineString', 'Polygon' }, YES, NO) == YES"];
auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter;
NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter);
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"%@ IN {'LineString', 'Polygon'}", [NSExpression expressionForVariable:@"geometryType"]];
XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:expected], predicateAfter);
[self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:expected]
mustRoundTrip:YES];
}
{
NSArray *expected = @[@"match", @[@"get", @"x"], @[@6, @5, @4, @3], @YES, @NO];
NSArray *expected = @[@"in", @[@"get", @"x"], @[@"literal", @[@6, @5, @4, @3]]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"{ 6, 5, 4, 3 } CONTAINS x"];
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(x, { 3, 4, 5, 6 }, YES, NO) == YES"];
auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter;
NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter);
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"x IN { 6, 5, 4, 3 }"];
XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:expected], predicateAfter);
[self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:expected]
mustRoundTrip:YES];
}
{
NSArray *expected = @[@"match", @[@"geometry-type"], @[@"LineString", @"Polygon"], @YES, @NO];
NSArray *expected = @[@"in", @[@"geometry-type"], @[@"literal", @[@"LineString", @"Polygon"]]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ CONTAINS %@", @[@"LineString", @"Polygon"], [NSExpression expressionForVariable:@"geometryType"]];
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($geometryType, { 'LineString', 'Polygon' }, YES, NO) == YES"];
auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter;
NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter);
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"%@ IN {'LineString', 'Polygon'}", [NSExpression expressionForVariable:@"geometryType"]];
XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:expected], predicateAfter);
[self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:expected]
mustRoundTrip:YES];
}
{
NSArray *expected = @[@"match", @[@"id"], @[@6, @5, @4, @3], @YES, @NO];
NSArray *expected = @[@"in", @[@"id"], @[@"literal", @[@6, @5, @4, @3]]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"{ 6, 5, 4, 3} CONTAINS $featureIdentifier"];
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($featureIdentifier, { 3, 4, 5, 6 }, YES, NO) == YES"];
auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter;
NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter);
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"$featureIdentifier IN {6, 5, 4, 3}"];
XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:expected], predicateAfter);
[self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:expected]
mustRoundTrip:YES];
}
{
NSArray *expected = @[@"in", @"📍", @[@"get", @"name"]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"'📍' IN name"];
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:expected], predicate);
[self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:expected]
mustRoundTrip:YES];
}
{
NSArray *expected = @[@"in", @"📍", @[@"get", @"name"]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name CONTAINS '📍'"];
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"'📍' IN name"];
XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:expected], predicateAfter);
[self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:expected]
mustRoundTrip:YES];
}
{
NSArray *expected = @[@"in", @"📍", @[@"literal", @"Pinole"]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"'📍' IN 'Pinole'"];
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:expected], predicate);
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:predicate.mgl_filter], [NSPredicate predicateWithValue:NO]);
}
{
NSArray *expected = @[@"in", @"📍", @[@"literal", @"Pinole"]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"'Pinole' CONTAINS '📍'"];
XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"'📍' IN 'Pinole'"];
XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:expected], predicateAfter);
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:predicate.mgl_filter], [NSPredicate predicateWithValue:NO]);
}
}

View File

@@ -6,7 +6,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
### Styles and rendering
* Added the `in` expression function for testing whether a value is included in an array expression or whether a string is a substring of another string. Use this function in expressions in style JSON or with the `MGL_FUNCTION()` syntax in an `NSExpression` format string. ([#16162](https://github.com/mapbox/mapbox-gl-native/pull/16162))
* The `IN` and `CONTAINS` predicate operators can now test whether a string is a substring of another string. ([#16162](https://github.com/mapbox/mapbox-gl-native/pull/16162), [#183](https://github.com/mapbox/mapbox-gl-native-ios/pull/183))
* Added the `within` expression function for testing whether the evaluated feature lies within the given GeoJSON object. Use this function in expressions in style JSON or with the `MGL_FUNCTION()` syntax in an `NSExpression` format string. ([#16157](https://github.com/mapbox/mapbox-gl-native/pull/16157))
* Added the `MGLLineStyleLayer.lineSortKey` and `MGLFillStyleLayer.fillSortKey` properties. ([#179](https://github.com/mapbox/mapbox-gl-native-ios/pull/179), [#16194](https://github.com/mapbox/mapbox-gl-native/pull/16194), [#16220](https://github.com/mapbox/mapbox-gl-native/pull/16220))
* The `MGLSymbolStyleLayer.iconTextFit` property now respects the cap insets of any [nine-part stretchable image](https://developer.apple.com/documentation/uikit/uiimage#1658362) passed into the `-[MGLStyle setImage:forName:]` method. You can define the stretchable area in Xcode’s asset catalog or by calling the `-[UIImage resizableImageWithCapInsets:]` method. ([#182](https://github.com/mapbox/mapbox-gl-native-ios/pull/182))

View File

@@ -6,7 +6,7 @@
* Added the `-[MGLMapViewDelegate mapView:shouldRemoveStyleImage:]` method for optimizing style image caching. ([#14769](https://github.com/mapbox/mapbox-gl-native/pull/14769))
* Added the `image` expression function for converting an image name into a style image. Use this function in expressions in style JSON or with the `MGL_FUNCTION()` syntax in an `NSExpression` format string. Image expressions are compatible with the `mgl_attributed:` expression function and `MGLAttributedExpression` classes for embedding icons inline in text labels. ([#15877](https://github.com/mapbox/mapbox-gl-native/pull/15877), [#15937](https://github.com/mapbox/mapbox-gl-native/pull/15937))
* Added the `in` expression function for testing whether a value is included in an array expression or whether a string is a substring of another string. Use this function in expressions in style JSON or with the `MGL_FUNCTION()` syntax in an `NSExpression` format string. ([#16162](https://github.com/mapbox/mapbox-gl-native/pull/16162))
* The `IN` and `CONTAINS` predicate operators can now test whether a string is a substring of another string. ([#16162](https://github.com/mapbox/mapbox-gl-native/pull/16162), [#183](https://github.com/mapbox/mapbox-gl-native-ios/pull/183))
* Added the `within` expression function for testing whether the evaluated feature lies within the given GeoJSON object. Use this function in expressions in style JSON or with the `MGL_FUNCTION()` syntax in an `NSExpression` format string. ([#16157](https://github.com/mapbox/mapbox-gl-native/pull/16157), [#16194](https://github.com/mapbox/mapbox-gl-native/pull/16194), [#16220](https://github.com/mapbox/mapbox-gl-native/pull/16220))
* Added the `MGLSymbolStyleLayer.textWritingModes` layout property. This property can be set to `MGLTextWritingModeHorizontal` or `MGLTextWritingModeVertical`. ([#14932](https://github.com/mapbox/mapbox-gl-native/pull/14932))
* Added the `MGLLineStyleLayer.lineSortKey` and `MGLFillStyleLayer.fillSortKey` properties. ([#179](https://github.com/mapbox/mapbox-gl-native-ios/pull/179))