Skip to content

Commit

Permalink
Feature: operators in data-prepper expression (#1065)
Browse files Browse the repository at this point in the history
* FEAT: operators and tests

Signed-off-by: Chen <[email protected]>

* MAINT: lower -> less

Signed-off-by: Chen <[email protected]>

* MAINT: use int for symbol

Signed-off-by: Chen <[email protected]>

* MAINT: include null in equality operators

Signed-off-by: Chen <[email protected]>

* MAINT: operator classes package private

Signed-off-by: Chen <[email protected]>

* STY: eval -> evaluate

Signed-off-by: Chen <[email protected]>

* MAINT: simplify numerical value comparison operators

Signed-off-by: Chen <[email protected]>

* MAINT: DI annotation on operators

Signed-off-by: Chen <[email protected]>

* MAINT: OperatorFactory to produce negate and numeric comparison operator

Signed-off-by: Chen <[email protected]>

* MAINT: use NumericCompareOperator

Signed-off-by: Chen <[email protected]>

* MAINT: generic operator to cover negate

Signed-off-by: Chen <[email protected]>

* MAINT: generic operators

Signed-off-by: Chen <[email protected]>

* STY: static final

Signed-off-by: Chen <[email protected]>

* MNT: Objects::equals

Signed-off-by: Chen <[email protected]>
  • Loading branch information
chenqi0805 authored Mar 3, 2022
1 parent 7f8d83d commit 3831d57
Show file tree
Hide file tree
Showing 23 changed files with 1,032 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.dataprepper.expression;

import org.opensearch.dataprepper.expression.antlr.DataPrepperExpressionParser;

import javax.inject.Named;

import static com.google.common.base.Preconditions.checkArgument;

@Named
class AndOperator implements Operator<Boolean> {
private static final Integer SYMBOL = DataPrepperExpressionParser.AND;
private static final String DISPLAY_NAME = DataPrepperExpressionParser.VOCABULARY
.getDisplayName(DataPrepperExpressionParser.AND);

@Override
public Integer getSymbol() {
return SYMBOL;
}

@Override
public Boolean evaluate(final Object... args) {
checkArgument(args.length == 2, DISPLAY_NAME + " requires operands length to be 2.");
checkArgument(args[0] instanceof Boolean, DISPLAY_NAME + " requires left operand to be Boolean.");
checkArgument(args[1] instanceof Boolean, DISPLAY_NAME + " requires right operand to be Boolean.");
return ((Boolean) args[0]) && ((Boolean) args[1]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.dataprepper.expression;

import org.opensearch.dataprepper.expression.antlr.DataPrepperExpressionParser;

import java.util.function.BiPredicate;

import static com.google.common.base.Preconditions.checkArgument;

public class GenericEqualOperator implements Operator<Boolean> {
private final Integer symbol;
private final String displayName;
private final BiPredicate<Object, Object> operation;

public GenericEqualOperator(final Integer symbol, BiPredicate<Object, Object> operation) {
this.symbol = symbol;
displayName = DataPrepperExpressionParser.VOCABULARY.getDisplayName(symbol);
this.operation = operation;
}

@Override
public Integer getSymbol() {
return symbol;
}

@Override
public Boolean evaluate(final Object... args) {
checkArgument(args.length == 2, displayName + " requires operands length to be 2.");
final Object leftOperand = args[0];
final Object rightOperand = args[1];
return operation.test(leftOperand, rightOperand);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.dataprepper.expression;

import org.opensearch.dataprepper.expression.antlr.DataPrepperExpressionParser;

import java.util.Set;
import java.util.function.BiPredicate;

import static com.google.common.base.Preconditions.checkArgument;

public class GenericInSetOperator implements Operator<Boolean> {
private final Integer symbol;
private final String displayName;
private final BiPredicate<Object, Object> operation;

public GenericInSetOperator(final Integer symbol, BiPredicate<Object, Object> operation) {
this.symbol = symbol;
displayName = DataPrepperExpressionParser.VOCABULARY.getDisplayName(symbol);
this.operation = operation;
}

@Override
public Integer getSymbol() {
return symbol;
}

@Override
public Boolean evaluate(final Object... args) {
checkArgument(args.length == 2, displayName + " requires operands length to be 2.");
if (!(args[1] instanceof Set)) {
throw new IllegalArgumentException(displayName + " requires right operand to be Set.");
}
return operation.test(args[0], args[1]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.dataprepper.expression;

import org.opensearch.dataprepper.expression.antlr.DataPrepperExpressionParser;

import java.util.function.BiPredicate;
import java.util.regex.PatternSyntaxException;

import static com.google.common.base.Preconditions.checkArgument;

public class GenericRegexMatchOperator implements Operator<Boolean> {
private final Integer symbol;
private final String displayName;
private final BiPredicate<Object, Object> operation;

public GenericRegexMatchOperator(final Integer symbol, BiPredicate<Object, Object> operation) {
this.symbol = symbol;
displayName = DataPrepperExpressionParser.VOCABULARY.getDisplayName(symbol);
this.operation = operation;
}

@Override
public Integer getSymbol() {
return symbol;
}

@Override
public Boolean evaluate(final Object... args) {
checkArgument(args.length == 2, displayName + " requires operands length needs to be 2.");
checkArgument(args[0] instanceof String, displayName + " requires left operand to be String.");
checkArgument(args[1] instanceof String, displayName + " requires right operand to be String.");
try {
return operation.test(args[0], args[1]);
} catch (final PatternSyntaxException e) {
throw new IllegalArgumentException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.dataprepper.expression;

import org.opensearch.dataprepper.expression.antlr.DataPrepperExpressionParser;

import javax.inject.Named;

import static com.google.common.base.Preconditions.checkArgument;

@Named
class NotOperator implements Operator<Boolean> {
private static final Integer SYMBOL = DataPrepperExpressionParser.NOT;
private static final String DISPLAY_NAME = DataPrepperExpressionParser.VOCABULARY
.getDisplayName(DataPrepperExpressionParser.NOT);

@Override
public Integer getSymbol() {
return SYMBOL;
}

@Override
public Boolean evaluate(final Object... args) {
checkArgument(args.length == 1, DISPLAY_NAME + " requires operands length to be 1.");
checkArgument(args[0] instanceof Boolean, DISPLAY_NAME + " requires operand to be Boolean.");
return !((Boolean) args[0]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.dataprepper.expression;

import org.opensearch.dataprepper.expression.antlr.DataPrepperExpressionParser;

import java.util.Map;
import java.util.function.BiFunction;

import static com.google.common.base.Preconditions.checkArgument;

public class NumericCompareOperator implements Operator<Boolean> {
private final Integer symbol;
private final String displayName;
private final Map<Class<? extends Number>, Map<Class<? extends Number>, BiFunction<Object, Object, Boolean>>> operandsToOperationMap;

public NumericCompareOperator(
final Integer symbol,
final Map<Class<? extends Number>, Map<Class<? extends Number>, BiFunction<Object, Object, Boolean>>> operandsToOperationMap) {
this.symbol = symbol;
displayName = DataPrepperExpressionParser.VOCABULARY.getDisplayName(symbol);
this.operandsToOperationMap = operandsToOperationMap;
}

@Override
public Integer getSymbol() {
return symbol;
}

@Override
public Boolean evaluate(final Object... args) {
checkArgument(args.length == 2, displayName + " requires operands length needs to be 2.");
final Object leftValue = args[0];
final Object rightValue = args[1];
final Class<?> leftValueClass = leftValue.getClass();
final Class<?> rightValueClass = rightValue.getClass();
if (!operandsToOperationMap.containsKey(leftValueClass)) {
throw new IllegalArgumentException(displayName + " requires left operand to be either Float or Integer.");
}
Map<Class<? extends Number>, BiFunction<Object, Object, Boolean>> rightOperandToOperation =
operandsToOperationMap.get(leftValueClass);
if (!rightOperandToOperation.containsKey(rightValueClass)) {
throw new IllegalArgumentException(displayName + " requires right operand to be either Float or Integer.");
}
final BiFunction<Object, Object, Boolean> operation = rightOperandToOperation.get(rightValueClass);
return operation.apply(leftValue, rightValue);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.dataprepper.expression;

interface Operator<T> {
Integer getSymbol();

/**
* @since 1.3
* Placeholder interface for implementing Data-Prepper supported binary/unary operations on operands that
* returns custom type T.
* @param args operands
* @return T
*/
T evaluate(final Object... args);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.dataprepper.expression;

import org.opensearch.dataprepper.expression.antlr.DataPrepperExpressionParser;
import org.springframework.context.annotation.Bean;

import javax.inject.Named;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;

@Named
public class OperatorFactory {
public final BiPredicate<Object, Object> regexEquals = (x, y) -> ((String) x).matches((String) y);
public final BiPredicate<Object, Object> equals = Objects::equals;
public final BiPredicate<Object, Object> inSet = (x, y) -> ((Set<?>) y).contains(x);

@Bean
public NumericCompareOperator greaterThanOperator() {
final Map<Class<? extends Number>, Map<Class<? extends Number>, BiFunction<Object, Object, Boolean>>>
operandsToOperationMap = new HashMap<>();
final Map<Class<? extends Number>, BiFunction<Object, Object, Boolean>> intOperations =
new HashMap<Class<? extends Number>, BiFunction<Object, Object, Boolean>>() {{
put(Integer.class, (lhs, rhs) -> (Integer) lhs > (Integer) rhs);
put(Float.class, (lhs, rhs) -> (Integer) lhs > (Float) rhs);}};
final Map<Class<? extends Number>, BiFunction<Object, Object, Boolean>> floatOperations =
new HashMap<Class<? extends Number>, BiFunction<Object, Object, Boolean>>() {{
put(Integer.class, (lhs, rhs) -> (Float) lhs > (Integer) rhs);
put(Float.class, (lhs, rhs) -> (Float) lhs > (Float) rhs);}};

operandsToOperationMap.put(Integer.class, intOperations);
operandsToOperationMap.put(Float.class, floatOperations);

return new NumericCompareOperator(DataPrepperExpressionParser.GT, operandsToOperationMap);
}

@Bean
public NumericCompareOperator greaterThanOrEqualOperator() {
final Map<Class<? extends Number>, Map<Class<? extends Number>, BiFunction<Object, Object, Boolean>>>
operandsToOperationMap = new HashMap<>();
final Map<Class<? extends Number>, BiFunction<Object, Object, Boolean>> intOperations =
new HashMap<Class<? extends Number>, BiFunction<Object, Object, Boolean>>() {{
put(Integer.class, (lhs, rhs) -> (Integer) lhs >= (Integer) rhs);
put(Float.class, (lhs, rhs) -> (Integer) lhs >= (Float) rhs);}};
final Map<Class<? extends Number>, BiFunction<Object, Object, Boolean>> floatOperations =
new HashMap<Class<? extends Number>, BiFunction<Object, Object, Boolean>>() {{
put(Integer.class, (lhs, rhs) -> (Float) lhs >= (Integer) rhs);
put(Float.class, (lhs, rhs) -> (Float) lhs >= (Float) rhs);}};

operandsToOperationMap.put(Integer.class, intOperations);
operandsToOperationMap.put(Float.class, floatOperations);

return new NumericCompareOperator(DataPrepperExpressionParser.GTE, operandsToOperationMap);
}

@Bean
public NumericCompareOperator lessThanOperator() {
final Map<Class<? extends Number>, Map<Class<? extends Number>, BiFunction<Object, Object, Boolean>>>
operandsToOperationMap = new HashMap<>();
final Map<Class<? extends Number>, BiFunction<Object, Object, Boolean>> intOperations =
new HashMap<Class<? extends Number>, BiFunction<Object, Object, Boolean>>() {{
put(Integer.class, (lhs, rhs) -> (Integer) lhs < (Integer) rhs);
put(Float.class, (lhs, rhs) -> (Integer) lhs < (Float) rhs);}};
final Map<Class<? extends Number>, BiFunction<Object, Object, Boolean>> floatOperations =
new HashMap<Class<? extends Number>, BiFunction<Object, Object, Boolean>>() {{
put(Integer.class, (lhs, rhs) -> (Float) lhs < (Integer) rhs);
put(Float.class, (lhs, rhs) -> (Float) lhs < (Float) rhs);}};

operandsToOperationMap.put(Integer.class, intOperations);
operandsToOperationMap.put(Float.class, floatOperations);

return new NumericCompareOperator(DataPrepperExpressionParser.LT, operandsToOperationMap);
}

@Bean
public NumericCompareOperator lessThanOrEqualOperator() {
final Map<Class<? extends Number>, Map<Class<? extends Number>, BiFunction<Object, Object, Boolean>>>
operandsToOperationMap = new HashMap<>();
final Map<Class<? extends Number>, BiFunction<Object, Object, Boolean>> intOperations =
new HashMap<Class<? extends Number>, BiFunction<Object, Object, Boolean>>() {{
put(Integer.class, (lhs, rhs) -> (Integer) lhs <= (Integer) rhs);
put(Float.class, (lhs, rhs) -> (Integer) lhs <= (Float) rhs);}};
final Map<Class<? extends Number>, BiFunction<Object, Object, Boolean>> floatOperations =
new HashMap<Class<? extends Number>, BiFunction<Object, Object, Boolean>>() {{
put(Integer.class, (lhs, rhs) -> (Float) lhs <= (Integer) rhs);
put(Float.class, (lhs, rhs) -> (Float) lhs <= (Float) rhs);}};

operandsToOperationMap.put(Integer.class, intOperations);
operandsToOperationMap.put(Float.class, floatOperations);

return new NumericCompareOperator(DataPrepperExpressionParser.LTE, operandsToOperationMap);
}

@Bean
public GenericRegexMatchOperator regexEqualOperator() {
return new GenericRegexMatchOperator(DataPrepperExpressionParser.MATCH_REGEX_PATTERN, regexEquals);
}

@Bean
public GenericRegexMatchOperator regexNotEqualOperator() {
return new GenericRegexMatchOperator(DataPrepperExpressionParser.NOT_MATCH_REGEX_PATTERN, regexEquals.negate());
}

@Bean
public GenericEqualOperator equalOperator() {
return new GenericEqualOperator(DataPrepperExpressionParser.EQUAL, equals);
}

@Bean
public GenericEqualOperator notEqualOperator() {
return new GenericEqualOperator(DataPrepperExpressionParser.NOT_EQUAL, equals.negate());
}

@Bean
public GenericInSetOperator inSetOperator() {
return new GenericInSetOperator(DataPrepperExpressionParser.IN_SET, inSet);
}

@Bean
public GenericInSetOperator notInSetOperator() {
return new GenericInSetOperator(DataPrepperExpressionParser.NOT_IN_SET, inSet.negate());
}
}
Loading

0 comments on commit 3831d57

Please sign in to comment.