This is an automated email from the ASF dual-hosted git repository.
cancai pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/main by this push:
new e3184180ba [CALCITE-2109] MongoAdapter: Support in condition with and
condition
e3184180ba is described below
commit e3184180ba170827e0e91682a693cd02b46b8657
Author: tuichenchuxin <[email protected]>
AuthorDate: Thu Mar 27 11:39:04 2025 +0800
[CALCITE-2109] MongoAdapter: Support in condition with and condition
---
.../calcite/adapter/mongodb/MongoFilter.java | 67 ++++++++++++++--------
.../calcite/adapter/mongodb/MongoAdapterTest.java | 60 +++++++++++++++++++
2 files changed, 103 insertions(+), 24 deletions(-)
diff --git
a/mongodb/src/main/java/org/apache/calcite/adapter/mongodb/MongoFilter.java
b/mongodb/src/main/java/org/apache/calcite/adapter/mongodb/MongoFilter.java
index bb8299b202..455877aa94 100644
--- a/mongodb/src/main/java/org/apache/calcite/adapter/mongodb/MongoFilter.java
+++ b/mongodb/src/main/java/org/apache/calcite/adapter/mongodb/MongoFilter.java
@@ -81,10 +81,6 @@ public MongoFilter(
/** Translates {@link RexNode} expressions into MongoDB expression strings.
*/
static class Translator {
final JsonBuilder builder = new JsonBuilder();
- final Multimap<String, Pair<String, RexLiteral>> multimap =
- HashMultimap.create();
- final Map<String, RexLiteral> eqMap =
- new LinkedHashMap<>();
private final RexBuilder rexBuilder;
private final List<String> fieldNames;
@@ -99,11 +95,11 @@ private String translateMatch(RexNode condition) {
return builder.toJsonString(map);
}
- private Object translateOr(RexNode condition) {
+ private Map<String, Object> translateOr(RexNode condition) {
final RexNode condition2 =
RexUtil.expandSearch(rexBuilder, null, condition);
- List<Object> list = new ArrayList<>();
+ List<Map<String, Object>> list = new ArrayList<>();
for (RexNode node : RelOptUtil.disjunctions(condition2)) {
list.add(translateAnd(node));
}
@@ -120,10 +116,13 @@ private Object translateOr(RexNode condition) {
/** 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();
+ final Multimap<String, Pair<String, RexLiteral>> multimap =
+ HashMultimap.create();
+ final Map<String, RexLiteral> eqMap =
+ new LinkedHashMap<>();
+ final List<Map<String, Object>> orMapList = new ArrayList<>();
for (RexNode node : RelOptUtil.conjunctions(node0)) {
- translateMatch2(node);
+ translateMatch2(node, orMapList, multimap, eqMap);
}
Map<String, Object> map = builder.map();
for (Map.Entry<String, RexLiteral> entry : eqMap.entrySet()) {
@@ -138,6 +137,14 @@ private Map<String, Object> translateAnd(RexNode node0) {
}
map.put(entry.getKey(), map2);
}
+ if (!orMapList.isEmpty()) {
+ Map<String, Object> andMap = builder.map();
+ if (!map.isEmpty()) {
+ orMapList.add(map);
+ }
+ andMap.put("$and", orMapList);
+ return andMap;
+ }
return map;
}
@@ -173,35 +180,45 @@ private static Object literalValue(RexLiteral literal) {
return literal.getValue2();
}
- private Void translateMatch2(RexNode node) {
+ private Void translateMatch2(RexNode node, List<Map<String, Object>>
orMapList,
+ Multimap<String, Pair<String, RexLiteral>> multimap, Map<String,
RexLiteral> eqMap) {
switch (node.getKind()) {
case EQUALS:
- return translateBinary(null, null, (RexCall) node);
+ return translateBinary(null, null, (RexCall) node, multimap, eqMap);
case LESS_THAN:
- return translateBinary("$lt", "$gt", (RexCall) node);
+ return translateBinary("$lt", "$gt", (RexCall) node, multimap, eqMap);
case LESS_THAN_OR_EQUAL:
- return translateBinary("$lte", "$gte", (RexCall) node);
+ return translateBinary("$lte", "$gte", (RexCall) node, multimap,
eqMap);
case NOT_EQUALS:
- return translateBinary("$ne", "$ne", (RexCall) node);
+ return translateBinary("$ne", "$ne", (RexCall) node, multimap, eqMap);
case GREATER_THAN:
- return translateBinary("$gt", "$lt", (RexCall) node);
+ return translateBinary("$gt", "$lt", (RexCall) node, multimap, eqMap);
case GREATER_THAN_OR_EQUAL:
- return translateBinary("$gte", "$lte", (RexCall) node);
+ return translateBinary("$gte", "$lte", (RexCall) node, multimap,
eqMap);
+ case OR:
+ return translateOrAddToList(node, orMapList);
default:
throw new AssertionError("cannot translate " + node);
}
}
+ private Void translateOrAddToList(RexNode node, List<Map<String, Object>>
orMapList) {
+ Map<String, Object> or = translateOr(node);
+ orMapList.add(or);
+ return null;
+ }
+
/** Translates a call to a binary operator, reversing arguments if
* necessary. */
- private Void translateBinary(String op, String rop, RexCall call) {
+ private Void translateBinary(String op, String rop, RexCall call,
+ Multimap<String, Pair<String, RexLiteral>> multimap, Map<String,
RexLiteral> eqMap) {
final RexNode left = call.operands.get(0);
final RexNode right = call.operands.get(1);
- boolean b = translateBinary2(op, left, right);
+ boolean b = translateBinary2(op, left, right, multimap, eqMap);
if (b) {
return null;
}
- b = translateBinary2(rop, right, left);
+ b = translateBinary2(rop, right, left, multimap, eqMap);
if (b) {
return null;
}
@@ -209,7 +226,8 @@ private Void translateBinary(String op, String rop, RexCall
call) {
}
/** Translates a call to a binary operator. Returns whether successful. */
- private boolean translateBinary2(String op, RexNode left, RexNode right) {
+ private boolean translateBinary2(String op, RexNode left, RexNode right,
+ Multimap<String, Pair<String, RexLiteral>> multimap, Map<String,
RexLiteral> eqMap) {
switch (right.getKind()) {
case LITERAL:
break;
@@ -221,14 +239,14 @@ private boolean translateBinary2(String op, RexNode left,
RexNode right) {
case INPUT_REF:
final RexInputRef left1 = (RexInputRef) left;
String name = fieldNames.get(left1.getIndex());
- translateOp2(op, name, rightLiteral);
+ translateOp2(op, name, rightLiteral, multimap, eqMap);
return true;
case CAST:
- return translateBinary2(op, ((RexCall) left).operands.get(0), right);
+ return translateBinary2(op, ((RexCall) left).operands.get(0), right,
multimap, eqMap);
case ITEM:
String itemName = MongoRules.isItem((RexCall) left);
if (itemName != null) {
- translateOp2(op, itemName, rightLiteral);
+ translateOp2(op, itemName, rightLiteral, multimap, eqMap);
return true;
}
// fall through
@@ -237,7 +255,8 @@ private boolean translateBinary2(String op, RexNode left,
RexNode right) {
}
}
- private void translateOp2(String op, String name, RexLiteral right) {
+ private void translateOp2(String op, String name, RexLiteral right,
+ Multimap<String, Pair<String, RexLiteral>> multimap, Map<String,
RexLiteral> eqMap) {
if (op == null) {
// E.g.: {deptno: 100}
eqMap.put(name, right);
diff --git
a/mongodb/src/test/java/org/apache/calcite/adapter/mongodb/MongoAdapterTest.java
b/mongodb/src/test/java/org/apache/calcite/adapter/mongodb/MongoAdapterTest.java
index 0aeb6ef87c..1876862d2e 100644
---
a/mongodb/src/test/java/org/apache/calcite/adapter/mongodb/MongoAdapterTest.java
+++
b/mongodb/src/test/java/org/apache/calcite/adapter/mongodb/MongoAdapterTest.java
@@ -859,4 +859,64 @@ private static Consumer<List> mongoChecker(final String...
expected) {
.limit(2)
.returns("STATE=VT; AVG(pop)=26408\nSTATE=AK; AVG(pop)=26856\n");
}
+
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-2109">[CALCITE-2109]
+ * Mongo adapter: unable to translate (A AND B) conditional case</a>. */
+ @Test void testTranslateAndInCondition() {
+ assertModel(MODEL)
+ .query("select state, city from zips "
+ + "where city='LEWISTON' and state in ('ME', 'VT') "
+ + "order by state")
+ .queryContains(
+ mongoChecker(
+ "{$match: {$and: [{$or: [{state: \"ME\"}, {state: \"VT\"}]},
{city: \"LEWISTON\"}]}}",
+ "{$project: {STATE: '$state', CITY: '$city'}}",
+ "{$sort: {STATE: 1}}"))
+ .returns("STATE=ME; CITY=LEWISTON\n");
+ }
+
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-2109">[CALCITE-2109]
+ * Mongo adapter: unable to translate (A AND B) conditional case</a>. */
+ @Test void testTranslateOrAndCondition() {
+ assertModel(MODEL)
+ .query("select state, city from zips "
+ + "where (state = 'MI' or state = 'VT') and city='TAYLOR' "
+ + "order by state")
+ .queryContains(
+ mongoChecker(
+ "{$match: {$and: [{$or: [{state: \"MI\"}, {state: \"VT\"}]},
{city: \"TAYLOR\"}]}}",
+ "{$project: {STATE: '$state', CITY: '$city'}}",
+ "{$sort: {STATE: 1}}"))
+ .returns("STATE=MI; CITY=TAYLOR\n");
+ }
+
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-2109">[CALCITE-2109]
+ * Mongo adapter: unable to translate (A AND B) conditional case</a>. */
+ @Test void testAndAlwaysFalseCondition() {
+ assertModel(MODEL)
+ .query("select state, city from zips "
+ + "where city='LEWISTON' and 1=0 "
+ + "order by state")
+ .explainContains("PLAN=EnumerableValues(tuples=[[]])")
+ .returns("");
+ }
+
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-2109">[CALCITE-2109]
+ * Mongo adapter: unable to translate (A AND B) conditional case</a>. */
+ @Test void testCNFCondition() {
+ assertModel(MODEL)
+ .query("select state, city from zips "
+ + "where (state='ME' OR state='VT') AND (city='LEWISTON' OR
city='BRATTLEBORO') "
+ + "order by state")
+ .queryContains(
+ mongoChecker(
+ "{$match: {$and: [{$or: [{state: \"ME\"}, {state: \"VT\"}]},
{$or: [{city: \"BRATTLEBORO\"}, {city: \"LEWISTON\"}]}]}}",
+ "{$project: {STATE: '$state', CITY: '$city'}}",
+ "{$sort: {STATE: 1}}"))
+ .returns("STATE=ME; CITY=LEWISTON\nSTATE=VT; CITY=BRATTLEBORO\n");
+ }
}