Skip to content

Commit

Permalink
Updated MongoDB/Panache parser to latest ORM parser
Browse files Browse the repository at this point in the history
This way, we keep it up to date, because the previous version was a
pre-release of ORM 5-6.
This required some visitor changes, and in particular there's a
precedence change between 'and/or' expresions who now require paren
grouping.
Also, literals are not implemented correctly: only strings are supported
but this was already the case: tests do not test other literals.
  • Loading branch information
FroMage committed Feb 13, 2024
1 parent 490992e commit ae57329
Show file tree
Hide file tree
Showing 5 changed files with 1,516 additions and 438 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
import java.util.Map;

import io.quarkus.panacheql.internal.HqlParser;
import io.quarkus.panacheql.internal.HqlParser.ComparisonPredicateContext;
import io.quarkus.panacheql.internal.HqlParser.GroupedExpressionContext;
import io.quarkus.panacheql.internal.HqlParser.GroupedPredicateContext;
import io.quarkus.panacheql.internal.HqlParser.NamedParameterContext;
import io.quarkus.panacheql.internal.HqlParser.ParameterContext;
import io.quarkus.panacheql.internal.HqlParser.PositionalParameterContext;
import io.quarkus.panacheql.internal.HqlParserBaseVisitor;

class MongoParserVisitor extends HqlParserBaseVisitor<String> {
Expand Down Expand Up @@ -38,18 +44,28 @@ public String visitOrPredicate(HqlParser.OrPredicateContext ctx) {
}

@Override
public String visitEqualityPredicate(HqlParser.EqualityPredicateContext ctx) {
return ctx.expression(0).accept(this) + ":" + ctx.expression(1).accept(this);
}

@Override
public String visitInequalityPredicate(HqlParser.InequalityPredicateContext ctx) {
return ctx.expression(0).accept(this) + ":{'$ne':" + ctx.expression(1).accept(this) + "}";
}

@Override
public String visitLessThanOrEqualPredicate(HqlParser.LessThanOrEqualPredicateContext ctx) {
return ctx.expression(0).accept(this) + ":{'$lte':" + ctx.expression(1).accept(this) + "}";
public String visitComparisonPredicate(ComparisonPredicateContext ctx) {
String lhs = ctx.expression(0).accept(this);
String rhs = ctx.expression(1).accept(this);
if (ctx.comparisonOperator().EQUAL() != null) {
return lhs + ":" + rhs;
}
if (ctx.comparisonOperator().NOT_EQUAL() != null) {
return lhs + ":{'$ne':" + rhs + "}";
}
if (ctx.comparisonOperator().GREATER() != null) {
return lhs + ":{'$gt':" + rhs + "}";
}
if (ctx.comparisonOperator().GREATER_EQUAL() != null) {
return lhs + ":{'$gte':" + rhs + "}";
}
if (ctx.comparisonOperator().LESS() != null) {
return lhs + ":{'$lt':" + rhs + "}";
}
if (ctx.comparisonOperator().LESS_EQUAL() != null) {
return lhs + ":{'$lte':" + rhs + "}";
}
return super.visitComparisonPredicate(ctx);
}

@Override
Expand All @@ -64,33 +80,47 @@ public String visitLikePredicate(HqlParser.LikePredicateContext ctx) {
}

@Override
public String visitGreaterThanPredicate(HqlParser.GreaterThanPredicateContext ctx) {
return ctx.expression(0).accept(this) + ":{'$gt':" + ctx.expression(1).accept(this) + "}";
public String visitIsNullPredicate(HqlParser.IsNullPredicateContext ctx) {
boolean exists = ctx.NOT() != null;
return ctx.expression().accept(this) + ":{'$exists':" + exists + "}";
}

@Override
public String visitLiteralExpression(HqlParser.LiteralExpressionContext ctx) {
String text = ctx.getText();
// FIXME: this only really supports text literals
if (ctx.literal().STRING_LITERAL() != null) {
text = text.substring(1, text.length() - 1);
}
return CommonQueryBinder.escape(text);
}

@Override
public String visitLessThanPredicate(HqlParser.LessThanPredicateContext ctx) {
return ctx.expression(0).accept(this) + ":{'$lt':" + ctx.expression(1).accept(this) + "}";
public String visitNamedParameter(NamedParameterContext ctx) {
return visitParameter(ctx);
}

@Override
public String visitGreaterThanOrEqualPredicate(HqlParser.GreaterThanOrEqualPredicateContext ctx) {
return ctx.expression(0).accept(this) + ":{'$gte':" + ctx.expression(1).accept(this) + "}";
public String visitPositionalParameter(PositionalParameterContext ctx) {
return visitParameter(ctx);
}

@Override
public String visitIsNullPredicate(HqlParser.IsNullPredicateContext ctx) {
boolean exists = ctx.NOT() != null;
return ctx.expression().accept(this) + ":{'$exists':" + exists + "}";
public String visitParameterExpression(HqlParser.ParameterExpressionContext ctx) {
return visitParameter(ctx.parameter());
}

@Override
public String visitLiteralExpression(HqlParser.LiteralExpressionContext ctx) {
return CommonQueryBinder.escape(ctx.getText());
public String visitGroupedExpression(GroupedExpressionContext ctx) {
return ctx.expression().accept(this);
}

@Override
public String visitParameterExpression(HqlParser.ParameterExpressionContext ctx) {
public String visitGroupedPredicate(GroupedPredicateContext ctx) {
return ctx.predicate().accept(this);
}

private String visitParameter(ParameterContext ctx) {
// this will match parameters used by PanacheQL : '?1' for index based or ':key' for named one.
if (parameterMaps.containsKey(ctx.getText())) {
Object value = parameterMaps.get(ctx.getText());
Expand All @@ -102,7 +132,7 @@ public String visitParameterExpression(HqlParser.ParameterExpressionContext ctx)
}

@Override
public String visitPathExpression(HqlParser.PathExpressionContext ctx) {
public String visitGeneralPathExpression(HqlParser.GeneralPathExpressionContext ctx) {
// this is the name of the field, we apply replacement and escape with '
return "'" + replacementMap.getOrDefault(ctx.getText(), ctx.getText()) + "'";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,12 +285,12 @@ public void testBindEnhancedFilterByIndex() {
assertEquals("{'field':{'$in':['f1', 'f2']},'isOk':true}", query);

query = operations.bindFilter(DemoObj.class,
"field in ?1 and property = ?2 or property = ?3",
"field in ?1 and (property = ?2 or property = ?3)",
new Object[] { list, "jpg", "gif" });
assertEquals("{'field':{'$in':['f1', 'f2']},'$or':[{'value':'jpg'},{'value':'gif'}]}", query);

query = operations.bindFilter(DemoObj.class,
"field in ?1 and isOk = ?2 and property = ?3 or property = ?4",
"field in ?1 and isOk = ?2 and (property = ?3 or property = ?4)",
new Object[] { list, true, "jpg", "gif" });
assertEquals("{'field':{'$in':['f1', 'f2']},'isOk':true,'$or':[{'value':'jpg'},{'value':'gif'}]}", query);
}
Expand Down Expand Up @@ -361,12 +361,12 @@ public void testBindEnhancedFilterByName() {
assertEquals("{'field':{'$in':['f1', 'f2']},'isOk':true}", query);

query = operations.bindFilter(DemoObj.class,
"field in :fields and property = :p1 or property = :p2",
"field in :fields and (property = :p1 or property = :p2)",
Parameters.with("fields", list).and("p1", "jpg").and("p2", "gif").map());
assertEquals("{'field':{'$in':['f1', 'f2']},'$or':[{'value':'jpg'},{'value':'gif'}]}", query);

query = operations.bindFilter(DemoObj.class,
"field in :fields and isOk = :isOk and property = :p1 or property = :p2",
"field in :fields and isOk = :isOk and (property = :p1 or property = :p2)",
Parameters.with("fields", list)
.and("isOk", true)
.and("p1", "jpg")
Expand Down
Loading

0 comments on commit ae57329

Please sign in to comment.