[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:
committed by
Minh Nguyễn
parent
363610c7df
commit
bc6f434a88
@@ -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
|
||||
|
||||
@@ -54,6 +54,7 @@ static NSDictionary * const MGLPredicateOperatorTypesByJSONOperator = @{
|
||||
@"<=": @(NSLessThanOrEqualToPredicateOperatorType),
|
||||
@">": @(NSGreaterThanPredicateOperatorType),
|
||||
@">=": @(NSGreaterThanOrEqualToPredicateOperatorType),
|
||||
@"in": @(NSInPredicateOperatorType),
|
||||
};
|
||||
|
||||
+ (instancetype)predicateWithMGLJSONObject:(id)object {
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user