This is an automated email from the ASF dual-hosted git repository.
jackie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-pinot.git
The following commit(s) were added to refs/heads/master by this push:
new b49098c [QueryContext] Enhance Predicate.toString() to return valid
expression (#5594)
b49098c is described below
commit b49098cfec58a20aa61c645287c3a00ab83c63b1
Author: Xiaotian (Jackie) Jiang <[email protected]>
AuthorDate: Fri Jun 19 14:16:46 2020 -0700
[QueryContext] Enhance Predicate.toString() to return valid expression
(#5594)
- The expression returned can be parsed back to the same predicate
- The expression returned is the standardized string representation of the
predicate
- This change can help solve the predicate standardization problem in
DistinctCountThetaSketch
Also extract the common converter methods in
BrokerRequestToQueryContextConverter into QueryContextConverterUtils so that
they can be used by other classes
---
.../context/predicate/IsNotNullPredicate.java | 2 +-
.../request/context/predicate/IsNullPredicate.java | 2 +-
.../request/context/predicate/RangePredicate.java | 13 +-
.../context/predicate/RegexpLikePredicate.java | 2 +-
.../context/predicate/TextMatchPredicate.java | 2 +-
.../BrokerRequestToQueryContextConverter.java | 253 +--------------
...verter.java => QueryContextConverterUtils.java} | 345 ++++++---------------
.../request/context/predicate/PredicateTest.java | 111 +++++++
.../BrokerRequestToQueryContextConverterTest.java | 2 +-
9 files changed, 238 insertions(+), 494 deletions(-)
diff --git
a/pinot-core/src/main/java/org/apache/pinot/core/query/request/context/predicate/IsNotNullPredicate.java
b/pinot-core/src/main/java/org/apache/pinot/core/query/request/context/predicate/IsNotNullPredicate.java
index 36907af..f38ab3b 100644
---
a/pinot-core/src/main/java/org/apache/pinot/core/query/request/context/predicate/IsNotNullPredicate.java
+++
b/pinot-core/src/main/java/org/apache/pinot/core/query/request/context/predicate/IsNotNullPredicate.java
@@ -61,6 +61,6 @@ public class IsNotNullPredicate implements Predicate {
@Override
public String toString() {
- return _lhs + "IS NOT NULL";
+ return _lhs + " IS NOT NULL";
}
}
diff --git
a/pinot-core/src/main/java/org/apache/pinot/core/query/request/context/predicate/IsNullPredicate.java
b/pinot-core/src/main/java/org/apache/pinot/core/query/request/context/predicate/IsNullPredicate.java
index cc11724..cbd3b23 100644
---
a/pinot-core/src/main/java/org/apache/pinot/core/query/request/context/predicate/IsNullPredicate.java
+++
b/pinot-core/src/main/java/org/apache/pinot/core/query/request/context/predicate/IsNullPredicate.java
@@ -61,6 +61,6 @@ public class IsNullPredicate implements Predicate {
@Override
public String toString() {
- return _lhs + "IS NULL";
+ return _lhs + " IS NULL";
}
}
diff --git
a/pinot-core/src/main/java/org/apache/pinot/core/query/request/context/predicate/RangePredicate.java
b/pinot-core/src/main/java/org/apache/pinot/core/query/request/context/predicate/RangePredicate.java
index 0396283..ef5db00 100644
---
a/pinot-core/src/main/java/org/apache/pinot/core/query/request/context/predicate/RangePredicate.java
+++
b/pinot-core/src/main/java/org/apache/pinot/core/query/request/context/predicate/RangePredicate.java
@@ -119,7 +119,16 @@ public class RangePredicate implements Predicate {
@Override
public String toString() {
- return _lhs + " IN RANGE " + (_lowerInclusive ? LOWER_INCLUSIVE :
LOWER_EXCLUSIVE) + _lowerBound + ',' + _upperBound
- + (_upperInclusive ? UPPER_INCLUSIVE : UPPER_EXCLUSIVE);
+ if (_lowerBound.equals(UNBOUNDED)) {
+ return _lhs + (_upperInclusive ? " <= '" : " < '") + _upperBound + '\'';
+ }
+ if (_upperBound.equals(UNBOUNDED)) {
+ return _lhs + (_lowerInclusive ? " >= '" : " > '") + _lowerBound + '\'';
+ }
+ if (_lowerInclusive && _upperInclusive) {
+ return _lhs + " BETWEEN '" + _lowerBound + "' AND '" + _upperBound +
'\'';
+ }
+ return "(" + _lhs + (_lowerInclusive ? " >= '" : " > '") + _lowerBound +
"' AND " + _lhs + (_upperInclusive
+ ? " <= '" : " < '") + _upperBound + "')";
}
}
diff --git
a/pinot-core/src/main/java/org/apache/pinot/core/query/request/context/predicate/RegexpLikePredicate.java
b/pinot-core/src/main/java/org/apache/pinot/core/query/request/context/predicate/RegexpLikePredicate.java
index c4eb655..2399cbc 100644
---
a/pinot-core/src/main/java/org/apache/pinot/core/query/request/context/predicate/RegexpLikePredicate.java
+++
b/pinot-core/src/main/java/org/apache/pinot/core/query/request/context/predicate/RegexpLikePredicate.java
@@ -67,6 +67,6 @@ public class RegexpLikePredicate implements Predicate {
@Override
public String toString() {
- return _lhs + " REGEXP_LIKE '" + _value + '\'';
+ return "regexp_like(" + _lhs + ",'" + _value + "')";
}
}
diff --git
a/pinot-core/src/main/java/org/apache/pinot/core/query/request/context/predicate/TextMatchPredicate.java
b/pinot-core/src/main/java/org/apache/pinot/core/query/request/context/predicate/TextMatchPredicate.java
index 23944a2..2cfc596 100644
---
a/pinot-core/src/main/java/org/apache/pinot/core/query/request/context/predicate/TextMatchPredicate.java
+++
b/pinot-core/src/main/java/org/apache/pinot/core/query/request/context/predicate/TextMatchPredicate.java
@@ -67,6 +67,6 @@ public class TextMatchPredicate implements Predicate {
@Override
public String toString() {
- return _lhs + " TEXT_MATCH '" + _value + '\'';
+ return "text_match(" + _lhs + ",'" + _value + "')";
}
}
diff --git
a/pinot-core/src/main/java/org/apache/pinot/core/query/request/context/utils/BrokerRequestToQueryContextConverter.java
b/pinot-core/src/main/java/org/apache/pinot/core/query/request/context/utils/BrokerRequestToQueryContextConverter.java
index 261981e..ac0abb5 100644
---
a/pinot-core/src/main/java/org/apache/pinot/core/query/request/context/utils/BrokerRequestToQueryContextConverter.java
+++
b/pinot-core/src/main/java/org/apache/pinot/core/query/request/context/utils/BrokerRequestToQueryContextConverter.java
@@ -25,12 +25,10 @@ import java.util.List;
import java.util.Map;
import org.apache.commons.collections.CollectionUtils;
import org.apache.pinot.common.function.AggregationFunctionType;
-import org.apache.pinot.common.function.FunctionDefinitionRegistry;
import org.apache.pinot.common.request.AggregationInfo;
import org.apache.pinot.common.request.BrokerRequest;
import org.apache.pinot.common.request.Expression;
import org.apache.pinot.common.request.ExpressionType;
-import org.apache.pinot.common.request.FilterOperator;
import org.apache.pinot.common.request.Function;
import org.apache.pinot.common.request.GroupBy;
import org.apache.pinot.common.request.PinotQuery;
@@ -38,39 +36,21 @@ import org.apache.pinot.common.request.Selection;
import org.apache.pinot.common.request.SelectionSort;
import org.apache.pinot.common.utils.request.FilterQueryTree;
import org.apache.pinot.common.utils.request.RequestUtils;
-import org.apache.pinot.core.query.exception.BadQueryRequestException;
import org.apache.pinot.core.query.request.context.ExpressionContext;
import org.apache.pinot.core.query.request.context.FilterContext;
import org.apache.pinot.core.query.request.context.FunctionContext;
import org.apache.pinot.core.query.request.context.OrderByExpressionContext;
import org.apache.pinot.core.query.request.context.QueryContext;
-import org.apache.pinot.core.query.request.context.predicate.EqPredicate;
-import org.apache.pinot.core.query.request.context.predicate.InPredicate;
-import
org.apache.pinot.core.query.request.context.predicate.IsNotNullPredicate;
-import org.apache.pinot.core.query.request.context.predicate.IsNullPredicate;
-import org.apache.pinot.core.query.request.context.predicate.NotEqPredicate;
-import org.apache.pinot.core.query.request.context.predicate.NotInPredicate;
-import org.apache.pinot.core.query.request.context.predicate.RangePredicate;
-import
org.apache.pinot.core.query.request.context.predicate.RegexpLikePredicate;
-import
org.apache.pinot.core.query.request.context.predicate.TextMatchPredicate;
-import org.apache.pinot.pql.parsers.Pql2Compiler;
-import org.apache.pinot.pql.parsers.pql2.ast.AstNode;
-import org.apache.pinot.pql.parsers.pql2.ast.FilterKind;
-import org.apache.pinot.pql.parsers.pql2.ast.FunctionCallAstNode;
-import org.apache.pinot.pql.parsers.pql2.ast.IdentifierAstNode;
-import org.apache.pinot.pql.parsers.pql2.ast.LiteralAstNode;
public class BrokerRequestToQueryContextConverter {
private BrokerRequestToQueryContextConverter() {
}
- private static final Pql2Compiler PQL_COMPILER = new Pql2Compiler();
-
/**
- * Converts the given BrokerRequest to a QueryContext.
- * <p>Use PinotQuery if available to avoid the unnecessary parsing of the
expression.
- * <p>TODO: We cannot use PinotQuery to generate the {@code filter} because
{@code BrokerRequestOptimizer} only
+ * Converts the given {@link BrokerRequest} into a {@link QueryContext}.
+ * <p>Use {@link PinotQuery} if available to avoid the unnecessary parsing
of the expressions.
+ * <p>TODO: We cannot use PinotQuery to generate the WHERE clause filter
because {@code BrokerRequestOptimizer} only
* optimizes the BrokerRequest but not the PinotQuery.
*/
public static QueryContext convert(BrokerRequest brokerRequest) {
@@ -93,10 +73,10 @@ public class BrokerRequestToQueryContextConverter {
.equalsIgnoreCase("AS")) {
// Handle alias
List<Expression> operands =
thriftExpression.getFunctionCall().getOperands();
- expression = getExpression(operands.get(0));
+ expression =
QueryContextConverterUtils.getExpression(operands.get(0));
aliasMap.put(expression, operands.get(1).getIdentifier().getName());
} else {
- expression = getExpression(thriftExpression);
+ expression =
QueryContextConverterUtils.getExpression(thriftExpression);
}
if (expression.getType() == ExpressionContext.Type.FUNCTION
&& expression.getFunction().getType() ==
FunctionContext.Type.AGGREGATION) {
@@ -117,7 +97,7 @@ public class BrokerRequestToQueryContextConverter {
if (CollectionUtils.isNotEmpty(groupByList)) {
groupByExpressions = new ArrayList<>(groupByList.size());
for (Expression thriftExpression : groupByList) {
- groupByExpressions.add(getExpression(thriftExpression));
+
groupByExpressions.add(QueryContextConverterUtils.getExpression(thriftExpression));
}
}
}
@@ -132,7 +112,7 @@ public class BrokerRequestToQueryContextConverter {
List<String> selectionColumns = selections.getSelectionColumns();
selectExpressions = new ArrayList<>(selectionColumns.size());
for (String expression : selectionColumns) {
- selectExpressions.add(getExpression(expression));
+
selectExpressions.add(QueryContextConverterUtils.getExpression(expression));
}
// NOTE: Pinot ignores the GROUP-BY clause for selection queries.
groupByExpressions = null;
@@ -151,13 +131,13 @@ public class BrokerRequestToQueryContextConverter {
// NOTE: For DistinctCountThetaSketch, because of the legacy
behavior of PQL compiler treating string
// literal as identifier in aggregation, here we treat all
expressions except for the first one as
// string literal.
- arguments.add(getExpression(stringExpressions.get(0)));
+
arguments.add(QueryContextConverterUtils.getExpression(stringExpressions.get(0)));
for (int i = 1; i < numArguments; i++) {
arguments.add(ExpressionContext.forLiteral(stringExpressions.get(i)));
}
} else {
for (String expression : stringExpressions) {
- arguments.add(getExpression(expression));
+
arguments.add(QueryContextConverterUtils.getExpression(expression));
}
}
FunctionContext function = new
FunctionContext(FunctionContext.Type.AGGREGATION, functionName, arguments);
@@ -170,7 +150,7 @@ public class BrokerRequestToQueryContextConverter {
List<String> stringExpressions = groupBy.getExpressions();
groupByExpressions = new ArrayList<>(stringExpressions.size());
for (String stringExpression : stringExpressions) {
- groupByExpressions.add(getExpression(stringExpression));
+
groupByExpressions.add(QueryContextConverterUtils.getExpression(stringExpression));
}
// NOTE: Use TOP in GROUP-BY clause as LIMIT for
backward-compatibility.
@@ -190,7 +170,7 @@ public class BrokerRequestToQueryContextConverter {
// NOTE: Order-by is always a Function with the ordering of the
Expression
Function thriftFunction = orderBy.getFunctionCall();
boolean isAsc = thriftFunction.getOperator().equalsIgnoreCase("ASC");
- ExpressionContext expression =
getExpression(thriftFunction.getOperands().get(0));
+ ExpressionContext expression =
QueryContextConverterUtils.getExpression(thriftFunction.getOperands().get(0));
orderByExpressions.add(new OrderByExpressionContext(expression,
isAsc));
}
}
@@ -199,8 +179,9 @@ public class BrokerRequestToQueryContextConverter {
if (CollectionUtils.isNotEmpty(orderBy)) {
orderByExpressions = new ArrayList<>(orderBy.size());
for (SelectionSort selectionSort : orderBy) {
- orderByExpressions
- .add(new
OrderByExpressionContext(getExpression(selectionSort.getColumn()),
selectionSort.isIsAsc()));
+ orderByExpressions.add(
+ new
OrderByExpressionContext(QueryContextConverterUtils.getExpression(selectionSort.getColumn()),
+ selectionSort.isIsAsc()));
}
}
}
@@ -210,7 +191,7 @@ public class BrokerRequestToQueryContextConverter {
FilterContext filter = null;
FilterQueryTree root = RequestUtils.generateFilterQueryTree(brokerRequest);
if (root != null) {
- filter = getFilter(root);
+ filter = QueryContextConverterUtils.getFilter(root);
}
// NOTE: Always use PinotQuery to generate HAVING filter because PQL does
not support HAVING clause.
@@ -218,7 +199,7 @@ public class BrokerRequestToQueryContextConverter {
if (pinotQuery != null) {
Expression havingExpression = pinotQuery.getHavingExpression();
if (havingExpression != null) {
- havingFilter = getFilter(havingExpression);
+ havingFilter = QueryContextConverterUtils.getFilter(havingExpression);
}
}
@@ -228,206 +209,4 @@ public class BrokerRequestToQueryContextConverter {
.setQueryOptions(brokerRequest.getQueryOptions()).setDebugOptions(brokerRequest.getDebugOptions())
.setBrokerRequest(brokerRequest).build();
}
-
- private static ExpressionContext getExpression(Expression thriftExpression) {
- switch (thriftExpression.getType()) {
- case LITERAL:
- return
ExpressionContext.forLiteral(thriftExpression.getLiteral().getFieldValue().toString());
- case IDENTIFIER:
- return
ExpressionContext.forIdentifier(thriftExpression.getIdentifier().getName());
- case FUNCTION:
- return
ExpressionContext.forFunction(getFunction(thriftExpression.getFunctionCall()));
- default:
- throw new IllegalStateException();
- }
- }
-
- private static FunctionContext getFunction(Function thriftFunction) {
- String functionName = thriftFunction.getOperator();
- if
(functionName.equalsIgnoreCase(AggregationFunctionType.COUNT.getName())) {
- // NOTE: COUNT always take one single argument "*"
- return new FunctionContext(FunctionContext.Type.AGGREGATION,
AggregationFunctionType.COUNT.getName(),
- Collections.singletonList(ExpressionContext.forIdentifier("*")));
- }
- FunctionContext.Type functionType =
- FunctionDefinitionRegistry.isAggFunc(functionName) ?
FunctionContext.Type.AGGREGATION
- : FunctionContext.Type.TRANSFORM;
- List<Expression> operands = thriftFunction.getOperands();
- List<ExpressionContext> arguments = new ArrayList<>(operands.size());
- for (Expression operand : operands) {
- arguments.add(getExpression(operand));
- }
- return new FunctionContext(functionType, functionName, arguments);
- }
-
- private static ExpressionContext getExpression(String stringExpression) {
- if (stringExpression.equals("*")) {
- // For 'SELECT *' and 'SELECT COUNT(*)'
- return ExpressionContext.forIdentifier("*");
- } else {
- return getExpression(PQL_COMPILER.parseToAstNode(stringExpression));
- }
- }
-
- private static ExpressionContext getExpression(AstNode astNode) {
- if (astNode instanceof IdentifierAstNode) {
- return ExpressionContext.forIdentifier(((IdentifierAstNode)
astNode).getName());
- }
- if (astNode instanceof FunctionCallAstNode) {
- return ExpressionContext.forFunction(getFunction((FunctionCallAstNode)
astNode));
- }
- if (astNode instanceof LiteralAstNode) {
- return ExpressionContext.forLiteral(((LiteralAstNode)
astNode).getValueAsString());
- }
- throw new IllegalStateException();
- }
-
- private static FunctionContext getFunction(FunctionCallAstNode astNode) {
- String functionName = astNode.getName();
- FunctionContext.Type functionType =
- FunctionDefinitionRegistry.isAggFunc(functionName) ?
FunctionContext.Type.AGGREGATION
- : FunctionContext.Type.TRANSFORM;
- List<? extends AstNode> children = astNode.getChildren();
- List<ExpressionContext> arguments = new ArrayList<>(children.size());
- for (AstNode child : children) {
- arguments.add(getExpression(child));
- }
- return new FunctionContext(functionType, functionName, arguments);
- }
-
- private static FilterContext getFilter(FilterQueryTree node) {
- FilterOperator filterOperator = node.getOperator();
- switch (filterOperator) {
- case AND:
- List<FilterQueryTree> childNodes = node.getChildren();
- List<FilterContext> children = new ArrayList<>(childNodes.size());
- for (FilterQueryTree childNode : childNodes) {
- children.add(getFilter(childNode));
- }
- return new FilterContext(FilterContext.Type.AND, children, null);
- case OR:
- childNodes = node.getChildren();
- children = new ArrayList<>(childNodes.size());
- for (FilterQueryTree childNode : childNodes) {
- children.add(getFilter(childNode));
- }
- return new FilterContext(FilterContext.Type.OR, children, null);
- case EQUALITY:
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new EqPredicate(getExpression(node.getColumn()),
node.getValue().get(0)));
- case NOT:
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new NotEqPredicate(getExpression(node.getColumn()),
node.getValue().get(0)));
- case IN:
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new InPredicate(getExpression(node.getColumn()), node.getValue()));
- case NOT_IN:
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new NotInPredicate(getExpression(node.getColumn()),
node.getValue()));
- case RANGE:
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new RangePredicate(getExpression(node.getColumn()),
node.getValue().get(0)));
- case REGEXP_LIKE:
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new RegexpLikePredicate(getExpression(node.getColumn()),
node.getValue().get(0)));
- case TEXT_MATCH:
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new TextMatchPredicate(getExpression(node.getColumn()),
node.getValue().get(0)));
- case IS_NULL:
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new IsNullPredicate(getExpression(node.getColumn())));
- case IS_NOT_NULL:
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new IsNotNullPredicate(getExpression(node.getColumn())));
- default:
- throw new IllegalStateException();
- }
- }
-
- /**
- * NOTE: Currently the query engine only accepts string literals as the
right-hand side of the predicate, so we always
- * convert the right-hand side expressions into strings.
- */
- private static FilterContext getFilter(Expression thriftExpression) {
- Function thriftFunction = thriftExpression.getFunctionCall();
- FilterKind filterKind = FilterKind.valueOf(thriftFunction.getOperator());
- List<Expression> operands = thriftFunction.getOperands();
- int numOperands = operands.size();
- switch (filterKind) {
- case AND:
- List<FilterContext> children = new ArrayList<>(numOperands);
- for (Expression operand : operands) {
- children.add(getFilter(operand));
- }
- return new FilterContext(FilterContext.Type.AND, children, null);
- case OR:
- children = new ArrayList<>(numOperands);
- for (Expression operand : operands) {
- children.add(getFilter(operand));
- }
- return new FilterContext(FilterContext.Type.OR, children, null);
- case EQUALS:
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new EqPredicate(getExpression(operands.get(0)),
getStringValue(operands.get(1))));
- case NOT_EQUALS:
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new NotEqPredicate(getExpression(operands.get(0)),
getStringValue(operands.get(1))));
- case IN:
- List<String> values = new ArrayList<>(numOperands - 1);
- for (int i = 1; i < numOperands; i++) {
- values.add(getStringValue(operands.get(i)));
- }
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new InPredicate(getExpression(operands.get(0)), values));
- case NOT_IN:
- values = new ArrayList<>(numOperands - 1);
- for (int i = 1; i < numOperands; i++) {
- values.add(getStringValue(operands.get(i)));
- }
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new NotInPredicate(getExpression(operands.get(0)), values));
- case GREATER_THAN:
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new RangePredicate(getExpression(operands.get(0)), false,
getStringValue(operands.get(1)), false,
- RangePredicate.UNBOUNDED));
- case GREATER_THAN_OR_EQUAL:
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new RangePredicate(getExpression(operands.get(0)), true,
getStringValue(operands.get(1)), false,
- RangePredicate.UNBOUNDED));
- case LESS_THAN:
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new RangePredicate(getExpression(operands.get(0)), false,
RangePredicate.UNBOUNDED, false,
- getStringValue(operands.get(1))));
- case LESS_THAN_OR_EQUAL:
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new RangePredicate(getExpression(operands.get(0)), false,
RangePredicate.UNBOUNDED, true,
- getStringValue(operands.get(1))));
- case BETWEEN:
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new RangePredicate(getExpression(operands.get(0)), true,
getStringValue(operands.get(1)), true,
- getStringValue(operands.get(2))));
- case REGEXP_LIKE:
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new RegexpLikePredicate(getExpression(operands.get(0)),
getStringValue(operands.get(1))));
- case TEXT_MATCH:
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new TextMatchPredicate(getExpression(operands.get(0)),
getStringValue(operands.get(1))));
- case IS_NULL:
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new IsNullPredicate(getExpression(operands.get(0))));
- case IS_NOT_NULL:
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new IsNotNullPredicate(getExpression(operands.get(0))));
- default:
- throw new IllegalStateException();
- }
- }
-
- private static String getStringValue(Expression thriftExpression) {
- if (thriftExpression.getType() != ExpressionType.LITERAL) {
- throw new BadQueryRequestException(
- "Pinot does not support column or expression on the right-hand side
of the predicate");
- }
- return thriftExpression.getLiteral().getFieldValue().toString();
- }
}
diff --git
a/pinot-core/src/main/java/org/apache/pinot/core/query/request/context/utils/BrokerRequestToQueryContextConverter.java
b/pinot-core/src/main/java/org/apache/pinot/core/query/request/context/utils/QueryContextConverterUtils.java
similarity index 55%
copy from
pinot-core/src/main/java/org/apache/pinot/core/query/request/context/utils/BrokerRequestToQueryContextConverter.java
copy to
pinot-core/src/main/java/org/apache/pinot/core/query/request/context/utils/QueryContextConverterUtils.java
index 261981e..54703a1 100644
---
a/pinot-core/src/main/java/org/apache/pinot/core/query/request/context/utils/BrokerRequestToQueryContextConverter.java
+++
b/pinot-core/src/main/java/org/apache/pinot/core/query/request/context/utils/QueryContextConverterUtils.java
@@ -20,30 +20,18 @@ package org.apache.pinot.core.query.request.context.utils;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
-import org.apache.commons.collections.CollectionUtils;
import org.apache.pinot.common.function.AggregationFunctionType;
import org.apache.pinot.common.function.FunctionDefinitionRegistry;
-import org.apache.pinot.common.request.AggregationInfo;
-import org.apache.pinot.common.request.BrokerRequest;
import org.apache.pinot.common.request.Expression;
import org.apache.pinot.common.request.ExpressionType;
import org.apache.pinot.common.request.FilterOperator;
import org.apache.pinot.common.request.Function;
-import org.apache.pinot.common.request.GroupBy;
-import org.apache.pinot.common.request.PinotQuery;
-import org.apache.pinot.common.request.Selection;
-import org.apache.pinot.common.request.SelectionSort;
import org.apache.pinot.common.utils.request.FilterQueryTree;
-import org.apache.pinot.common.utils.request.RequestUtils;
import org.apache.pinot.core.query.exception.BadQueryRequestException;
import org.apache.pinot.core.query.request.context.ExpressionContext;
import org.apache.pinot.core.query.request.context.FilterContext;
import org.apache.pinot.core.query.request.context.FunctionContext;
-import org.apache.pinot.core.query.request.context.OrderByExpressionContext;
-import org.apache.pinot.core.query.request.context.QueryContext;
import org.apache.pinot.core.query.request.context.predicate.EqPredicate;
import org.apache.pinot.core.query.request.context.predicate.InPredicate;
import
org.apache.pinot.core.query.request.context.predicate.IsNotNullPredicate;
@@ -61,175 +49,16 @@ import
org.apache.pinot.pql.parsers.pql2.ast.IdentifierAstNode;
import org.apache.pinot.pql.parsers.pql2.ast.LiteralAstNode;
-public class BrokerRequestToQueryContextConverter {
- private BrokerRequestToQueryContextConverter() {
+public class QueryContextConverterUtils {
+ private QueryContextConverterUtils() {
}
private static final Pql2Compiler PQL_COMPILER = new Pql2Compiler();
/**
- * Converts the given BrokerRequest to a QueryContext.
- * <p>Use PinotQuery if available to avoid the unnecessary parsing of the
expression.
- * <p>TODO: We cannot use PinotQuery to generate the {@code filter} because
{@code BrokerRequestOptimizer} only
- * optimizes the BrokerRequest but not the PinotQuery.
+ * Converts the given Thrift {@link Expression} into an {@link
ExpressionContext}.
*/
- public static QueryContext convert(BrokerRequest brokerRequest) {
- PinotQuery pinotQuery = brokerRequest.getPinotQuery();
-
- List<ExpressionContext> selectExpressions;
- Map<ExpressionContext, String> aliasMap;
- List<ExpressionContext> groupByExpressions = null;
- int limit;
- int offset = 0;
- if (pinotQuery != null) {
- aliasMap = new HashMap<>();
- List<Expression> selectList = pinotQuery.getSelectList();
- int numExpressions = selectList.size();
- List<ExpressionContext> aggregationExpressions = new
ArrayList<>(numExpressions);
- List<ExpressionContext> nonAggregationExpressions = new
ArrayList<>(numExpressions);
- for (Expression thriftExpression : selectList) {
- ExpressionContext expression;
- if (thriftExpression.getType() == ExpressionType.FUNCTION &&
thriftExpression.getFunctionCall().getOperator()
- .equalsIgnoreCase("AS")) {
- // Handle alias
- List<Expression> operands =
thriftExpression.getFunctionCall().getOperands();
- expression = getExpression(operands.get(0));
- aliasMap.put(expression, operands.get(1).getIdentifier().getName());
- } else {
- expression = getExpression(thriftExpression);
- }
- if (expression.getType() == ExpressionContext.Type.FUNCTION
- && expression.getFunction().getType() ==
FunctionContext.Type.AGGREGATION) {
- aggregationExpressions.add(expression);
- } else {
- nonAggregationExpressions.add(expression);
- }
- }
- if (aggregationExpressions.isEmpty()) {
- // NOTE: Pinot ignores the GROUP-BY clause when there is no
aggregation expressions in the SELECT clause.
- selectExpressions = nonAggregationExpressions;
- } else {
- // NOTE: Pinot ignores the non-aggregation expressions when there are
aggregation expressions in the SELECT
- // clause. E.g. SELECT a, SUM(b) -> SELECT SUM(b).
- selectExpressions = aggregationExpressions;
-
- List<Expression> groupByList = pinotQuery.getGroupByList();
- if (CollectionUtils.isNotEmpty(groupByList)) {
- groupByExpressions = new ArrayList<>(groupByList.size());
- for (Expression thriftExpression : groupByList) {
- groupByExpressions.add(getExpression(thriftExpression));
- }
- }
- }
- limit = pinotQuery.getLimit();
- offset = pinotQuery.getOffset();
- } else {
- // NOTE: Alias is not supported for PQL queries.
- aliasMap = Collections.emptyMap();
- Selection selections = brokerRequest.getSelections();
- if (selections != null) {
- // Selection query
- List<String> selectionColumns = selections.getSelectionColumns();
- selectExpressions = new ArrayList<>(selectionColumns.size());
- for (String expression : selectionColumns) {
- selectExpressions.add(getExpression(expression));
- }
- // NOTE: Pinot ignores the GROUP-BY clause for selection queries.
- groupByExpressions = null;
- limit = brokerRequest.getLimit();
- offset = selections.getOffset();
- } else {
- // Aggregation query
- List<AggregationInfo> aggregationsInfo =
brokerRequest.getAggregationsInfo();
- selectExpressions = new ArrayList<>(aggregationsInfo.size());
- for (AggregationInfo aggregationInfo : aggregationsInfo) {
- String functionName = aggregationInfo.getAggregationType();
- List<String> stringExpressions = aggregationInfo.getExpressions();
- int numArguments = stringExpressions.size();
- List<ExpressionContext> arguments = new ArrayList<>(numArguments);
- if
(functionName.equalsIgnoreCase(AggregationFunctionType.DISTINCTCOUNTTHETASKETCH.getName()))
{
- // NOTE: For DistinctCountThetaSketch, because of the legacy
behavior of PQL compiler treating string
- // literal as identifier in aggregation, here we treat all
expressions except for the first one as
- // string literal.
- arguments.add(getExpression(stringExpressions.get(0)));
- for (int i = 1; i < numArguments; i++) {
-
arguments.add(ExpressionContext.forLiteral(stringExpressions.get(i)));
- }
- } else {
- for (String expression : stringExpressions) {
- arguments.add(getExpression(expression));
- }
- }
- FunctionContext function = new
FunctionContext(FunctionContext.Type.AGGREGATION, functionName, arguments);
- selectExpressions.add(ExpressionContext.forFunction(function));
- }
-
- GroupBy groupBy = brokerRequest.getGroupBy();
- if (groupBy != null) {
- // Aggregation group-by query
- List<String> stringExpressions = groupBy.getExpressions();
- groupByExpressions = new ArrayList<>(stringExpressions.size());
- for (String stringExpression : stringExpressions) {
- groupByExpressions.add(getExpression(stringExpression));
- }
-
- // NOTE: Use TOP in GROUP-BY clause as LIMIT for
backward-compatibility.
- limit = (int) groupBy.getTopN();
- } else {
- limit = brokerRequest.getLimit();
- }
- }
- }
-
- List<OrderByExpressionContext> orderByExpressions = null;
- if (pinotQuery != null) {
- List<Expression> orderByList = pinotQuery.getOrderByList();
- if (CollectionUtils.isNotEmpty(orderByList)) {
- orderByExpressions = new ArrayList<>(orderByList.size());
- for (Expression orderBy : orderByList) {
- // NOTE: Order-by is always a Function with the ordering of the
Expression
- Function thriftFunction = orderBy.getFunctionCall();
- boolean isAsc = thriftFunction.getOperator().equalsIgnoreCase("ASC");
- ExpressionContext expression =
getExpression(thriftFunction.getOperands().get(0));
- orderByExpressions.add(new OrderByExpressionContext(expression,
isAsc));
- }
- }
- } else {
- List<SelectionSort> orderBy = brokerRequest.getOrderBy();
- if (CollectionUtils.isNotEmpty(orderBy)) {
- orderByExpressions = new ArrayList<>(orderBy.size());
- for (SelectionSort selectionSort : orderBy) {
- orderByExpressions
- .add(new
OrderByExpressionContext(getExpression(selectionSort.getColumn()),
selectionSort.isIsAsc()));
- }
- }
- }
-
- // NOTE: Always use BrokerRequest to generate filter because
BrokerRequestOptimizer only optimizes the BrokerRequest
- // but not the PinotQuery.
- FilterContext filter = null;
- FilterQueryTree root = RequestUtils.generateFilterQueryTree(brokerRequest);
- if (root != null) {
- filter = getFilter(root);
- }
-
- // NOTE: Always use PinotQuery to generate HAVING filter because PQL does
not support HAVING clause.
- FilterContext havingFilter = null;
- if (pinotQuery != null) {
- Expression havingExpression = pinotQuery.getHavingExpression();
- if (havingExpression != null) {
- havingFilter = getFilter(havingExpression);
- }
- }
-
- return new
QueryContext.Builder().setSelectExpressions(selectExpressions).setAliasMap(aliasMap).setFilter(filter)
-
.setGroupByExpressions(groupByExpressions).setOrderByExpressions(orderByExpressions)
- .setHavingFilter(havingFilter).setLimit(limit).setOffset(offset)
-
.setQueryOptions(brokerRequest.getQueryOptions()).setDebugOptions(brokerRequest.getDebugOptions())
- .setBrokerRequest(brokerRequest).build();
- }
-
- private static ExpressionContext getExpression(Expression thriftExpression) {
+ public static ExpressionContext getExpression(Expression thriftExpression) {
switch (thriftExpression.getType()) {
case LITERAL:
return
ExpressionContext.forLiteral(thriftExpression.getLiteral().getFieldValue().toString());
@@ -242,25 +71,10 @@ public class BrokerRequestToQueryContextConverter {
}
}
- private static FunctionContext getFunction(Function thriftFunction) {
- String functionName = thriftFunction.getOperator();
- if
(functionName.equalsIgnoreCase(AggregationFunctionType.COUNT.getName())) {
- // NOTE: COUNT always take one single argument "*"
- return new FunctionContext(FunctionContext.Type.AGGREGATION,
AggregationFunctionType.COUNT.getName(),
- Collections.singletonList(ExpressionContext.forIdentifier("*")));
- }
- FunctionContext.Type functionType =
- FunctionDefinitionRegistry.isAggFunc(functionName) ?
FunctionContext.Type.AGGREGATION
- : FunctionContext.Type.TRANSFORM;
- List<Expression> operands = thriftFunction.getOperands();
- List<ExpressionContext> arguments = new ArrayList<>(operands.size());
- for (Expression operand : operands) {
- arguments.add(getExpression(operand));
- }
- return new FunctionContext(functionType, functionName, arguments);
- }
-
- private static ExpressionContext getExpression(String stringExpression) {
+ /**
+ * Converts the given string representation of the expression into an {@link
ExpressionContext}.
+ */
+ public static ExpressionContext getExpression(String stringExpression) {
if (stringExpression.equals("*")) {
// For 'SELECT *' and 'SELECT COUNT(*)'
return ExpressionContext.forIdentifier("*");
@@ -269,7 +83,10 @@ public class BrokerRequestToQueryContextConverter {
}
}
- private static ExpressionContext getExpression(AstNode astNode) {
+ /**
+ * Converts the given {@link AstNode} into an {@link ExpressionContext}.
+ */
+ public static ExpressionContext getExpression(AstNode astNode) {
if (astNode instanceof IdentifierAstNode) {
return ExpressionContext.forIdentifier(((IdentifierAstNode)
astNode).getName());
}
@@ -282,7 +99,31 @@ public class BrokerRequestToQueryContextConverter {
throw new IllegalStateException();
}
- private static FunctionContext getFunction(FunctionCallAstNode astNode) {
+ /**
+ * Converts the given Thrift {@link Function} into a {@link FunctionContext}.
+ */
+ public static FunctionContext getFunction(Function thriftFunction) {
+ String functionName = thriftFunction.getOperator();
+ if
(functionName.equalsIgnoreCase(AggregationFunctionType.COUNT.getName())) {
+ // NOTE: COUNT always take one single argument "*"
+ return new FunctionContext(FunctionContext.Type.AGGREGATION,
AggregationFunctionType.COUNT.getName(),
+ Collections.singletonList(ExpressionContext.forIdentifier("*")));
+ }
+ FunctionContext.Type functionType =
+ FunctionDefinitionRegistry.isAggFunc(functionName) ?
FunctionContext.Type.AGGREGATION
+ : FunctionContext.Type.TRANSFORM;
+ List<Expression> operands = thriftFunction.getOperands();
+ List<ExpressionContext> arguments = new ArrayList<>(operands.size());
+ for (Expression operand : operands) {
+ arguments.add(getExpression(operand));
+ }
+ return new FunctionContext(functionType, functionName, arguments);
+ }
+
+ /**
+ * Converts the given {@link FunctionCallAstNode} into a {@link
FunctionContext}.
+ */
+ public static FunctionContext getFunction(FunctionCallAstNode astNode) {
String functionName = astNode.getName();
FunctionContext.Type functionType =
FunctionDefinitionRegistry.isAggFunc(functionName) ?
FunctionContext.Type.AGGREGATION
@@ -295,62 +136,14 @@ public class BrokerRequestToQueryContextConverter {
return new FunctionContext(functionType, functionName, arguments);
}
- private static FilterContext getFilter(FilterQueryTree node) {
- FilterOperator filterOperator = node.getOperator();
- switch (filterOperator) {
- case AND:
- List<FilterQueryTree> childNodes = node.getChildren();
- List<FilterContext> children = new ArrayList<>(childNodes.size());
- for (FilterQueryTree childNode : childNodes) {
- children.add(getFilter(childNode));
- }
- return new FilterContext(FilterContext.Type.AND, children, null);
- case OR:
- childNodes = node.getChildren();
- children = new ArrayList<>(childNodes.size());
- for (FilterQueryTree childNode : childNodes) {
- children.add(getFilter(childNode));
- }
- return new FilterContext(FilterContext.Type.OR, children, null);
- case EQUALITY:
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new EqPredicate(getExpression(node.getColumn()),
node.getValue().get(0)));
- case NOT:
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new NotEqPredicate(getExpression(node.getColumn()),
node.getValue().get(0)));
- case IN:
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new InPredicate(getExpression(node.getColumn()), node.getValue()));
- case NOT_IN:
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new NotInPredicate(getExpression(node.getColumn()),
node.getValue()));
- case RANGE:
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new RangePredicate(getExpression(node.getColumn()),
node.getValue().get(0)));
- case REGEXP_LIKE:
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new RegexpLikePredicate(getExpression(node.getColumn()),
node.getValue().get(0)));
- case TEXT_MATCH:
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new TextMatchPredicate(getExpression(node.getColumn()),
node.getValue().get(0)));
- case IS_NULL:
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new IsNullPredicate(getExpression(node.getColumn())));
- case IS_NOT_NULL:
- return new FilterContext(FilterContext.Type.PREDICATE, null,
- new IsNotNullPredicate(getExpression(node.getColumn())));
- default:
- throw new IllegalStateException();
- }
- }
-
/**
- * NOTE: Currently the query engine only accepts string literals as the
right-hand side of the predicate, so we always
- * convert the right-hand side expressions into strings.
+ * Converts the given Thrift {@link Expression} into a {@link FilterContext}.
+ * <p>NOTE: Currently the query engine only accepts string literals as the
right-hand side of the predicate, so we
+ * always convert the right-hand side expressions into strings.
*/
- private static FilterContext getFilter(Expression thriftExpression) {
+ public static FilterContext getFilter(Expression thriftExpression) {
Function thriftFunction = thriftExpression.getFunctionCall();
- FilterKind filterKind = FilterKind.valueOf(thriftFunction.getOperator());
+ FilterKind filterKind =
FilterKind.valueOf(thriftFunction.getOperator().toUpperCase());
List<Expression> operands = thriftFunction.getOperands();
int numOperands = operands.size();
switch (filterKind) {
@@ -426,8 +219,60 @@ public class BrokerRequestToQueryContextConverter {
private static String getStringValue(Expression thriftExpression) {
if (thriftExpression.getType() != ExpressionType.LITERAL) {
throw new BadQueryRequestException(
- "Pinot does not support column or expression on the right-hand side
of the predicate");
+ "Pinot does not support column or function on the right-hand side of
the predicate");
}
return thriftExpression.getLiteral().getFieldValue().toString();
}
+
+ /**
+ * Converts the given {@link FilterQueryTree} into a {@link FilterContext}.
+ */
+ public static FilterContext getFilter(FilterQueryTree node) {
+ FilterOperator filterOperator = node.getOperator();
+ switch (filterOperator) {
+ case AND:
+ List<FilterQueryTree> childNodes = node.getChildren();
+ List<FilterContext> children = new ArrayList<>(childNodes.size());
+ for (FilterQueryTree childNode : childNodes) {
+ children.add(getFilter(childNode));
+ }
+ return new FilterContext(FilterContext.Type.AND, children, null);
+ case OR:
+ childNodes = node.getChildren();
+ children = new ArrayList<>(childNodes.size());
+ for (FilterQueryTree childNode : childNodes) {
+ children.add(getFilter(childNode));
+ }
+ return new FilterContext(FilterContext.Type.OR, children, null);
+ case EQUALITY:
+ return new FilterContext(FilterContext.Type.PREDICATE, null,
+ new EqPredicate(getExpression(node.getColumn()),
node.getValue().get(0)));
+ case NOT:
+ return new FilterContext(FilterContext.Type.PREDICATE, null,
+ new NotEqPredicate(getExpression(node.getColumn()),
node.getValue().get(0)));
+ case IN:
+ return new FilterContext(FilterContext.Type.PREDICATE, null,
+ new InPredicate(getExpression(node.getColumn()), node.getValue()));
+ case NOT_IN:
+ return new FilterContext(FilterContext.Type.PREDICATE, null,
+ new NotInPredicate(getExpression(node.getColumn()),
node.getValue()));
+ case RANGE:
+ return new FilterContext(FilterContext.Type.PREDICATE, null,
+ new RangePredicate(getExpression(node.getColumn()),
node.getValue().get(0)));
+ case REGEXP_LIKE:
+ return new FilterContext(FilterContext.Type.PREDICATE, null,
+ new RegexpLikePredicate(getExpression(node.getColumn()),
node.getValue().get(0)));
+ case TEXT_MATCH:
+ return new FilterContext(FilterContext.Type.PREDICATE, null,
+ new TextMatchPredicate(getExpression(node.getColumn()),
node.getValue().get(0)));
+ case IS_NULL:
+ return new FilterContext(FilterContext.Type.PREDICATE, null,
+ new IsNullPredicate(getExpression(node.getColumn())));
+ case IS_NOT_NULL:
+ return new FilterContext(FilterContext.Type.PREDICATE, null,
+ new IsNotNullPredicate(getExpression(node.getColumn())));
+ default:
+ throw new IllegalStateException();
+ }
+ }
}
diff --git
a/pinot-core/src/test/java/org/apache/pinot/core/query/request/context/predicate/PredicateTest.java
b/pinot-core/src/test/java/org/apache/pinot/core/query/request/context/predicate/PredicateTest.java
new file mode 100644
index 0000000..0d1d06b
--- /dev/null
+++
b/pinot-core/src/test/java/org/apache/pinot/core/query/request/context/predicate/PredicateTest.java
@@ -0,0 +1,111 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.pinot.core.query.request.context.predicate;
+
+import java.util.List;
+import org.apache.pinot.common.request.Expression;
+import org.apache.pinot.core.query.request.context.ExpressionContext;
+import org.apache.pinot.core.query.request.context.FilterContext;
+import
org.apache.pinot.core.query.request.context.utils.QueryContextConverterUtils;
+import org.apache.pinot.sql.parsers.CalciteSqlParser;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+
+public class PredicateTest {
+
+ @Test
+ public void testSerDe()
+ throws Exception {
+ // EqPredicate
+ assertEquals(testSerDe("foo\t= 123"), "foo = '123'");
+ assertEquals(testSerDe("foo='123'"), "foo = '123'");
+
+ // NotEqPredicate
+ assertEquals(testSerDe("foo !=123"), "foo != '123'");
+ assertEquals(testSerDe("foo <>\t123"), "foo != '123'");
+ assertEquals(testSerDe("foo\t\t!= '123'"), "foo != '123'");
+ assertEquals(testSerDe("foo<> '123'"), "foo != '123'");
+
+ // InPredicate
+ assertEquals(testSerDe("foo in (123, \t456,789)"), "foo IN
('123','456','789')");
+ assertEquals(testSerDe("foo In (\t '123', '456', '789')"), "foo IN
('123','456','789')");
+
+ // NotInPredicate
+ assertEquals(testSerDe("foo NOT in (123, \t456,789)"), "foo NOT IN
('123','456','789')");
+ assertEquals(testSerDe("foo NoT In (\t '123', '456', '789')"), "foo NOT
IN ('123','456','789')");
+
+ // RangePredicate
+ assertEquals(testSerDe("foo >123"), "foo > '123'");
+ assertEquals(testSerDe("foo >=\t'123'"), "foo >= '123'");
+ assertEquals(testSerDe("foo< 123"), "foo < '123'");
+ assertEquals(testSerDe("foo\t<= '123'"), "foo <= '123'");
+ assertEquals(testSerDe("foo bEtWeEn 123 AnD 456"), "foo BETWEEN '123' AND
'456'");
+
+ // RegexpLikePredicate
+ assertEquals(testSerDe("ReGexP_lIKe(foo,\t\t'bar')"),
"regexp_like(foo,'bar')");
+
+ // TextMatchPredicate
+ assertEquals(testSerDe("TEXT_MATCH(foo\t ,\t'bar')"),
"text_match(foo,'bar')");
+
+ // IsNullPredicate
+ assertEquals(testSerDe("foo\tis\tnull"), "foo IS NULL");
+
+ // IsNotNullPredicate
+ assertEquals(testSerDe("foo\tIS not\tNulL"), "foo IS NOT NULL");
+
+ // Non-standard RangePredicate (merged ranges)
+ RangePredicate rangePredicate =
+ new RangePredicate(ExpressionContext.forIdentifier("foo"), true,
"123", false, "456");
+ String predicateExpression = rangePredicate.toString();
+ assertEquals(predicateExpression, "(foo >= '123' AND foo < '456')");
+ Expression thriftExpression =
CalciteSqlParser.compileToExpression(predicateExpression);
+ FilterContext filter =
QueryContextConverterUtils.getFilter(thriftExpression);
+ assertEquals(filter.getType(), FilterContext.Type.AND);
+ List<FilterContext> children = filter.getChildren();
+ assertEquals(children.size(), 2);
+ assertEquals(children.get(0).toString(), "foo >= '123'");
+ assertEquals(children.get(1).toString(), "foo < '456'");
+ }
+
+ /**
+ * Tests that the serialized predicate can be parsed and converted back to
the same predicate, and returns the
+ * serialized predicate (standardized string representation of the predicate
expression).
+ */
+ private String testSerDe(String predicateExpression)
+ throws Exception {
+ // Parse and convert the string predicate expression into Predicate
+ Expression thriftExpression =
CalciteSqlParser.compileToExpression(predicateExpression);
+ FilterContext filter =
QueryContextConverterUtils.getFilter(thriftExpression);
+ assertEquals(filter.getType(), FilterContext.Type.PREDICATE);
+ Predicate predicate = filter.getPredicate();
+
+ // Serialize the predicate into string
+ predicateExpression = predicate.toString();
+
+ // Deserialize and compare
+ thriftExpression =
CalciteSqlParser.compileToExpression(predicateExpression);
+ filter = QueryContextConverterUtils.getFilter(thriftExpression);
+ assertEquals(filter.getType(), FilterContext.Type.PREDICATE);
+ assertEquals(filter.getPredicate(), predicate);
+
+ return predicateExpression;
+ }
+}
diff --git
a/pinot-core/src/test/java/org/apache/pinot/core/query/request/context/utils/BrokerRequestToQueryContextConverterTest.java
b/pinot-core/src/test/java/org/apache/pinot/core/query/request/context/utils/BrokerRequestToQueryContextConverterTest.java
index 2e1b88e..ec59015 100644
---
a/pinot-core/src/test/java/org/apache/pinot/core/query/request/context/utils/BrokerRequestToQueryContextConverterTest.java
+++
b/pinot-core/src/test/java/org/apache/pinot/core/query/request/context/utils/BrokerRequestToQueryContextConverterTest.java
@@ -258,7 +258,7 @@ public class BrokerRequestToQueryContextConverterTest {
assertEquals(orFilter.getChildren().get(1), new
FilterContext(FilterContext.Type.PREDICATE, null,
new TextMatchPredicate(ExpressionContext.forIdentifier("foobar"),
"potato")));
assertEquals(filter.toString(),
- "(foo IN RANGE (15,*) AND (div(bar,foo) IN RANGE [10,20] OR foobar
TEXT_MATCH 'potato'))");
+ "(foo > '15' AND (div(bar,foo) BETWEEN '10' AND '20' OR
text_match(foobar,'potato')))");
assertNull(queryContext.getGroupByExpressions());
assertNull(queryContext.getOrderByExpressions());
assertNull(queryContext.getHavingFilter());
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]