Skip to content

Commit

Permalink
[AD-203] Fix where clause (#7)
Browse files Browse the repository at this point in the history
* AD-204 Fixed comparisons against NULL/undefined (#89)

* AD-215 Fixed comparisons for CASE

* AD-204 Fix for multi-field conditions

* Commit Code Coverage Badge

* Commit Code Coverage Badge

* AD-204 Added test in query mapping service

* Commit Code Coverage Badge

* AD-204 Added check for cast, removed list copying

* Commit Code Coverage Badge

Co-authored-by: mitchell-elholm <mitchell-elholm@users.noreply.github.com>

* AD-203 Initial commit with unit tests

* AD-203 Used RexToMongoTranslator

* AD-203 Added fix for quotes, and Long parsing, also fixed unit tests

* AD-203 Added test for nested OR in WHERE clause

* AD-203 Changed $literal to $numberLong

* AD-203 Format and fixed comment

* Commit Code Coverage Badge

* AD-203 Removed $literal

* Commit Code Coverage Badge

* AD-203 Added note on $literal, fixed some types

* Commit Code Coverage Badge

* AD-203 Changed to use placeholder constant, added DECIMAL to types

* Commit Code Coverage Badge

Co-authored-by: mitchell-elholm <mitchell-elholm@users.noreply.github.com>
  • Loading branch information
mitchell-elholm and mitchell-elholm authored Jun 18, 2021
1 parent 24a496a commit d24aa2e
Show file tree
Hide file tree
Showing 6 changed files with 495 additions and 208 deletions.
2 changes: 1 addition & 1 deletion .github/badges/branches.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion .github/badges/jacoco.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -16,42 +16,36 @@
*/
package software.amazon.documentdb.jdbc.calcite.adapter;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import org.apache.calcite.adapter.java.JavaTypeFactory;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptCost;
import org.apache.calcite.plan.RelOptPlanner;
import org.apache.calcite.plan.RelOptUtil;
import org.apache.calcite.plan.RelTraitSet;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.core.Filter;
import org.apache.calcite.rel.metadata.RelMetadataQuery;
import org.apache.calcite.rex.RexBuilder;
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.RexUtil;
import org.apache.calcite.util.JsonBuilder;
import org.apache.calcite.util.Pair;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
* Implementation of a {@link Filter}
* relational expression in MongoDB.
*/
public class DocumentDbFilter extends Filter implements DocumentDbRel {

/**
* This is a placeholder field to contain the output of the boolean expression in a
* where clause.
*/
public static final String BOOLEAN_FLAG_FIELD = "\"placeholderField1F84EB1G3K47\"";

/**
* Creates a new {@link DocumentDbFilter}
* @param cluster the relational option cluster.
* @param traitSet the trait set.
* @param child the child.
*
* @param cluster the relational option cluster.
* @param traitSet the trait set.
* @param child the child.
* @param condition the condition.
*/
public DocumentDbFilter(
Expand All @@ -64,32 +58,39 @@ public DocumentDbFilter(
assert getConvention() == child.getConvention();
}

@Override public @Nullable RelOptCost computeSelfCost(final RelOptPlanner planner,
final RelMetadataQuery mq) {
@Override
public @Nullable RelOptCost computeSelfCost(final RelOptPlanner planner,
final RelMetadataQuery mq) {
return super.computeSelfCost(planner, mq).multiplyBy(DocumentDbRules.FILTER_COST_FACTOR);
}

@Override public DocumentDbFilter copy(final RelTraitSet traitSet, final RelNode input,
final RexNode condition) {
@Override
public DocumentDbFilter copy(final RelTraitSet traitSet, final RelNode input,
final RexNode condition) {
return new DocumentDbFilter(getCluster(), traitSet, input, condition);
}

@Override public void implement(final Implementor implementor) {
@Override
public void implement(final Implementor implementor) {
implementor.visitChild(0, getInput());
// DocumentDB: modified - start
final DocumentDbRel.Implementor mongoImplementor =
new DocumentDbRel.Implementor(implementor.getRexBuilder());
mongoImplementor.visitChild(0, getInput());
final Translator translator =
new Translator(implementor.getRexBuilder(),
DocumentDbRules.mongoFieldNames(getRowType(),
final DocumentDbRules.RexToMongoTranslator rexToMongoTranslator =
new DocumentDbRules.RexToMongoTranslator(
(JavaTypeFactory) getCluster().getTypeFactory(),
DocumentDbRules.mongoFieldNames(getInput().getRowType(),
mongoImplementor.getMetadataTable()));
// DocumentDB: modified - end
final String match = translator.translateMatch(condition);
implementor.add(null, match);
final RexNode expandedCondition = RexUtil.expandSearch(implementor.getRexBuilder(), null, condition);
final String match = expandedCondition.accept(rexToMongoTranslator);
implementor.add(null, "{\"$addFields\": {" + BOOLEAN_FLAG_FIELD + ": " + match + "}}");
implementor.add(null, "{\"$match\": {" + BOOLEAN_FLAG_FIELD + ": {\"$eq\": true}}}");
implementor.add(null, "{\"$project\": {" + BOOLEAN_FLAG_FIELD + ":0}}");
}

/** Translates {@link RexNode} expressions into MongoDB expression strings. */
/*
/** Translates {@link RexNode} expressions into MongoDB expression strings. o/
static class Translator {
private final JsonBuilder builder = new JsonBuilder();
private final Multimap<String, Pair<String, RexLiteral>> multiMap =
Expand Down Expand Up @@ -129,7 +130,7 @@ private Object translateOr(final RexNode condition) {
}
/** Translates a condition that may be an AND of other conditions. Gathers
* together conditions that apply to the same field. */
* together conditions that apply to the same field. o/
private Map<String, Object> translateAnd(final RexNode node0) {
eqMap.clear();
multiMap.clear();
Expand Down Expand Up @@ -178,7 +179,7 @@ private static void addPredicate(final Map<String, Object> map, final String op,
*
* <p>For example, {@code stronger("$lt", 100, 200)} returns true, because
* "&lt; 100" is a more powerful condition than "&lt; 200".
*/
o/
private static boolean stronger(final String key, final Object v0, final Object v1) {
if ("$lt".equals(key) || "$lte".equals(key)) {
if (v0 instanceof Number && v1 instanceof Number) {
Expand Down Expand Up @@ -218,7 +219,7 @@ private Void translateMatch2(final RexNode node) {
}
/** Translates a call to a binary operator, reversing arguments if
* necessary. */
* necessary. o/
private Void translateBinary(final String op, final String rop, final RexCall call) {
final RexNode left = call.operands.get(0);
final RexNode right = call.operands.get(1);
Expand All @@ -233,7 +234,7 @@ private Void translateBinary(final String op, final String rop, final RexCall ca
throw new AssertionError("cannot translate op " + op + " call " + call);
}
/** Translates a call to a binary operator. Returns whether successful. */
/** Translates a call to a binary operator. Returns whether successful. o/
private boolean translateBinary2(final String op, final RexNode left, final RexNode right) {
switch (right.getKind()) {
case LITERAL:
Expand Down Expand Up @@ -274,4 +275,5 @@ private void translateOp2(final String op, final String name, final RexLiteral r
}
}
}
*/
}
Original file line number Diff line number Diff line change
Expand Up @@ -218,10 +218,36 @@ protected RexToMongoTranslator(final JavaTypeFactory typeFactory,
if (literal.getValue() == null) {
return "null";
}
return "{$literal: "
+ RexToLixTranslator.translateLiteral(literal, literal.getType(),
typeFactory, RexImpTable.NullAs.NOT_POSSIBLE)
+ "}";

switch (literal.getType().getSqlTypeName()) {
case DOUBLE:
case DECIMAL:
return "{\"$numberDouble\": \"" + literal.getValueAs(Double.class) + "\"}";
case BIGINT:
case INTERVAL_DAY:
case INTERVAL_HOUR:
case INTERVAL_MINUTE:
case INTERVAL_SECOND:
// Convert supported intervals to milliseconds.
return "{\"$numberLong\": \"" + literal.getValueAs(Long.class) + "\"}";
case DATE:
return "{\"$date\": {\"$numberLong\": \"" + literal.getValueAs(Integer.class) + "\" } }";
case TIMESTAMP:
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
// Convert from date in milliseconds to MongoDb date.
return "{\"$date\": {\"$numberLong\": \"" + literal.getValueAs(Long.class) + "\" } }";
default:
/*
TODO: AD-239: Re-add use of literal here.
return "{\"$literal\": "
+ RexToLixTranslator.translateLiteral(literal, literal.getType(),
typeFactory, RexImpTable.NullAs.NOT_POSSIBLE)
+ "}";
*/
return RexToLixTranslator.translateLiteral(literal, literal.getType(),
typeFactory, RexImpTable.NullAs.NOT_POSSIBLE).toString();
}
}

@Override public String visitInputRef(final RexInputRef inputRef) {
Expand All @@ -241,7 +267,7 @@ protected RexToMongoTranslator(final JavaTypeFactory typeFactory,
final String stdOperator = MONGO_OPERATORS.get(call.getOperator());
if (stdOperator != null) {
// For comparisons other than equals we must check it exists and is not null.
final String op = "{" + stdOperator + ": [" + Util.commaList(strings) + "]}";
final String op = "{" + maybeQuote(stdOperator) + ": [" + Util.commaList(strings) + "]}";
if (MONGO_OPERATORS.get(SqlStdOperatorTable.LESS_THAN).equals(stdOperator) ||
MONGO_OPERATORS.get(SqlStdOperatorTable.LESS_THAN_OR_EQUAL).equals(stdOperator) ||
MONGO_OPERATORS.get(SqlStdOperatorTable.NOT_EQUALS).equals(stdOperator) ||
Expand Down Expand Up @@ -293,12 +319,12 @@ protected RexToMongoTranslator(final JavaTypeFactory typeFactory,
}

private String addNullChecksToQuery(final List<String> strings, final String op) {
final StringBuilder sb = new StringBuilder("{$and: [");
final StringBuilder sb = new StringBuilder("{\"$and\": [");
sb.append(op);
for (int i = 0; i < 2; i++) {
if (!strings.get(i).equals("null")) {
// The operator {$gt null} filters out any values that are null or undefined.
sb.append("{$gt: [");
sb.append(",{\"$gt\": [");
sb.append(strings.get(i));
sb.append(", null]}");
}
Expand Down
Loading

0 comments on commit d24aa2e

Please sign in to comment.