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 <[email protected]> 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; + } +}
