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]

Reply via email to