From 23f0222fd973956c5508f8ae879d8ce97c1b09c0 Mon Sep 17 00:00:00 2001 From: Fabian Guerra Soto Date: Thu, 12 Apr 2018 17:43:01 -0400 Subject: [ios, macos] Add predicate like expressions to NSExpression (#11632) * [ios, macos] Add NSPredicate between function. * [ios, macos] Add NSPredicate 'IN' expression filter support . * [ios, macos] Add NSPredicate 'CONTAINS' expression filter support. * [ios, macos] Refactor NSPredicate operators. * [ios, macos] Simplify NSPredicate's expression conversion code. * [ios, macos] Make 'match' the default predicate conversion option. --- .../src/NSComparisonPredicate+MGLAdditions.mm | 37 +++++++++++- platform/darwin/src/NSPredicate+MGLAdditions.mm | 49 ++++++++++++++-- platform/darwin/test/MGLPredicateTests.mm | 65 ++++++++++++++++++++++ 3 files changed, 144 insertions(+), 7 deletions(-) diff --git a/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm b/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm index 5b54d66aeb..c39e987d85 100644 --- a/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm +++ b/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm @@ -318,14 +318,45 @@ case NSNotEqualToPredicateOperatorType: op = @"!="; break; + case NSBetweenPredicateOperatorType: { + op = @"all"; + NSArray *limits = self.rightExpression.constantValue; + NSPredicate *leftHandPredicate = [NSComparisonPredicate predicateWithLeftExpression:limits[0] + rightExpression:self.leftExpression + modifier:NSDirectPredicateModifier + type:NSLessThanOrEqualToPredicateOperatorType + options:0]; + NSPredicate *rightHandPredicate = [NSComparisonPredicate predicateWithLeftExpression:self.leftExpression + rightExpression:limits[1] + modifier:NSDirectPredicateModifier + type:NSLessThanOrEqualToPredicateOperatorType + options:0]; + return @[op, leftHandPredicate.mgl_jsonExpressionObject, rightHandPredicate.mgl_jsonExpressionObject]; + } + case NSInPredicateOperatorType: { + NSMutableArray *elements = [NSMutableArray arrayWithObjects:@"match", self.leftExpression.mgl_jsonExpressionObject, nil]; + NSArray *optionsExpressions = self.rightExpression.constantValue; + for (id object in optionsExpressions) { + id option = ((NSExpression *)object).mgl_jsonExpressionObject; + [elements addObject:option]; + [elements addObject:@YES]; + } + [elements addObject:@NO]; + return elements; + } + case NSContainsPredicateOperatorType: { + NSPredicate *inPredicate = [NSComparisonPredicate predicateWithLeftExpression:self.rightExpression + rightExpression:self.leftExpression + modifier:self.comparisonPredicateModifier + type:NSInPredicateOperatorType + options:self.options]; + return inPredicate.mgl_jsonExpressionObject; + } case NSMatchesPredicateOperatorType: case NSLikePredicateOperatorType: case NSBeginsWithPredicateOperatorType: case NSEndsWithPredicateOperatorType: - case NSInPredicateOperatorType: case NSCustomSelectorPredicateOperatorType: - case NSContainsPredicateOperatorType: - case NSBetweenPredicateOperatorType: [NSException raise:NSInvalidArgumentException format:@"NSPredicateOperatorType:%lu is not supported.", (unsigned long)self.predicateOperatorType]; } diff --git a/platform/darwin/src/NSPredicate+MGLAdditions.mm b/platform/darwin/src/NSPredicate+MGLAdditions.mm index 8fe5735bf9..ed48c794aa 100644 --- a/platform/darwin/src/NSPredicate+MGLAdditions.mm +++ b/platform/darwin/src/NSPredicate+MGLAdditions.mm @@ -280,7 +280,7 @@ NSArray *MGLSubpredicatesWithJSONObjects(NSArray *objects) { } if ([op isEqualToString:@">="]) { NSArray *subexpressions = MGLSubexpressionsWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]); - return [NSPredicate predicateWithFormat:@"%K >= %@" argumentArray:subexpressions]; + return [NSPredicate predicateWithFormat:@"%@ >= %@" argumentArray:subexpressions]; } if ([op isEqualToString:@"!"]) { NSArray *subpredicates = MGLSubpredicatesWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]); @@ -294,7 +294,43 @@ NSArray *MGLSubpredicatesWithJSONObjects(NSArray *objects) { return [NSPredicate predicateWithValue:YES]; } if ([op isEqualToString:@"all"]) { - NSArray *subpredicates = MGLSubpredicatesWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]); + NSArray *subpredicates = MGLSubpredicatesWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]); + if (subpredicates.count == 2) { + // Determine if the expression is of BETWEEN type + if ([subpredicates[0] isKindOfClass:[NSComparisonPredicate class]] && + [subpredicates[1] isKindOfClass:[NSComparisonPredicate class]]) { + NSComparisonPredicate *leftCondition = (NSComparisonPredicate *)subpredicates[0]; + NSComparisonPredicate *rightCondition = (NSComparisonPredicate *)subpredicates[1]; + + NSArray *limits; + NSExpression *leftConditionExpression; + + if(leftCondition.predicateOperatorType == NSGreaterThanOrEqualToPredicateOperatorType && + rightCondition.predicateOperatorType == NSLessThanOrEqualToPredicateOperatorType) { + limits = @[leftCondition.rightExpression, rightCondition.rightExpression]; + leftConditionExpression = leftCondition.leftExpression; + + } else if (leftCondition.predicateOperatorType == NSLessThanOrEqualToPredicateOperatorType && + rightCondition.predicateOperatorType == NSLessThanOrEqualToPredicateOperatorType) { + limits = @[leftCondition.leftExpression, rightCondition.rightExpression]; + leftConditionExpression = leftCondition.rightExpression; + + } else if(leftCondition.predicateOperatorType == NSLessThanOrEqualToPredicateOperatorType && + rightCondition.predicateOperatorType == NSGreaterThanOrEqualToPredicateOperatorType) { + limits = @[leftCondition.leftExpression, rightCondition.leftExpression]; + leftConditionExpression = leftCondition.rightExpression; + + } else if(leftCondition.predicateOperatorType == NSGreaterThanOrEqualToPredicateOperatorType && + rightCondition.predicateOperatorType == NSGreaterThanOrEqualToPredicateOperatorType) { + limits = @[leftCondition.rightExpression, rightCondition.leftExpression]; + leftConditionExpression = leftCondition.leftExpression; + } + + if (limits && leftConditionExpression) { + return [NSPredicate predicateWithFormat:@"%@ BETWEEN %@", leftConditionExpression, [NSExpression expressionForAggregate:limits]]; + } + } + } return [NSCompoundPredicate andPredicateWithSubpredicates:subpredicates]; } if ([op isEqualToString:@"any"]) { @@ -302,8 +338,13 @@ NSArray *MGLSubpredicatesWithJSONObjects(NSArray *objects) { return [NSCompoundPredicate orPredicateWithSubpredicates:subpredicates]; } - NSAssert(NO, @"Unrecognized expression conditional operator %@.", op); - return nil; + NSExpression *expression = [NSExpression expressionWithMGLJSONObject:object]; + return [NSComparisonPredicate predicateWithLeftExpression:expression + rightExpression:[NSExpression expressionForConstantValue:@YES] + modifier:NSDirectPredicateModifier + type:NSEqualToPredicateOperatorType + options:0]; + } - (id)mgl_jsonExpressionObject { diff --git a/platform/darwin/test/MGLPredicateTests.mm b/platform/darwin/test/MGLPredicateTests.mm index d8cad0b166..5671de3252 100644 --- a/platform/darwin/test/MGLPredicateTests.mm +++ b/platform/darwin/test/MGLPredicateTests.mm @@ -576,6 +576,71 @@ namespace mbgl { NSArray *expected = @[@"==", @1, @2]; XCTAssertEqualObjects([NSPredicate predicateWithFormat:@"1 = 2"].mgl_jsonExpressionObject, expected); } + { + NSArray *expected = @[@"all", @[@"<=", @10, @[@"get", @"x"]], @[@"<=", @[@"get", @"x"], @100]]; + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"x BETWEEN {10, 100}"]; + XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); + XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicate); + } + { + NSArray *expected = @[@"all", @[@">=", @[@"get", @"x"], @10], @[@"<=", @[@"get", @"x"], @100]]; + NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]]; + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"x BETWEEN %@", limits]; + XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicate); + } + { + NSArray *expected = @[@"all", @[@"<=", @10, @[@"get", @"x"]], @[@"<=", @[@"get", @"x"], @100]]; + NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]]; + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"x BETWEEN %@", limits]; + XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); + XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicate); + } + { + NSArray *expected = @[@"all", @[@"<=", @10, @[@"get", @"x"]], @[@">=", @100, @[@"get", @"x"]]]; + NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]]; + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"x BETWEEN %@", limits]; + XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicate); + } + { + NSArray *expected = @[@"all", @[@">=", @[@"get", @"x"], @10], @[@">=", @100, @[@"get", @"x"]]]; + NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]]; + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"x BETWEEN %@", limits]; + XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicate); + } + { + NSArray *expected = @[@"all", @[@"==", @10, @[@"get", @"x"]], @[@"<=", @[@"get", @"x"], @100]]; + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ == x && x <= %@", [NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]; + XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); + XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicate); + } + { + NSArray *expected = @[@"match", @[@"id"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO]; + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$mgl_featureIdentifier IN { 6, 5, 4, 3}"]; + XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($mgl_featureIdentifier, 6, YES, 5, YES, 4, YES, 3, YES, NO) == YES"]; + XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicateAfter); + } + { + NSArray *expected = @[@"!", @[@"match", @[@"get", @"x"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO]]; + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT x IN { 6, 5, 4, 3}"]; + XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"NOT MGL_MATCH(x, 6, YES, 5, YES, 4, YES, 3, YES, NO) == YES"]; + XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicateAfter); + } + { + NSArray *expected = @[@"match", @[@"get", @"x"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO]; + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"{ 6, 5, 4, 3} CONTAINS x"]; + XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(x, 6, YES, 5, YES, 4, YES, 3, YES, NO) == YES"]; + XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicateAfter); + } + { + NSArray *expected = @[@"match", @[@"id"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO]; + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"{ 6, 5, 4, 3} CONTAINS $mgl_featureIdentifier"]; + XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($mgl_featureIdentifier, 6, YES, 5, YES, 4, YES, 3, YES, NO) == YES"]; + XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicateAfter); + } } @end -- cgit v1.2.1