You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@drill.apache.org by vo...@apache.org on 2021/08/05 17:23:23 UTC

[drill] 10/13: Additional cleanup

This is an automated email from the ASF dual-hosted git repository.

volodymyr pushed a commit to branch mongo
in repository https://gitbox.apache.org/repos/asf/drill.git

commit ba71548c121099f75ecbdbc00f3cf1851ccc94a2
Author: Volodymyr Vysotskyi <vv...@gmail.com>
AuthorDate: Sat Jul 31 22:37:59 2021 +0300

    Additional cleanup
---
 .../exec/store/mongo/MongoAggregateUtils.java      |   4 +-
 .../drill/exec/store/mongo/MongoFilterBuilder.java |  68 +++---
 .../store/mongo/MongoPushDownFilterForScan.java    |  96 --------
 .../store/mongo/plan/MongoPluginImplementor.java   | 146 +------------
 .../drill/exec/store/mongo/plan/MongoRules.java    | 243 ---------------------
 .../store/mongo/plan/RexToMongoTranslator.java     | 184 ++++++++++++++++
 6 files changed, 223 insertions(+), 518 deletions(-)

diff --git a/contrib/storage-mongo/src/main/java/org/apache/drill/exec/store/mongo/MongoAggregateUtils.java b/contrib/storage-mongo/src/main/java/org/apache/drill/exec/store/mongo/MongoAggregateUtils.java
index 817644d..e362707 100644
--- a/contrib/storage-mongo/src/main/java/org/apache/drill/exec/store/mongo/MongoAggregateUtils.java
+++ b/contrib/storage-mongo/src/main/java/org/apache/drill/exec/store/mongo/MongoAggregateUtils.java
@@ -34,14 +34,14 @@ public class MongoAggregateUtils {
     return SqlValidatorUtil.uniquify(renamed, true);
   }
 
-  static String maybeQuote(String s) {
+  public static String maybeQuote(String s) {
     if (!needsQuote(s)) {
       return s;
     }
     return quote(s);
   }
 
-  static String quote(String s) {
+  public static String quote(String s) {
     return "'" + s + "'";
   }
 
diff --git a/contrib/storage-mongo/src/main/java/org/apache/drill/exec/store/mongo/MongoFilterBuilder.java b/contrib/storage-mongo/src/main/java/org/apache/drill/exec/store/mongo/MongoFilterBuilder.java
index 5bea34e..68114e3 100644
--- a/contrib/storage-mongo/src/main/java/org/apache/drill/exec/store/mongo/MongoFilterBuilder.java
+++ b/contrib/storage-mongo/src/main/java/org/apache/drill/exec/store/mongo/MongoFilterBuilder.java
@@ -33,46 +33,35 @@ import org.slf4j.LoggerFactory;
 public class MongoFilterBuilder extends
     AbstractExprVisitor<Document, Void, RuntimeException> implements
     DrillMongoConstants {
-  private static final Logger logger = LoggerFactory
-      .getLogger(MongoFilterBuilder.class);
-  final MongoGroupScan groupScan;
-  final LogicalExpression le;
+  private static final Logger logger = LoggerFactory.getLogger(MongoFilterBuilder.class);
+
+  private final LogicalExpression le;
   private boolean allExpressionsConverted = true;
 
-  public MongoFilterBuilder(MongoGroupScan groupScan,
-      LogicalExpression conditionExp) {
-    this.groupScan = groupScan;
+  public MongoFilterBuilder(LogicalExpression conditionExp) {
     this.le = conditionExp;
   }
 
   public Document parseTree() {
-    Document parsedSpec = le.accept(this, null);
-    if (parsedSpec != null) {
-      parsedSpec = mergeScanSpecs(FunctionNames.AND, null,
-          parsedSpec);
-    }
-    return parsedSpec;
+    return le.accept(this, null);
   }
 
-  private Document mergeScanSpecs(String functionName,
-      Document leftScanSpec, Document rightScanSpec) {
+  private Document mergeFilters(String functionName,
+      Document left, Document right) {
     Document newFilter = new Document();
 
     switch (functionName) {
     case FunctionNames.AND:
-      if (leftScanSpec != null
-          && rightScanSpec != null) {
-        newFilter = MongoUtils.andFilterAtIndex(leftScanSpec,
-            rightScanSpec);
-      } else if (leftScanSpec != null) {
-        newFilter = leftScanSpec;
+      if (left != null && right != null) {
+        newFilter = MongoUtils.andFilterAtIndex(left, right);
+      } else if (left != null) {
+        newFilter = left;
       } else {
-        newFilter = rightScanSpec;
+        newFilter = right;
       }
       break;
     case FunctionNames.OR:
-      newFilter = MongoUtils.orFilterAtIndex(leftScanSpec,
-          rightScanSpec);
+      newFilter = MongoUtils.orFilterAtIndex(left, right);
     }
     return newFilter;
   }
@@ -91,18 +80,18 @@ public class MongoFilterBuilder extends
   @Override
   public Document visitBooleanOperator(BooleanOperator op, Void value) {
     List<LogicalExpression> args = op.args();
-    Document nodeScanSpec = null;
+    Document condition = null;
     String functionName = op.getName();
     for (LogicalExpression arg : args) {
       switch (functionName) {
         case FunctionNames.AND:
         case FunctionNames.OR:
-          if (nodeScanSpec == null) {
-            nodeScanSpec = arg.accept(this, null);
+          if (condition == null) {
+            condition = arg.accept(this, null);
           } else {
             Document scanSpec = arg.accept(this, null);
             if (scanSpec != null) {
-              nodeScanSpec = mergeScanSpecs(functionName, nodeScanSpec, scanSpec);
+              condition = mergeFilters(functionName, condition, scanSpec);
             } else {
               allExpressionsConverted = false;
             }
@@ -110,13 +99,13 @@ public class MongoFilterBuilder extends
           break;
       }
     }
-    return nodeScanSpec;
+    return condition;
   }
 
   @Override
   public Document visitFunctionCall(FunctionCall call, Void value)
       throws RuntimeException {
-    Document nodeScanSpec = null;
+    Document functionCall = null;
     String functionName = call.getName();
     List<LogicalExpression> args = call.args();
 
@@ -125,7 +114,7 @@ public class MongoFilterBuilder extends
           .process(call);
       if (processor.isSuccess()) {
         try {
-          nodeScanSpec = createMongoScanSpec(processor.getFunctionName(),
+          functionCall = createFunctionCall(processor.getFunctionName(),
               processor.getPath(), processor.getValue());
         } catch (Exception e) {
           logger.error(" Failed to creare Filter ", e);
@@ -136,29 +125,28 @@ public class MongoFilterBuilder extends
       switch (functionName) {
       case FunctionNames.AND:
       case FunctionNames.OR:
-        Document leftScanSpec = args.get(0).accept(this, null);
-        Document rightScanSpec = args.get(1).accept(this, null);
-        if (leftScanSpec != null && rightScanSpec != null) {
-          nodeScanSpec = mergeScanSpecs(functionName, leftScanSpec,
-              rightScanSpec);
+        Document left = args.get(0).accept(this, null);
+        Document right = args.get(1).accept(this, null);
+        if (left != null && right != null) {
+          functionCall = mergeFilters(functionName, left, right);
         } else {
           allExpressionsConverted = false;
           if (FunctionNames.AND.equals(functionName)) {
-            nodeScanSpec = leftScanSpec == null ? rightScanSpec : leftScanSpec;
+            functionCall = left == null ? right : left;
           }
         }
         break;
       }
     }
 
-    if (nodeScanSpec == null) {
+    if (functionCall == null) {
       allExpressionsConverted = false;
     }
 
-    return nodeScanSpec;
+    return functionCall;
   }
 
-  private Document createMongoScanSpec(String functionName,
+  private Document createFunctionCall(String functionName,
       SchemaPath field, Object fieldValue) {
     // extract the field name
     String fieldName = field.getRootSegmentPath();
diff --git a/contrib/storage-mongo/src/main/java/org/apache/drill/exec/store/mongo/MongoPushDownFilterForScan.java b/contrib/storage-mongo/src/main/java/org/apache/drill/exec/store/mongo/MongoPushDownFilterForScan.java
deleted file mode 100644
index 8dcd9d0..0000000
--- a/contrib/storage-mongo/src/main/java/org/apache/drill/exec/store/mongo/MongoPushDownFilterForScan.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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.drill.exec.store.mongo;
-
-import java.io.IOException;
-import java.util.Collections;
-
-import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.rel.core.Filter;
-import org.apache.drill.common.exceptions.DrillRuntimeException;
-import org.apache.drill.common.expression.LogicalExpression;
-import org.apache.drill.exec.planner.common.DrillScanRelBase;
-import org.apache.drill.exec.planner.logical.DrillOptiq;
-import org.apache.drill.exec.planner.logical.DrillParseContext;
-import org.apache.drill.exec.planner.logical.RelOptHelper;
-import org.apache.drill.exec.planner.physical.PrelUtil;
-import org.apache.drill.exec.store.StoragePluginOptimizerRule;
-import org.apache.calcite.plan.RelOptRuleCall;
-import org.apache.calcite.rex.RexNode;
-import org.bson.Document;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class MongoPushDownFilterForScan extends StoragePluginOptimizerRule {
-  private static final Logger logger = LoggerFactory
-      .getLogger(MongoPushDownFilterForScan.class);
-  public static final StoragePluginOptimizerRule INSTANCE = new MongoPushDownFilterForScan();
-
-  private MongoPushDownFilterForScan() {
-    super(
-        RelOptHelper.some(Filter.class, RelOptHelper.any(DrillScanRelBase.class)),
-        "MongoPushDownFilterForScan");
-  }
-
-  @Override
-  public void onMatch(RelOptRuleCall call) {
-    final DrillScanRelBase scan = call.rel(1);
-    final Filter filter = call.rel(0);
-    final RexNode condition = filter.getCondition();
-
-    MongoGroupScan groupScan = (MongoGroupScan) scan.getGroupScan();
-
-    LogicalExpression conditionExp = DrillOptiq.toDrill(
-        new DrillParseContext(PrelUtil.getPlannerSettings(call.getPlanner())), scan, condition);
-    MongoFilterBuilder mongoFilterBuilder = new MongoFilterBuilder(groupScan,
-        conditionExp);
-    Document newScanSpec = mongoFilterBuilder.parseTree();
-    if (newScanSpec == null) {
-      return; // no filter pushdown so nothing to apply.
-    }
-
-    MongoGroupScan newGroupsScan;
-    try {
-      newGroupsScan = new MongoGroupScan(groupScan.getUserName(), groupScan.getStoragePlugin(),
-          null, groupScan.getColumns(), groupScan.isUseAggregate());
-    } catch (IOException e) {
-      logger.error(e.getMessage(), e);
-      throw new DrillRuntimeException(e.getMessage(), e);
-    }
-
-    RelNode newScanPrel = scan.copy(filter.getTraitSet(), newGroupsScan, filter.getRowType());
-
-    if (mongoFilterBuilder.isAllExpressionsConverted()) {
-      /*
-       * Since we could convert the entire filter condition expression into an
-       * Mongo filter, we can eliminate the filter operator altogether.
-       */
-      call.transformTo(newScanPrel);
-    } else {
-      call.transformTo(filter.copy(filter.getTraitSet(),
-          Collections.singletonList(newScanPrel)));
-    }
-
-  }
-
-  @Override
-  public boolean matches(RelOptRuleCall call) {
-    DrillScanRelBase scan = call.rel(1);
-    return scan.getGroupScan() instanceof MongoGroupScan && super.matches(call);
-  }
-}
diff --git a/contrib/storage-mongo/src/main/java/org/apache/drill/exec/store/mongo/plan/MongoPluginImplementor.java b/contrib/storage-mongo/src/main/java/org/apache/drill/exec/store/mongo/plan/MongoPluginImplementor.java
index d51447c..5561047 100644
--- a/contrib/storage-mongo/src/main/java/org/apache/drill/exec/store/mongo/plan/MongoPluginImplementor.java
+++ b/contrib/storage-mongo/src/main/java/org/apache/drill/exec/store/mongo/plan/MongoPluginImplementor.java
@@ -69,14 +69,11 @@ public class MongoPluginImplementor implements PluginImplementor {
     visitChild(filter.getInput());
 
     LogicalExpression conditionExp = DrillOptiq.toDrill(
-        new DrillParseContext(PrelUtil.getPlannerSettings(filter.getCluster().getPlanner())), filter.getInput(), filter.getCondition());
-    MongoFilterBuilder mongoFilterBuilder = new MongoFilterBuilder(groupScan, conditionExp);
+        new DrillParseContext(PrelUtil.getPlannerSettings(filter.getCluster().getPlanner())),
+        filter.getInput(),
+        filter.getCondition());
+    MongoFilterBuilder mongoFilterBuilder = new MongoFilterBuilder(conditionExp);
     if (runAggregate) {
-//      MongoRules.RexToMongoTranslator translator =
-//          new MongoRules.RexToMongoTranslator(
-//              (JavaTypeFactory) filter.getCluster().getTypeFactory(),
-//              MongoRules.mongoFieldNames(filter.getInput().getRowType()));
-//      Bson convertedFilterExpression = Aggregates.match(filter.getCondition().accept(translator).asDocument()).toBsonDocument();
       Bson convertedFilterExpression = Aggregates.match(mongoFilterBuilder.parseTree()).toBsonDocument();
       operations.add(convertedFilterExpression);
     } else {
@@ -84,131 +81,6 @@ public class MongoPluginImplementor implements PluginImplementor {
     }
   }
 
-//  private String translateMatch(RexNode condition) {
-//    Map<String, Object> map = new HashMap<>();
-//    map.put("$match", translateOr(condition));
-//    return builder.toJsonString(map);
-//  }
-//
-//  private Object translateOr(RexNode condition) {
-//    final RexNode condition2 =
-//        RexUtil.expandSearch(rexBuilder, null, condition);
-//
-//    List<Object> list = new ArrayList<>();
-//    for (RexNode node : RelOptUtil.disjunctions(condition2)) {
-//      list.add(translateAnd(node));
-//    }
-//    switch (list.size()) {
-//      case 1:
-//        return list.get(0);
-//      default:
-//        Map<String, Object> map = builder.map();
-//        map.put("$or", list);
-//        return map;
-//    }
-//  }
-//
-//  /** Translates a condition that may be an AND of other conditions. Gathers
-//   * together conditions that apply to the same field. */
-//  private Map<String, Object> translateAnd(RexNode node0) {
-//    eqMap.clear();
-//    multimap.clear();
-//    for (RexNode node : RelOptUtil.conjunctions(node0)) {
-//      translateMatch2(node);
-//    }
-//    Map<String, Object> map = builder.map();
-//    for (Map.Entry<String, RexLiteral> entry : eqMap.entrySet()) {
-//      multimap.removeAll(entry.getKey());
-//      map.put(entry.getKey(), literalValue(entry.getValue()));
-//    }
-//    for (Map.Entry<String, Collection<Pair<String, RexLiteral>>> entry
-//        : multimap.asMap().entrySet()) {
-//      Map<String, Object> map2 = builder.map();
-//      for (Pair<String, RexLiteral> s : entry.getValue()) {
-//        addPredicate(map2, s.left, literalValue(s.right));
-//      }
-//      map.put(entry.getKey(), map2);
-//    }
-//    return map;
-//  }
-//
-//  private Void translateMatch2(RexNode node) {
-//    switch (node.getKind()) {
-//      case EQUALS:
-//        return translateBinary(null, null, (RexCall) node);
-//      case LESS_THAN:
-//        return translateBinary("$lt", "$gt", (RexCall) node);
-//      case LESS_THAN_OR_EQUAL:
-//        return translateBinary("$lte", "$gte", (RexCall) node);
-//      case NOT_EQUALS:
-//        return translateBinary("$ne", "$ne", (RexCall) node);
-//      case GREATER_THAN:
-//        return translateBinary("$gt", "$lt", (RexCall) node);
-//      case GREATER_THAN_OR_EQUAL:
-//        return translateBinary("$gte", "$lte", (RexCall) node);
-//      default:
-//        throw new AssertionError("cannot translate " + node);
-//    }
-//  }
-//
-//  /** Translates a call to a binary operator, reversing arguments if
-//   * necessary. */
-//  private Void translateBinary(String op, String rop, RexCall call) {
-//    final RexNode left = call.operands.get(0);
-//    final RexNode right = call.operands.get(1);
-//    boolean b = translateBinary2(op, left, right);
-//    if (b) {
-//      return null;
-//    }
-//    b = translateBinary2(rop, right, left);
-//    if (b) {
-//      return null;
-//    }
-//    throw new AssertionError("cannot translate op " + op + " call " + call);
-//  }
-//
-//  /** Translates a call to a binary operator. Returns whether successful. */
-//  private boolean translateBinary2(String op, RexNode left, RexNode right) {
-//    switch (right.getKind()) {
-//      case LITERAL:
-//        break;
-//      default:
-//        return false;
-//    }
-//    final RexLiteral rightLiteral = (RexLiteral) right;
-//    switch (left.getKind()) {
-//      case INPUT_REF:
-//        final RexInputRef left1 = (RexInputRef) left;
-//        String name = fieldNames.get(left1.getIndex());
-//        translateOp2(op, name, rightLiteral);
-//        return true;
-//      case CAST:
-//        return translateBinary2(op, ((RexCall) left).operands.get(0), right);
-//      case ITEM:
-//        String itemName = MongoRules.isItem((RexCall) left);
-//        if (itemName != null) {
-//          translateOp2(op, itemName, rightLiteral);
-//          return true;
-//        }
-//        // fall through
-//      default:
-//        return false;
-//    }
-//  }
-//
-//  private void translateOp2(String op, String name, RexLiteral right) {
-//    if (op == null) {
-//      // E.g.: {deptno: 100}
-//      eqMap.put(name, right);
-//    } else {
-//      // E.g. {deptno: {$lt: 100}}
-//      // which may later be combined with other conditions:
-//      // E.g. {deptno: [$lt: 100, $gt: 50]}
-//      multimap.put(name, Pair.of(op, right));
-//    }
-//  }
-
-
   @Override
   public void implement(PluginLimitRel limit) throws IOException {
     runAggregate = true;
@@ -232,17 +104,17 @@ public class MongoPluginImplementor implements PluginImplementor {
     visitChild(project.getInput());
 
     if (runAggregate) {
-      MongoRules.RexToMongoTranslator translator =
-          new MongoRules.RexToMongoTranslator(
+      RexToMongoTranslator translator =
+          new RexToMongoTranslator(
               (JavaTypeFactory) project.getCluster().getTypeFactory(),
-              MongoRules.mongoFieldNames(project.getInput().getRowType()));
+              MongoAggregateUtils.mongoFieldNames(project.getInput().getRowType()));
       List<BsonElement> items = new ArrayList<>();
       for (Pair<RexNode, String> pair : project.getNamedProjects()) {
         String name = pair.right;
         BsonValue expr = pair.left.accept(translator);
         items.add(expr.equals(new BsonString("$" + name))
-            ? new BsonElement(MongoRules.maybeQuote(name), new BsonInt32(1))
-            : new BsonElement(MongoRules.maybeQuote(name), expr));
+            ? new BsonElement(MongoAggregateUtils.maybeQuote(name), new BsonInt32(1))
+            : new BsonElement(MongoAggregateUtils.maybeQuote(name), expr));
       }
       BsonDocument projection = Aggregates.project(new BsonDocument(items)).toBsonDocument();
 
diff --git a/contrib/storage-mongo/src/main/java/org/apache/drill/exec/store/mongo/plan/MongoRules.java b/contrib/storage-mongo/src/main/java/org/apache/drill/exec/store/mongo/plan/MongoRules.java
deleted file mode 100644
index 953983b..0000000
--- a/contrib/storage-mongo/src/main/java/org/apache/drill/exec/store/mongo/plan/MongoRules.java
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * 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.drill.exec.store.mongo.plan;
-
-import org.apache.calcite.adapter.enumerable.RexImpTable;
-import org.apache.calcite.adapter.enumerable.RexToLixTranslator;
-import org.apache.calcite.adapter.java.JavaTypeFactory;
-import org.apache.calcite.rel.type.RelDataType;
-import org.apache.calcite.rex.RexCall;
-import org.apache.calcite.rex.RexInputRef;
-import org.apache.calcite.rex.RexLiteral;
-import org.apache.calcite.rex.RexNode;
-import org.apache.calcite.rex.RexVisitorImpl;
-import org.apache.calcite.sql.SqlKind;
-import org.apache.calcite.sql.SqlOperator;
-import org.apache.calcite.sql.fun.SqlStdOperatorTable;
-import org.apache.calcite.sql.type.SqlTypeName;
-import org.apache.calcite.sql.validate.SqlValidatorUtil;
-import org.apache.drill.exec.store.mongo.common.MongoOp;
-import org.bson.BsonArray;
-import org.bson.BsonDocument;
-import org.bson.BsonInt32;
-import org.bson.BsonNull;
-import org.bson.BsonString;
-import org.bson.BsonValue;
-
-import java.util.AbstractList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-public class MongoRules {
-
-  /** Returns 'string' if it is a call to item['string'], null otherwise. */
-  public static String isItem(RexCall call) {
-    if (call.getOperator() != SqlStdOperatorTable.ITEM) {
-      return null;
-    }
-    final RexNode op0 = call.operands.get(0);
-    final RexNode op1 = call.operands.get(1);
-    if (op0 instanceof RexInputRef
-        && ((RexInputRef) op0).getIndex() == 0
-        && op1 instanceof RexLiteral
-        && ((RexLiteral) op1).getValue2() instanceof String) {
-      return (String) ((RexLiteral) op1).getValue2();
-    }
-    return null;
-  }
-
-  static List<String> mongoFieldNames(final RelDataType rowType) {
-    return SqlValidatorUtil.uniquify(
-        new AbstractList<String>() {
-          @Override public String get(int index) {
-            final String name = rowType.getFieldList().get(index).getName();
-            return name.startsWith("$") ? "_" + name.substring(2) : name;
-          }
-
-          @Override public int size() {
-            return rowType.getFieldCount();
-          }
-        },
-        SqlValidatorUtil.EXPR_SUGGESTER, true);
-  }
-
-  static String maybeQuote(String s) {
-    if (!needsQuote(s)) {
-      return s;
-    }
-    return quote(s);
-  }
-
-  static String quote(String s) {
-    return "'" + s + "'"; // TODO: handle embedded quotes
-  }
-
-  private static boolean needsQuote(String s) {
-    for (int i = 0, n = s.length(); i < n; i++) {
-      char c = s.charAt(i);
-      if (!Character.isJavaIdentifierPart(c)
-          || c == '$') {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  /** Translator from {@link RexNode} to strings in MongoDB's expression
-   * language. */
-  static class RexToMongoTranslator extends RexVisitorImpl<BsonValue> {
-    private final JavaTypeFactory typeFactory;
-    private final List<String> inFields;
-
-    private static final Map<SqlOperator, String> MONGO_OPERATORS =
-        new HashMap<>();
-
-    static {
-      MONGO_OPERATORS.put(SqlStdOperatorTable.DIVIDE, "$divide");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.MULTIPLY, "$multiply");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.ABS, "$abs");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.ACOS, "$acos");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.ASIN, "$asin");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.ATAN, "$atan");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.ATAN2, "$atan2");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.CEIL, "$ceil");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.CONCAT, "$concat");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.COS, "$cos");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.DAYOFMONTH, "$dayOfMonth");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.WEEK, "$isoWeek");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.YEAR, "$isoWeekYear");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.DAYOFWEEK, "$isoDayOfWeek");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.DAYOFYEAR, "$dayOfYear");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.RADIANS, "$degreesToRadians");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.DENSE_RANK, "$denseRank");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.EXP, "$exp");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.FLOOR, "$floor");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.HOUR, "$hour");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.LN, "$ln");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.LOG10, "$log10");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.MINUTE, "$minute");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.MOD, "$mod");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.MONTH, "$month");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.POWER, "$pow");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.DEGREES, "$radiansToDegrees");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.RAND, "$rand");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.REPLACE, "$replaceAll");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.ROUND, "$round");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.SECOND, "$second");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.SIN, "$sin");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.SQRT, "$sqrt");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.SUBSTRING, "$substr");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.PLUS, "$add");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.MINUS, "$subtract");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.TAN, "$tan");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.TRIM, "trim");
-      MONGO_OPERATORS.put(SqlStdOperatorTable.TRUNCATE, "$trunc");
-      // Boolean
-      MONGO_OPERATORS.put(SqlStdOperatorTable.AND, MongoOp.AND.getCompareOp());
-      MONGO_OPERATORS.put(SqlStdOperatorTable.OR, MongoOp.OR.getCompareOp());
-      MONGO_OPERATORS.put(SqlStdOperatorTable.NOT, MongoOp.NOT.getCompareOp());
-      // Comparison
-      MONGO_OPERATORS.put(SqlStdOperatorTable.EQUALS, MongoOp.EQUAL.getCompareOp());
-      MONGO_OPERATORS.put(SqlStdOperatorTable.NOT_EQUALS, MongoOp.NOT_EQUAL.getCompareOp());
-      MONGO_OPERATORS.put(SqlStdOperatorTable.GREATER_THAN, MongoOp.GREATER.getCompareOp());
-      MONGO_OPERATORS.put(SqlStdOperatorTable.GREATER_THAN_OR_EQUAL, MongoOp.GREATER_OR_EQUAL.getCompareOp());
-      MONGO_OPERATORS.put(SqlStdOperatorTable.LESS_THAN, MongoOp.LESS.getCompareOp());
-      MONGO_OPERATORS.put(SqlStdOperatorTable.LESS_THAN_OR_EQUAL, MongoOp.LESS_OR_EQUAL.getCompareOp());
-    }
-
-    protected RexToMongoTranslator(JavaTypeFactory typeFactory,
-        List<String> inFields) {
-      super(true);
-      this.typeFactory = typeFactory;
-      this.inFields = inFields;
-    }
-
-    @Override
-    public BsonValue visitLiteral(RexLiteral literal) {
-      if (literal.getValue() == null) {
-        return BsonNull.VALUE;
-      }
-      return new BsonDocument("$literal", new BsonString(
-          RexToLixTranslator.translateLiteral(literal, literal.getType(),
-              typeFactory, RexImpTable.NullAs.NOT_POSSIBLE).toString()));
-    }
-
-    @Override
-    public BsonValue visitInputRef(RexInputRef inputRef) {
-      return new BsonString(
-          "$" + inFields.get(inputRef.getIndex()));
-    }
-
-    @Override
-    public BsonValue visitCall(RexCall call) {
-      String name = isItem(call);
-      if (name != null) {
-        return new BsonString("'$" + name + "'");
-      }
-      List<BsonValue> strings = call.operands.stream()
-          .map(operand -> operand.accept(this))
-          .collect(Collectors.toList());
-
-      if (call.getKind() == SqlKind.CAST) {
-        return strings.get(0);
-      }
-      String stdOperator = MONGO_OPERATORS.get(call.getOperator());
-      if (stdOperator != null) {
-        return new BsonDocument(stdOperator, new BsonArray(strings));
-      }
-      if (call.getOperator() == SqlStdOperatorTable.ITEM) {
-        final RexNode op1 = call.operands.get(1);
-        if (op1 instanceof RexLiteral
-            && op1.getType().getSqlTypeName() == SqlTypeName.INTEGER) {
-          return new BsonDocument("$arrayElemAt", new BsonArray(
-              Arrays.asList(strings.get(0), new BsonInt32(((RexLiteral) op1).getValueAs(Integer.class)))));
-        }
-      }
-      if (call.getOperator() == SqlStdOperatorTable.CASE) {
-        StringBuilder sb = new StringBuilder();
-        StringBuilder finish = new StringBuilder();
-        // case(a, b, c)  -> $cond:[a, b, c]
-        // case(a, b, c, d) -> $cond:[a, b, $cond:[c, d, null]]
-        // case(a, b, c, d, e) -> $cond:[a, b, $cond:[c, d, e]]
-        for (int i = 0; i < strings.size(); i += 2) {
-          sb.append("{$cond:[");
-          finish.append("]}");
-
-          sb.append(strings.get(i));
-          sb.append(',');
-          sb.append(strings.get(i + 1));
-          sb.append(',');
-          if (i == strings.size() - 3) {
-            sb.append(strings.get(i + 2));
-            break;
-          }
-          if (i == strings.size() - 2) {
-            sb.append("null");
-            break;
-          }
-        }
-        sb.append(finish);
-        return BsonDocument.parse(sb.toString());
-      }
-      throw new IllegalArgumentException("Translation of " + call
-          + " is not supported by MongoProject");
-    }
-  }
-}
diff --git a/contrib/storage-mongo/src/main/java/org/apache/drill/exec/store/mongo/plan/RexToMongoTranslator.java b/contrib/storage-mongo/src/main/java/org/apache/drill/exec/store/mongo/plan/RexToMongoTranslator.java
new file mode 100644
index 0000000..478591f
--- /dev/null
+++ b/contrib/storage-mongo/src/main/java/org/apache/drill/exec/store/mongo/plan/RexToMongoTranslator.java
@@ -0,0 +1,184 @@
+package org.apache.drill.exec.store.mongo.plan;
+
+import org.apache.calcite.adapter.enumerable.RexImpTable;
+import org.apache.calcite.adapter.enumerable.RexToLixTranslator;
+import org.apache.calcite.adapter.java.JavaTypeFactory;
+import org.apache.calcite.rex.RexCall;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexVisitorImpl;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.drill.exec.store.mongo.common.MongoOp;
+import org.bson.BsonArray;
+import org.bson.BsonDocument;
+import org.bson.BsonInt32;
+import org.bson.BsonNull;
+import org.bson.BsonString;
+import org.bson.BsonValue;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * Translator from {@link RexNode} to strings in MongoDB's expression
+ * language.
+ */
+class RexToMongoTranslator extends RexVisitorImpl<BsonValue> {
+  private final JavaTypeFactory typeFactory;
+
+  private final List<String> inFields;
+
+  private static final Map<SqlOperator, String> MONGO_OPERATORS =
+      new HashMap<>();
+
+  static {
+    MONGO_OPERATORS.put(SqlStdOperatorTable.DIVIDE, "$divide");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.MULTIPLY, "$multiply");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.ABS, "$abs");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.ACOS, "$acos");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.ASIN, "$asin");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.ATAN, "$atan");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.ATAN2, "$atan2");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.CEIL, "$ceil");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.CONCAT, "$concat");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.COS, "$cos");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.DAYOFMONTH, "$dayOfMonth");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.WEEK, "$isoWeek");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.YEAR, "$isoWeekYear");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.DAYOFWEEK, "$isoDayOfWeek");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.DAYOFYEAR, "$dayOfYear");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.RADIANS, "$degreesToRadians");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.DENSE_RANK, "$denseRank");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.EXP, "$exp");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.FLOOR, "$floor");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.HOUR, "$hour");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.LN, "$ln");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.LOG10, "$log10");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.MINUTE, "$minute");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.MOD, "$mod");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.MONTH, "$month");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.POWER, "$pow");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.DEGREES, "$radiansToDegrees");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.RAND, "$rand");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.REPLACE, "$replaceAll");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.ROUND, "$round");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.SECOND, "$second");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.SIN, "$sin");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.SQRT, "$sqrt");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.SUBSTRING, "$substr");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.PLUS, "$add");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.MINUS, "$subtract");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.TAN, "$tan");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.TRIM, "trim");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.TRUNCATE, "$trunc");
+    MONGO_OPERATORS.put(SqlStdOperatorTable.AND, MongoOp.AND.getCompareOp());
+    MONGO_OPERATORS.put(SqlStdOperatorTable.OR, MongoOp.OR.getCompareOp());
+    MONGO_OPERATORS.put(SqlStdOperatorTable.NOT, MongoOp.NOT.getCompareOp());
+    MONGO_OPERATORS.put(SqlStdOperatorTable.EQUALS, MongoOp.EQUAL.getCompareOp());
+    MONGO_OPERATORS.put(SqlStdOperatorTable.NOT_EQUALS, MongoOp.NOT_EQUAL.getCompareOp());
+    MONGO_OPERATORS.put(SqlStdOperatorTable.GREATER_THAN, MongoOp.GREATER.getCompareOp());
+    MONGO_OPERATORS.put(SqlStdOperatorTable.GREATER_THAN_OR_EQUAL, MongoOp.GREATER_OR_EQUAL.getCompareOp());
+    MONGO_OPERATORS.put(SqlStdOperatorTable.LESS_THAN, MongoOp.LESS.getCompareOp());
+    MONGO_OPERATORS.put(SqlStdOperatorTable.LESS_THAN_OR_EQUAL, MongoOp.LESS_OR_EQUAL.getCompareOp());
+  }
+
+  protected RexToMongoTranslator(JavaTypeFactory typeFactory,
+      List<String> inFields) {
+    super(true);
+    this.typeFactory = typeFactory;
+    this.inFields = inFields;
+  }
+
+  @Override
+  public BsonValue visitLiteral(RexLiteral literal) {
+    if (literal.getValue() == null) {
+      return BsonNull.VALUE;
+    }
+    return new BsonDocument("$literal", new BsonString(
+        RexToLixTranslator.translateLiteral(literal, literal.getType(),
+            typeFactory, RexImpTable.NullAs.NOT_POSSIBLE).toString()));
+  }
+
+  @Override
+  public BsonValue visitInputRef(RexInputRef inputRef) {
+    return new BsonString("$" + inFields.get(inputRef.getIndex()));
+  }
+
+  @Override
+  public BsonValue visitCall(RexCall call) {
+    String name = isItem(call);
+    if (name != null) {
+      return new BsonString("'$" + name + "'");
+    }
+    List<BsonValue> strings = call.operands.stream()
+        .map(operand -> operand.accept(this))
+        .collect(Collectors.toList());
+
+    if (call.getKind() == SqlKind.CAST) {
+      return strings.get(0);
+    }
+    String stdOperator = MONGO_OPERATORS.get(call.getOperator());
+    if (stdOperator != null) {
+      return new BsonDocument(stdOperator, new BsonArray(strings));
+    }
+    if (call.getOperator() == SqlStdOperatorTable.ITEM) {
+      final RexNode op1 = call.operands.get(1);
+      if (op1 instanceof RexLiteral
+          && op1.getType().getSqlTypeName() == SqlTypeName.INTEGER) {
+        return new BsonDocument("$arrayElemAt", new BsonArray(
+            Arrays.asList(strings.get(0), new BsonInt32(((RexLiteral) op1).getValueAs(Integer.class)))));
+      }
+    }
+    if (call.getOperator() == SqlStdOperatorTable.CASE) {
+      // case(a, b, c)  -> $cond:[a, b, c]
+      // case(a, b, c, d) -> $cond:[a, b, $cond:[c, d, null]]
+      // case(a, b, c, d, e) -> $cond:[a, b, $cond:[c, d, e]]
+      BsonDocument result = new BsonDocument();
+      BsonArray args = new BsonArray();
+      result.put("$cond", args);
+      for (int i = 0; i < strings.size(); i += 2) {
+        args.add(strings.get(i));
+        args.add(strings.get(i + 1));
+        if (i == strings.size() - 3) {
+          args.add(strings.get(i + 2));
+          break;
+        }
+        if (i == strings.size() - 2) {
+          args.add(BsonNull.VALUE);
+          break;
+        }
+        BsonArray innerArgs = new BsonArray();
+        args.add(innerArgs);
+        args = innerArgs;
+      }
+      return result;
+    }
+    throw new IllegalArgumentException("Translation of " + call + " is not supported by MongoProject");
+  }
+
+
+  /**
+   * Returns 'string' if it is a call to item['string'], null otherwise.
+   */
+  public static String isItem(RexCall call) {
+    if (call.getOperator() != SqlStdOperatorTable.ITEM) {
+      return null;
+    }
+    final RexNode op0 = call.operands.get(0);
+    final RexNode op1 = call.operands.get(1);
+    if (op0 instanceof RexInputRef
+        && ((RexInputRef) op0).getIndex() == 0
+        && op1 instanceof RexLiteral
+        && ((RexLiteral) op1).getValue2() instanceof String) {
+      return (String) ((RexLiteral) op1).getValue2();
+    }
+    return null;
+  }
+}