From bd60175e14513e6d6549bde669fc694180df2ab4 Mon Sep 17 00:00:00 2001 From: Ramesh Reddy Date: Fri, 15 Nov 2013 10:42:43 -0600 Subject: [PATCH] TEIID-2736: 1) Fixed the bug with nested function expressions 2) added support for boolean expressions in select clause using mongodb's operator --- .../mongodb/MongoDBExecutionFactory.java | 5 + .../mongodb/MongoDBSelectVisitor.java | 173 ++++++++++++------ .../mongodb/MongoDBUpdateVisitor.java | 8 +- .../mongodb/TestMongoDBSelectVisitor.java | 56 ++++-- 4 files changed, 170 insertions(+), 72 deletions(-) diff --git a/connectors/translator-mongodb/src/main/java/org/teiid/translator/mongodb/MongoDBExecutionFactory.java b/connectors/translator-mongodb/src/main/java/org/teiid/translator/mongodb/MongoDBExecutionFactory.java index 75170dd861..244dd4f356 100644 --- a/connectors/translator-mongodb/src/main/java/org/teiid/translator/mongodb/MongoDBExecutionFactory.java +++ b/connectors/translator-mongodb/src/main/java/org/teiid/translator/mongodb/MongoDBExecutionFactory.java @@ -281,6 +281,11 @@ public boolean supportsSelectExpression() { return true; } + @Override + public boolean supportsOnlyLiteralComparison() { + return true; + } + /** * @param field * @param expectedClass diff --git a/connectors/translator-mongodb/src/main/java/org/teiid/translator/mongodb/MongoDBSelectVisitor.java b/connectors/translator-mongodb/src/main/java/org/teiid/translator/mongodb/MongoDBSelectVisitor.java index 3a58a2c1dc..b4742222b5 100644 --- a/connectors/translator-mongodb/src/main/java/org/teiid/translator/mongodb/MongoDBSelectVisitor.java +++ b/connectors/translator-mongodb/src/main/java/org/teiid/translator/mongodb/MongoDBSelectVisitor.java @@ -58,7 +58,6 @@ public class MongoDBSelectVisitor extends HierarchyVisitor { private Select command; protected ArrayList exceptions = new ArrayList(); - protected Stack onGoingCriteria = new Stack(); protected Stack onGoingPullCriteria = new Stack(); protected Stack onGoingExpression = new Stack(); protected ConcurrentHashMap expressionMap = new ConcurrentHashMap(); @@ -80,6 +79,7 @@ public class MongoDBSelectVisitor extends HierarchyVisitor { protected LinkedList unwindTables = new LinkedList(); protected ArrayList pendingConditions = new ArrayList(); protected LinkedList joinedDocuments = new LinkedList(); + private boolean processingDerivedColumn = false; public MongoDBSelectVisitor(MongoDBExecutionFactory executionFactory, RuntimeMetadata metadata) { this.executionFactory = executionFactory; @@ -146,7 +146,12 @@ public String getColumnName(ColumnReference obj) { @Override public void visit(DerivedColumn obj) { this.onGoingAlias = buildAlias(obj.getAlias()); - append(obj.getExpression()); + + Expression originalExpr = obj.getExpression(); + + this.processingDerivedColumn = true; + append(originalExpr); + this.processingDerivedColumn = false; Object expr = this.onGoingExpression.pop(); @@ -155,7 +160,7 @@ public void visit(DerivedColumn obj) { previousAlias = this.onGoingAlias; } - if (obj.getExpression() instanceof ColumnReference) { + if (originalExpr instanceof ColumnReference) { String elementName = getColumnName((ColumnReference)obj.getExpression()); this.selectColumnReferences.add(elementName); // the the expression is already part of group by then the projection should be $_id.{name} @@ -179,6 +184,23 @@ public void visit(DerivedColumn obj) { } } else { + if (originalExpr instanceof AggregateFunction) { + ColumnAlias alias = addToProject(expr, false); + if (!this.group.values().contains(expr)) { + this.group.put(alias.projectedName, expr); + } + } + else if (originalExpr instanceof Function) { + addToProject(expr, true); + } + else if (originalExpr instanceof Condition) { + // needs to be in the form "_mo: {$cond: [{$eq :["$city", "FREEDOM"]}, true, false]}}}" + BasicDBList values = new BasicDBList(); + values.add(0, expr); + values.add(1, true); + values.add(2, false); + addToProject(new BasicDBObject("$cond", values), true); //$NON-NLS-1$ + } // what user sees as project this.selectColumns.add(previousAlias.projectedName); this.selectColumnReferences.add(previousAlias.projectedName); @@ -327,15 +349,11 @@ else if (obj.getName().equals(AggregateFunction.MAX)) { } if (expr != null) { - ColumnAlias alias = addToProject(expr, false); - if (!this.group.values().contains(expr)) { - this.group.put(alias.projectedName, expr); - } this.onGoingExpression.push(expr); } } - private ColumnAlias addToProject(BasicDBObject expr, boolean addExprAsProject) { + private ColumnAlias addToProject(Object expr, boolean addExprAsProject) { ColumnAlias previousAlias = this.expressionMap.get(expr); if (previousAlias == null) { // if expression is in having clause there is will be no alias; however mongo expects this @@ -371,8 +389,7 @@ public void visit(Function obj) { expr = new BasicDBObject(obj.getName(), params); } - if(expr != null) { - addToProject(expr, true); + if(expr != null) { this.onGoingExpression.push(expr); } } @@ -472,12 +489,12 @@ public void visit(Select obj) { append(obj.getWhere()); } - if (!this.onGoingCriteria.isEmpty()) { + if (!this.onGoingExpression.isEmpty()) { if (this.match != null) { - this.match = QueryBuilder.start().and(this.match, this.onGoingCriteria.pop()).get(); + this.match = QueryBuilder.start().and(this.match, (DBObject)this.onGoingExpression.pop()).get(); } else { - this.match = this.onGoingCriteria.pop(); + this.match = (DBObject)this.onGoingExpression.pop(); } } @@ -498,8 +515,8 @@ public void visit(Select obj) { append(obj.getHaving()); } - if (!this.onGoingCriteria.isEmpty()) { - this.having = this.onGoingCriteria.pop(); + if (!this.onGoingExpression.isEmpty()) { + this.having = (DBObject)this.onGoingExpression.pop(); } if (!this.group.isEmpty()) { @@ -522,9 +539,15 @@ public void visit(Select obj) { @Override public void visit(Comparison obj) { + + // this for $cond in the select statement, and formatting of command for $cond vs $match is different + if (this.processingDerivedColumn) { + visitDerivedExpression(obj); + return; + } + + // this for the normal where clause ColumnAlias exprAlias = getExpressionAlias(obj.getLeftExpression()); - QueryBuilder query = QueryBuilder.start(exprAlias.selectionName); - QueryBuilder pullQuery = QueryBuilder.start(exprAlias.pullColumnName); append(obj.getRightExpression()); @@ -532,36 +555,38 @@ public void visit(Comparison obj) { if (this.expressionMap.get(rightExpr) != null) { rightExpr = this.expressionMap.get(rightExpr).projectedName; } - if (query != null) { - switch(obj.getOperator()) { - case EQ: - query.is(rightExpr); - pullQuery.is(rightExpr); - break; - case NE: - query.notEquals(rightExpr); - pullQuery.notEquals(rightExpr); - break; - case LT: - query.lessThan(rightExpr); - pullQuery.lessThan(rightExpr); - break; - case LE: - query.lessThanEquals(rightExpr); - pullQuery.lessThanEquals(rightExpr); - break; - case GT: - query.greaterThan(rightExpr); - pullQuery.greaterThan(rightExpr); - break; - case GE: - query.greaterThanEquals(rightExpr); - pullQuery.greaterThanEquals(rightExpr); - break; - } - this.onGoingCriteria.push(query.get()); + + QueryBuilder query = QueryBuilder.start(exprAlias.selectionName); + QueryBuilder pullQuery = QueryBuilder.start(exprAlias.pullColumnName); + + switch(obj.getOperator()) { + case EQ: + query.is(rightExpr); + pullQuery.is(rightExpr); + break; + case NE: + query.notEquals(rightExpr); + pullQuery.notEquals(rightExpr); + break; + case LT: + query.lessThan(rightExpr); + pullQuery.lessThan(rightExpr); + break; + case LE: + query.lessThanEquals(rightExpr); + pullQuery.lessThanEquals(rightExpr); + break; + case GT: + query.greaterThan(rightExpr); + pullQuery.greaterThan(rightExpr); + break; + case GE: + query.greaterThanEquals(rightExpr); + pullQuery.greaterThanEquals(rightExpr); + break; } - + this.onGoingExpression.push(query.get()); + if (obj.getLeftExpression() instanceof ColumnReference) { ColumnReference colum = (ColumnReference)obj.getLeftExpression(); this.mongoDoc.updateReferenceColumnValue(colum.getTable().getName(), exprAlias.columnName, rightExpr); @@ -571,23 +596,61 @@ public void visit(Comparison obj) { this.onGoingPullCriteria.push(pullQuery.get()); } + private void visitDerivedExpression(Comparison obj) { + append(obj.getLeftExpression()); + Object leftExpr = this.onGoingExpression.pop(); + append(obj.getRightExpression()); + Object rightExpr = this.onGoingExpression.pop(); + + BasicDBList values = new BasicDBList(); + values.add(0, leftExpr); + values.add(1, rightExpr); + + switch(obj.getOperator()) { + case EQ: + this.onGoingExpression.push(new BasicDBObject("$eq", values)); //$NON-NLS-1$ + this.onGoingPullCriteria.push(new BasicDBObject("$eq", values)); //$NON-NLS-1$ + break; + case NE: + this.onGoingExpression.push(new BasicDBObject("$ne", values)); //$NON-NLS-1$ + this.onGoingPullCriteria.push(new BasicDBObject("$ne", values)); //$NON-NLS-1$ + break; + case LT: + this.onGoingExpression.push(new BasicDBObject("$lt", values)); //$NON-NLS-1$ + this.onGoingPullCriteria.push(new BasicDBObject("$lt", values)); //$NON-NLS-1$ + break; + case LE: + this.onGoingExpression.push(new BasicDBObject("$lte", values)); //$NON-NLS-1$ + this.onGoingPullCriteria.push(new BasicDBObject("$lte", values)); //$NON-NLS-1$ + break; + case GT: + this.onGoingExpression.push(new BasicDBObject("$gt", values)); //$NON-NLS-1$ + this.onGoingPullCriteria.push(new BasicDBObject("$gt", values)); //$NON-NLS-1$ + break; + case GE: + this.onGoingExpression.push(new BasicDBObject("$gte", values)); //$NON-NLS-1$ + this.onGoingPullCriteria.push(new BasicDBObject("$gte", values)); //$NON-NLS-1$ + break; + } + } + @Override public void visit(AndOr obj) { append(obj.getLeftCondition()); append(obj.getRightCondition()); - DBObject right = this.onGoingCriteria.pop(); - DBObject left = this.onGoingCriteria.pop(); + DBObject right = (DBObject)this.onGoingExpression.pop(); + DBObject left = (DBObject) this.onGoingExpression.pop(); DBObject pullRight = this.onGoingPullCriteria.pop(); DBObject pullLeft = this.onGoingPullCriteria.pop(); switch(obj.getOperator()) { case AND: - this.onGoingCriteria.push(QueryBuilder.start().and(left, right).get()); + this.onGoingExpression.push(QueryBuilder.start().and(left, right).get()); this.onGoingPullCriteria.push(QueryBuilder.start().and(pullLeft, pullRight).get()); break; case OR: - this.onGoingCriteria.push(QueryBuilder.start().or(left, right).get()); + this.onGoingExpression.push(QueryBuilder.start().or(left, right).get()); this.onGoingPullCriteria.push(QueryBuilder.start().or(pullLeft, pullRight).get()); break; } @@ -623,7 +686,7 @@ public void visit(In obj) { query.in(values); pullQuery.in(values); } - this.onGoingCriteria.push(query.get()); + this.onGoingExpression.push(query.get()); this.onGoingPullCriteria.push(pullQuery.get()); } } @@ -637,8 +700,8 @@ private ColumnAlias getExpressionAlias(Expression obj) { Object expr = this.onGoingExpression.pop(); ColumnAlias exprAlias = this.expressionMap.get(expr); if (exprAlias == null) { - //exprAlias = buildAlias(null); - //this.expressionMap.put(expr, exprAlias); + exprAlias = buildAlias(null); + this.expressionMap.put(expr, exprAlias); } // when expression shows up in a condition, but it is not a derived column @@ -661,7 +724,7 @@ public void visit(IsNull obj) { query.is(null); pullQuery.is(null); } - this.onGoingCriteria.push(query.get()); + this.onGoingExpression.push(query.get()); this.onGoingPullCriteria.push(pullQuery.get()); } } @@ -709,7 +772,7 @@ public void visit(Like obj) { String regex = value.toString().replaceAll("%", ""); //$NON-NLS-1$ //$NON-NLS-2$ query.is(Pattern.compile(regex)); pullQuery.is(Pattern.compile(regex)); - this.onGoingCriteria.push(query.get()); + this.onGoingExpression.push(query.get()); this.onGoingPullCriteria.push(pullQuery.get()); } } diff --git a/connectors/translator-mongodb/src/main/java/org/teiid/translator/mongodb/MongoDBUpdateVisitor.java b/connectors/translator-mongodb/src/main/java/org/teiid/translator/mongodb/MongoDBUpdateVisitor.java index c839f63a6c..eb1b78da74 100644 --- a/connectors/translator-mongodb/src/main/java/org/teiid/translator/mongodb/MongoDBUpdateVisitor.java +++ b/connectors/translator-mongodb/src/main/java/org/teiid/translator/mongodb/MongoDBUpdateVisitor.java @@ -110,8 +110,8 @@ public void visit(Update obj) { append(obj.getWhere()); - if (!this.onGoingCriteria.isEmpty()) { - this.match = this.onGoingCriteria.pop(); + if (!this.onGoingExpression.isEmpty()) { + this.match = (DBObject)this.onGoingExpression.pop(); } } @@ -121,8 +121,8 @@ public void visit(Delete obj) { append(obj.getTable()); append(obj.getWhere()); - if (!this.onGoingCriteria.isEmpty()) { - this.match = this.onGoingCriteria.pop(); + if (!this.onGoingExpression.isEmpty()) { + this.match = (DBObject)this.onGoingExpression.pop(); } } diff --git a/connectors/translator-mongodb/src/test/java/org/teiid/translator/mongodb/TestMongoDBSelectVisitor.java b/connectors/translator-mongodb/src/test/java/org/teiid/translator/mongodb/TestMongoDBSelectVisitor.java index 26cfa6c756..828cd35503 100644 --- a/connectors/translator-mongodb/src/test/java/org/teiid/translator/mongodb/TestMongoDBSelectVisitor.java +++ b/connectors/translator-mongodb/src/test/java/org/teiid/translator/mongodb/TestMongoDBSelectVisitor.java @@ -375,20 +375,10 @@ public void testPlusOperatorWithOutAlias() throws Exception { helpExecute(query, "users", "{ \"_m0\" : { \"$add\" : [ \"$age\" , \"$age\"]}}", null, null, null); } - //TODO: fix this - public void testPlusOperatorInWhere() throws Exception { - String query = "SELECT age FROM users WHERE age*2 > 2.5"; - helpExecute(query, "users", "{ \"_m0\" : \"$age\"}", "{ \"$divide\" : [ \"$age\" , 2]}"); - } - @Test - public void testPlusOperatorInWhere2() throws Exception { - String query = "SELECT age FROM users WHERE age/2 > age*3"; - helpExecute( - query, - "users", - "{ \"_m0\" : { \"$divide\" : [ \"$age\" , 2]} , \"_m1\" : { \"$multiply\" : [ \"$age\" , 3]} , \"_m2\" : \"$age\"}", - "{ \"_m0\" : { \"$gt\" : \"_m1\"}}"); + public void testPlusOperatorInWhere() throws Exception { + String query = "SELECT age FROM users WHERE age > 5.0"; + helpExecute(query, "users", "{ \"_m0\" : \"$age\"}", "{ \"age\" : { \"$gt\" : 5}}"); } @Test @@ -398,7 +388,47 @@ public void testFunction() throws Exception { "{ \"_m0\" : { \"$concat\" : [ \"$user_id\" , \"$user_id\"]}}", null); } + + @Test + public void testSelectBooleanExpression() throws Exception { + String query = "SELECT (user_id = 'USER') as X1 FROM users"; + helpExecute(query, "users", + "{ \"X1\" : { \"$cond\" : [ { \"$eq\" : [ \"$user_id\" , \"USER\"]} , true , false]}}", + null); + } + + @Test + public void testSelectBooleanExpression2() throws Exception { + String query = "SELECT (user_id > 'USER') as X1 FROM users"; + helpExecute(query, "users", + "{ \"X1\" : { \"$cond\" : [ { \"$gt\" : [ \"$user_id\" , \"USER\"]} , true , false]}}", + null); + } + + @Test + public void testSelectBooleanExpression3() throws Exception { + String query = "SELECT (user_id = 'USER' OR user_id = 'user') as X1 FROM users"; + helpExecute(query, "users", + "{ \"X1\" : { \"$cond\" : [ { \"_m0\" : { \"$in\" : [ \"user\" , \"USER\"]}} , true , false]}}", + null); + } + + @Test + public void testSelectBooleanExpression4() throws Exception { + String query = "SELECT (user_id = 'USER' AND age > 30) as X1 FROM users"; + helpExecute(query, "users", + "{ \"X1\" : { \"$cond\" : [ { \"$and\" : [ { \"$eq\" : [ \"$user_id\" , \"USER\"]} , { \"$gt\" : [ \"$age\" , 30]}]} , true , false]}}", + null); + } + @Test + public void testNestedFunction() throws Exception { + String query = "SELECT concat(concat(user_id, user_id), user_id) FROM users"; + helpExecute(query, "users", + "{ \"_m0\" : { \"$concat\" : [ { \"$concat\" : [ \"$user_id\" , \"$user_id\"]} , \"$user_id\"]}}", + null); + } + @Test public void testWhereReference() throws Exception { String query = "SELECT age FROM users WHERE user_id = 'bob'";