diff --git a/src/main/java/com/aerospike/dsl/DSLParser.java b/src/main/java/com/aerospike/dsl/DSLParser.java new file mode 100644 index 0000000..a6d70ae --- /dev/null +++ b/src/main/java/com/aerospike/dsl/DSLParser.java @@ -0,0 +1,111 @@ +package com.aerospike.dsl; + +import com.aerospike.client.exp.Expression; +import com.aerospike.client.query.Filter; +import com.aerospike.dsl.exception.AerospikeDSLException; + +import java.util.List; + +/** + * Contains API to convert dot separated String path into an Aerospike filter - + * a functional language for applying predicates to bin data and record metadata. + *
+ * Such filters are used in different areas of Aerospike Server functionality including the following: + * + */ +public interface DSLParser { + + /** + * Parse String DSL path into Aerospike filter Expression. + *

+ * Examples: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
$.binName Bin “binName”
a Map key “a”
'1' Map key (String) “1”
1 Map key 1
{1} Map index 1
{=1} Map value (int) 1
{=bb} Map value “bb”
{='1'} Map value (String) “1”
{#1} Map rank 1
[1] List index 1
[=1] List value 1
[#1] List rank 1
+ *
+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
$.binName [binName]
$.mapBinName.k [mapBinName -> mapKey("a")]
$.mapBinName.a.aa.aaa [mapBinName -> mapKey("a") -> mapKey("aa") -> mapKey("aaa")]
$.mapBinName.a.55 [mapBinName -> mapKey("a") -> mapKey(55)]
$.listBinName.[1].aa [listBinName -> listIndex(1) -> mapKey("aa")]
$.mapBinName.ab.cd.[-1].'10' [mapBinName -> mapKey("ab") -> mapKey("cd") -> listIndex(-1) -> + * mapKey("10")]
+ * @param input String consisting of dot separated elements, typically bin name and optional context + * @return Expression object + * @throws AerospikeDSLException in case of unsupported DSL String or invalid syntax + */ + Expression parseExpression(String input); + + /** + * Parse String DSL path into Aerospike secondary index Filter. + *

+ * Examples: + * + * + * + * + * + * + * + * + * + * + *
$.intBin1 == 10 Filter.equal("intBin1", 10)
$.intBin1 > 10 Filter.range("intBin1", 11, Long.MAX_VALUE)
$.stringBin1 == 'text' Filter.equal("stringBin1", "text")
+ * @param input String consisting of dot separated elements, typically bin name and value + * @return List of Filter objects + * @throws AerospikeDSLException in case of unsupported DSL String or invalid syntax + */ + List parseFilters(String input); +} diff --git a/src/main/java/com/aerospike/dsl/ConditionTranslator.java b/src/main/java/com/aerospike/dsl/DSLParserImpl.java similarity index 55% rename from src/main/java/com/aerospike/dsl/ConditionTranslator.java rename to src/main/java/com/aerospike/dsl/DSLParserImpl.java index 4f26b7c..8a9287b 100644 --- a/src/main/java/com/aerospike/dsl/ConditionTranslator.java +++ b/src/main/java/com/aerospike/dsl/DSLParserImpl.java @@ -2,19 +2,22 @@ import com.aerospike.client.exp.Exp; import com.aerospike.client.exp.Expression; +import com.aerospike.client.query.Filter; import com.aerospike.dsl.annotation.Beta; import com.aerospike.dsl.exception.AerospikeDSLException; import com.aerospike.dsl.model.AbstractPart; -import lombok.experimental.UtilityClass; +import com.aerospike.dsl.visitor.ExpressionConditionVisitor; +import com.aerospike.dsl.visitor.FilterConditionVisitor; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.tree.ParseTree; -@UtilityClass -public class ConditionTranslator { +import java.util.List; + +public class DSLParserImpl implements DSLParser { @Beta - public static Expression translate(String input) { + public Expression parseExpression(String input) { ConditionLexer lexer = new ConditionLexer(CharStreams.fromString(input)); ConditionParser parser = new ConditionParser(new CommonTokenStream(lexer)); ParseTree tree = parser.parse(); @@ -29,4 +32,19 @@ public static Expression translate(String input) { Exp expResult = abstractPart.getExp(); return Exp.build(expResult); } + + @Beta + public List parseFilters(String input) { + ConditionLexer lexer = new ConditionLexer(CharStreams.fromString(input)); + ConditionParser parser = new ConditionParser(new CommonTokenStream(lexer)); + ParseTree tree = parser.parse(); + + FilterConditionVisitor visitor = new FilterConditionVisitor(); + AbstractPart abstractPart = visitor.visit(tree); + + if (abstractPart == null) { + throw new AerospikeDSLException("Could not parse given input, wrong syntax"); + } + return abstractPart.getSIndexFilter().getFilters(); + } } diff --git a/src/main/java/com/aerospike/dsl/model/AbstractPart.java b/src/main/java/com/aerospike/dsl/model/AbstractPart.java index 8d7d71d..2499c79 100644 --- a/src/main/java/com/aerospike/dsl/model/AbstractPart.java +++ b/src/main/java/com/aerospike/dsl/model/AbstractPart.java @@ -11,6 +11,7 @@ public abstract class AbstractPart { protected Exp.Type expType; protected PartType partType; protected Exp exp; + protected SIndexFilter sIndexFilter; protected AbstractPart(PartType partType) { this.partType = partType; @@ -22,6 +23,11 @@ protected AbstractPart(PartType partType, Exp exp) { this.exp = exp; } + protected AbstractPart(PartType partType, SIndexFilter filters) { + this.partType = partType; + this.sIndexFilter = filters; + } + public enum PartType { INT_OPERAND, FLOAT_OPERAND, diff --git a/src/main/java/com/aerospike/dsl/model/BinPart.java b/src/main/java/com/aerospike/dsl/model/BinPart.java index c037bb7..98098ac 100644 --- a/src/main/java/com/aerospike/dsl/model/BinPart.java +++ b/src/main/java/com/aerospike/dsl/model/BinPart.java @@ -9,7 +9,7 @@ public class BinPart extends Expr { private final String binName; public BinPart(String binName) { - super(null); // Exp unknown + super((Exp) null); // Exp unknown this.binName = binName; this.partType = PartType.BIN_PART; this.expType = null; // Exp type unknown diff --git a/src/main/java/com/aerospike/dsl/model/Expr.java b/src/main/java/com/aerospike/dsl/model/Expr.java index 116a52a..ce68eeb 100644 --- a/src/main/java/com/aerospike/dsl/model/Expr.java +++ b/src/main/java/com/aerospike/dsl/model/Expr.java @@ -6,7 +6,29 @@ @Getter public class Expr extends AbstractPart { + protected AbstractPart left; + protected AbstractPart right; + private ExprPartsOperation operationType; + public Expr(Exp exp) { super(PartType.EXPR, exp); } + + public Expr(SIndexFilter filter) { + super(PartType.EXPR, filter); + } + + public Expr(AbstractPart left, AbstractPart right, ExprPartsOperation operationType) { + super(PartType.EXPR); + this.left = left; + this.right = right; + this.operationType = operationType; + } + + public enum ExprPartsOperation { + ADD, + SUB, + DIV, + MUL + } } diff --git a/src/main/java/com/aerospike/dsl/model/SIndexFilter.java b/src/main/java/com/aerospike/dsl/model/SIndexFilter.java new file mode 100644 index 0000000..54a9c15 --- /dev/null +++ b/src/main/java/com/aerospike/dsl/model/SIndexFilter.java @@ -0,0 +1,22 @@ +package com.aerospike.dsl.model; + +import com.aerospike.client.query.Filter; +import lombok.Getter; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class SIndexFilter { + + @Getter + protected final List filters = new ArrayList<>(); + + public SIndexFilter(Filter filter) { + filters.add(filter); + } + + public SIndexFilter(Collection filters) { + this.filters.addAll(filters); + } +} diff --git a/src/main/java/com/aerospike/dsl/model/cdt/list/ListRankRangeRelative.java b/src/main/java/com/aerospike/dsl/model/cdt/list/ListRankRangeRelative.java index 1d603a5..cad2325 100644 --- a/src/main/java/com/aerospike/dsl/model/cdt/list/ListRankRangeRelative.java +++ b/src/main/java/com/aerospike/dsl/model/cdt/list/ListRankRangeRelative.java @@ -7,8 +7,8 @@ import com.aerospike.dsl.ConditionParser; import com.aerospike.dsl.exception.AerospikeDSLException; import com.aerospike.dsl.model.BasePath; -import com.aerospike.dsl.util.ParsingUtils; +import static com.aerospike.dsl.util.ParsingUtils.unquote; import static com.aerospike.dsl.util.ParsingUtils.subtractNullable; public class ListRankRangeRelative extends ListPart { @@ -51,7 +51,7 @@ public static ListRankRangeRelative from(ConditionParser.ListRankRangeRelativeCo } else if (valueIdentifierContext.NAME_IDENTIFIER() != null) { relativeValue = valueIdentifierContext.NAME_IDENTIFIER().getText(); } else if (valueIdentifierContext.QUOTED_STRING() != null) { - relativeValue = ParsingUtils.getWithoutQuotes(valueIdentifierContext.QUOTED_STRING().getText()); + relativeValue = unquote(valueIdentifierContext.QUOTED_STRING().getText()); } } diff --git a/src/main/java/com/aerospike/dsl/model/cdt/list/ListValue.java b/src/main/java/com/aerospike/dsl/model/cdt/list/ListValue.java index ac3f165..580ec96 100644 --- a/src/main/java/com/aerospike/dsl/model/cdt/list/ListValue.java +++ b/src/main/java/com/aerospike/dsl/model/cdt/list/ListValue.java @@ -6,7 +6,8 @@ import com.aerospike.client.exp.ListExp; import com.aerospike.dsl.ConditionParser; import com.aerospike.dsl.model.BasePath; -import com.aerospike.dsl.util.ParsingUtils; + +import static com.aerospike.dsl.util.ParsingUtils.unquote; public class ListValue extends ListPart { private final Object value; @@ -21,7 +22,7 @@ public static ListValue from(ConditionParser.ListValueContext ctx) { if (ctx.valueIdentifier().NAME_IDENTIFIER() != null) { listValue = ctx.valueIdentifier().NAME_IDENTIFIER().getText(); } else if (ctx.valueIdentifier().QUOTED_STRING() != null) { - listValue = ParsingUtils.getWithoutQuotes(ctx.valueIdentifier().QUOTED_STRING().getText()); + listValue = unquote(ctx.valueIdentifier().QUOTED_STRING().getText()); } else if (ctx.valueIdentifier().INT() != null) { listValue = Integer.parseInt(ctx.valueIdentifier().INT().getText()); } diff --git a/src/main/java/com/aerospike/dsl/model/cdt/list/ListValueList.java b/src/main/java/com/aerospike/dsl/model/cdt/list/ListValueList.java index 8cf5926..de762e4 100644 --- a/src/main/java/com/aerospike/dsl/model/cdt/list/ListValueList.java +++ b/src/main/java/com/aerospike/dsl/model/cdt/list/ListValueList.java @@ -10,7 +10,7 @@ import java.util.List; -import static com.aerospike.dsl.util.ParsingUtils.getWithoutQuotes; +import static com.aerospike.dsl.util.ParsingUtils.unquote; public class ListValueList extends ListPart { private final boolean inverted; @@ -36,7 +36,7 @@ public static ListValueList from(ConditionParser.ListValueListContext ctx) { if (listValue.NAME_IDENTIFIER() != null) { return listValue.NAME_IDENTIFIER().getText(); } else if (listValue.QUOTED_STRING() != null) { - return getWithoutQuotes(listValue.QUOTED_STRING().getText()); + return unquote(listValue.QUOTED_STRING().getText()); } return Integer.parseInt(listValue.INT().getText()); } diff --git a/src/main/java/com/aerospike/dsl/model/cdt/map/MapIndexRangeRelative.java b/src/main/java/com/aerospike/dsl/model/cdt/map/MapIndexRangeRelative.java index 9efcee1..e0fc6c6 100644 --- a/src/main/java/com/aerospike/dsl/model/cdt/map/MapIndexRangeRelative.java +++ b/src/main/java/com/aerospike/dsl/model/cdt/map/MapIndexRangeRelative.java @@ -7,8 +7,8 @@ import com.aerospike.dsl.ConditionParser; import com.aerospike.dsl.exception.AerospikeDSLException; import com.aerospike.dsl.model.BasePath; -import com.aerospike.dsl.util.ParsingUtils; +import static com.aerospike.dsl.util.ParsingUtils.unquote; import static com.aerospike.dsl.util.ParsingUtils.subtractNullable; public class MapIndexRangeRelative extends MapPart { @@ -47,7 +47,7 @@ public static MapIndexRangeRelative from(ConditionParser.MapIndexRangeRelativeCo if (mapKeyContext.NAME_IDENTIFIER() != null) { relativeKey = mapKeyContext.NAME_IDENTIFIER().getText(); } else if (mapKeyContext.QUOTED_STRING() != null) { - relativeKey = ParsingUtils.getWithoutQuotes(mapKeyContext.QUOTED_STRING().getText()); + relativeKey = unquote(mapKeyContext.QUOTED_STRING().getText()); } } return new MapIndexRangeRelative(isInverted, start, end, relativeKey); diff --git a/src/main/java/com/aerospike/dsl/model/cdt/map/MapKey.java b/src/main/java/com/aerospike/dsl/model/cdt/map/MapKey.java index a52bf49..8bd7774 100644 --- a/src/main/java/com/aerospike/dsl/model/cdt/map/MapKey.java +++ b/src/main/java/com/aerospike/dsl/model/cdt/map/MapKey.java @@ -7,7 +7,8 @@ import com.aerospike.dsl.ConditionParser; import com.aerospike.dsl.exception.AerospikeDSLException; import com.aerospike.dsl.model.BasePath; -import com.aerospike.dsl.util.ParsingUtils; + +import static com.aerospike.dsl.util.ParsingUtils.unquote; public class MapKey extends MapPart { private final String key; @@ -19,7 +20,7 @@ public MapKey(String key) { public static MapKey from(ConditionParser.MapKeyContext ctx) { if (ctx.QUOTED_STRING() != null) { - return new MapKey(ParsingUtils.getWithoutQuotes(ctx.QUOTED_STRING().getText())); + return new MapKey(unquote(ctx.QUOTED_STRING().getText())); } if (ctx.NAME_IDENTIFIER() != null) { return new MapKey(ctx.NAME_IDENTIFIER().getText()); diff --git a/src/main/java/com/aerospike/dsl/model/cdt/map/MapKeyList.java b/src/main/java/com/aerospike/dsl/model/cdt/map/MapKeyList.java index f7e273a..be5c428 100644 --- a/src/main/java/com/aerospike/dsl/model/cdt/map/MapKeyList.java +++ b/src/main/java/com/aerospike/dsl/model/cdt/map/MapKeyList.java @@ -7,10 +7,11 @@ import com.aerospike.dsl.ConditionParser; import com.aerospike.dsl.exception.AerospikeDSLException; import com.aerospike.dsl.model.BasePath; -import com.aerospike.dsl.util.ParsingUtils; import java.util.List; +import static com.aerospike.dsl.util.ParsingUtils.unquote; + public class MapKeyList extends MapPart { private final boolean inverted; private final List keyList; @@ -35,7 +36,7 @@ public static MapKeyList from(ConditionParser.MapKeyListContext ctx) { if (mapKey.NAME_IDENTIFIER() != null) { return mapKey.NAME_IDENTIFIER().getText(); } else { - return ParsingUtils.getWithoutQuotes(mapKey.QUOTED_STRING().getText()); + return unquote(mapKey.QUOTED_STRING().getText()); } } ).toList(); diff --git a/src/main/java/com/aerospike/dsl/model/cdt/map/MapKeyRange.java b/src/main/java/com/aerospike/dsl/model/cdt/map/MapKeyRange.java index 4ffbeae..1c223da 100644 --- a/src/main/java/com/aerospike/dsl/model/cdt/map/MapKeyRange.java +++ b/src/main/java/com/aerospike/dsl/model/cdt/map/MapKeyRange.java @@ -7,10 +7,11 @@ import com.aerospike.dsl.ConditionParser; import com.aerospike.dsl.exception.AerospikeDSLException; import com.aerospike.dsl.model.BasePath; -import com.aerospike.dsl.util.ParsingUtils; import java.util.Optional; +import static com.aerospike.dsl.util.ParsingUtils.unquote; + public class MapKeyRange extends MapPart { private final boolean inverted; private final String start; @@ -34,12 +35,12 @@ public static MapKeyRange from(ConditionParser.MapKeyRangeContext ctx) { String startKey = range.mapKey(0).NAME_IDENTIFIER() != null ? range.mapKey(0).NAME_IDENTIFIER().getText() - : ParsingUtils.getWithoutQuotes(range.mapKey(0).QUOTED_STRING().getText()); + : unquote(range.mapKey(0).QUOTED_STRING().getText()); String endKey = Optional.ofNullable(range.mapKey(1)) .map(keyCtx -> keyCtx.NAME_IDENTIFIER() != null ? keyCtx.NAME_IDENTIFIER().getText() - : ParsingUtils.getWithoutQuotes(keyCtx.QUOTED_STRING().getText())) + : unquote(keyCtx.QUOTED_STRING().getText())) .orElse(null); return new MapKeyRange(isInverted, startKey, endKey); diff --git a/src/main/java/com/aerospike/dsl/model/cdt/map/MapRankRangeRelative.java b/src/main/java/com/aerospike/dsl/model/cdt/map/MapRankRangeRelative.java index cdf78df..7afef60 100644 --- a/src/main/java/com/aerospike/dsl/model/cdt/map/MapRankRangeRelative.java +++ b/src/main/java/com/aerospike/dsl/model/cdt/map/MapRankRangeRelative.java @@ -7,8 +7,8 @@ import com.aerospike.dsl.ConditionParser; import com.aerospike.dsl.exception.AerospikeDSLException; import com.aerospike.dsl.model.BasePath; -import com.aerospike.dsl.util.ParsingUtils; +import static com.aerospike.dsl.util.ParsingUtils.unquote; import static com.aerospike.dsl.util.ParsingUtils.subtractNullable; public class MapRankRangeRelative extends MapPart { @@ -50,7 +50,7 @@ public static MapRankRangeRelative from(ConditionParser.MapRankRangeRelativeCont } else if (valueIdentifierContext.NAME_IDENTIFIER() != null) { relativeValue = valueIdentifierContext.NAME_IDENTIFIER().getText(); } else if (valueIdentifierContext.QUOTED_STRING() != null) { - relativeValue = ParsingUtils.getWithoutQuotes(valueIdentifierContext.QUOTED_STRING().getText()); + relativeValue = unquote(valueIdentifierContext.QUOTED_STRING().getText()); } } diff --git a/src/main/java/com/aerospike/dsl/model/cdt/map/MapValue.java b/src/main/java/com/aerospike/dsl/model/cdt/map/MapValue.java index 40dda20..4427e9d 100644 --- a/src/main/java/com/aerospike/dsl/model/cdt/map/MapValue.java +++ b/src/main/java/com/aerospike/dsl/model/cdt/map/MapValue.java @@ -6,7 +6,8 @@ import com.aerospike.client.exp.MapExp; import com.aerospike.dsl.ConditionParser; import com.aerospike.dsl.model.BasePath; -import com.aerospike.dsl.util.ParsingUtils; + +import static com.aerospike.dsl.util.ParsingUtils.unquote; public class MapValue extends MapPart { private final Object value; @@ -21,7 +22,7 @@ public static MapValue from(ConditionParser.MapValueContext ctx) { if (ctx.valueIdentifier().NAME_IDENTIFIER() != null) { mapValue = ctx.valueIdentifier().NAME_IDENTIFIER().getText(); } else if (ctx.valueIdentifier().QUOTED_STRING() != null) { - mapValue = ParsingUtils.getWithoutQuotes(ctx.valueIdentifier().QUOTED_STRING().getText()); + mapValue = unquote(ctx.valueIdentifier().QUOTED_STRING().getText()); } else if (ctx.valueIdentifier().INT() != null) { mapValue = Integer.parseInt(ctx.valueIdentifier().INT().getText()); } diff --git a/src/main/java/com/aerospike/dsl/model/cdt/map/MapValueList.java b/src/main/java/com/aerospike/dsl/model/cdt/map/MapValueList.java index 13307b8..e0be45c 100644 --- a/src/main/java/com/aerospike/dsl/model/cdt/map/MapValueList.java +++ b/src/main/java/com/aerospike/dsl/model/cdt/map/MapValueList.java @@ -10,7 +10,7 @@ import java.util.List; -import static com.aerospike.dsl.util.ParsingUtils.getWithoutQuotes; +import static com.aerospike.dsl.util.ParsingUtils.unquote; public class MapValueList extends MapPart { private final boolean inverted; @@ -36,7 +36,7 @@ public static MapValueList from(ConditionParser.MapValueListContext ctx) { if (listValue.NAME_IDENTIFIER() != null) { return listValue.NAME_IDENTIFIER().getText(); } else if (listValue.QUOTED_STRING() != null) { - return getWithoutQuotes(listValue.QUOTED_STRING().getText()); + return unquote(listValue.QUOTED_STRING().getText()); } return Integer.parseInt(listValue.INT().getText()); } diff --git a/src/main/java/com/aerospike/dsl/util/ParsingUtils.java b/src/main/java/com/aerospike/dsl/util/ParsingUtils.java index a1cd428..8677e88 100644 --- a/src/main/java/com/aerospike/dsl/util/ParsingUtils.java +++ b/src/main/java/com/aerospike/dsl/util/ParsingUtils.java @@ -12,7 +12,7 @@ public class ParsingUtils { * @param str String input * @return String inside the quotes */ - public static String getWithoutQuotes(String str) { + public static String unquote(String str) { if (str.length() > 2) { return str.substring(1, str.length() - 1); } else { diff --git a/src/main/java/com/aerospike/dsl/ExpressionConditionVisitor.java b/src/main/java/com/aerospike/dsl/visitor/ExpressionConditionVisitor.java similarity index 63% rename from src/main/java/com/aerospike/dsl/ExpressionConditionVisitor.java rename to src/main/java/com/aerospike/dsl/visitor/ExpressionConditionVisitor.java index 8cad64f..175e948 100644 --- a/src/main/java/com/aerospike/dsl/ExpressionConditionVisitor.java +++ b/src/main/java/com/aerospike/dsl/visitor/ExpressionConditionVisitor.java @@ -1,6 +1,8 @@ -package com.aerospike.dsl; +package com.aerospike.dsl.visitor; import com.aerospike.client.exp.Exp; +import com.aerospike.dsl.ConditionBaseVisitor; +import com.aerospike.dsl.ConditionParser; import com.aerospike.dsl.exception.AerospikeDSLException; import com.aerospike.dsl.model.*; import com.aerospike.dsl.model.cdt.list.ListIndex; @@ -14,18 +16,15 @@ import com.aerospike.dsl.model.cdt.list.ListValueRange; import com.aerospike.dsl.model.cdt.map.*; import com.aerospike.dsl.util.TypeUtils; -import com.aerospike.dsl.util.ValidationUtils; import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.RuleNode; import java.util.ArrayList; -import java.util.Base64; import java.util.List; import java.util.TreeMap; -import java.util.function.BinaryOperator; -import java.util.function.UnaryOperator; -import static com.aerospike.dsl.util.ParsingUtils.getWithoutQuotes; +import static com.aerospike.dsl.util.ParsingUtils.unquote; +import static com.aerospike.dsl.visitor.VisitorUtils.*; public class ExpressionConditionVisitor extends ConditionBaseVisitor { @@ -101,17 +100,6 @@ public AbstractPart visitExclusiveExpression(ConditionParser.ExclusiveExpression return new Expr(Exp.exclusive(expressions.toArray(new Exp[0]))); } - private void logicalSetBinsAsBooleanExpr(Expr left, Expr right) { - logicalSetBinAsBooleanExpr(left); - logicalSetBinAsBooleanExpr(right); - } - - private void logicalSetBinAsBooleanExpr(Expr expr) { - if (expr instanceof BinPart) { - ((BinPart) expr).updateExp(Exp.Type.BOOL); - } - } - @Override public AbstractPart visitGreaterThanExpression(ConditionParser.GreaterThanExpressionContext ctx) { AbstractPart left = visit(ctx.operand(0)); @@ -264,150 +252,6 @@ public AbstractPart visitIntRShiftExpression(ConditionParser.IntRShiftExpression return new Expr(exp); } - // 2 operands Expressions - private Exp getExpOrFail(AbstractPart left, AbstractPart right, BinaryOperator operator) { - if (left == null) { - throw new AerospikeDSLException("Unable to parse left operand"); - } - if (right == null) { - throw new AerospikeDSLException("Unable to parse right operand"); - } - - if (left.getPartType() == AbstractPart.PartType.BIN_PART) { - return getExpLeftBinTypeComparison((BinPart) left, right, operator); - } - if (right.getPartType() == AbstractPart.PartType.BIN_PART) { - return getExpRightBinTypeComparison(left, (BinPart) right, operator); - } - - // Handle non Bin operands cases - Exp leftExp = left.getExp(); - Exp rightExp = right.getExp(); - return operator.apply(leftExp, rightExp); - } - - private Exp getExpLeftBinTypeComparison(BinPart left, AbstractPart right, BinaryOperator operator) { - String binNameLeft = left.getBinName(); - return switch (right.getPartType()) { - case INT_OPERAND -> { - ValidationUtils.validateComparableTypes(left.getExpType(), Exp.Type.INT); - yield operator.apply(left.getExp(), right.getExp()); - } - case FLOAT_OPERAND -> { - ValidationUtils.validateComparableTypes(left.getExpType(), Exp.Type.FLOAT); - yield operator.apply(left.getExp(), right.getExp()); - } - case BOOL_OPERAND -> { - ValidationUtils.validateComparableTypes(left.getExpType(), Exp.Type.BOOL); - yield operator.apply(left.getExp(), right.getExp()); - } - case STRING_OPERAND -> { - if (left.getExpType() != null && - left.getExpType().equals(Exp.Type.BLOB)) { - // Base64 Blob - ValidationUtils.validateComparableTypes(left.getExpType(), Exp.Type.BLOB); - String base64String = ((StringOperand) right).getValue(); - byte[] value = Base64.getDecoder().decode(base64String); - yield operator.apply(left.getExp(), Exp.val(value)); - } else { - // String - ValidationUtils.validateComparableTypes(left.getExpType(), Exp.Type.STRING); - yield operator.apply(left.getExp(), right.getExp()); - } - } - case METADATA_OPERAND -> { - // No need to validate, types are determined by metadata function - Exp.Type binType = Exp.Type.valueOf(((MetadataOperand) right).getMetadataType().toString()); - yield operator.apply( - Exp.bin(binNameLeft, binType), - right.getExp() - ); - } - case EXPR, PATH_OPERAND -> - operator.apply(left.getExp(), right.getExp()); // Can't validate with Expr on one side - // Left and right are both bin parts - case BIN_PART -> { - // Validate types if possible - ValidationUtils.validateComparableTypes(left.getExpType(), right.getExpType()); - yield operator.apply(left.getExp(), right.getExp()); - } - case LIST_OPERAND -> { - ValidationUtils.validateComparableTypes(left.getExpType(), Exp.Type.LIST); - yield operator.apply(left.getExp(), right.getExp()); - } - case MAP_OPERAND -> { - ValidationUtils.validateComparableTypes(left.getExpType(), Exp.Type.MAP); - yield operator.apply(left.getExp(), right.getExp()); - } - default -> throw new AerospikeDSLException("Operand type not supported: %s".formatted(right.getPartType())); - }; - } - - private Exp getExpRightBinTypeComparison(AbstractPart left, BinPart right, BinaryOperator operator) { - String binNameRight = right.getBinName(); - return switch (left.getPartType()) { - case INT_OPERAND -> { - ValidationUtils.validateComparableTypes(Exp.Type.INT, right.getExpType()); - yield operator.apply(left.getExp(), right.getExp()); - } - case FLOAT_OPERAND -> { - ValidationUtils.validateComparableTypes(Exp.Type.FLOAT, right.getExpType()); - yield operator.apply(left.getExp(), right.getExp()); - } - case BOOL_OPERAND -> { - ValidationUtils.validateComparableTypes(Exp.Type.BOOL, right.getExpType()); - yield operator.apply(left.getExp(), right.getExp()); - } - case STRING_OPERAND -> { - if (right.getExpType() != null && - right.getExpType().equals(Exp.Type.BLOB)) { - // Base64 Blob - ValidationUtils.validateComparableTypes(Exp.Type.BLOB, right.getExpType()); - String base64String = ((StringOperand) left).getValue(); - byte[] value = Base64.getDecoder().decode(base64String); - yield operator.apply(Exp.val(value), right.getExp()); - } else { - // String - ValidationUtils.validateComparableTypes(Exp.Type.STRING, right.getExpType()); - yield operator.apply(left.getExp(), right.getExp()); - } - } - case METADATA_OPERAND -> { - // No need to validate, types are determined by metadata function - Exp.Type binType = Exp.Type.valueOf(((MetadataOperand) left).getMetadataType().toString()); - yield operator.apply( - left.getExp(), - Exp.bin(binNameRight, binType) - ); - } - case EXPR, PATH_OPERAND -> - operator.apply(left.getExp(), right.getExp()); // Can't validate with Expr on one side - // No need for 2 BIN_OPERAND handling since it's covered in the left condition - case LIST_OPERAND -> { - ValidationUtils.validateComparableTypes(Exp.Type.LIST, right.getExpType()); - yield operator.apply(left.getExp(), right.getExp()); - } - case MAP_OPERAND -> { - ValidationUtils.validateComparableTypes(Exp.Type.MAP, right.getExpType()); - yield operator.apply(left.getExp(), right.getExp()); - } - default -> throw new AerospikeDSLException("Operand type not supported: %s".formatted(left.getPartType())); - }; - } - - // 1 operand Expressions - private Exp getExpOrFail(AbstractPart operand, UnaryOperator operator) { - if (operand == null) { - throw new AerospikeDSLException("Unable to parse operand"); - } - - // 1 Operand Expression is always a BIN Operand - String binName = ((BinPart) operand).getBinName(); - - // There is only 1 case of a single operand expression (int not), and it always gets an integer - return operator.apply(Exp.bin(binName, Exp.Type.INT)); - } - @Override public AbstractPart visitPathFunctionGet(ConditionParser.PathFunctionGetContext ctx) { PathFunction.ReturnParam returnParam = null; @@ -429,20 +273,6 @@ public AbstractPart visitPathFunctionCount(ConditionParser.PathFunctionCountCont return new PathFunction(PathFunction.PathFunctionType.COUNT, PathFunction.ReturnParam.COUNT, null); } - private String getPathFunctionParam(ConditionParser.PathFunctionParamContext paramCtx, String paramName) { - String paramNameText; - String paramNameValue; - String paramValue = null; - if (paramCtx.pathFunctionParamName() != null) { - paramNameText = paramCtx.pathFunctionParamName().getText(); - paramNameValue = paramCtx.pathFunctionParamValue().getText(); - if (paramNameText.equalsIgnoreCase(paramName)) { - paramValue = paramNameValue; - } - } - return paramValue; - } - @Override public AbstractPart visitPathFunctionCast(ConditionParser.PathFunctionCastContext ctx) { String typeVal = extractTypeFromMethod(ctx.PATH_FUNCTION_CAST().getText()); @@ -452,14 +282,6 @@ public AbstractPart visitPathFunctionCast(ConditionParser.PathFunctionCastContex return new PathFunction(PathFunction.PathFunctionType.CAST, null, binType); } - private static String extractTypeFromMethod(String methodName) { - if (methodName.startsWith("as") && methodName.endsWith("()")) { - return methodName.substring(2, methodName.length() - 2); - } else { - throw new AerospikeDSLException("Invalid method name: %s".formatted(methodName)); - } - } - @Override public AbstractPart visitMetadata(ConditionParser.MetadataContext ctx) { String text = ctx.METADATA_FUNCTION().getText(); @@ -473,22 +295,6 @@ public AbstractPart visitMetadata(ConditionParser.MetadataContext ctx) { } } - private String extractFunctionName(String text) { - int startParen = text.indexOf('('); - return (startParen != -1) ? text.substring(0, startParen) : text; - } - - private Integer extractParameter(String text) { - int startParen = text.indexOf('('); - int endParen = text.indexOf(')'); - - if (startParen != -1 && endParen != -1 && endParen > startParen + 1) { - String numberStr = text.substring(startParen + 1, endParen); - return Integer.parseInt(numberStr); - } - return null; - } - @Override public AbstractPart visitBinPart(ConditionParser.BinPartContext ctx) { return new BinPart(ctx.NAME_IDENTIFIER().getText()); @@ -528,13 +334,6 @@ public ListOperand readChildrenIntoListOperand(RuleNode listNode) { return new ListOperand(list); } - private boolean shouldVisitListElement(int i, int size, ParseTree child) { - return size > 0 // size is not 0 - && i != 0 // not the first element ('[') - && i != size - 1 // not the last element (']') - && !child.getText().equals(","); // not a comma (list elements separator) - } - @Override public AbstractPart visitOrderedMapConstant(ConditionParser.OrderedMapConstantContext ctx) { return readChildrenIntoMapOperand(ctx); @@ -572,17 +371,9 @@ public MapOperand readChildrenIntoMapOperand(RuleNode mapNode) { return new MapOperand(map); } - private boolean shouldVisitMapElement(int i, int size, ParseTree child) { - return size > 0 // size is not 0 - && i != 0 // not the first element ('{') - && i != size - 1 // not the last element ('}') - && !child.getText().equals(":") // not a colon (map key and value separator) - && !child.getText().equals(","); // not a comma (map pairs separator) - } - @Override public AbstractPart visitStringOperand(ConditionParser.StringOperandContext ctx) { - String text = getWithoutQuotes(ctx.getText()); + String text = unquote(ctx.getText()); return new StringOperand(text); } @@ -640,14 +431,6 @@ public AbstractPart visitVariable(ConditionParser.VariableContext ctx) { return new VariableOperand(extractVariableName(text)); } - private String extractVariableName(String variableReference) { - if (variableReference.startsWith("${") && variableReference.endsWith("}")) { - return variableReference.substring(2, variableReference.length() - 1); - } else { - throw new IllegalArgumentException("Input string is not in the correct format"); - } - } - private AbstractPart overrideType(AbstractPart part, ParseTree ctx) { ConditionParser.PathFunctionContext pathFunctionContext = ((ConditionParser.PathContext) ctx.getParent()).pathFunction(); @@ -683,34 +466,6 @@ private AbstractPart overrideType(AbstractPart part, ParseTree ctx) { return part; } - private Exp.Type detectImplicitTypeFromUpperTree(ParseTree ctx) { - // Search for a "leaf" operand child (Int, Float, String and Boolean) - // in the above levels of the current path in the expression tree - while (ctx.getParent() != null) { - ctx = ctx.getParent(); - - for (int i = 0; i < ctx.getChildCount(); i++) { - ParseTree child = ctx.getChild(i); - - if (child instanceof ConditionParser.OperandContext operandContext) { - if (operandContext.numberOperand() != null) { - if (operandContext.numberOperand().intOperand() != null) { - return Exp.Type.INT; - } else if (operandContext.numberOperand().floatOperand() != null) { - return Exp.Type.FLOAT; - } - } else if (operandContext.stringOperand() != null) { - return Exp.Type.STRING; - } else if (operandContext.booleanOperand() != null) { - return Exp.Type.BOOL; - } - } - } - } - // Could not detect, return null and determine defaults later on - return null; - } - @Override public AbstractPart visitPath(ConditionParser.PathContext ctx) { BasePath basePath = (BasePath) visit(ctx.basePath()); diff --git a/src/main/java/com/aerospike/dsl/visitor/FilterConditionVisitor.java b/src/main/java/com/aerospike/dsl/visitor/FilterConditionVisitor.java new file mode 100644 index 0000000..c11829f --- /dev/null +++ b/src/main/java/com/aerospike/dsl/visitor/FilterConditionVisitor.java @@ -0,0 +1,102 @@ +package com.aerospike.dsl.visitor; + +import com.aerospike.client.query.Filter; +import com.aerospike.dsl.ConditionParser; +import com.aerospike.dsl.exception.AerospikeDSLException; +import com.aerospike.dsl.model.AbstractPart; +import com.aerospike.dsl.model.Expr; +import com.aerospike.dsl.model.SIndexFilter; + +import static com.aerospike.dsl.model.Expr.ExprPartsOperation.*; +import static com.aerospike.dsl.visitor.VisitorUtils.FilterOperationType.*; +import static com.aerospike.dsl.visitor.VisitorUtils.getFilterOrFail; +import static com.aerospike.dsl.visitor.VisitorUtils.validateNumericBin; + +public class FilterConditionVisitor extends ExpressionConditionVisitor { + + @Override + public AbstractPart visitGreaterThanExpression(ConditionParser.GreaterThanExpressionContext ctx) { + AbstractPart left = visit(ctx.operand(0)); + AbstractPart right = visit(ctx.operand(1)); + + Filter filter = getFilterOrFail(left, right, GT); + return new Expr(new SIndexFilter(filter)); + } + + @Override + public AbstractPart visitGreaterThanOrEqualExpression(ConditionParser.GreaterThanOrEqualExpressionContext ctx) { + AbstractPart left = visit(ctx.operand(0)); + AbstractPart right = visit(ctx.operand(1)); + + Filter filter = getFilterOrFail(left, right, GTEQ); + return new Expr(new SIndexFilter(filter)); + } + + @Override + public AbstractPart visitLessThanExpression(ConditionParser.LessThanExpressionContext ctx) { + AbstractPart left = visit(ctx.operand(0)); + AbstractPart right = visit(ctx.operand(1)); + + Filter filter = getFilterOrFail(left, right, LT); + return new Expr(new SIndexFilter(filter)); + } + + @Override + public AbstractPart visitLessThanOrEqualExpression(ConditionParser.LessThanOrEqualExpressionContext ctx) { + AbstractPart left = visit(ctx.operand(0)); + AbstractPart right = visit(ctx.operand(1)); + + Filter filter = getFilterOrFail(left, right, LTEQ); + return new Expr(new SIndexFilter(filter)); + } + + @Override + public AbstractPart visitEqualityExpression(ConditionParser.EqualityExpressionContext ctx) { + AbstractPart left = visit(ctx.operand(0)); + AbstractPart right = visit(ctx.operand(1)); + + Filter filter = getFilterOrFail(left, right, EQ); + return new Expr(new SIndexFilter(filter)); + } + + @Override + public AbstractPart visitInequalityExpression(ConditionParser.InequalityExpressionContext ctx) { + throw new AerospikeDSLException("The operation is not supported by secondary index filter"); + } + + @Override + public AbstractPart visitAddExpression(ConditionParser.AddExpressionContext ctx) { + AbstractPart left = visit(ctx.operand(0)); + AbstractPart right = visit(ctx.operand(1)); + + validateNumericBin(left, right); + return new Expr(left, right, ADD); + } + + @Override + public AbstractPart visitSubExpression(ConditionParser.SubExpressionContext ctx) { + AbstractPart left = visit(ctx.operand(0)); + AbstractPart right = visit(ctx.operand(1)); + + validateNumericBin(left, right); + return new Expr(left, right, SUB); + } + + @Override + public AbstractPart visitDivExpression(ConditionParser.DivExpressionContext ctx) { + AbstractPart left = visit(ctx.operand(0)); + AbstractPart right = visit(ctx.operand(1)); + + validateNumericBin(left, right); + return new Expr(left, right, DIV); + } + + @Override + public AbstractPart visitMulExpression(ConditionParser.MulExpressionContext ctx) { + AbstractPart left = visit(ctx.operand(0)); + AbstractPart right = visit(ctx.operand(1)); + + validateNumericBin(left, right); + return new Expr(left, right, MUL); + } +} diff --git a/src/main/java/com/aerospike/dsl/visitor/VisitorUtils.java b/src/main/java/com/aerospike/dsl/visitor/VisitorUtils.java new file mode 100644 index 0000000..744416e --- /dev/null +++ b/src/main/java/com/aerospike/dsl/visitor/VisitorUtils.java @@ -0,0 +1,629 @@ +package com.aerospike.dsl.visitor; + +import com.aerospike.client.exp.Exp; +import com.aerospike.client.query.Filter; +import com.aerospike.dsl.ConditionParser; +import com.aerospike.dsl.exception.AerospikeDSLException; +import com.aerospike.dsl.model.AbstractPart; +import com.aerospike.dsl.model.BinPart; +import com.aerospike.dsl.model.Expr; +import com.aerospike.dsl.model.IntOperand; +import com.aerospike.dsl.model.MetadataOperand; +import com.aerospike.dsl.model.StringOperand; +import lombok.experimental.UtilityClass; +import org.antlr.v4.runtime.misc.Pair; +import org.antlr.v4.runtime.tree.ParseTree; + +import java.util.Base64; +import java.util.Objects; +import java.util.function.BinaryOperator; +import java.util.function.UnaryOperator; + +import static com.aerospike.dsl.model.Expr.ExprPartsOperation.*; +import static com.aerospike.dsl.util.ValidationUtils.validateComparableTypes; +import static com.aerospike.dsl.visitor.VisitorUtils.ArithmeticTermType.*; +import static com.aerospike.dsl.visitor.VisitorUtils.FilterOperationType.*; + +@UtilityClass +public class VisitorUtils { + + protected enum FilterOperationType { + GT, + GTEQ, + LT, + LTEQ, + EQ, + NOTEQ + } + + protected enum ArithmeticTermType { + ADDEND, + SUBTR, + MIN, + DIFFERENCE, + DIVIDEND, + DIVISOR, + QUOTIENT, + MULTIPLICAND, + MULTIPLIER, + PRODUCT + } + + static String extractVariableName(String variableReference) { + if (variableReference.startsWith("${") && variableReference.endsWith("}")) { + return variableReference.substring(2, variableReference.length() - 1); + } + throw new IllegalArgumentException("Input string is not in the correct format"); + + } + + static Exp.Type detectImplicitTypeFromUpperTree(ParseTree ctx) { + // Search for a "leaf" operand child (Int, Float, String and Boolean) + // in the above levels of the current path in the expression tree + while (ctx.getParent() != null) { + ctx = ctx.getParent(); + + for (int i = 0; i < ctx.getChildCount(); i++) { + ParseTree child = ctx.getChild(i); + + if (child instanceof ConditionParser.OperandContext operandContext) { + if (operandContext.numberOperand() != null) { + if (operandContext.numberOperand().intOperand() != null) { + return Exp.Type.INT; + } else if (operandContext.numberOperand().floatOperand() != null) { + return Exp.Type.FLOAT; + } + } else if (operandContext.stringOperand() != null) { + return Exp.Type.STRING; + } else if (operandContext.booleanOperand() != null) { + return Exp.Type.BOOL; + } + } + } + } + // Could not detect, return null and determine defaults later on + return null; + } + + static void logicalSetBinsAsBooleanExpr(Expr left, Expr right) { + logicalSetBinAsBooleanExpr(left); + logicalSetBinAsBooleanExpr(right); + } + + static void logicalSetBinAsBooleanExpr(Expr expr) { + if (expr instanceof BinPart) { + ((BinPart) expr).updateExp(Exp.Type.BOOL); + } + } + + static boolean shouldVisitListElement(int i, int size, ParseTree child) { + return size > 0 // size is not 0 + && i != 0 // not the first element ('[') + && i != size - 1 // not the last element (']') + && !child.getText().equals(","); // not a comma (list elements separator) + } + + static boolean shouldVisitMapElement(int i, int size, ParseTree child) { + return size > 0 // size is not 0 + && i != 0 // not the first element ('{') + && i != size - 1 // not the last element ('}') + && !child.getText().equals(":") // not a colon (map key and value separator) + && !child.getText().equals(","); // not a comma (map pairs separator) + } + + // 2 operands Expressions + static Exp getExpOrFail(AbstractPart left, AbstractPart right, BinaryOperator operator) { + if (left == null) { + throw new AerospikeDSLException("Unable to parse left operand"); + } + if (right == null) { + throw new AerospikeDSLException("Unable to parse right operand"); + } + + if (left.getPartType() == AbstractPart.PartType.BIN_PART) { + return getExpLeftBinTypeComparison((BinPart) left, right, operator); + } + if (right.getPartType() == AbstractPart.PartType.BIN_PART) { + return getExpRightBinTypeComparison(left, (BinPart) right, operator); + } + + // Handle non Bin operands cases + Exp leftExp = left.getExp(); + Exp rightExp = right.getExp(); + return operator.apply(leftExp, rightExp); + } + + static Exp getExpLeftBinTypeComparison(BinPart left, AbstractPart right, BinaryOperator operator) { + String binNameLeft = left.getBinName(); + return switch (right.getPartType()) { + case INT_OPERAND -> { + validateComparableTypes(left.getExpType(), Exp.Type.INT); + yield operator.apply(left.getExp(), right.getExp()); + } + case FLOAT_OPERAND -> { + validateComparableTypes(left.getExpType(), Exp.Type.FLOAT); + yield operator.apply(left.getExp(), right.getExp()); + } + case BOOL_OPERAND -> { + validateComparableTypes(left.getExpType(), Exp.Type.BOOL); + yield operator.apply(left.getExp(), right.getExp()); + } + case STRING_OPERAND -> { + if (left.getExpType() != null && + left.getExpType().equals(Exp.Type.BLOB)) { + // Base64 Blob + validateComparableTypes(left.getExpType(), Exp.Type.BLOB); + String base64String = ((StringOperand) right).getValue(); + byte[] value = Base64.getDecoder().decode(base64String); + yield operator.apply(left.getExp(), Exp.val(value)); + } else { + // String + validateComparableTypes(left.getExpType(), Exp.Type.STRING); + yield operator.apply(left.getExp(), right.getExp()); + } + } + case METADATA_OPERAND -> { + // No need to validate, types are determined by metadata function + Exp.Type binType = Exp.Type.valueOf(((MetadataOperand) right).getMetadataType().toString()); + yield operator.apply( + Exp.bin(binNameLeft, binType), + right.getExp() + ); + } + case EXPR, PATH_OPERAND -> + operator.apply(left.getExp(), right.getExp()); // Can't validate with Expr on one side + // Left and right are both bin parts + case BIN_PART -> { + // Validate types if possible + validateComparableTypes(left.getExpType(), right.getExpType()); + yield operator.apply(left.getExp(), right.getExp()); + } + case LIST_OPERAND -> { + validateComparableTypes(left.getExpType(), Exp.Type.LIST); + yield operator.apply(left.getExp(), right.getExp()); + } + case MAP_OPERAND -> { + validateComparableTypes(left.getExpType(), Exp.Type.MAP); + yield operator.apply(left.getExp(), right.getExp()); + } + default -> throw new AerospikeDSLException("Operand type not supported: %s".formatted(right.getPartType())); + }; + } + + static Exp getExpRightBinTypeComparison(AbstractPart left, BinPart right, BinaryOperator operator) { + String binNameRight = right.getBinName(); + return switch (left.getPartType()) { + case INT_OPERAND -> { + validateComparableTypes(Exp.Type.INT, right.getExpType()); + yield operator.apply(left.getExp(), right.getExp()); + } + case FLOAT_OPERAND -> { + validateComparableTypes(Exp.Type.FLOAT, right.getExpType()); + yield operator.apply(left.getExp(), right.getExp()); + } + case BOOL_OPERAND -> { + validateComparableTypes(Exp.Type.BOOL, right.getExpType()); + yield operator.apply(left.getExp(), right.getExp()); + } + case STRING_OPERAND -> { + if (right.getExpType() != null && + right.getExpType().equals(Exp.Type.BLOB)) { + // Base64 Blob + validateComparableTypes(Exp.Type.BLOB, right.getExpType()); + String base64String = ((StringOperand) left).getValue(); + byte[] value = Base64.getDecoder().decode(base64String); + yield operator.apply(Exp.val(value), right.getExp()); + } else { + // String + validateComparableTypes(Exp.Type.STRING, right.getExpType()); + yield operator.apply(left.getExp(), right.getExp()); + } + } + case METADATA_OPERAND -> { + // No need to validate, types are determined by metadata function + Exp.Type binType = Exp.Type.valueOf(((MetadataOperand) left).getMetadataType().toString()); + yield operator.apply( + left.getExp(), + Exp.bin(binNameRight, binType) + ); + } + case EXPR, PATH_OPERAND -> + operator.apply(left.getExp(), right.getExp()); // Can't validate with Expr on one side + // No need for 2 BIN_OPERAND handling since it's covered in the left condition + case LIST_OPERAND -> { + validateComparableTypes(Exp.Type.LIST, right.getExpType()); + yield operator.apply(left.getExp(), right.getExp()); + } + case MAP_OPERAND -> { + validateComparableTypes(Exp.Type.MAP, right.getExpType()); + yield operator.apply(left.getExp(), right.getExp()); + } + default -> throw new AerospikeDSLException("Operand type not supported: %s".formatted(left.getPartType())); + }; + } + + // 1 operand Expressions + static Exp getExpOrFail(AbstractPart operand, UnaryOperator operator) { + if (operand == null) { + throw new AerospikeDSLException("Unable to parse operand"); + } + + // 1 Operand Expression is always a BIN Operand + String binName = ((BinPart) operand).getBinName(); + + // There is only 1 case of a single operand expression (int not), and it always gets an integer + return operator.apply(Exp.bin(binName, Exp.Type.INT)); + } + + static String getPathFunctionParam(ConditionParser.PathFunctionParamContext paramCtx, String paramName) { + String paramNameText; + String paramNameValue; + String paramValue = null; + if (paramCtx.pathFunctionParamName() != null) { + paramNameText = paramCtx.pathFunctionParamName().getText(); + paramNameValue = paramCtx.pathFunctionParamValue().getText(); + if (paramNameText.equalsIgnoreCase(paramName)) { + paramValue = paramNameValue; + } + } + return paramValue; + } + + static String extractTypeFromMethod(String methodName) { + if (methodName.startsWith("as") && methodName.endsWith("()")) { + return methodName.substring(2, methodName.length() - 2); + } else { + throw new AerospikeDSLException("Invalid method name: %s".formatted(methodName)); + } + } + + static String extractFunctionName(String text) { + int startParen = text.indexOf('('); + return (startParen != -1) ? text.substring(0, startParen) : text; + } + + static Integer extractParameter(String text) { + int startParen = text.indexOf('('); + int endParen = text.indexOf(')'); + + if (startParen != -1 && endParen != -1 && endParen > startParen + 1) { + String numberStr = text.substring(startParen + 1, endParen); + return Integer.parseInt(numberStr); + } + return null; + } + + // 2 operands Filters + static Filter getFilterOrFail(AbstractPart left, AbstractPart right, FilterOperationType type) { + if (left == null) { + throw new AerospikeDSLException("Unable to parse left operand"); + } + if (right == null) { + throw new AerospikeDSLException("Unable to parse right operand"); + } + + if (left.getPartType() == AbstractPart.PartType.BIN_PART) { + return getFilter((BinPart) left, right, type); + } + if (right.getPartType() == AbstractPart.PartType.BIN_PART) { + return getFilter((BinPart) right, left, invertType(type)); + } + + // Handle non Bin operands cases + if (left instanceof Expr leftExpr) { + return getFilterOrFail(leftExpr.getLeft(), leftExpr.getRight(), leftExpr.getOperationType(), right, type); + } + if (right instanceof Expr rightExpr) { + return getFilterOrFail(rightExpr.getLeft(), rightExpr.getRight(), rightExpr.getOperationType(), left, type); + } + return null; + } + + // 2 operands Filters + static Filter getFilterOrFail(AbstractPart exprLeft, AbstractPart exprRight, Expr.ExprPartsOperation operationType, + AbstractPart right, FilterOperationType type) { + if (exprLeft == null) { + throw new AerospikeDSLException("Unable to parse left operand of expression"); + } + if (exprRight == null) { + throw new AerospikeDSLException("Unable to parse right operand of expression"); + } + + if (exprLeft.getPartType() == AbstractPart.PartType.BIN_PART) { // bin is on the left side + if (exprRight instanceof IntOperand leftOperand && right instanceof IntOperand rightOperand) { + validateComparableTypes(exprLeft.getExpType(), Exp.Type.INT); + return applyFilterOperator(((BinPart) exprLeft).getBinName(), leftOperand, rightOperand, + operationType, type, getTermType(operationType, true)); + } + throw new AerospikeDSLException( + String.format("Operands not supported in secondary index Filter: %s, %s", exprRight, right)); + } + if (exprRight.getPartType() == AbstractPart.PartType.BIN_PART) { // bin is on the right side + if (exprLeft instanceof IntOperand leftOperand && right instanceof IntOperand rightOperand) { + validateComparableTypes(exprRight.getExpType(), Exp.Type.INT); + return applyFilterOperator(((BinPart) exprRight).getBinName(), leftOperand, rightOperand, + operationType, type, getTermType(operationType, false)); + } + throw new AerospikeDSLException( + String.format("Operands not supported in secondary index Filter: %s, %s", exprRight, right)); + } + + // Handle non Bin operands cases + if (exprLeft instanceof Expr leftExpr) { + return getFilterOrFail(leftExpr.getLeft(), leftExpr.getRight(), type); + } + return null; + } + + static void validateNumericBin(AbstractPart left, AbstractPart right) { + if (!isNumericBin(left, right)) { + throw new AerospikeDSLException("The operation is not supported by secondary index filter"); + } + } + + private static boolean isNumericBin(AbstractPart left, AbstractPart right) { + return (left instanceof BinPart && right instanceof IntOperand) + || (right instanceof BinPart && left instanceof IntOperand); + } + + private static ArithmeticTermType getTermType(Expr.ExprPartsOperation operationType, boolean isLeftTerm) { + return switch (operationType) { + case ADD -> ADDEND; + case SUB -> isLeftTerm ? SUBTR : MIN; + case DIV -> isLeftTerm ? DIVIDEND : DIVISOR; + case MUL -> isLeftTerm ? MULTIPLICAND : MULTIPLIER; + default -> throw new UnsupportedOperationException("Not supported: " + operationType); + }; + } + + private static Pair getLimitsForDivision(long left, long right, FilterOperationType type, + ArithmeticTermType termType) { + // Prevent division by zero + if (right == 0) { + throw new AerospikeDSLException("Cannot divide by zero"); + } + + return switch (termType) { + case DIVIDEND -> LimitsForBinDividend(left, right, type); + case DIVISOR -> getLimitsForBinDivisor(left, right, type); + default -> throw new UnsupportedOperationException("Unsupported term type for division: " + termType); + }; + } + + private static Pair LimitsForBinDividend(long left, long right, + FilterOperationType operationType) { + if (left > 0 && right > 0) { + // both operands are positive + return getLimitsForBinDividendWithLeftNumberPositive(operationType, left, right); + } else if (left == 0 && right == 0) { + throw new AerospikeDSLException("Undefined division for 0 / 0"); + } else if (left < 0 && right < 0) { + // both operands are negative + return getLimitsForBinDividendWithLeftNumberNegative(operationType, left, right); + } else if (left > 0 && right < 0) { + // left positive, right negative + return getLimitsForBinDividendWithLeftNumberPositive(operationType, left, right); + } else if (right > 0 && left < 0) { + // left negative, right positive + return getLimitsForBinDividendWithLeftNumberNegative(operationType, left, right); + } else if (left != 0) { + throw new AerospikeDSLException("Division by zero is not allowed"); + } else { + return new Pair<>(null, null); + } + } + + private static Pair getLimitsForBinDividendWithLeftNumberNegative(FilterOperationType operationType, + long left, long right) { + return switch (operationType) { + case GT: + yield new Pair<>(Long.MIN_VALUE, left * right - 1); + case GTEQ: + yield new Pair<>(Long.MIN_VALUE, left * right); + case LT: + yield new Pair<>(left * right + 1, Long.MAX_VALUE); + case LTEQ: + yield new Pair<>(left * right, Long.MAX_VALUE); + default: + throw new AerospikeDSLException("OperationType not supported for division: " + operationType); + }; + } + + private static Pair getLimitsForBinDividendWithLeftNumberPositive(FilterOperationType operationType, + long left, long right) { + return switch (operationType) { + case GT: + yield new Pair<>(left * right + 1, Long.MAX_VALUE); + case GTEQ: + yield new Pair<>(left * right, Long.MAX_VALUE); + case LT: + yield new Pair<>(Long.MIN_VALUE, left * right - 1); + case LTEQ: + yield new Pair<>(Long.MIN_VALUE, left * right); + default: + throw new AerospikeDSLException("OperationType not supported for division: " + operationType); + }; + } + + private static Pair getLimitsForBinDivisor(long left, long right, FilterOperationType operationType) { + if (left > 0 && right > 0) { + // both operands are positive + return switch (operationType) { + case GT: + yield new Pair<>(1L, getClosestLongToTheLeft((float) left / right)); + case GTEQ: + yield new Pair<>(1L, left / right); + case LT, LTEQ: + yield new Pair<>(null, null); + default: + throw new AerospikeDSLException("OperationType not supported for division: " + operationType); + }; + } else if (left == 0 && right == 0) { + throw new AerospikeDSLException("Cannot divide by zero"); + } else if (left < 0 && right < 0) { + // both operands are negative + return switch (operationType) { + case GT, GTEQ: + yield new Pair<>(null, null); + case LT: + yield new Pair<>(1L, getClosestLongToTheLeft((float) left / right)); + case LTEQ: + yield new Pair<>(1L, left / right); + default: + throw new AerospikeDSLException("OperationType not supported for division: " + operationType); + }; + } else if (left > 0 && right < 0) { + // left positive, right negative + return switch (operationType) { + case GT, GTEQ: + yield new Pair<>(null, null); + case LT: + yield new Pair<>(getClosestLongToTheRight((float) left / right), -1L); + case LTEQ: + yield new Pair<>(left / right, -1L); + default: + throw new AerospikeDSLException("OperationType not supported for division: " + operationType); + }; + } else if (right > 0 && left < 0) { + // right positive, left negative + return switch (operationType) { + case GT: + yield new Pair<>(getClosestLongToTheRight((float) left / right), -1L); + case GTEQ: + yield new Pair<>(left / right, -1L); + case LT, LTEQ: + yield new Pair<>(null, null); + default: + throw new AerospikeDSLException("OperationType not supported for division: " + operationType); + }; + } else if (left != 0) { + throw new AerospikeDSLException("Division by zero is not allowed"); + } else { + return new Pair<>(null, null); + } + } + + private static Filter getFilterForDivOrFail(String binName, Pair value, FilterOperationType type) { + // Based on the operation type, generate the appropriate filter range + return switch (type) { + case GT, GTEQ, LT, LTEQ -> Filter.range(binName, value.a, value.b); // Range from 1 to value - 1 + case EQ -> Filter.equal(binName, value.a); // Exact match for equality case + default -> throw new AerospikeDSLException("OperationType not supported for division: " + type); + }; + } + + private static Filter getFilter(BinPart bin, AbstractPart operand, FilterOperationType type) { + String binName = bin.getBinName(); + return switch (operand.getPartType()) { + case INT_OPERAND -> { + validateComparableTypes(bin.getExpType(), Exp.Type.INT); + yield getFilterForArithmeticOrFail(binName, ((IntOperand) operand).getValue(), type); + + } + case STRING_OPERAND -> { + if (type != EQ) throw new AerospikeDSLException("Operand type not supported"); + + if (bin.getExpType() != null && + bin.getExpType().equals(Exp.Type.BLOB)) { + // Base64 Blob + validateComparableTypes(bin.getExpType(), Exp.Type.BLOB); + String base64String = ((StringOperand) operand).getValue(); + byte[] value = Base64.getDecoder().decode(base64String); + yield Filter.equal(binName, value); + } else { + // String + validateComparableTypes(bin.getExpType(), Exp.Type.STRING); + yield Filter.equal(binName, ((StringOperand) operand).getValue()); + } + } + default -> + throw new AerospikeDSLException("Operand type not supported: %s".formatted(operand.getPartType())); + }; + } + + private static Filter applyFilterOperator(String binName, IntOperand leftOperand, IntOperand rightOperand, + Expr.ExprPartsOperation operationType, FilterOperationType type, + ArithmeticTermType termType) { + long leftValue = leftOperand.getValue(); + long rightValue = rightOperand.getValue(); + float value; + if (Objects.requireNonNull(operationType) == ADD) { + value = rightValue - leftValue; + } else if (operationType == SUB) { + value = switch (termType) { + case SUBTR -> rightValue + leftValue; + case MIN -> { + type = invertType(type); + yield leftValue - rightValue; + } + default -> throw new IllegalStateException("Unexpected term type: " + termType); + }; + } else if (operationType == DIV) { + Pair valueForDiv = getLimitsForDivision(leftValue, rightValue, type, termType); + if (valueForDiv.a == null + || valueForDiv.b == null + || valueForDiv.a > valueForDiv.b + || (valueForDiv.a == 0 && valueForDiv.b == 0)) { + throw new AerospikeDSLException("The operation is not supported by secondary index filter"); + } + return getFilterForDivOrFail(binName, valueForDiv, type); + } else if (operationType == MUL) { + if (leftValue <= 0) { + if (leftValue == 0) throw new AerospikeDSLException("Cannot divide by zero"); + type = invertType(type); + } + float val = (float) rightValue / leftValue; + return getFilterForArithmeticOrFail(binName, val, type); + } else { + throw new UnsupportedOperationException("Not supported"); + } + + return getFilterForArithmeticOrFail(binName, value, type); + } + + private static Filter getFilterForArithmeticOrFail(String binName, float value, FilterOperationType type) { + return switch (type) { + // "$.intBin1 > 100" and "100 < $.intBin1" represent the same Filter + case GT -> Filter.range(binName, getClosestLongToTheRight(value), Long.MAX_VALUE); + case GTEQ -> Filter.range(binName, (long) value, Long.MAX_VALUE); + case LT -> Filter.range(binName, Long.MIN_VALUE, getClosestLongToTheLeft(value)); + case LTEQ -> Filter.range(binName, Long.MIN_VALUE, (long) value); + case EQ -> Filter.equal(binName, (long) value); + default -> throw new AerospikeDSLException("The operation is not supported by secondary index filter"); + }; + } + + private static long getClosestLongToTheLeft(float value) { + // Get the largest integer less than or equal to the float + long flooredValue = (long) Math.floor(value); + + // If the float is a round number, subtract 1 + if (value == flooredValue) { + return flooredValue - 1; + } + + return flooredValue; + } + + private static long getClosestLongToTheRight(float value) { + // Get the smallest integer greater than or equal to the float + long ceiledValue = (long) Math.ceil(value); + + // If the float is a round number, add 1 + if (value == ceiledValue) { + return ceiledValue + 1; + } + + return ceiledValue; + } + + private FilterOperationType invertType(FilterOperationType type) { + return switch (type) { + case GT -> LT; + case GTEQ -> LTEQ; + case LT -> GT; + case LTEQ -> GTEQ; + default -> type; + }; + } +} diff --git a/src/test/java/com/aerospike/dsl/BinExpressionsTests.java b/src/test/java/com/aerospike/dsl/BinExpressionsTests.java deleted file mode 100644 index 9d319e4..0000000 --- a/src/test/java/com/aerospike/dsl/BinExpressionsTests.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.aerospike.dsl; - -import com.aerospike.client.exp.Exp; -import com.aerospike.dsl.exception.AerospikeDSLException; -import org.junit.jupiter.api.Test; - -import static com.aerospike.dsl.util.TestUtils.translate; -import static com.aerospike.dsl.util.TestUtils.translateAndCompare; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -class BinExpressionsTests { - - @Test - void binGT() { - // Bin as right - translateAndCompare("$.intBin1 > 100", Exp.gt(Exp.intBin("intBin1"), Exp.val(100))); - translateAndCompare("$.stringBin1 > 'text'", Exp.gt(Exp.stringBin("stringBin1"), Exp.val("text"))); - translateAndCompare("$.stringBin1 > \"text\"", Exp.gt(Exp.stringBin("stringBin1"), Exp.val("text"))); - - // Bin as left (considered as lt) - translateAndCompare("100 < $.intBin1", Exp.lt(Exp.val(100), Exp.intBin("intBin1"))); - translateAndCompare("'text' < $.stringBin1", Exp.lt(Exp.val("text"), Exp.stringBin("stringBin1"))); - translateAndCompare("\"text\" < $.stringBin1", Exp.lt(Exp.val("text"), Exp.stringBin("stringBin1"))); - } - - @Test - void binGE() { - translateAndCompare("$.intBin1 >= 100", Exp.ge(Exp.intBin("intBin1"), Exp.val(100))); - translateAndCompare("$.stringBin1 >= 'text'", Exp.ge(Exp.stringBin("stringBin1"), Exp.val("text"))); - translateAndCompare("$.stringBin1 >= \"text\"", Exp.ge(Exp.stringBin("stringBin1"), Exp.val("text"))); - } - - @Test - void binLT() { - translateAndCompare("$.intBin1 < 100", Exp.lt(Exp.intBin("intBin1"), Exp.val(100))); - translateAndCompare("$.stringBin1 < 'text'", Exp.lt(Exp.stringBin("stringBin1"), Exp.val("text"))); - translateAndCompare("$.stringBin1 < \"text\"", Exp.lt(Exp.stringBin("stringBin1"), Exp.val("text"))); - } - - @Test - void binLE() { - translateAndCompare("$.intBin1 <= 100", Exp.le(Exp.intBin("intBin1"), Exp.val(100))); - translateAndCompare("$.stringBin1 <= 'text'", Exp.le(Exp.stringBin("stringBin1"), Exp.val("text"))); - translateAndCompare("$.stringBin1 <= \"text\"", Exp.le(Exp.stringBin("stringBin1"), Exp.val("text"))); - } - - @Test - void binEquals() { - translateAndCompare("$.intBin1 == 100", Exp.eq(Exp.intBin("intBin1"), Exp.val(100))); - translateAndCompare("$.strBin == \"yes\"", Exp.eq(Exp.stringBin("strBin"), Exp.val("yes"))); - translateAndCompare("$.strBin == 'yes'", Exp.eq(Exp.stringBin("strBin"), Exp.val("yes"))); - } - - @Test - void binNotEquals() { - translateAndCompare("$.intBin1 != 100", Exp.ne(Exp.intBin("intBin1"), Exp.val(100))); - translateAndCompare("$.strBin != \"yes\"", Exp.ne(Exp.stringBin("strBin"), Exp.val("yes"))); - translateAndCompare("$.strBin != 'yes'", Exp.ne(Exp.stringBin("strBin"), Exp.val("yes"))); - } - - @Test - void negativeStringBinEquals() { - assertThatThrownBy(() -> translate("$.strBin == yes")) - .isInstanceOf(AerospikeDSLException.class) - .hasMessage("Unable to parse right operand"); - } - - // TODO: Will be handled in FMWK-486 - //@Test - //void logicalOperators_functionCalls() { - // translate("$.a.exists() and $.b.exists()"); - //} -} diff --git a/src/test/java/com/aerospike/dsl/ArithmeticExpressionsTests.java b/src/test/java/com/aerospike/dsl/expression/ArithmeticExpressionsTests.java similarity index 66% rename from src/test/java/com/aerospike/dsl/ArithmeticExpressionsTests.java rename to src/test/java/com/aerospike/dsl/expression/ArithmeticExpressionsTests.java index db1d8da..7876e58 100644 --- a/src/test/java/com/aerospike/dsl/ArithmeticExpressionsTests.java +++ b/src/test/java/com/aerospike/dsl/expression/ArithmeticExpressionsTests.java @@ -1,123 +1,123 @@ -package com.aerospike.dsl; +package com.aerospike.dsl.expression; import com.aerospike.client.exp.Exp; import com.aerospike.dsl.exception.AerospikeDSLException; import org.junit.jupiter.api.Test; -import static com.aerospike.dsl.util.TestUtils.translate; -import static com.aerospike.dsl.util.TestUtils.translateAndCompare; +import static com.aerospike.dsl.util.TestUtils.parseExpression; +import static com.aerospike.dsl.util.TestUtils.parseExpressionAndCompare; import static org.assertj.core.api.Assertions.assertThatThrownBy; public class ArithmeticExpressionsTests { @Test void add() { - translateAndCompare("($.apples + $.bananas) > 10", + parseExpressionAndCompare("($.apples + $.bananas) > 10", Exp.gt(Exp.add(Exp.intBin("apples"), Exp.intBin("bananas")), Exp.val(10))); - translateAndCompare("($.apples + 5) > 10", + parseExpressionAndCompare("($.apples + 5) > 10", Exp.gt(Exp.add(Exp.intBin("apples"), Exp.val(5)), Exp.val(10))); - translateAndCompare("(5 + $.bananas) > 10", + parseExpressionAndCompare("(5 + $.bananas) > 10", Exp.gt(Exp.add(Exp.val(5), Exp.intBin("bananas")), Exp.val(10))); - translateAndCompare("(5.2 + $.bananas) > 10.2", + parseExpressionAndCompare("(5.2 + $.bananas) > 10.2", Exp.gt(Exp.add(Exp.val(5.2), Exp.floatBin("bananas")), Exp.val(10.2))); } @Test void sub() { - translateAndCompare("($.apples - $.bananas) == 10", + parseExpressionAndCompare("($.apples - $.bananas) == 10", Exp.eq(Exp.sub(Exp.intBin("apples"), Exp.intBin("bananas")), Exp.val(10))); - translateAndCompare("($.apples - 5) == 10", + parseExpressionAndCompare("($.apples - 5) == 10", Exp.eq(Exp.sub(Exp.intBin("apples"), Exp.val(5)), Exp.val(10))); - translateAndCompare("(15 - $.bananas) == 10", + parseExpressionAndCompare("(15 - $.bananas) == 10", Exp.eq(Exp.sub(Exp.val(15), Exp.intBin("bananas")), Exp.val(10))); } @Test void mul() { - translateAndCompare("($.apples * $.bananas) != 10", + parseExpressionAndCompare("($.apples * $.bananas) != 10", Exp.ne(Exp.mul(Exp.intBin("apples"), Exp.intBin("bananas")), Exp.val(10))); - translateAndCompare("($.apples * 7) != 10", + parseExpressionAndCompare("($.apples * 7) != 10", Exp.ne(Exp.mul(Exp.intBin("apples"), Exp.val(7)), Exp.val(10))); - translateAndCompare("(3 * $.bananas) != 10", + parseExpressionAndCompare("(3 * $.bananas) != 10", Exp.ne(Exp.mul(Exp.val(3), Exp.intBin("bananas")), Exp.val(10))); } @Test void div() { - translateAndCompare("($.apples / $.bananas) <= 10", + parseExpressionAndCompare("($.apples / $.bananas) <= 10", Exp.le(Exp.div(Exp.intBin("apples"), Exp.intBin("bananas")), Exp.val(10))); - translateAndCompare("($.apples / 5) <= 10", + parseExpressionAndCompare("($.apples / 5) <= 10", Exp.le(Exp.div(Exp.intBin("apples"), Exp.val(5)), Exp.val(10))); - translateAndCompare("(33 / $.bananas) <= 10", + parseExpressionAndCompare("(33 / $.bananas) <= 10", Exp.le(Exp.div(Exp.val(33), Exp.intBin("bananas")), Exp.val(10))); // Exp should be constructed and equal and therefore the test is passing // but when actually triggered server will throw divide by zero exception - translateAndCompare("($.apples / 0) <= 10", + parseExpressionAndCompare("($.apples / 0) <= 10", Exp.le(Exp.div(Exp.intBin("apples"), Exp.val(0)), Exp.val(10))); } @Test void mod() { - translateAndCompare("($.apples % $.bananas) != 10", + parseExpressionAndCompare("($.apples % $.bananas) != 10", Exp.ne(Exp.mod(Exp.intBin("apples"), Exp.intBin("bananas")), Exp.val(10))); - translateAndCompare("($.apples % 7) != 10", + parseExpressionAndCompare("($.apples % 7) != 10", Exp.ne(Exp.mod(Exp.intBin("apples"), Exp.val(7)), Exp.val(10))); - translateAndCompare("(3 % $.bananas) != 10", + parseExpressionAndCompare("(3 % $.bananas) != 10", Exp.ne(Exp.mod(Exp.val(3), Exp.intBin("bananas")), Exp.val(10))); } @Test void intAnd() { - translateAndCompare("($.apples & $.bananas) != 10", + parseExpressionAndCompare("($.apples & $.bananas) != 10", Exp.ne(Exp.intAnd(Exp.intBin("apples"), Exp.intBin("bananas")), Exp.val(10))); - translateAndCompare("($.apples & 7) != 10", + parseExpressionAndCompare("($.apples & 7) != 10", Exp.ne(Exp.intAnd(Exp.intBin("apples"), Exp.val(7)), Exp.val(10))); - translateAndCompare("(3 & $.bananas) != 10", + parseExpressionAndCompare("(3 & $.bananas) != 10", Exp.ne(Exp.intAnd(Exp.val(3), Exp.intBin("bananas")), Exp.val(10))); } @Test void intOr() { - translateAndCompare("($.apples | $.bananas) != 10", + parseExpressionAndCompare("($.apples | $.bananas) != 10", Exp.ne(Exp.intOr(Exp.intBin("apples"), Exp.intBin("bananas")), Exp.val(10))); - translateAndCompare("($.apples | 7) != 10", + parseExpressionAndCompare("($.apples | 7) != 10", Exp.ne(Exp.intOr(Exp.intBin("apples"), Exp.val(7)), Exp.val(10))); - translateAndCompare("(3 | $.bananas) != 10", + parseExpressionAndCompare("(3 | $.bananas) != 10", Exp.ne(Exp.intOr(Exp.val(3), Exp.intBin("bananas")), Exp.val(10))); } @Test void intXor() { - translateAndCompare("($.apples ^ $.bananas) != 10", + parseExpressionAndCompare("($.apples ^ $.bananas) != 10", Exp.ne(Exp.intXor(Exp.intBin("apples"), Exp.intBin("bananas")), Exp.val(10))); - translateAndCompare("($.apples ^ 7) != 10", + parseExpressionAndCompare("($.apples ^ 7) != 10", Exp.ne(Exp.intXor(Exp.intBin("apples"), Exp.val(7)), Exp.val(10))); - translateAndCompare("(3 ^ $.bananas) != 10", + parseExpressionAndCompare("(3 ^ $.bananas) != 10", Exp.ne(Exp.intXor(Exp.val(3), Exp.intBin("bananas")), Exp.val(10))); } @Test void intNot() { - translateAndCompare("(~$.apples) != 10", + parseExpressionAndCompare("(~$.apples) != 10", Exp.ne(Exp.intNot(Exp.intBin("apples")), Exp.val(10))); } @Test void intLShift() { - translateAndCompare("$.visits << 1", + parseExpressionAndCompare("$.visits << 1", Exp.lshift(Exp.intBin("visits"), Exp.val(1))); } @Test void intRShift() { - translateAndCompare("(($.flags >> 6) & 1) == 1", + parseExpressionAndCompare("(($.flags >> 6) & 1) == 1", Exp.eq(Exp.intAnd(Exp.rshift(Exp.intBin("flags"), Exp.val(6)), Exp.val(1)), Exp.val(1))); } @Test void negativeArithmetic() { - assertThatThrownBy(() -> translate("($.apples.get(type: STRING) + 5) > 10")) + assertThatThrownBy(() -> parseExpression("($.apples.get(type: STRING) + 5) > 10")) .isInstanceOf(AerospikeDSLException.class) .hasMessageContaining("Cannot compare STRING to INT"); diff --git a/src/test/java/com/aerospike/dsl/expression/BinExpressionsTests.java b/src/test/java/com/aerospike/dsl/expression/BinExpressionsTests.java new file mode 100644 index 0000000..eb0d85a --- /dev/null +++ b/src/test/java/com/aerospike/dsl/expression/BinExpressionsTests.java @@ -0,0 +1,71 @@ +package com.aerospike.dsl.expression; + +import com.aerospike.client.exp.Exp; +import com.aerospike.dsl.exception.AerospikeDSLException; +import org.junit.jupiter.api.Test; + +import static com.aerospike.dsl.util.TestUtils.parseExpression; +import static com.aerospike.dsl.util.TestUtils.parseExpressionAndCompare; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class BinExpressionsTests { + + @Test + void binGT() { + parseExpressionAndCompare("$.intBin1 > 100", Exp.gt(Exp.intBin("intBin1"), Exp.val(100))); + parseExpressionAndCompare("$.stringBin1 > 'text'", Exp.gt(Exp.stringBin("stringBin1"), Exp.val("text"))); + parseExpressionAndCompare("$.stringBin1 > \"text\"", Exp.gt(Exp.stringBin("stringBin1"), Exp.val("text"))); + + parseExpressionAndCompare("100 < $.intBin1", Exp.lt(Exp.val(100), Exp.intBin("intBin1"))); + parseExpressionAndCompare("'text' < $.stringBin1", Exp.lt(Exp.val("text"), Exp.stringBin("stringBin1"))); + parseExpressionAndCompare("\"text\" < $.stringBin1", Exp.lt(Exp.val("text"), Exp.stringBin("stringBin1"))); + } + + @Test + void binGE() { + parseExpressionAndCompare("$.intBin1 >= 100", Exp.ge(Exp.intBin("intBin1"), Exp.val(100))); + parseExpressionAndCompare("$.stringBin1 >= 'text'", Exp.ge(Exp.stringBin("stringBin1"), Exp.val("text"))); + parseExpressionAndCompare("$.stringBin1 >= \"text\"", Exp.ge(Exp.stringBin("stringBin1"), Exp.val("text"))); + } + + @Test + void binLT() { + parseExpressionAndCompare("$.intBin1 < 100", Exp.lt(Exp.intBin("intBin1"), Exp.val(100))); + parseExpressionAndCompare("$.stringBin1 < 'text'", Exp.lt(Exp.stringBin("stringBin1"), Exp.val("text"))); + parseExpressionAndCompare("$.stringBin1 < \"text\"", Exp.lt(Exp.stringBin("stringBin1"), Exp.val("text"))); + } + + @Test + void binLE() { + parseExpressionAndCompare("$.intBin1 <= 100", Exp.le(Exp.intBin("intBin1"), Exp.val(100))); + parseExpressionAndCompare("$.stringBin1 <= 'text'", Exp.le(Exp.stringBin("stringBin1"), Exp.val("text"))); + parseExpressionAndCompare("$.stringBin1 <= \"text\"", Exp.le(Exp.stringBin("stringBin1"), Exp.val("text"))); + } + + @Test + void binEquals() { + parseExpressionAndCompare("$.intBin1 == 100", Exp.eq(Exp.intBin("intBin1"), Exp.val(100))); + parseExpressionAndCompare("$.strBin == \"yes\"", Exp.eq(Exp.stringBin("strBin"), Exp.val("yes"))); + parseExpressionAndCompare("$.strBin == 'yes'", Exp.eq(Exp.stringBin("strBin"), Exp.val("yes"))); + } + + @Test + void binNotEquals() { + parseExpressionAndCompare("$.intBin1 != 100", Exp.ne(Exp.intBin("intBin1"), Exp.val(100))); + parseExpressionAndCompare("$.strBin != \"yes\"", Exp.ne(Exp.stringBin("strBin"), Exp.val("yes"))); + parseExpressionAndCompare("$.strBin != 'yes'", Exp.ne(Exp.stringBin("strBin"), Exp.val("yes"))); + } + + @Test + void negativeStringBinEquals() { + assertThatThrownBy(() -> parseExpression("$.strBin == yes")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessage("Unable to parse right operand"); + } + + // TODO: Will be handled in FMWK-486 + //@Test + //void logicalOperators_functionCalls() { + // translate("$.a.exists() and $.b.exists()"); + //} +} diff --git a/src/test/java/com/aerospike/dsl/CastingTests.java b/src/test/java/com/aerospike/dsl/expression/CastingTests.java similarity index 56% rename from src/test/java/com/aerospike/dsl/CastingTests.java rename to src/test/java/com/aerospike/dsl/expression/CastingTests.java index 9dafa7b..c398386 100644 --- a/src/test/java/com/aerospike/dsl/CastingTests.java +++ b/src/test/java/com/aerospike/dsl/expression/CastingTests.java @@ -1,11 +1,11 @@ -package com.aerospike.dsl; +package com.aerospike.dsl.expression; import com.aerospike.client.exp.Exp; import com.aerospike.dsl.exception.AerospikeDSLException; import org.junit.jupiter.api.Test; -import static com.aerospike.dsl.util.TestUtils.translate; -import static com.aerospike.dsl.util.TestUtils.translateAndCompare; +import static com.aerospike.dsl.util.TestUtils.parseExpression; +import static com.aerospike.dsl.util.TestUtils.parseExpressionAndCompare; import static org.assertj.core.api.Assertions.assertThatThrownBy; public class CastingTests { @@ -14,19 +14,19 @@ public class CastingTests { void floatToIntComparison() { Exp expectedExp = Exp.gt(Exp.intBin("intBin1"), Exp.intBin("floatBin1")); // Int is default - translateAndCompare("$.intBin1 > $.floatBin1.asInt()", expectedExp); - translateAndCompare("$.intBin1.get(type: INT) > $.floatBin1.asInt()", expectedExp); + parseExpressionAndCompare("$.intBin1 > $.floatBin1.asInt()", expectedExp); + parseExpressionAndCompare("$.intBin1.get(type: INT) > $.floatBin1.asInt()", expectedExp); } @Test void intToFloatComparison() { - translateAndCompare("$.intBin1.get(type: INT) > $.intBin2.asFloat()", + parseExpressionAndCompare("$.intBin1.get(type: INT) > $.intBin2.asFloat()", Exp.gt(Exp.intBin("intBin1"), Exp.floatBin("intBin2"))); } @Test void negativeInvalidTypesComparison() { - assertThatThrownBy(() -> translate("$.stringBin1.get(type: STRING) > $.intBin2.asFloat()")) + assertThatThrownBy(() -> parseExpression("$.stringBin1.get(type: STRING) > $.intBin2.asFloat()")) .isInstanceOf(AerospikeDSLException.class) .hasMessageContaining("Cannot compare STRING to FLOAT"); } diff --git a/src/test/java/com/aerospike/dsl/ControlStructuresTests.java b/src/test/java/com/aerospike/dsl/expression/ControlStructuresTests.java similarity index 74% rename from src/test/java/com/aerospike/dsl/ControlStructuresTests.java rename to src/test/java/com/aerospike/dsl/expression/ControlStructuresTests.java index 8ecbf6e..14b7c94 100644 --- a/src/test/java/com/aerospike/dsl/ControlStructuresTests.java +++ b/src/test/java/com/aerospike/dsl/expression/ControlStructuresTests.java @@ -1,9 +1,9 @@ -package com.aerospike.dsl; +package com.aerospike.dsl.expression; import com.aerospike.client.exp.Exp; import org.junit.jupiter.api.Test; -import static com.aerospike.dsl.util.TestUtils.translateAndCompare; +import static com.aerospike.dsl.util.TestUtils.parseExpressionAndCompare; public class ControlStructuresTests { @@ -17,10 +17,10 @@ void whenWithASingleDeclaration() { Exp.val("other") ); - translateAndCompare("when ($.who == 1 => \"bob\", default => \"other\")", + parseExpressionAndCompare("when ($.who == 1 => \"bob\", default => \"other\")", expected); // different spacing style - translateAndCompare("when($.who == 1 => \"bob\", default => \"other\")", + parseExpressionAndCompare("when($.who == 1 => \"bob\", default => \"other\")", expected); } @@ -40,7 +40,7 @@ void whenUsingTheResult() { // Implicit detect as String //translateAndCompare("$.stringBin1 == (when ($.who == 1 => \"bob\", default => \"other\"))", // expected); - translateAndCompare("$.stringBin1.get(type: STRING) == (when ($.who == 1 => \"bob\", default => \"other\"))", + parseExpressionAndCompare("$.stringBin1.get(type: STRING) == (when ($.who == 1 => \"bob\", default => \"other\"))", expected); } @@ -58,7 +58,7 @@ void whenWithMultipleDeclarations() { Exp.val("other") ); - translateAndCompare("when ($.who == 1 => \"bob\", $.who == 2 => \"fred\", default => \"other\")", + parseExpressionAndCompare("when ($.who == 1 => \"bob\", $.who == 2 => \"fred\", default => \"other\")", expected); } @@ -74,10 +74,10 @@ void withMultipleVariablesDefinitionAndUsage() { Exp.add(Exp.var("x"), Exp.var("y")) ); - translateAndCompare("with (x = 1, y = ${x} + 1) do (${x} + ${y})", + parseExpressionAndCompare("with (x = 1, y = ${x} + 1) do (${x} + ${y})", expected); // different spacing style - translateAndCompare("with(x = 1, y = ${x}+1) do(${x}+${y})", + parseExpressionAndCompare("with(x = 1, y = ${x}+1) do(${x}+${y})", expected); } } diff --git a/src/test/java/com/aerospike/dsl/ExplicitTypesTests.java b/src/test/java/com/aerospike/dsl/expression/ExplicitTypesTests.java similarity index 69% rename from src/test/java/com/aerospike/dsl/ExplicitTypesTests.java rename to src/test/java/com/aerospike/dsl/expression/ExplicitTypesTests.java index f738b2d..7774aa5 100644 --- a/src/test/java/com/aerospike/dsl/ExplicitTypesTests.java +++ b/src/test/java/com/aerospike/dsl/expression/ExplicitTypesTests.java @@ -1,4 +1,4 @@ -package com.aerospike.dsl; +package com.aerospike.dsl.expression; import com.aerospike.client.exp.Exp; import com.aerospike.dsl.exception.AerospikeDSLException; @@ -8,8 +8,8 @@ import java.util.List; import java.util.TreeMap; -import static com.aerospike.dsl.util.TestUtils.translate; -import static com.aerospike.dsl.util.TestUtils.translateAndCompare; +import static com.aerospike.dsl.util.TestUtils.parseExpression; +import static com.aerospike.dsl.util.TestUtils.parseExpressionAndCompare; import static org.assertj.core.api.Assertions.assertThatThrownBy; // Explicit types tests, list and map explicit types are tested in their own test classes @@ -17,33 +17,33 @@ public class ExplicitTypesTests { @Test void integerComparison() { - translateAndCompare("$.intBin1.get(type: INT) > 5", + parseExpressionAndCompare("$.intBin1.get(type: INT) > 5", Exp.gt(Exp.intBin("intBin1"), Exp.val(5))); - translateAndCompare("5 < $.intBin1.get(type: INT)", + parseExpressionAndCompare("5 < $.intBin1.get(type: INT)", Exp.lt(Exp.val(5), Exp.intBin("intBin1"))); } @Test void stringComparison() { // A String constant must contain quoted Strings - translateAndCompare("$.stringBin1.get(type: STRING) == \"yes\"", + parseExpressionAndCompare("$.stringBin1.get(type: STRING) == \"yes\"", Exp.eq(Exp.stringBin("stringBin1"), Exp.val("yes"))); - translateAndCompare("$.stringBin1.get(type: STRING) == 'yes'", + parseExpressionAndCompare("$.stringBin1.get(type: STRING) == 'yes'", Exp.eq(Exp.stringBin("stringBin1"), Exp.val("yes"))); - translateAndCompare("\"yes\" == $.stringBin1.get(type: STRING)", + parseExpressionAndCompare("\"yes\" == $.stringBin1.get(type: STRING)", Exp.eq(Exp.val("yes"), Exp.stringBin("stringBin1"))); - translateAndCompare("'yes' == $.stringBin1.get(type: STRING)", + parseExpressionAndCompare("'yes' == $.stringBin1.get(type: STRING)", Exp.eq(Exp.val("yes"), Exp.stringBin("stringBin1"))); } @Test void stringComparisonNegativeTest() { // A String constant must be quoted - assertThatThrownBy(() -> translateAndCompare("$.stringBin1.get(type: STRING) == yes", + assertThatThrownBy(() -> parseExpressionAndCompare("$.stringBin1.get(type: STRING) == yes", Exp.eq(Exp.stringBin("stringBin1"), Exp.val("yes")))) .isInstanceOf(AerospikeDSLException.class) .hasMessage("Unable to parse right operand"); @@ -53,65 +53,65 @@ void stringComparisonNegativeTest() { void blobComparison() { byte[] data = new byte[]{1, 2, 3}; String encodedString = Base64.getEncoder().encodeToString(data); - translateAndCompare("$.blobBin1.get(type: BLOB) == \"" + encodedString + "\"", + parseExpressionAndCompare("$.blobBin1.get(type: BLOB) == \"" + encodedString + "\"", Exp.eq(Exp.blobBin("blobBin1"), Exp.val(data))); // Reverse - translateAndCompare("\"" + encodedString + "\"" + " == $.blobBin1.get(type: BLOB)", + parseExpressionAndCompare("\"" + encodedString + "\"" + " == $.blobBin1.get(type: BLOB)", Exp.eq(Exp.val(data), Exp.blobBin("blobBin1"))); } @Test void floatComparison() { - translateAndCompare("$.floatBin1.get(type: FLOAT) == 1.5", + parseExpressionAndCompare("$.floatBin1.get(type: FLOAT) == 1.5", Exp.eq(Exp.floatBin("floatBin1"), Exp.val(1.5))); - translateAndCompare("1.5 == $.floatBin1.get(type: FLOAT)", + parseExpressionAndCompare("1.5 == $.floatBin1.get(type: FLOAT)", Exp.eq(Exp.val(1.5), Exp.floatBin("floatBin1"))); } @Test void booleanComparison() { - translateAndCompare("$.boolBin1.get(type: BOOL) == true", + parseExpressionAndCompare("$.boolBin1.get(type: BOOL) == true", Exp.eq(Exp.boolBin("boolBin1"), Exp.val(true))); - translateAndCompare("true == $.boolBin1.get(type: BOOL)", + parseExpressionAndCompare("true == $.boolBin1.get(type: BOOL)", Exp.eq(Exp.val(true), Exp.boolBin("boolBin1"))); } @Test void negativeBooleanComparison() { - assertThatThrownBy(() -> translate("$.boolBin1.get(type: BOOL) == 5")) + assertThatThrownBy(() -> parseExpression("$.boolBin1.get(type: BOOL) == 5")) .isInstanceOf(AerospikeDSLException.class) .hasMessageContaining("Cannot compare BOOL to INT"); } @Test void listComparison_constantOnRightSide() { - translateAndCompare("$.listBin1.get(type: LIST) == [100]", + parseExpressionAndCompare("$.listBin1.get(type: LIST) == [100]", Exp.eq(Exp.listBin("listBin1"), Exp.val(List.of(100)))); - translateAndCompare("$.listBin1.[] == [100]", + parseExpressionAndCompare("$.listBin1.[] == [100]", Exp.eq(Exp.listBin("listBin1"), Exp.val(List.of(100)))); // integer values are read as long - translateAndCompare("$.listBin1.get(type: LIST) == [100, 200, 300, 400]", + parseExpressionAndCompare("$.listBin1.get(type: LIST) == [100, 200, 300, 400]", Exp.eq(Exp.listBin("listBin1"), Exp.val(List.of(100, 200, 300, 400)))); // integer values are read as long - translateAndCompare("$.listBin1.get(type: LIST) == [100, 200, 300, 400]", + parseExpressionAndCompare("$.listBin1.get(type: LIST) == [100, 200, 300, 400]", Exp.eq(Exp.listBin("listBin1"), Exp.val(List.of(100L, 200L, 300L, 400L)))); - translateAndCompare("$.listBin1.get(type: LIST) == ['yes']", + parseExpressionAndCompare("$.listBin1.get(type: LIST) == ['yes']", Exp.eq(Exp.listBin("listBin1"), Exp.val(List.of("yes")))); - translateAndCompare("$.listBin1.get(type: LIST) == ['yes', 'of course']", + parseExpressionAndCompare("$.listBin1.get(type: LIST) == ['yes', 'of course']", Exp.eq(Exp.listBin("listBin1"), Exp.val(List.of("yes", "of course")))); - translateAndCompare("$.listBin1.get(type: LIST) == [\"yes\"]", + parseExpressionAndCompare("$.listBin1.get(type: LIST) == [\"yes\"]", Exp.eq(Exp.listBin("listBin1"), Exp.val(List.of("yes")))); - translateAndCompare("$.listBin1.get(type: LIST) == [\"yes\", \"of course\"]", + parseExpressionAndCompare("$.listBin1.get(type: LIST) == [\"yes\", \"of course\"]", Exp.eq(Exp.listBin("listBin1"), Exp.val(List.of("yes", "of course")))); } @@ -119,7 +119,7 @@ void listComparison_constantOnRightSide() { void listComparison_constantOnRightSide_NegativeTest() { // A String constant must be quoted assertThatThrownBy(() -> - translateAndCompare("$.listBin1.get(type: LIST) == [yes, of course]", + parseExpressionAndCompare("$.listBin1.get(type: LIST) == [yes, of course]", Exp.eq(Exp.listBin("listBin1"), Exp.val(List.of("yes", "of course")))) ) .isInstanceOf(AerospikeDSLException.class) @@ -128,30 +128,30 @@ void listComparison_constantOnRightSide_NegativeTest() { @Test void listComparison_constantOnLeftSide() { - translateAndCompare("[100] == $.listBin1.get(type: LIST)", + parseExpressionAndCompare("[100] == $.listBin1.get(type: LIST)", Exp.eq(Exp.val(List.of(100)), Exp.listBin("listBin1"))); - translateAndCompare("[100] == $.listBin1.[]", + parseExpressionAndCompare("[100] == $.listBin1.[]", Exp.eq(Exp.val(List.of(100)), Exp.listBin("listBin1"))); // integer values are read as long - translateAndCompare("[100, 200, 300, 400] == $.listBin1.get(type: LIST)", + parseExpressionAndCompare("[100, 200, 300, 400] == $.listBin1.get(type: LIST)", Exp.eq(Exp.val(List.of(100, 200, 300, 400)), Exp.listBin("listBin1"))); // integer values are read as long - translateAndCompare("[100, 200, 300, 400] == $.listBin1.get(type: LIST)", + parseExpressionAndCompare("[100, 200, 300, 400] == $.listBin1.get(type: LIST)", Exp.eq(Exp.val(List.of(100L, 200L, 300L, 400L)), Exp.listBin("listBin1"))); - translateAndCompare("['yes'] == $.listBin1.get(type: LIST)", + parseExpressionAndCompare("['yes'] == $.listBin1.get(type: LIST)", Exp.eq(Exp.val(List.of("yes")), Exp.listBin("listBin1"))); - translateAndCompare("['yes', 'of course'] == $.listBin1.get(type: LIST)", + parseExpressionAndCompare("['yes', 'of course'] == $.listBin1.get(type: LIST)", Exp.eq(Exp.val(List.of("yes", "of course")), Exp.listBin("listBin1"))); - translateAndCompare("[\"yes\"] == $.listBin1.get(type: LIST)", + parseExpressionAndCompare("[\"yes\"] == $.listBin1.get(type: LIST)", Exp.eq(Exp.val(List.of("yes")), Exp.listBin("listBin1"))); - translateAndCompare("[\"yes\", \"of course\"] == $.listBin1.get(type: LIST)", + parseExpressionAndCompare("[\"yes\", \"of course\"] == $.listBin1.get(type: LIST)", Exp.eq(Exp.val(List.of("yes", "of course")), Exp.listBin("listBin1"))); } @@ -159,7 +159,7 @@ void listComparison_constantOnLeftSide() { void listComparison_constantOnLeftSide_NegativeTest() { // A String constant must be quoted assertThatThrownBy(() -> - translateAndCompare("[yes, of course] == $.listBin1.get(type: LIST)", + parseExpressionAndCompare("[yes, of course] == $.listBin1.get(type: LIST)", Exp.eq(Exp.val(List.of("yes", "of course")), Exp.listBin("listBin1"))) ) .isInstanceOf(AerospikeDSLException.class) @@ -185,39 +185,39 @@ public static TreeMap treeMapOf(Object... entries) { @Test void mapComparison_constantOnRightSide() { // Prerequisite for comparing maps: both sides must be ordered maps - translateAndCompare("$.mapBin1.get(type: MAP) == {100:100}", + parseExpressionAndCompare("$.mapBin1.get(type: MAP) == {100:100}", Exp.eq(Exp.mapBin("mapBin1"), Exp.val(treeMapOf(100, 100)))); - translateAndCompare("$.mapBin1.get(type: MAP) == {100 : 100}", + parseExpressionAndCompare("$.mapBin1.get(type: MAP) == {100 : 100}", Exp.eq(Exp.mapBin("mapBin1"), Exp.val(treeMapOf(100, 100)))); - translateAndCompare("$.mapBin1.{} == {100:100}", + parseExpressionAndCompare("$.mapBin1.{} == {100:100}", Exp.eq(Exp.mapBin("mapBin1"), Exp.val(treeMapOf(100, 100)))); byte[] blobKey = new byte[]{1, 2, 3}; String encodedBlobKey = Base64.getEncoder().encodeToString(blobKey); // encoded blob key must be quoted as it is a String - translateAndCompare("$.mapBin1.{} == {'" + encodedBlobKey + "':100}", + parseExpressionAndCompare("$.mapBin1.{} == {'" + encodedBlobKey + "':100}", Exp.eq(Exp.mapBin("mapBin1"), Exp.val(treeMapOf(encodedBlobKey, 100)))); // integer values are read as long - translateAndCompare("$.mapBin1.get(type: MAP) == {100:200, 300:400}", + parseExpressionAndCompare("$.mapBin1.get(type: MAP) == {100:200, 300:400}", Exp.eq(Exp.mapBin("mapBin1"), Exp.val(treeMapOf(100L, 200L, 300L, 400L)))); - translateAndCompare("$.mapBin1.get(type: MAP) == {100:200, 300:400}", + parseExpressionAndCompare("$.mapBin1.get(type: MAP) == {100:200, 300:400}", Exp.eq(Exp.mapBin("mapBin1"), Exp.val(treeMapOf(100, 200, 300, 400)))); - translateAndCompare("$.mapBin1.get(type: MAP) == {'yes?':'yes!'}", + parseExpressionAndCompare("$.mapBin1.get(type: MAP) == {'yes?':'yes!'}", Exp.eq(Exp.mapBin("mapBin1"), Exp.val(treeMapOf("yes?", "yes!")))); - translateAndCompare("$.mapBin1.get(type: MAP) == {\"yes\" : \"yes\"}", + parseExpressionAndCompare("$.mapBin1.get(type: MAP) == {\"yes\" : \"yes\"}", Exp.eq(Exp.mapBin("mapBin1"), Exp.val(treeMapOf("yes", "yes")))); - translateAndCompare( + parseExpressionAndCompare( "$.mapBin1.get(type: MAP) == {\"yes of course\" : \"yes of course\"}", Exp.eq(Exp.mapBin("mapBin1"), Exp.val(treeMapOf("yes of course", "yes of course")))); - translateAndCompare("$.mapBin1.get(type: MAP) == {\"yes\" : [\"yes\", \"of course\"]}", + parseExpressionAndCompare("$.mapBin1.get(type: MAP) == {\"yes\" : [\"yes\", \"of course\"]}", Exp.eq(Exp.mapBin("mapBin1"), Exp.val(treeMapOf("yes", List.of("yes", "of course"))))); } @@ -225,14 +225,14 @@ void mapComparison_constantOnRightSide() { void mapComparison_constantOnRightSide_NegativeTest() { // A String constant must be quoted assertThatThrownBy(() -> - translateAndCompare("$.mapBin1.get(type: MAP) == {yes, of course}", + parseExpressionAndCompare("$.mapBin1.get(type: MAP) == {yes, of course}", Exp.eq(Exp.mapBin("mapBin1"), Exp.val(treeMapOf("yes", "of course")))) ) .isInstanceOf(AerospikeDSLException.class) .hasMessage("Unable to parse map operand"); assertThatThrownBy(() -> - translateAndCompare("$.mapBin1.get(type: MAP) == ['yes', 'of course']", + parseExpressionAndCompare("$.mapBin1.get(type: MAP) == ['yes', 'of course']", Exp.eq(Exp.mapBin("mapBin1"), Exp.val(List.of("yes", "of course")))) ) .isInstanceOf(AerospikeDSLException.class) @@ -240,7 +240,7 @@ void mapComparison_constantOnRightSide_NegativeTest() { // Map key can only be Integer or String assertThatThrownBy(() -> - translateAndCompare("$.mapBin1.get(type: MAP) == {[100]:[100]}", + parseExpressionAndCompare("$.mapBin1.get(type: MAP) == {[100]:[100]}", Exp.eq(Exp.mapBin("mapBin1"), Exp.val(List.of("yes", "of course")))) ) .isInstanceOf(AerospikeDSLException.class) @@ -250,39 +250,39 @@ void mapComparison_constantOnRightSide_NegativeTest() { @Test void mapComparison_constantOnLeftSide() { // Prerequisite for comparing maps: both sides must be ordered maps - translateAndCompare("{100:100} == $.mapBin1.get(type: MAP)", + parseExpressionAndCompare("{100:100} == $.mapBin1.get(type: MAP)", Exp.eq(Exp.val(treeMapOf(100, 100)), Exp.mapBin("mapBin1"))); - translateAndCompare("{100 : 100} == $.mapBin1.get(type: MAP)", + parseExpressionAndCompare("{100 : 100} == $.mapBin1.get(type: MAP)", Exp.eq(Exp.val(treeMapOf(100, 100)), Exp.mapBin("mapBin1"))); - translateAndCompare("{100:100} == $.mapBin1.{}", + parseExpressionAndCompare("{100:100} == $.mapBin1.{}", Exp.eq(Exp.val(treeMapOf(100, 100)), Exp.mapBin("mapBin1"))); byte[] blobKey = new byte[]{1, 2, 3}; String encodedBlobKey = Base64.getEncoder().encodeToString(blobKey); // encoded blob key must be quoted as it is a String - translateAndCompare("{'" + encodedBlobKey + "':100} == $.mapBin1.{}", + parseExpressionAndCompare("{'" + encodedBlobKey + "':100} == $.mapBin1.{}", Exp.eq(Exp.val(treeMapOf(encodedBlobKey, 100)), Exp.mapBin("mapBin1"))); // integer values are read as long - translateAndCompare("{100:200, 300:400} == $.mapBin1.get(type: MAP)", + parseExpressionAndCompare("{100:200, 300:400} == $.mapBin1.get(type: MAP)", Exp.eq(Exp.val(treeMapOf(100L, 200L, 300L, 400L)), Exp.mapBin("mapBin1"))); - translateAndCompare("{100:200, 300:400} == $.mapBin1.get(type: MAP)", + parseExpressionAndCompare("{100:200, 300:400} == $.mapBin1.get(type: MAP)", Exp.eq(Exp.val(treeMapOf(100, 200, 300, 400)), Exp.mapBin("mapBin1"))); - translateAndCompare("{'yes?':'yes!'} == $.mapBin1.get(type: MAP)", + parseExpressionAndCompare("{'yes?':'yes!'} == $.mapBin1.get(type: MAP)", Exp.eq(Exp.val(treeMapOf("yes?", "yes!")), Exp.mapBin("mapBin1"))); - translateAndCompare("{\"yes\" : \"yes\"} == $.mapBin1.get(type: MAP)", + parseExpressionAndCompare("{\"yes\" : \"yes\"} == $.mapBin1.get(type: MAP)", Exp.eq(Exp.val(treeMapOf("yes", "yes")), Exp.mapBin("mapBin1"))); - translateAndCompare( + parseExpressionAndCompare( "{\"yes of course\" : \"yes of course\"} == $.mapBin1.get(type: MAP)", Exp.eq(Exp.val(treeMapOf("yes of course", "yes of course")), Exp.mapBin("mapBin1"))); - translateAndCompare("{\"yes\" : [\"yes\", \"of course\"]} == $.mapBin1.get(type: MAP)", + parseExpressionAndCompare("{\"yes\" : [\"yes\", \"of course\"]} == $.mapBin1.get(type: MAP)", Exp.eq(Exp.val(treeMapOf("yes", List.of("yes", "of course"))), Exp.mapBin("mapBin1"))); } @@ -290,14 +290,14 @@ void mapComparison_constantOnLeftSide() { void mapComparison_constantOnLeftSide_NegativeTest() { // A String constant must be quoted assertThatThrownBy(() -> - translateAndCompare("{yes, of course} == $.mapBin1.get(type: MAP)", + parseExpressionAndCompare("{yes, of course} == $.mapBin1.get(type: MAP)", Exp.eq(Exp.mapBin("mapBin1"), Exp.val(treeMapOf("of course", "yes")))) ) .isInstanceOf(AerospikeDSLException.class) .hasMessage("Could not parse given input, wrong syntax"); assertThatThrownBy(() -> - translateAndCompare("['yes', 'of course'] == $.mapBin1.get(type: MAP)", // incorrect: must be {} + parseExpressionAndCompare("['yes', 'of course'] == $.mapBin1.get(type: MAP)", // incorrect: must be {} Exp.eq(Exp.val(List.of("yes", "of course")), Exp.mapBin("mapBin1"))) ) .isInstanceOf(AerospikeDSLException.class) @@ -305,7 +305,7 @@ void mapComparison_constantOnLeftSide_NegativeTest() { // Map key can only be Integer or String assertThatThrownBy(() -> - translateAndCompare("{[100]:[100]} == $.mapBin1.get(type: MAP)", + parseExpressionAndCompare("{[100]:[100]} == $.mapBin1.get(type: MAP)", Exp.eq(Exp.val(List.of("yes", "of course")), Exp.mapBin("mapBin1"))) ) .isInstanceOf(AerospikeDSLException.class) @@ -314,44 +314,44 @@ void mapComparison_constantOnLeftSide_NegativeTest() { @Test void twoStringBinsComparison() { - translateAndCompare("$.stringBin1.get(type: STRING) == $.stringBin2.get(type: STRING)", + parseExpressionAndCompare("$.stringBin1.get(type: STRING) == $.stringBin2.get(type: STRING)", Exp.eq(Exp.stringBin("stringBin1"), Exp.stringBin("stringBin2"))); } @Test void twoIntegerBinsComparison() { - translateAndCompare("$.intBin1.get(type: INT) == $.intBin2.get(type: INT)", + parseExpressionAndCompare("$.intBin1.get(type: INT) == $.intBin2.get(type: INT)", Exp.eq(Exp.intBin("intBin1"), Exp.intBin("intBin2"))); } @Test void twoFloatBinsComparison() { - translateAndCompare("$.floatBin1.get(type: FLOAT) == $.floatBin2.get(type: FLOAT)", + parseExpressionAndCompare("$.floatBin1.get(type: FLOAT) == $.floatBin2.get(type: FLOAT)", Exp.eq(Exp.floatBin("floatBin1"), Exp.floatBin("floatBin2"))); } @Test void twoBlobBinsComparison() { - translateAndCompare("$.blobBin1.get(type: BLOB) == $.blobBin2.get(type: BLOB)", + parseExpressionAndCompare("$.blobBin1.get(type: BLOB) == $.blobBin2.get(type: BLOB)", Exp.eq(Exp.blobBin("blobBin1"), Exp.blobBin("blobBin2"))); } @Test void negativeTwoDifferentBinTypesComparison() { - assertThatThrownBy(() -> translate("$.stringBin1.get(type: STRING) == $.floatBin2.get(type: FLOAT)")) + assertThatThrownBy(() -> parseExpression("$.stringBin1.get(type: STRING) == $.floatBin2.get(type: FLOAT)")) .isInstanceOf(AerospikeDSLException.class) .hasMessageContaining("Cannot compare STRING to FLOAT"); } @Test void secondDegreeExplicitFloat() { - translateAndCompare("($.apples.get(type: FLOAT) + $.bananas.get(type: FLOAT)) > 10.5", + parseExpressionAndCompare("($.apples.get(type: FLOAT) + $.bananas.get(type: FLOAT)) > 10.5", Exp.gt(Exp.add(Exp.floatBin("apples"), Exp.floatBin("bananas")), Exp.val(10.5))); } @Test void forthDegreeComplicatedExplicitFloat() { - translateAndCompare("(($.apples.get(type: FLOAT) + $.bananas.get(type: FLOAT))" + + parseExpressionAndCompare("(($.apples.get(type: FLOAT) + $.bananas.get(type: FLOAT))" + " + ($.oranges.get(type: FLOAT) + $.acai.get(type: FLOAT))) > 10.5", Exp.gt( Exp.add( @@ -382,7 +382,7 @@ void complicatedWhenExplicitTypeIntDefault() { ) ); - translateAndCompare("$.a.get(type: INT) == " + + parseExpressionAndCompare("$.a.get(type: INT) == " + "(when($.b.get(type: INT) == 1 => $.a1.get(type: INT)," + " $.b.get(type: INT) == 2 => $.a2.get(type: INT)," + " $.b.get(type: INT) == 3 => $.a3.get(type: INT)," + @@ -411,7 +411,7 @@ void complicatedWhenExplicitTypeString() { ) ); - translateAndCompare("$.a.get(type: STRING) == " + + parseExpressionAndCompare("$.a.get(type: STRING) == " + "(when($.b == 1 => $.a1.get(type: STRING)," + " $.b == 2 => $.a2.get(type: STRING)," + " $.b == 3 => $.a3.get(type: STRING)," + diff --git a/src/test/java/com/aerospike/dsl/ImplicitTypesTests.java b/src/test/java/com/aerospike/dsl/expression/ImplicitTypesTests.java similarity index 75% rename from src/test/java/com/aerospike/dsl/ImplicitTypesTests.java rename to src/test/java/com/aerospike/dsl/expression/ImplicitTypesTests.java index fe3de74..641f6aa 100644 --- a/src/test/java/com/aerospike/dsl/ImplicitTypesTests.java +++ b/src/test/java/com/aerospike/dsl/expression/ImplicitTypesTests.java @@ -1,25 +1,25 @@ -package com.aerospike.dsl; +package com.aerospike.dsl.expression; import com.aerospike.client.exp.Exp; import org.junit.jupiter.api.Test; -import static com.aerospike.dsl.util.TestUtils.translateAndCompare; +import static com.aerospike.dsl.util.TestUtils.parseExpressionAndCompare; public class ImplicitTypesTests { @Test void floatComparison() { - translateAndCompare("$.floatBin1 >= 100.25", + parseExpressionAndCompare("$.floatBin1 >= 100.25", Exp.ge(Exp.floatBin("floatBin1"), Exp.val(100.25))); } @Test void booleanComparison() { - translateAndCompare("$.boolBin1 == true", + parseExpressionAndCompare("$.boolBin1 == true", Exp.eq(Exp.boolBin("boolBin1"), Exp.val(true))); - translateAndCompare("false == $.boolBin1", + parseExpressionAndCompare("false == $.boolBin1", Exp.eq(Exp.val(false), Exp.boolBin("boolBin1"))); - translateAndCompare("$.boolBin1 != false", + parseExpressionAndCompare("$.boolBin1 != false", Exp.ne(Exp.boolBin("boolBin1"), Exp.val(false))); } @@ -27,31 +27,31 @@ void booleanComparison() { // this can also be an expression that evaluates to a boolean result @Test void binBooleanImplicitLogicalComparison() { - translateAndCompare("$.boolBin1 and $.boolBin2", + parseExpressionAndCompare("$.boolBin1 and $.boolBin2", Exp.and(Exp.boolBin("boolBin1"), Exp.boolBin("boolBin2"))); - translateAndCompare("$.boolBin1 or $.boolBin2", + parseExpressionAndCompare("$.boolBin1 or $.boolBin2", Exp.or(Exp.boolBin("boolBin1"), Exp.boolBin("boolBin2"))); - translateAndCompare("not($.boolBin1)", + parseExpressionAndCompare("not($.boolBin1)", Exp.not(Exp.boolBin("boolBin1"))); - translateAndCompare("exclusive($.boolBin1, $.boolBin2)", + parseExpressionAndCompare("exclusive($.boolBin1, $.boolBin2)", Exp.exclusive(Exp.boolBin("boolBin1"), Exp.boolBin("boolBin2"))); } @Test void implicitDefaultIntComparison() { - translateAndCompare("$.intBin1 < $.intBin2", + parseExpressionAndCompare("$.intBin1 < $.intBin2", Exp.lt(Exp.intBin("intBin1"), Exp.intBin("intBin2"))); } @Test void secondDegreeImplicitCastingFloat() { - translateAndCompare("($.apples + $.bananas) > 10.5", + parseExpressionAndCompare("($.apples + $.bananas) > 10.5", Exp.gt(Exp.add(Exp.floatBin("apples"), Exp.floatBin("bananas")), Exp.val(10.5))); } @Test void secondDegreeComplicatedFloatFirstImplicitCastingFloat() { - translateAndCompare("($.apples + $.bananas) > 10.5 and ($.oranges + $.grapes) <= 5", + parseExpressionAndCompare("($.apples + $.bananas) > 10.5 and ($.oranges + $.grapes) <= 5", Exp.and( Exp.gt(Exp.add(Exp.floatBin("apples"), Exp.floatBin("bananas")), Exp.val(10.5)), Exp.le(Exp.add(Exp.intBin("oranges"), Exp.intBin("grapes")), Exp.val(5))) @@ -60,7 +60,7 @@ void secondDegreeComplicatedFloatFirstImplicitCastingFloat() { @Test void secondDegreeComplicatedIntFirstImplicitCastingFloat() { - translateAndCompare("($.apples + $.bananas) > 5 and ($.oranges + $.grapes) <= 10.5", + parseExpressionAndCompare("($.apples + $.bananas) > 5 and ($.oranges + $.grapes) <= 10.5", Exp.and( Exp.gt(Exp.add(Exp.intBin("apples"), Exp.intBin("bananas")), Exp.val(5)), Exp.le(Exp.add(Exp.floatBin("oranges"), Exp.floatBin("grapes")), Exp.val(10.5))) @@ -69,7 +69,7 @@ void secondDegreeComplicatedIntFirstImplicitCastingFloat() { @Test void thirdDegreeComplicatedDefaultInt() { - translateAndCompare("(($.apples + $.bananas) + $.oranges) > 10", + parseExpressionAndCompare("(($.apples + $.bananas) + $.oranges) > 10", Exp.gt( Exp.add(Exp.add(Exp.intBin("apples"), Exp.intBin("bananas")), Exp.intBin("oranges")), Exp.val(10)) @@ -78,7 +78,7 @@ void thirdDegreeComplicatedDefaultInt() { @Test void thirdDegreeComplicatedImplicitCastingFloat() { - translateAndCompare("(($.apples + $.bananas) + $.oranges) > 10.5", + parseExpressionAndCompare("(($.apples + $.bananas) + $.oranges) > 10.5", Exp.gt( Exp.add(Exp.add(Exp.floatBin("apples"), Exp.floatBin("bananas")), Exp.floatBin("oranges")), Exp.val(10.5)) @@ -87,7 +87,7 @@ void thirdDegreeComplicatedImplicitCastingFloat() { @Test void forthDegreeComplicatedDefaultInt() { - translateAndCompare("(($.apples + $.bananas) + ($.oranges + $.acai)) > 10", + parseExpressionAndCompare("(($.apples + $.bananas) + ($.oranges + $.acai)) > 10", Exp.gt( Exp.add( Exp.add(Exp.intBin("apples"), Exp.intBin("bananas")), @@ -98,7 +98,7 @@ void forthDegreeComplicatedDefaultInt() { @Test void forthDegreeComplicatedImplicitCastingFloat() { - translateAndCompare("(($.apples + $.bananas) + ($.oranges + $.acai)) > 10.5", + parseExpressionAndCompare("(($.apples + $.bananas) + ($.oranges + $.acai)) > 10.5", Exp.gt( Exp.add( Exp.add(Exp.floatBin("apples"), Exp.floatBin("bananas")), @@ -128,7 +128,7 @@ void complicatedWhenImplicitTypeInt() { ) ); - translateAndCompare("$.a == (when($.b == 1 => $.a1, $.b == 2 => $.a2, $.b == 3 => $.a3, default => $.a4+1))", + parseExpressionAndCompare("$.a == (when($.b == 1 => $.a1, $.b == 2 => $.a2, $.b == 3 => $.a3, default => $.a4+1))", expected); } @@ -154,7 +154,7 @@ void complicatedWhenImplicitTypeString() { ) ); - translateAndCompare("$.a == (when($.b == 1 => $.a1, $.b == 2 => $.a2, $.b == 3 => $.a3, default => \"hello\"))", + parseExpressionAndCompare("$.a == (when($.b == 1 => $.a1, $.b == 2 => $.a2, $.b == 3 => $.a3, default => \"hello\"))", expected); } } diff --git a/src/test/java/com/aerospike/dsl/ListExpressionsTests.java b/src/test/java/com/aerospike/dsl/expression/ListExpressionsTests.java similarity index 69% rename from src/test/java/com/aerospike/dsl/ListExpressionsTests.java rename to src/test/java/com/aerospike/dsl/expression/ListExpressionsTests.java index 45b2c93..fa6f36c 100644 --- a/src/test/java/com/aerospike/dsl/ListExpressionsTests.java +++ b/src/test/java/com/aerospike/dsl/expression/ListExpressionsTests.java @@ -1,4 +1,4 @@ -package com.aerospike.dsl; +package com.aerospike.dsl.expression; import com.aerospike.client.cdt.CTX; import com.aerospike.client.cdt.ListReturnType; @@ -9,8 +9,8 @@ import java.util.List; -import static com.aerospike.dsl.util.TestUtils.translate; -import static com.aerospike.dsl.util.TestUtils.translateAndCompare; +import static com.aerospike.dsl.util.TestUtils.parseExpression; +import static com.aerospike.dsl.util.TestUtils.parseExpressionAndCompare; import static org.assertj.core.api.Assertions.assertThatThrownBy; class ListExpressionsTests { @@ -26,10 +26,10 @@ void listByIndexInteger() { ), Exp.val(100)); // Implicit detect as Int - translateAndCompare("$.listBin1.[0] == 100", expected); - translateAndCompare("$.listBin1.[0].get(type: INT) == 100", expected); - translateAndCompare("$.listBin1.[0].get(type: INT, return: VALUE) == 100", expected); - translateAndCompare("$.listBin1.[0].asInt() == 100", expected); + parseExpressionAndCompare("$.listBin1.[0] == 100", expected); + parseExpressionAndCompare("$.listBin1.[0].get(type: INT) == 100", expected); + parseExpressionAndCompare("$.listBin1.[0].get(type: INT, return: VALUE) == 100", expected); + parseExpressionAndCompare("$.listBin1.[0].asInt() == 100", expected); } @Test @@ -43,9 +43,9 @@ void listByIndexOtherTypes() { ), Exp.val("stringVal")); // Implicit detect as string - translateAndCompare("$.listBin1.[0] == \"stringVal\"", expected); - translateAndCompare("$.listBin1.[0].get(type: STRING) == \"stringVal\"", expected); - translateAndCompare("$.listBin1.[0].get(type: STRING, return: VALUE) == \"stringVal\"", expected); + parseExpressionAndCompare("$.listBin1.[0] == \"stringVal\"", expected); + parseExpressionAndCompare("$.listBin1.[0].get(type: STRING) == \"stringVal\"", expected); + parseExpressionAndCompare("$.listBin1.[0].get(type: STRING, return: VALUE) == \"stringVal\"", expected); expected = Exp.eq( ListExp.getByIndex( @@ -56,9 +56,9 @@ void listByIndexOtherTypes() { ), Exp.val(true)); // Implicit detect as boolean - translateAndCompare("$.listBin1.[0] == true", expected); - translateAndCompare("$.listBin1.[0].get(type: BOOL) == true", expected); - translateAndCompare("$.listBin1.[0].get(type: BOOL, return: VALUE) == true", expected); + parseExpressionAndCompare("$.listBin1.[0] == true", expected); + parseExpressionAndCompare("$.listBin1.[0].get(type: BOOL) == true", expected); + parseExpressionAndCompare("$.listBin1.[0].get(type: BOOL, return: VALUE) == true", expected); } @Test @@ -70,10 +70,10 @@ void listByValue() { Exp.listBin("listBin1") ), Exp.val(100)); - translateAndCompare("$.listBin1.[=100] == 100", expected); - translateAndCompare("$.listBin1.[=100].get(type: INT) == 100", expected); - translateAndCompare("$.listBin1.[=100].get(type: INT, return: VALUE) == 100", expected); - translateAndCompare("$.listBin1.[=100].asInt() == 100", expected); + parseExpressionAndCompare("$.listBin1.[=100] == 100", expected); + parseExpressionAndCompare("$.listBin1.[=100].get(type: INT) == 100", expected); + parseExpressionAndCompare("$.listBin1.[=100].get(type: INT, return: VALUE) == 100", expected); + parseExpressionAndCompare("$.listBin1.[=100].asInt() == 100", expected); } @Test @@ -84,8 +84,8 @@ void listByValueCount() { Exp.listBin("listBin1")), Exp.val(0) ); - translateAndCompare("$.listBin1.[=100].count() > 0", expected); - translateAndCompare("$.listBin1.[=100].[].count() > 0", expected); + parseExpressionAndCompare("$.listBin1.[=100].count() > 0", expected); + parseExpressionAndCompare("$.listBin1.[=100].[].count() > 0", expected); } @Test @@ -98,10 +98,10 @@ void listByRank() { Exp.listBin("listBin1") ), Exp.val(100)); - translateAndCompare("$.listBin1.[#-1] == 100", expected); - translateAndCompare("$.listBin1.[#-1].get(type: INT) == 100", expected); - translateAndCompare("$.listBin1.[#-1].get(type: INT, return: VALUE) == 100", expected); - translateAndCompare("$.listBin1.[#-1].asInt() == 100", expected); + parseExpressionAndCompare("$.listBin1.[#-1] == 100", expected); + parseExpressionAndCompare("$.listBin1.[#-1].get(type: INT) == 100", expected); + parseExpressionAndCompare("$.listBin1.[#-1].get(type: INT, return: VALUE) == 100", expected); + parseExpressionAndCompare("$.listBin1.[#-1].asInt() == 100", expected); } @Test @@ -116,9 +116,9 @@ void listBinElementEquals_Nested() { CTX.listIndex(0) ), Exp.val(100)); - translateAndCompare("$.listBin1.[0].[0].[0] == 100", expected); - translateAndCompare("$.listBin1.[0].[0].[0].get(type: INT) == 100", expected); - translateAndCompare("$.listBin1.[0].[0].[0].get(type: INT, return: VALUE) == 100", expected); + parseExpressionAndCompare("$.listBin1.[0].[0].[0] == 100", expected); + parseExpressionAndCompare("$.listBin1.[0].[0].[0].get(type: INT) == 100", expected); + parseExpressionAndCompare("$.listBin1.[0].[0].[0].get(type: INT, return: VALUE) == 100", expected); } @Test @@ -126,10 +126,10 @@ void listSize() { Exp expected = Exp.eq( ListExp.size(Exp.listBin("listBin1")), Exp.val(1)); - translateAndCompare("$.listBin1.[].count() == 1", expected); + parseExpressionAndCompare("$.listBin1.[].count() == 1", expected); // the default behaviour for count() without List '[]' or Map '{}' designators is List - translateAndCompare("$.listBin1.count() == 1", expected); + parseExpressionAndCompare("$.listBin1.count() == 1", expected); } @Test @@ -143,10 +143,10 @@ void nestedListSize() { Exp.listBin("listBin1")) ), Exp.val(100)); - translateAndCompare("$.listBin1.[1].[].count() == 100", expected); + parseExpressionAndCompare("$.listBin1.[1].[].count() == 100", expected); // the default behaviour for count() without List '[]' or Map '{}' designators is List - translateAndCompare("$.listBin1.[1].count() == 100", expected); + parseExpressionAndCompare("$.listBin1.[1].count() == 100", expected); } @@ -162,10 +162,10 @@ void nestedListSizeWithContext() { CTX.listIndex(1)) ), Exp.val(100)); - translateAndCompare("$.listBin1.[1].[2].[].count() == 100", expected); + parseExpressionAndCompare("$.listBin1.[1].[2].[].count() == 100", expected); // the default behaviour for count() without List '[]' or Map '{}' designators is List - translateAndCompare("$.listBin1.[1].[2].count() == 100", expected); + parseExpressionAndCompare("$.listBin1.[1].[2].count() == 100", expected); } @Test @@ -179,7 +179,7 @@ void nestedLists() { CTX.listIndex(5) ), Exp.val("stringVal")); - translateAndCompare("$.listBin1.[5].[1].get(type: STRING) == \"stringVal\"", expected); + parseExpressionAndCompare("$.listBin1.[5].[1].get(type: STRING) == \"stringVal\"", expected); } @Test @@ -195,8 +195,8 @@ void nestedListsWithDifferentContextTypes() { ), Exp.val("stringVal")); // Implicit detect as String - translateAndCompare("$.listBin1.[5].[#-1] == \"stringVal\"", expected); - translateAndCompare("$.listBin1.[5].[#-1].get(type: STRING) == \"stringVal\"", expected); + parseExpressionAndCompare("$.listBin1.[5].[#-1] == \"stringVal\"", expected); + parseExpressionAndCompare("$.listBin1.[5].[#-1].get(type: STRING) == \"stringVal\"", expected); // Nested List Rank Value expected = Exp.eq( @@ -209,7 +209,7 @@ void nestedListsWithDifferentContextTypes() { ), Exp.val(200)); // Implicit detect as Int - translateAndCompare("$.listBin1.[5].[#-1].[=100] == 200", expected); + parseExpressionAndCompare("$.listBin1.[5].[#-1].[=100] == 200", expected); } @Test @@ -223,21 +223,21 @@ void listBinElementCount() { ), Exp.val(100) ); - translateAndCompare("$.listBin1.[0].count() == 100", expected); - translateAndCompare("$.listBin1.[0].[].count() == 100", expected); + parseExpressionAndCompare("$.listBin1.[0].count() == 100", expected); + parseExpressionAndCompare("$.listBin1.[0].[].count() == 100", expected); } @Test void negativeSyntaxList() { // TODO: throw meaningful exception (by ANTLR?) - assertThatThrownBy(() -> translate("$.listBin1.[stringValue] == 100")) + assertThatThrownBy(() -> parseExpression("$.listBin1.[stringValue] == 100")) .isInstanceOf(AerospikeDSLException.class); } //@Test void negativeTypeComparisonList() { // TODO: should fail? Exp is successfully created but comparing int to a string value (validations on List) - assertThatThrownBy(() -> translate("$.listBin1.[#-1].get(type: INT) == \"stringValue\"")) + assertThatThrownBy(() -> parseExpression("$.listBin1.[#-1].get(type: INT) == \"stringValue\"")) .isInstanceOf(NullPointerException.class); } @@ -248,7 +248,7 @@ void listIndexRange() { Exp.val(1), Exp.val(2), Exp.listBin("listBin1")); - translateAndCompare("$.listBin1.[1:3]", expected); + parseExpressionAndCompare("$.listBin1.[1:3]", expected); // Negative expected = ListExp.getByIndexRange( @@ -256,7 +256,7 @@ void listIndexRange() { Exp.val(-3), Exp.val(4), Exp.listBin("listBin1")); - translateAndCompare("$.listBin1.[-3:1]", expected); + parseExpressionAndCompare("$.listBin1.[-3:1]", expected); // Inverted expected = ListExp.getByIndexRange( @@ -264,14 +264,14 @@ void listIndexRange() { Exp.val(2), Exp.val(2), Exp.listBin("listBin1")); - translateAndCompare("$.listBin1.[!2:4]", expected); + parseExpressionAndCompare("$.listBin1.[!2:4]", expected); // From start till the end expected = ListExp.getByIndexRange( ListReturnType.VALUE, Exp.val(1), Exp.listBin("listBin1")); - translateAndCompare("$.listBin1.[1:]", expected); + parseExpressionAndCompare("$.listBin1.[1:]", expected); } @Test @@ -280,23 +280,23 @@ void listValueList() { ListReturnType.VALUE, Exp.val(List.of("a", "b", "c")), Exp.listBin("listBin1")); - translateAndCompare("$.listBin1.[=a,b,c]", expected); - translateAndCompare("$.listBin1.[=\"a\",\"b\",\"c\"]", expected); + parseExpressionAndCompare("$.listBin1.[=a,b,c]", expected); + parseExpressionAndCompare("$.listBin1.[=\"a\",\"b\",\"c\"]", expected); // Integer expected = ListExp.getByValueList( ListReturnType.VALUE, Exp.val(List.of(1, 2, 3)), Exp.listBin("listBin1")); - translateAndCompare("$.listBin1.[=1,2,3]", expected); + parseExpressionAndCompare("$.listBin1.[=1,2,3]", expected); // Inverted expected = ListExp.getByValueList( ListReturnType.VALUE | ListReturnType.INVERTED, Exp.val(List.of("a", "b", "c")), Exp.listBin("listBin1")); - translateAndCompare("$.listBin1.[!=a,b,c]", expected); - translateAndCompare("$.listBin1.[!=\"a\",\"b\",\"c\"]", expected); + parseExpressionAndCompare("$.listBin1.[!=a,b,c]", expected); + parseExpressionAndCompare("$.listBin1.[!=\"a\",\"b\",\"c\"]", expected); } @Test @@ -307,7 +307,7 @@ void listValueRange() { Exp.val(111), Exp.val(334), Exp.listBin("listBin1")); - translateAndCompare("$.listBin1.[=111:334]", expected); + parseExpressionAndCompare("$.listBin1.[=111:334]", expected); // Inverted expected = ListExp.getByValueRange( @@ -315,7 +315,7 @@ void listValueRange() { Exp.val(10), Exp.val(20), Exp.listBin("listBin1")); - translateAndCompare("$.listBin1.[!=10:20]", expected); + parseExpressionAndCompare("$.listBin1.[!=10:20]", expected); // From start till the end expected = ListExp.getByValueRange( @@ -323,7 +323,7 @@ void listValueRange() { Exp.val(111), null, Exp.listBin("listBin1")); - translateAndCompare("$.listBin1.[=111:]", expected); + parseExpressionAndCompare("$.listBin1.[=111:]", expected); } @Test @@ -333,7 +333,7 @@ void listRankRange() { Exp.val(0), Exp.val(3), Exp.listBin("listBin1")); - translateAndCompare("$.listBin1.[#0:3]", expected); + parseExpressionAndCompare("$.listBin1.[#0:3]", expected); // Inverted expected = ListExp.getByRankRange( @@ -341,14 +341,14 @@ void listRankRange() { Exp.val(0), Exp.val(3), Exp.listBin("listBin1")); - translateAndCompare("$.listBin1.[!#0:3]", expected); + parseExpressionAndCompare("$.listBin1.[!#0:3]", expected); // From start till the end expected = ListExp.getByRankRange( ListReturnType.VALUE, Exp.val(-3), Exp.listBin("listBin1")); - translateAndCompare("$.listBin1.[#-3:]", expected); + parseExpressionAndCompare("$.listBin1.[#-3:]", expected); // From start till the end with context expected = ListExp.getByRankRange( @@ -356,7 +356,7 @@ void listRankRange() { Exp.val(-3), Exp.listBin("listBin1"), CTX.listIndex(5)); - translateAndCompare("$.listBin1.[5].[#-3:]", expected); + parseExpressionAndCompare("$.listBin1.[5].[#-3:]", expected); } @Test @@ -367,7 +367,7 @@ void listRankRangeRelative() { Exp.val("b"), Exp.val(2), Exp.listBin("listBin1")); - translateAndCompare("$.listBin1.[#-3:-1~b]", expected); + parseExpressionAndCompare("$.listBin1.[#-3:-1~b]", expected); // Inverted expected = ListExp.getByValueRelativeRankRange( @@ -376,7 +376,7 @@ void listRankRangeRelative() { Exp.val("b"), Exp.val(2), Exp.listBin("listBin1")); - translateAndCompare("$.listBin1.[!#-3:-1~b]", expected); + parseExpressionAndCompare("$.listBin1.[!#-3:-1~b]", expected); // From start till the end expected = ListExp.getByValueRelativeRankRange( @@ -384,7 +384,7 @@ void listRankRangeRelative() { Exp.val(-3), Exp.val("b"), Exp.listBin("listBin1")); - translateAndCompare("$.listBin1.[#-3:~b]", expected); + parseExpressionAndCompare("$.listBin1.[#-3:~b]", expected); } @Test @@ -398,7 +398,7 @@ void listReturnTypes() { ), Exp.val(5)); // Implicit detect as Int - translateAndCompare("$.listBin1.[0].get(return: COUNT) == 5", expected); + parseExpressionAndCompare("$.listBin1.[0].get(return: COUNT) == 5", expected); expected = Exp.eq( ListExp.getByIndex( @@ -409,7 +409,7 @@ void listReturnTypes() { ), Exp.val(true)); // Implicit detect as Int - translateAndCompare("$.listBin1.[0].get(return: EXISTS) == true", expected); + parseExpressionAndCompare("$.listBin1.[0].get(return: EXISTS) == true", expected); expected = Exp.eq( ListExp.getByIndex( @@ -420,6 +420,6 @@ void listReturnTypes() { ), Exp.val(1)); // Implicit detect as Int - translateAndCompare("$.listBin1.[0].get(return: INDEX) == 1", expected); + parseExpressionAndCompare("$.listBin1.[0].get(return: INDEX) == 1", expected); } } diff --git a/src/test/java/com/aerospike/dsl/LogicalExpressionsTests.java b/src/test/java/com/aerospike/dsl/expression/LogicalExpressionsTests.java similarity index 67% rename from src/test/java/com/aerospike/dsl/LogicalExpressionsTests.java rename to src/test/java/com/aerospike/dsl/expression/LogicalExpressionsTests.java index c0f8f97..28a7b86 100644 --- a/src/test/java/com/aerospike/dsl/LogicalExpressionsTests.java +++ b/src/test/java/com/aerospike/dsl/expression/LogicalExpressionsTests.java @@ -1,12 +1,12 @@ -package com.aerospike.dsl; +package com.aerospike.dsl.expression; import com.aerospike.client.exp.Exp; import com.aerospike.dsl.exception.AerospikeDSLException; import org.junit.jupiter.api.Test; import org.opentest4j.AssertionFailedError; -import static com.aerospike.dsl.util.TestUtils.translate; -import static com.aerospike.dsl.util.TestUtils.translateAndCompare; +import static com.aerospike.dsl.util.TestUtils.parseExpression; +import static com.aerospike.dsl.util.TestUtils.parseExpressionAndCompare; import static org.assertj.core.api.Assertions.assertThatThrownBy; public class LogicalExpressionsTests { @@ -15,7 +15,7 @@ public class LogicalExpressionsTests { void binLogicalAndOrCombinations() { Exp expected1 = Exp.and(Exp.gt(Exp.intBin("intBin1"), Exp.val(100)), Exp.gt(Exp.intBin("intBin2"), Exp.val(100))); - translateAndCompare("$.intBin1 > 100 and $.intBin2 > 100", expected1); + parseExpressionAndCompare("$.intBin1 > 100 and $.intBin2 > 100", expected1); Exp expected2 = Exp.or( Exp.and( @@ -25,8 +25,8 @@ void binLogicalAndOrCombinations() { Exp.lt(Exp.intBin("intBin3"), Exp.val(100)) ); // TODO: what should be the default behaviour with no parentheses? - translateAndCompare("$.intBin1 > 100 and $.intBin2 > 100 or $.intBin3 < 100", expected2); - translateAndCompare("($.intBin1 > 100 and $.intBin2 > 100) or $.intBin3 < 100", expected2); + parseExpressionAndCompare("$.intBin1 > 100 and $.intBin2 > 100 or $.intBin3 < 100", expected2); + parseExpressionAndCompare("($.intBin1 > 100 and $.intBin2 > 100) or $.intBin3 < 100", expected2); Exp expected3 = Exp.and( Exp.gt(Exp.intBin("intBin1"), Exp.val(100)), @@ -35,27 +35,27 @@ void binLogicalAndOrCombinations() { Exp.lt(Exp.intBin("intBin3"), Exp.val(100)) ) ); - translateAndCompare("($.intBin1 > 100 and ($.intBin2 > 100 or $.intBin3 < 100))", expected3); + parseExpressionAndCompare("($.intBin1 > 100 and ($.intBin2 > 100 or $.intBin3 < 100))", expected3); // check that parentheses make difference - assertThatThrownBy(() -> translateAndCompare("($.intBin1 > 100 and ($.intBin2 > 100 or $.intBin3 < 100))", expected2)) + assertThatThrownBy(() -> parseExpressionAndCompare("($.intBin1 > 100 and ($.intBin2 > 100 or $.intBin3 < 100))", expected2)) .isInstanceOf(AssertionFailedError.class); } @Test void logicalNot() { - translateAndCompare("not($.keyExists())", Exp.not(Exp.keyExists())); + parseExpressionAndCompare("not($.keyExists())", Exp.not(Exp.keyExists())); } @Test void binLogicalExclusive() { - translateAndCompare("exclusive($.hand == \"hook\", $.leg == \"peg\")", + parseExpressionAndCompare("exclusive($.hand == \"hook\", $.leg == \"peg\")", Exp.exclusive( Exp.eq(Exp.stringBin("hand"), Exp.val("hook")), Exp.eq(Exp.stringBin("leg"), Exp.val("peg")))); // More than 2 expressions exclusive - translateAndCompare("exclusive($.a == \"aVal\", $.b == \"bVal\", $.c == \"cVal\", $.d == 4)", + parseExpressionAndCompare("exclusive($.a == \"aVal\", $.b == \"bVal\", $.c == \"cVal\", $.d == 4)", Exp.exclusive( Exp.eq(Exp.stringBin("a"), Exp.val("aVal")), Exp.eq(Exp.stringBin("b"), Exp.val("bVal")), @@ -66,7 +66,7 @@ void binLogicalExclusive() { //TODO: FMWK-488 //@Test void flatHierarchyAnd() { - translateAndCompare("$.intBin1 > 100 and $.intBin2 > 100 and $.intBin3 < 100", + parseExpressionAndCompare("$.intBin1 > 100 and $.intBin2 > 100 and $.intBin3 < 100", Exp.and( Exp.gt( Exp.intBin("intBin1"), @@ -81,26 +81,26 @@ void flatHierarchyAnd() { @Test void negativeSyntaxLogicalOperators() { - assertThatThrownBy(() -> translate("($.intBin1 > 100 and ($.intBin2 > 100) or")) + assertThatThrownBy(() -> parseExpression("($.intBin1 > 100 and ($.intBin2 > 100) or")) .isInstanceOf(AerospikeDSLException.class) .hasMessageContaining("Could not parse given input"); - assertThatThrownBy(() -> translate("and ($.intBin1 > 100 and ($.intBin2 > 100)")) + assertThatThrownBy(() -> parseExpression("and ($.intBin1 > 100 and ($.intBin2 > 100)")) .isInstanceOf(AerospikeDSLException.class) .hasMessageContaining("Could not parse given input"); - assertThatThrownBy(() -> translate("($.intBin1 > 100 and ($.intBin2 > 100) not")) + assertThatThrownBy(() -> parseExpression("($.intBin1 > 100 and ($.intBin2 > 100) not")) .isInstanceOf(AerospikeDSLException.class) .hasMessageContaining("Could not parse given input"); - assertThatThrownBy(() -> translate("($.intBin1 > 100 and ($.intBin2 > 100) exclusive")) + assertThatThrownBy(() -> parseExpression("($.intBin1 > 100 and ($.intBin2 > 100) exclusive")) .isInstanceOf(AerospikeDSLException.class) .hasMessageContaining("Could not parse given input"); } @Test void negativeBinLogicalExclusiveWithOneParam() { - assertThatThrownBy(() -> translateAndCompare("exclusive($.hand == \"hook\")", + assertThatThrownBy(() -> parseExpressionAndCompare("exclusive($.hand == \"hook\")", Exp.exclusive( Exp.eq(Exp.stringBin("hand"), Exp.val("hook"))))) .isInstanceOf(AerospikeDSLException.class) diff --git a/src/test/java/com/aerospike/dsl/MapAndListExpressionsTests.java b/src/test/java/com/aerospike/dsl/expression/MapAndListExpressionsTests.java similarity index 81% rename from src/test/java/com/aerospike/dsl/MapAndListExpressionsTests.java rename to src/test/java/com/aerospike/dsl/expression/MapAndListExpressionsTests.java index 795b7a3..5bff0a5 100644 --- a/src/test/java/com/aerospike/dsl/MapAndListExpressionsTests.java +++ b/src/test/java/com/aerospike/dsl/expression/MapAndListExpressionsTests.java @@ -1,4 +1,4 @@ -package com.aerospike.dsl; +package com.aerospike.dsl.expression; import com.aerospike.client.Value; import com.aerospike.client.cdt.CTX; @@ -9,7 +9,7 @@ import com.aerospike.client.exp.MapExp; import org.junit.jupiter.api.Test; -import static com.aerospike.dsl.util.TestUtils.translateAndCompare; +import static com.aerospike.dsl.util.TestUtils.parseExpressionAndCompare; public class MapAndListExpressionsTests { @@ -24,7 +24,7 @@ void listInsideAMap() { CTX.mapKey(Value.get("a")) ), Exp.val(100)); - translateAndCompare("$.mapBin1.a.[0] == 100", expected); + parseExpressionAndCompare("$.mapBin1.a.[0] == 100", expected); expected = Exp.gt( ListExp.getByIndex( @@ -35,7 +35,7 @@ void listInsideAMap() { CTX.mapKey(Value.get("a")), CTX.mapKey(Value.get("cc")) ), Exp.val(100)); - translateAndCompare("$.mapBin1.a.cc.[2].get(type: INT) > 100", expected); + parseExpressionAndCompare("$.mapBin1.a.cc.[2].get(type: INT) > 100", expected); } @Test @@ -50,7 +50,7 @@ void mapListList() { CTX.listIndex(0) ), Exp.val(100)); - translateAndCompare("$.mapBin1.a.[0].[0] == 100", expected); + parseExpressionAndCompare("$.mapBin1.a.[0].[0] == 100", expected); } @Test @@ -63,7 +63,7 @@ void mapInsideAList() { Exp.listBin("listBin1"), CTX.listIndex(2) ), Exp.val(100)); - translateAndCompare("$.listBin1.[2].cc.get(type: INT) > 100", expected); + parseExpressionAndCompare("$.listBin1.[2].cc.get(type: INT) > 100", expected); } @Test @@ -77,8 +77,8 @@ void listMapMap() { CTX.listIndex(2), CTX.mapKey(Value.get("aa")) ), Exp.val(100)); - translateAndCompare("$.listBin1.[2].aa.cc > 100", expected); - translateAndCompare("$.listBin1.[2].aa.cc.get(type: INT) > 100", expected); + parseExpressionAndCompare("$.listBin1.[2].aa.cc > 100", expected); + parseExpressionAndCompare("$.listBin1.[2].aa.cc.get(type: INT) > 100", expected); } @Test @@ -93,7 +93,7 @@ void listMapList() { CTX.mapKey(Value.get("a")) ), Exp.val(100)); - translateAndCompare("$.listBin1.[1].a.[0] == 100", expected); + parseExpressionAndCompare("$.listBin1.[1].a.[0] == 100", expected); } @Test @@ -110,8 +110,8 @@ void listMapListSize() { ) ), Exp.val(100)); - translateAndCompare("$.listBin1.[1].a.[0].count() == 100", expected); - translateAndCompare("$.listBin1.[1].a.[0].[].count() == 100", expected); + parseExpressionAndCompare("$.listBin1.[1].a.[0].count() == 100", expected); + parseExpressionAndCompare("$.listBin1.[1].a.[0].[].count() == 100", expected); } @Test @@ -125,8 +125,8 @@ void mapListMap() { CTX.mapKey(Value.get("a")), CTX.listIndex(0) ), Exp.val(100)); - translateAndCompare("$.mapBin1.a.[0].cc > 100", expected); - translateAndCompare("$.mapBin1.a.[0].cc.get(type: INT) > 100", expected); + parseExpressionAndCompare("$.mapBin1.a.[0].cc > 100", expected); + parseExpressionAndCompare("$.mapBin1.a.[0].cc.get(type: INT) > 100", expected); } //@Test diff --git a/src/test/java/com/aerospike/dsl/MapExpressionsTests.java b/src/test/java/com/aerospike/dsl/expression/MapExpressionsTests.java similarity index 69% rename from src/test/java/com/aerospike/dsl/MapExpressionsTests.java rename to src/test/java/com/aerospike/dsl/expression/MapExpressionsTests.java index 120da08..68f36fb 100644 --- a/src/test/java/com/aerospike/dsl/MapExpressionsTests.java +++ b/src/test/java/com/aerospike/dsl/expression/MapExpressionsTests.java @@ -1,4 +1,4 @@ -package com.aerospike.dsl; +package com.aerospike.dsl.expression; import com.aerospike.client.Value; import com.aerospike.client.cdt.CTX; @@ -11,7 +11,7 @@ import java.util.List; -import static com.aerospike.dsl.util.TestUtils.translateAndCompare; +import static com.aerospike.dsl.util.TestUtils.parseExpressionAndCompare; public class MapExpressionsTests { @@ -27,10 +27,10 @@ void mapOneLevelExpressions() { ), Exp.val(200)); // Implicit detect as Int - translateAndCompare("$.mapBin1.a == 200", expected); - translateAndCompare("$.mapBin1.a.get(type: INT) == 200", expected); - translateAndCompare("$.mapBin1.a.get(type: INT, return: VALUE) == 200", expected); - translateAndCompare("$.mapBin1.a.asInt() == 200", expected); + parseExpressionAndCompare("$.mapBin1.a == 200", expected); + parseExpressionAndCompare("$.mapBin1.a.get(type: INT) == 200", expected); + parseExpressionAndCompare("$.mapBin1.a.get(type: INT, return: VALUE) == 200", expected); + parseExpressionAndCompare("$.mapBin1.a.asInt() == 200", expected); // String expected = Exp.eq( @@ -42,9 +42,9 @@ void mapOneLevelExpressions() { ), Exp.val("stringVal")); // Implicit detect as String - translateAndCompare("$.mapBin1.a == \"stringVal\"", expected); - translateAndCompare("$.mapBin1.a.get(type: STRING) == \"stringVal\"", expected); - translateAndCompare("$.mapBin1.a.get(type: STRING, return: VALUE) == \"stringVal\"", expected); + parseExpressionAndCompare("$.mapBin1.a == \"stringVal\"", expected); + parseExpressionAndCompare("$.mapBin1.a.get(type: STRING) == \"stringVal\"", expected); + parseExpressionAndCompare("$.mapBin1.a.get(type: STRING, return: VALUE) == \"stringVal\"", expected); } @Test @@ -58,9 +58,9 @@ void mapNestedLevelExpressions() { CTX.mapKey(Value.get("a")), CTX.mapKey(Value.get("bb")) ), Exp.val(200)); - translateAndCompare("$.mapBin1.a.bb.bcc > 200", expected); - translateAndCompare("$.mapBin1.a.bb.bcc.get(type: INT) > 200", expected); - translateAndCompare("$.mapBin1.a.bb.bcc.get(type: INT, return: VALUE) > 200", expected); + parseExpressionAndCompare("$.mapBin1.a.bb.bcc > 200", expected); + parseExpressionAndCompare("$.mapBin1.a.bb.bcc.get(type: INT) > 200", expected); + parseExpressionAndCompare("$.mapBin1.a.bb.bcc.get(type: INT, return: VALUE) > 200", expected); // String expected = Exp.eq( @@ -73,9 +73,9 @@ void mapNestedLevelExpressions() { ), Exp.val("stringVal")); // Implicit detect as String - translateAndCompare("$.mapBin1.a.bb.bcc == \"stringVal\"", expected); - translateAndCompare("$.mapBin1.a.bb.bcc.get(type: STRING) == \"stringVal\"", expected); - translateAndCompare("$.mapBin1.a.bb.bcc.get(type: STRING, return: VALUE) == \"stringVal\"", expected); + parseExpressionAndCompare("$.mapBin1.a.bb.bcc == \"stringVal\"", expected); + parseExpressionAndCompare("$.mapBin1.a.bb.bcc.get(type: STRING) == \"stringVal\"", expected); + parseExpressionAndCompare("$.mapBin1.a.bb.bcc.get(type: STRING, return: VALUE) == \"stringVal\"", expected); } @Test @@ -89,9 +89,9 @@ void quotedStringInExpressionPath() { CTX.mapKey(Value.get("a")), CTX.mapKey(Value.get("bb")) ), Exp.val(200)); - translateAndCompare("$.mapBin1.a.bb.bcc.get(type: INT) > 200", expected); - translateAndCompare("$.mapBin1.a.\"bb\".bcc.get(type: INT) > 200", expected); - translateAndCompare("$.mapBin1.a.'bb'.bcc.get(type: INT) > 200", expected); + parseExpressionAndCompare("$.mapBin1.a.bb.bcc.get(type: INT) > 200", expected); + parseExpressionAndCompare("$.mapBin1.a.\"bb\".bcc.get(type: INT) > 200", expected); + parseExpressionAndCompare("$.mapBin1.a.'bb'.bcc.get(type: INT) > 200", expected); expected = Exp.gt( MapExp.getByKey( @@ -102,8 +102,8 @@ void quotedStringInExpressionPath() { CTX.mapKey(Value.get("127.0.0.1")) ), Exp.val(200)); - translateAndCompare("$.mapBin1.\"127.0.0.1\".bcc.get(type: INT) > 200", expected); - translateAndCompare("$.mapBin1.'127.0.0.1'.bcc.get(type: INT) > 200", expected); + parseExpressionAndCompare("$.mapBin1.\"127.0.0.1\".bcc.get(type: INT) > 200", expected); + parseExpressionAndCompare("$.mapBin1.'127.0.0.1'.bcc.get(type: INT) > 200", expected); } @Test @@ -113,7 +113,7 @@ void mapSize() { Exp.mapBin("mapBin1") ), Exp.val(200)); - translateAndCompare("$.mapBin1.{}.count() > 200", expected); + parseExpressionAndCompare("$.mapBin1.{}.count() > 200", expected); // the default behaviour for count() without List '[]' or Map '{}' designators is List Exp expected2 = Exp.gt( @@ -121,7 +121,7 @@ void mapSize() { Exp.listBin("mapBin1") ), Exp.val(200)); - translateAndCompare("$.mapBin1.count() > 200", expected2); + parseExpressionAndCompare("$.mapBin1.count() > 200", expected2); } @Test @@ -135,7 +135,7 @@ void nestedMapSize() { Exp.mapBin("mapBin1")) ), Exp.val(200)); - translateAndCompare("$.mapBin1.a.{}.count() == 200", expected); + parseExpressionAndCompare("$.mapBin1.a.{}.count() == 200", expected); // the default behaviour for count() without Map '{}' or List '[]' designators is List Exp expected2 = Exp.eq( @@ -146,7 +146,7 @@ void nestedMapSize() { Exp.mapBin("mapBin1")) ), Exp.val(200)); - translateAndCompare("$.mapBin1.a.count() == 200", expected2); + parseExpressionAndCompare("$.mapBin1.a.count() == 200", expected2); } @Test @@ -161,7 +161,7 @@ void nestedMapSizeWithContext() { CTX.mapKey(Value.get("a"))) ), Exp.val(200)); - translateAndCompare("$.mapBin1.a.b.{}.count() == 200", expected); + parseExpressionAndCompare("$.mapBin1.a.b.{}.count() == 200", expected); // the default behaviour for count() without Map '{}' or List '[]' designators is List Exp expected2 = Exp.eq( @@ -173,7 +173,7 @@ void nestedMapSizeWithContext() { CTX.mapKey(Value.get("a"))) ), Exp.val(200)); - translateAndCompare("$.mapBin1.a.b.count() == 200", expected2); + parseExpressionAndCompare("$.mapBin1.a.b.count() == 200", expected2); } @Test @@ -186,10 +186,10 @@ void mapByIndex() { Exp.mapBin("mapBin1") ), Exp.val(100)); - translateAndCompare("$.mapBin1.{0} == 100", expected); - translateAndCompare("$.mapBin1.{0}.get(type: INT) == 100", expected); - translateAndCompare("$.mapBin1.{0}.get(type: INT, return: VALUE) == 100", expected); - translateAndCompare("$.mapBin1.{0}.asInt() == 100", expected); + parseExpressionAndCompare("$.mapBin1.{0} == 100", expected); + parseExpressionAndCompare("$.mapBin1.{0}.get(type: INT) == 100", expected); + parseExpressionAndCompare("$.mapBin1.{0}.get(type: INT, return: VALUE) == 100", expected); + parseExpressionAndCompare("$.mapBin1.{0}.asInt() == 100", expected); } @Test @@ -201,10 +201,10 @@ void mapByValue() { Exp.mapBin("mapBin1") ), Exp.val(100)); - translateAndCompare("$.mapBin1.{=100} == 100", expected); - translateAndCompare("$.mapBin1.{=100}.get(type: INT) == 100", expected); - translateAndCompare("$.mapBin1.{=100}.get(type: INT, return: VALUE) == 100", expected); - translateAndCompare("$.mapBin1.{=100}.asInt() == 100", expected); + parseExpressionAndCompare("$.mapBin1.{=100} == 100", expected); + parseExpressionAndCompare("$.mapBin1.{=100}.get(type: INT) == 100", expected); + parseExpressionAndCompare("$.mapBin1.{=100}.get(type: INT, return: VALUE) == 100", expected); + parseExpressionAndCompare("$.mapBin1.{=100}.asInt() == 100", expected); } @Test @@ -215,8 +215,8 @@ void mapByValueCount() { Exp.mapBin("mapBin1")), Exp.val(0) ); - translateAndCompare("$.mapBin1.{=100}.count() > 0", expected); - translateAndCompare("$.mapBin1.{=100}.{}.count() > 0", expected); + parseExpressionAndCompare("$.mapBin1.{=100}.count() > 0", expected); + parseExpressionAndCompare("$.mapBin1.{=100}.{}.count() > 0", expected); } @Test @@ -229,10 +229,10 @@ void mapByRank() { Exp.mapBin("mapBin1") ), Exp.val(100)); - translateAndCompare("$.mapBin1.{#-1} == 100", expected); - translateAndCompare("$.mapBin1.{#-1}.get(type: INT) == 100", expected); - translateAndCompare("$.mapBin1.{#-1}.get(type: INT, return: VALUE) == 100", expected); - translateAndCompare("$.mapBin1.{#-1}.asInt() == 100", expected); + parseExpressionAndCompare("$.mapBin1.{#-1} == 100", expected); + parseExpressionAndCompare("$.mapBin1.{#-1}.get(type: INT) == 100", expected); + parseExpressionAndCompare("$.mapBin1.{#-1}.get(type: INT, return: VALUE) == 100", expected); + parseExpressionAndCompare("$.mapBin1.{#-1}.asInt() == 100", expected); } @Test @@ -246,10 +246,10 @@ void mapByRankWithNesting() { CTX.mapKey(Value.get("a")) ), Exp.val(100)); - translateAndCompare("$.mapBin1.a.{#-1} == 100", expected); - translateAndCompare("$.mapBin1.a.{#-1}.get(type: INT) == 100", expected); - translateAndCompare("$.mapBin1.a.{#-1}.get(type: INT, return: VALUE) == 100", expected); - translateAndCompare("$.mapBin1.a.{#-1}.asInt() == 100", expected); + parseExpressionAndCompare("$.mapBin1.a.{#-1} == 100", expected); + parseExpressionAndCompare("$.mapBin1.a.{#-1}.get(type: INT) == 100", expected); + parseExpressionAndCompare("$.mapBin1.a.{#-1}.get(type: INT, return: VALUE) == 100", expected); + parseExpressionAndCompare("$.mapBin1.a.{#-1}.asInt() == 100", expected); } @Test @@ -265,8 +265,8 @@ void nestedListsWithDifferentContextTypes() { ), Exp.val("stringVal")); // Implicit detect as String - translateAndCompare("$.mapBin1.{5}.{#-1} == \"stringVal\"", expected); - translateAndCompare("$.mapBin1.{5}.{#-1}.get(type: STRING) == \"stringVal\"", expected); + parseExpressionAndCompare("$.mapBin1.{5}.{#-1} == \"stringVal\"", expected); + parseExpressionAndCompare("$.mapBin1.{5}.{#-1}.get(type: STRING) == \"stringVal\"", expected); // Nested List Rank Value expected = Exp.eq( @@ -278,7 +278,7 @@ void nestedListsWithDifferentContextTypes() { CTX.mapRank(-1) ), Exp.val(200)); - translateAndCompare("$.mapBin1.{5}.{#-1}.{=100} == 200", expected); + parseExpressionAndCompare("$.mapBin1.{5}.{#-1}.{=100} == 200", expected); } @Test @@ -288,8 +288,8 @@ void mapKeyRange() { Exp.val("a"), Exp.val("c"), Exp.mapBin("mapBin1")); - translateAndCompare("$.mapBin1.{a-c}", expected); - translateAndCompare("$.mapBin1.{\"a\"-\"c\"}", expected); + parseExpressionAndCompare("$.mapBin1.{a-c}", expected); + parseExpressionAndCompare("$.mapBin1.{\"a\"-\"c\"}", expected); // Inverted expected = MapExp.getByKeyRange( @@ -297,8 +297,8 @@ void mapKeyRange() { Exp.val("a"), Exp.val("c"), Exp.mapBin("mapBin1")); - translateAndCompare("$.mapBin1.{!a-c}", expected); - translateAndCompare("$.mapBin1.{!\"a\"-\"c\"}", expected); + parseExpressionAndCompare("$.mapBin1.{!a-c}", expected); + parseExpressionAndCompare("$.mapBin1.{!\"a\"-\"c\"}", expected); // From start till the end expected = MapExp.getByKeyRange( @@ -306,8 +306,8 @@ void mapKeyRange() { Exp.val("a"), null, Exp.mapBin("mapBin1")); - translateAndCompare("$.mapBin1.{a-}", expected); - translateAndCompare("$.mapBin1.{\"a\"-}", expected); + parseExpressionAndCompare("$.mapBin1.{a-}", expected); + parseExpressionAndCompare("$.mapBin1.{\"a\"-}", expected); } @Test @@ -316,16 +316,16 @@ void mapKeyList() { MapReturnType.VALUE, Exp.val(List.of("a", "b", "c")), Exp.mapBin("mapBin1")); - translateAndCompare("$.mapBin1.{a,b,c}", expected); - translateAndCompare("$.mapBin1.{\"a\",\"b\",\"c\"}", expected); + parseExpressionAndCompare("$.mapBin1.{a,b,c}", expected); + parseExpressionAndCompare("$.mapBin1.{\"a\",\"b\",\"c\"}", expected); // Inverted expected = MapExp.getByKeyList( MapReturnType.VALUE | MapReturnType.INVERTED, Exp.val(List.of("a", "b", "c")), Exp.mapBin("mapBin1")); - translateAndCompare("$.mapBin1.{!a,b,c}", expected); - translateAndCompare("$.mapBin1.{!\"a\",\"b\",\"c\"}", expected); + parseExpressionAndCompare("$.mapBin1.{!a,b,c}", expected); + parseExpressionAndCompare("$.mapBin1.{!\"a\",\"b\",\"c\"}", expected); } @Test @@ -335,7 +335,7 @@ void mapIndexRange() { Exp.val(1), Exp.val(2), Exp.mapBin("mapBin1")); - translateAndCompare("$.mapBin1.{1:3}", expected); + parseExpressionAndCompare("$.mapBin1.{1:3}", expected); // Negative expected = MapExp.getByIndexRange( @@ -343,7 +343,7 @@ void mapIndexRange() { Exp.val(-3), Exp.val(4), Exp.mapBin("mapBin1")); - translateAndCompare("$.mapBin1.{-3:1}", expected); + parseExpressionAndCompare("$.mapBin1.{-3:1}", expected); // Inverted expected = MapExp.getByIndexRange( @@ -351,14 +351,14 @@ void mapIndexRange() { Exp.val(2), Exp.val(2), Exp.mapBin("mapBin1")); - translateAndCompare("$.mapBin1.{!2:4}", expected); + parseExpressionAndCompare("$.mapBin1.{!2:4}", expected); // From start till the end expected = MapExp.getByIndexRange( MapReturnType.VALUE, Exp.val(1), Exp.mapBin("mapBin1")); - translateAndCompare("$.mapBin1.{1:}", expected); + parseExpressionAndCompare("$.mapBin1.{1:}", expected); } @Test @@ -367,23 +367,23 @@ void mapValueList() { MapReturnType.VALUE, Exp.val(List.of("a", "b", "c")), Exp.mapBin("mapBin1")); - translateAndCompare("$.mapBin1.{=a,b,c}", expected); - translateAndCompare("$.mapBin1.{=\"a\",\"b\",\"c\"}", expected); + parseExpressionAndCompare("$.mapBin1.{=a,b,c}", expected); + parseExpressionAndCompare("$.mapBin1.{=\"a\",\"b\",\"c\"}", expected); // Integer expected = MapExp.getByValueList( MapReturnType.VALUE, Exp.val(List.of(1, 2, 3)), Exp.mapBin("mapBin1")); - translateAndCompare("$.mapBin1.{=1,2,3}", expected); + parseExpressionAndCompare("$.mapBin1.{=1,2,3}", expected); // Inverted expected = MapExp.getByValueList( MapReturnType.VALUE | MapReturnType.INVERTED, Exp.val(List.of("a", "b", "c")), Exp.mapBin("mapBin1")); - translateAndCompare("$.mapBin1.{!=a,b,c}", expected); - translateAndCompare("$.mapBin1.{!=\"a\",\"b\",\"c\"}", expected); + parseExpressionAndCompare("$.mapBin1.{!=a,b,c}", expected); + parseExpressionAndCompare("$.mapBin1.{!=\"a\",\"b\",\"c\"}", expected); } @Test @@ -393,7 +393,7 @@ void mapValueRange() { Exp.val(111), Exp.val(334), Exp.mapBin("mapBin1")); - translateAndCompare("$.mapBin1.{=111:334}", expected); + parseExpressionAndCompare("$.mapBin1.{=111:334}", expected); // Inverted expected = MapExp.getByValueRange( @@ -401,7 +401,7 @@ void mapValueRange() { Exp.val(10), Exp.val(20), Exp.mapBin("mapBin1")); - translateAndCompare("$.mapBin1.{!=10:20}", expected); + parseExpressionAndCompare("$.mapBin1.{!=10:20}", expected); // From start till the end expected = MapExp.getByValueRange( @@ -409,7 +409,7 @@ void mapValueRange() { Exp.val(111), null, Exp.mapBin("mapBin1")); - translateAndCompare("$.mapBin1.{=111:}", expected); + parseExpressionAndCompare("$.mapBin1.{=111:}", expected); } @Test @@ -419,7 +419,7 @@ void mapRankRange() { Exp.val(0), Exp.val(3), Exp.mapBin("mapBin1")); - translateAndCompare("$.mapBin1.{#0:3}", expected); + parseExpressionAndCompare("$.mapBin1.{#0:3}", expected); // Inverted expected = MapExp.getByRankRange( @@ -427,14 +427,14 @@ void mapRankRange() { Exp.val(0), Exp.val(3), Exp.mapBin("mapBin1")); - translateAndCompare("$.mapBin1.{!#0:3}", expected); + parseExpressionAndCompare("$.mapBin1.{!#0:3}", expected); // From start till the end expected = MapExp.getByRankRange( MapReturnType.VALUE, Exp.val(-3), Exp.mapBin("mapBin1")); - translateAndCompare("$.mapBin1.{#-3:}", expected); + parseExpressionAndCompare("$.mapBin1.{#-3:}", expected); // From start till the end with context expected = MapExp.getByRankRange( @@ -442,7 +442,7 @@ void mapRankRange() { Exp.val(-3), Exp.mapBin("mapBin1"), CTX.mapIndex(5)); - translateAndCompare("$.mapBin1.{5}.{#-3:}", expected); + parseExpressionAndCompare("$.mapBin1.{5}.{#-3:}", expected); } @Test @@ -453,7 +453,7 @@ void mapRankRangeRelative() { Exp.val(10), Exp.val(2), Exp.mapBin("mapBin1")); - translateAndCompare("$.mapBin1.{#-1:1~10}", expected); + parseExpressionAndCompare("$.mapBin1.{#-1:1~10}", expected); // Inverted expected = MapExp.getByValueRelativeRankRange( @@ -462,7 +462,7 @@ void mapRankRangeRelative() { Exp.val(10), Exp.val(2), Exp.mapBin("mapBin1")); - translateAndCompare("$.mapBin1.{!#-1:1~10}", expected); + parseExpressionAndCompare("$.mapBin1.{!#-1:1~10}", expected); // From start till the end expected = MapExp.getByValueRelativeRankRange( @@ -470,7 +470,7 @@ void mapRankRangeRelative() { Exp.val(-2), Exp.val(10), Exp.mapBin("mapBin1")); - translateAndCompare("$.mapBin1.{#-2:~10}", expected); + parseExpressionAndCompare("$.mapBin1.{#-2:~10}", expected); } @Test @@ -481,7 +481,7 @@ void mapIndexRangeRelative() { Exp.val(0), Exp.val(1), Exp.mapBin("mapBin1")); - translateAndCompare("$.mapBin1.{0:1~a}", expected); + parseExpressionAndCompare("$.mapBin1.{0:1~a}", expected); // Inverted expected = MapExp.getByKeyRelativeIndexRange( @@ -490,7 +490,7 @@ void mapIndexRangeRelative() { Exp.val(0), Exp.val(1), Exp.mapBin("mapBin1")); - translateAndCompare("$.mapBin1.{!0:1~a}", expected); + parseExpressionAndCompare("$.mapBin1.{!0:1~a}", expected); // From start till the end expected = MapExp.getByKeyRelativeIndexRange( @@ -498,7 +498,7 @@ void mapIndexRangeRelative() { Exp.val("a"), Exp.val(0), Exp.mapBin("mapBin1")); - translateAndCompare("$.mapBin1.{0:~a}", expected); + parseExpressionAndCompare("$.mapBin1.{0:~a}", expected); } @Test @@ -512,7 +512,7 @@ void mapReturnTypes() { ), Exp.val(5)); // Implicit detect as Int - translateAndCompare("$.mapBin1.a.get(type: INT, return: COUNT) == 5", expected); + parseExpressionAndCompare("$.mapBin1.a.get(type: INT, return: COUNT) == 5", expected); expected = MapExp.getByKey( MapReturnType.ORDERED_MAP, @@ -521,7 +521,7 @@ void mapReturnTypes() { Exp.mapBin("mapBin1") ); // Implicit detect as Int - translateAndCompare("$.mapBin1.a.get(return: ORDERED_MAP)", expected); + parseExpressionAndCompare("$.mapBin1.a.get(return: ORDERED_MAP)", expected); expected = Exp.eq( MapExp.getByKey( @@ -532,6 +532,6 @@ void mapReturnTypes() { ), Exp.val(5)); // Implicit detect as Int - translateAndCompare("$.mapBin1.a.get(type: INT, return: RANK) == 5", expected); + parseExpressionAndCompare("$.mapBin1.a.get(type: INT, return: RANK) == 5", expected); } } diff --git a/src/test/java/com/aerospike/dsl/RecordMetadataTests.java b/src/test/java/com/aerospike/dsl/expression/RecordMetadataTests.java similarity index 80% rename from src/test/java/com/aerospike/dsl/RecordMetadataTests.java rename to src/test/java/com/aerospike/dsl/expression/RecordMetadataTests.java index 1c2a398..ca59c28 100644 --- a/src/test/java/com/aerospike/dsl/RecordMetadataTests.java +++ b/src/test/java/com/aerospike/dsl/expression/RecordMetadataTests.java @@ -1,11 +1,11 @@ -package com.aerospike.dsl; +package com.aerospike.dsl.expression; import com.aerospike.client.exp.Exp; import com.aerospike.dsl.exception.AerospikeDSLException; import org.junit.jupiter.api.Test; -import static com.aerospike.dsl.util.TestUtils.translate; -import static com.aerospike.dsl.util.TestUtils.translateAndCompare; +import static com.aerospike.dsl.util.TestUtils.parseExpression; +import static com.aerospike.dsl.util.TestUtils.parseExpressionAndCompare; import static org.assertj.core.api.Assertions.assertThatThrownBy; class RecordMetadataTests { @@ -15,7 +15,7 @@ void deviceSize() { // Expression to find records that occupy more than 1 MiB of storage space String input = "$.deviceSize() > 1048576"; Exp expected = Exp.gt(Exp.deviceSize(), Exp.val(1024 * 1024)); - translateAndCompare(input, expected); + parseExpressionAndCompare(input, expected); } @Test @@ -23,7 +23,7 @@ void memorySize() { // Expression to find records that occupy more than 1 MiB of memory String input = "$.memorySize() > 1048576"; Exp expected = Exp.gt(Exp.memorySize(), Exp.val(1024 * 1024)); - translateAndCompare(input, expected); + parseExpressionAndCompare(input, expected); } @Test @@ -31,7 +31,7 @@ void recordSize() { // Expression to find records that occupy more than 1 MiB of memory String input = "$.recordSize() > 1048576"; Exp expected = Exp.gt(Exp.recordSize(), Exp.val(1024 * 1024)); - translateAndCompare(input, expected); + parseExpressionAndCompare(input, expected); } @Test @@ -39,12 +39,12 @@ void digestModulo() { // Expression to find records where digest mod 3 equals 0 String input = "$.digestModulo(3) == 0"; Exp expected = Exp.eq(Exp.digestModulo(3), Exp.val(0)); - translateAndCompare(input, expected); + parseExpressionAndCompare(input, expected); // Expression to find records where digest mod 3 equals the value stored in the bin called "digestModulo" String input2 = "$.digestModulo(3) == $.digestModulo"; Exp expected2 = Exp.eq(Exp.digestModulo(3), Exp.intBin("digestModulo")); - translateAndCompare(input2, expected2); + parseExpressionAndCompare(input2, expected2); } @Test @@ -52,7 +52,7 @@ void isTombstone() { // Expression to find records that are tombstones String input = "$.isTombstone()"; Exp expected = Exp.isTombstone(); - translateAndCompare(input, expected); + parseExpressionAndCompare(input, expected); } @Test @@ -60,7 +60,7 @@ void keyExists() { // Expression to find records that has a stored key String input = "$.keyExists()"; Exp expected = Exp.keyExists(); - translateAndCompare(input, expected); + parseExpressionAndCompare(input, expected); } // Comparing Metadata to a Bin @@ -69,12 +69,12 @@ void lastUpdate() { // Expression to find records where the last-update-time is less than bin 'updateBy' String inputMetadataLeft = "$.lastUpdate() < $.updateBy"; Exp expectedLeft = Exp.lt(Exp.lastUpdate(), Exp.intBin("updateBy")); - translateAndCompare(inputMetadataLeft, expectedLeft); + parseExpressionAndCompare(inputMetadataLeft, expectedLeft); // Expression to find records where the last-update-time is less than bin 'updateBy' String inputMetadataRight = "$.updateBy > $.lastUpdate()"; Exp expectedRight = Exp.gt(Exp.intBin("updateBy"), Exp.lastUpdate()); - translateAndCompare(inputMetadataRight, expectedRight); + parseExpressionAndCompare(inputMetadataRight, expectedRight); } @Test @@ -82,22 +82,22 @@ void sinceUpdate() { // Expression to find records that were updated within the last 2 hours String input = "$.sinceUpdate() < 7200000"; Exp expected = Exp.lt(Exp.sinceUpdate(), Exp.val(2 * 60 * 60 * 1000)); - translateAndCompare(input, expected); + parseExpressionAndCompare(input, expected); // Expression to find records that were update within the value stored in the bin called "intBin" String input2 = "$.sinceUpdate() < $.intBin"; Exp expected2 = Exp.lt(Exp.sinceUpdate(), Exp.intBin("intBin")); - translateAndCompare(input2, expected2); + parseExpressionAndCompare(input2, expected2); // Expression to find records that were updated within the value stored in the bin called "sinceUpdate" String input3 = "$.sinceUpdate() < $.sinceUpdate"; Exp expected3 = Exp.lt(Exp.sinceUpdate(), Exp.intBin("sinceUpdate")); - translateAndCompare(input3, expected3); + parseExpressionAndCompare(input3, expected3); // Expression to find records that were updated within the value stored in the bin called "sinceUpdate" String input4 = "$.sinceUpdate > $.sinceUpdate()"; Exp expected4 = Exp.gt(Exp.intBin("sinceUpdate"), Exp.sinceUpdate()); - translateAndCompare(input4, expected4); + parseExpressionAndCompare(input4, expected4); } @Test @@ -108,12 +108,12 @@ void setName() { Exp.eq(Exp.setName(), Exp.val("groupA")), Exp.eq(Exp.setName(), Exp.val("groupB")) ); - translateAndCompare(input, expected); + parseExpressionAndCompare(input, expected); // set name compared with String Bin input = "$.mySetBin == $.setName()"; expected = Exp.eq(Exp.stringBin("mySetBin"), Exp.setName()); - translateAndCompare(input, expected); + parseExpressionAndCompare(input, expected); } @Test @@ -121,13 +121,13 @@ void ttl() { // Expression to find records that will expire within 24 hours String input = "$.ttl() <= 86400"; Exp expected = Exp.le(Exp.ttl(), Exp.val(24 * 60 * 60)); - translateAndCompare(input, expected); + parseExpressionAndCompare(input, expected); } //@Test void negativeTtlAsDifferentType() { // TODO: should be supported when adding operator + metadata validations (requires a refactor) - assertThatThrownBy(() -> translate("$.ttl() == true")) + assertThatThrownBy(() -> parseExpression("$.ttl() == true")) .isInstanceOf(AerospikeDSLException.class) .hasMessageContaining("Expecting non-bin operand, got BOOL_OPERAND"); } @@ -137,7 +137,7 @@ void voidTime() { // Expression to find records where the void-time is set to 'never expire' String input = "$.voidTime() == -1"; Exp expected = Exp.eq(Exp.voidTime(), Exp.val(-1)); - translateAndCompare(input, expected); + parseExpressionAndCompare(input, expected); } @Test @@ -148,7 +148,7 @@ void metadataWithLogicalOperatorsExpressions() { Exp.gt(Exp.deviceSize(), Exp.val(1024)), Exp.lt(Exp.ttl(), Exp.val(300)) ); - translateAndCompare(input, expected); + parseExpressionAndCompare(input, expected); // test OR String input2 = "$.deviceSize() > 1024 or $.ttl() < 300"; @@ -156,7 +156,7 @@ void metadataWithLogicalOperatorsExpressions() { Exp.gt(Exp.deviceSize(), Exp.val(1024)), Exp.lt(Exp.ttl(), Exp.val(300)) ); - translateAndCompare(input2, expected2); + parseExpressionAndCompare(input2, expected2); } @Test @@ -166,13 +166,13 @@ void metadataAsExpressionWithLogicalOperator() { Exp.isTombstone(), Exp.lt(Exp.ttl(), Exp.val(300)) ); - translateAndCompare(input, expected); + parseExpressionAndCompare(input, expected); input = "$.ttl() < 300 or $.keyExists()"; expected = Exp.or( Exp.lt(Exp.ttl(), Exp.val(300)), Exp.keyExists() ); - translateAndCompare(input, expected); + parseExpressionAndCompare(input, expected); } } diff --git a/src/test/java/com/aerospike/dsl/filter/ArithmeticFiltersTests.java b/src/test/java/com/aerospike/dsl/filter/ArithmeticFiltersTests.java new file mode 100644 index 0000000..c5e3cef --- /dev/null +++ b/src/test/java/com/aerospike/dsl/filter/ArithmeticFiltersTests.java @@ -0,0 +1,376 @@ +package com.aerospike.dsl.filter; + +import com.aerospike.client.query.Filter; +import com.aerospike.dsl.exception.AerospikeDSLException; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static com.aerospike.dsl.util.TestUtils.parseFilters; +import static com.aerospike.dsl.util.TestUtils.parseFiltersAndCompare; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class ArithmeticFiltersTests { + + @Test + void add() { + assertThatThrownBy(() -> parseFilters("($.apples + $.bananas) > 10")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + + parseFiltersAndCompare("($.apples + 5) > 10", + List.of(Filter.range("apples", 10 - 5 + 1, Long.MAX_VALUE))); + parseFiltersAndCompare("($.apples + 5) >= 10", + List.of(Filter.range("apples", 10 - 5, Long.MAX_VALUE))); + parseFiltersAndCompare("($.apples + 5) < 10", + List.of(Filter.range("apples", Long.MIN_VALUE, 10 - 5 - 1))); + parseFiltersAndCompare("($.apples + 5) <= 10", + List.of(Filter.range("apples", Long.MIN_VALUE, 10 - 5))); + + parseFiltersAndCompare("(9 + $.bananas) > 10", + List.of(Filter.range("bananas", 10 - 9 + 1, Long.MAX_VALUE))); + parseFiltersAndCompare("(9 + $.bananas) >= 10", + List.of(Filter.range("bananas", 10 - 9, Long.MAX_VALUE))); + parseFiltersAndCompare("(9 + $.bananas) < 10", + List.of(Filter.range("bananas", Long.MIN_VALUE, 10 - 9 - 1))); + parseFiltersAndCompare("(9 + $.bananas) <= 10", + List.of(Filter.range("bananas", Long.MIN_VALUE, 10 - 9))); + + assertThatThrownBy(() -> parseFilters("(5.2 + $.bananas) > 10.2")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + + assertThatThrownBy(() -> parseFilters("($.apples + $.bananas + 5) > 10")) + .isInstanceOf(AerospikeDSLException.class) // not supported by the current grammar + .hasMessageContaining("Could not parse given input, wrong syntax"); + } + + @Test + void sub() { + assertThatThrownBy(() -> parseFilters("($.apples - $.bananas) > 10")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + + parseFiltersAndCompare("($.apples - 5) > 10", + List.of(Filter.range("apples", 10 + 5 + 1, Long.MAX_VALUE))); + parseFiltersAndCompare("($.apples - 5) >= 10", + List.of(Filter.range("apples", 10 + 5, Long.MAX_VALUE))); + parseFiltersAndCompare("($.apples - 5) < 10", + List.of(Filter.range("apples", Long.MIN_VALUE, 10 + 5 - 1))); + parseFiltersAndCompare("($.apples - 5) <= 10", + List.of(Filter.range("apples", Long.MIN_VALUE, 10 + 5))); + + parseFiltersAndCompare("($.apples - 5) > -10", + List.of(Filter.range("apples", -10 + 5 + 1, Long.MAX_VALUE))); + parseFiltersAndCompare("($.apples - 5) >= -10", + List.of(Filter.range("apples", -10 + 5, Long.MAX_VALUE))); + parseFiltersAndCompare("($.apples - 5) < -10", + List.of(Filter.range("apples", Long.MIN_VALUE, -10 + 5 - 1))); + parseFiltersAndCompare("($.apples - 5) <= -10", + List.of(Filter.range("apples", Long.MIN_VALUE, -10 + 5))); + + parseFiltersAndCompare("(9 - $.bananas) > 10", + List.of(Filter.range("bananas", Long.MIN_VALUE, 9 - 10 - 1))); + parseFiltersAndCompare("(9 - $.bananas) >= 10", + List.of(Filter.range("bananas", Long.MIN_VALUE, 9 - 10))); + parseFiltersAndCompare("(9 - $.bananas) < 10", + List.of(Filter.range("bananas", 9 - 10 + 1, Long.MAX_VALUE))); + parseFiltersAndCompare("(9 - $.bananas) <= 10", + List.of(Filter.range("bananas", 9 - 10, Long.MAX_VALUE))); + + assertThatThrownBy(() -> parseFilters("($.apples - $.bananas) > 10")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + assertThatThrownBy(() -> parseFilters("($.apples - $.bananas - 5) > 10")) + .isInstanceOf(AerospikeDSLException.class) // not supported by the current grammar + .hasMessageContaining("Could not parse given input, wrong syntax"); + } + + @Test + void mul() { + assertThatThrownBy(() -> parseFilters("($.apples * $.bananas) > 10")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + + parseFiltersAndCompare("($.apples * 5) > 10", + List.of(Filter.range("apples", 10 / 5 + 1, Long.MAX_VALUE))); + parseFiltersAndCompare("($.apples * 5) >= 10", + List.of(Filter.range("apples", 10 / 5, Long.MAX_VALUE))); + parseFiltersAndCompare("($.apples * 5) < 10", + List.of(Filter.range("apples", Long.MIN_VALUE, 10 / 5 - 1))); + parseFiltersAndCompare("($.apples * 5) <= 10", + List.of(Filter.range("apples", Long.MIN_VALUE, 10 / 5))); + + parseFiltersAndCompare("(9 * $.bananas) > 10", + List.of(Filter.range("bananas", 10 / 9 + 1, Long.MAX_VALUE))); + parseFiltersAndCompare("(9 * $.bananas) >= 10", + List.of(Filter.range("bananas", 10 / 9, Long.MAX_VALUE))); + parseFiltersAndCompare("(9 * $.bananas) < 10", + List.of(Filter.range("bananas", Long.MIN_VALUE, 10 / 9))); + parseFiltersAndCompare("(9 * $.bananas) <= 10", + List.of(Filter.range("bananas", Long.MIN_VALUE, 10 / 9))); + + parseFiltersAndCompare("($.apples * -5) > 10", + List.of(Filter.range("apples", Long.MIN_VALUE, 10 / -5 - 1))); + parseFiltersAndCompare("($.apples * -5) >= 10", + List.of(Filter.range("apples", Long.MIN_VALUE, 10 / -5))); + parseFiltersAndCompare("($.apples * -5) < 10", + List.of(Filter.range("apples", 10 / -5 + 1, Long.MAX_VALUE))); + parseFiltersAndCompare("($.apples * -5) <= 10", + List.of(Filter.range("apples", 10 / -5, Long.MAX_VALUE))); + + assertThatThrownBy(() -> parseFilters("(0 * $.bananas) > 10")) + .isInstanceOf(AerospikeDSLException.class) // not supported by the current grammar + .hasMessageContaining("Cannot divide by zero"); + + parseFiltersAndCompare("(9 * $.bananas) > 0", + List.of(Filter.range("bananas", 0 / 9 + 1, Long.MAX_VALUE))); + + assertThatThrownBy(() -> parseFilters("($.apples * $.bananas - 5) > 10")) + .isInstanceOf(AerospikeDSLException.class) // not supported by the current grammar + .hasMessageContaining("Could not parse given input, wrong syntax"); + } + + @Test + void div_twoBins() { + assertThatThrownBy(() -> parseFilters("($.apples / $.bananas) <= 10")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + } + + @Test + void div_binIsDivided_leftNumberIsLarger() { + parseFiltersAndCompare("($.apples / 50) > 10", + List.of(Filter.range("apples", 50 * 10 + 1, Long.MAX_VALUE))); // [501, 2^63 - 1] + parseFiltersAndCompare("($.apples / 50) >= 10", + List.of(Filter.range("apples", 50 * 10, Long.MAX_VALUE))); // [500, 2^63 - 1] + parseFiltersAndCompare("($.apples / 50) < 10", + List.of(Filter.range("apples", Long.MIN_VALUE, 50 * 10 - 1))); // [-2^63, 499] + parseFiltersAndCompare("($.apples / 50) <= 10", + List.of(Filter.range("apples", Long.MIN_VALUE, 50 * 10))); // [-2^63, 500] + + parseFiltersAndCompare("($.apples / -50) > 10", + List.of(Filter.range("apples", Long.MIN_VALUE, -50 * 10 - 1))); // [-2^63, -501] + parseFiltersAndCompare("($.apples / -50) >= 10", + List.of(Filter.range("apples", Long.MIN_VALUE, -50 * 10))); // [-2^63, -500] + parseFiltersAndCompare("($.apples / -50) < 10", + List.of(Filter.range("apples", -50 * 10 + 1, Long.MAX_VALUE))); // [-499, 2^63 - 1] + parseFiltersAndCompare("($.apples / -50) <= 10", + List.of(Filter.range("apples", -50 * 10, Long.MAX_VALUE))); // [-500, 2^63 - 1] + + parseFiltersAndCompare("($.apples / 50) > -10", + List.of(Filter.range("apples", 50 * -10 + 1, Long.MAX_VALUE))); // [-499, 2^63 - 1] + parseFiltersAndCompare("($.apples / 50) >= -10", + List.of(Filter.range("apples", 50 * -10, Long.MAX_VALUE))); // [-500, 2^63 - 1] + parseFiltersAndCompare("($.apples / 50) < -10", + List.of(Filter.range("apples", Long.MIN_VALUE, 50 * -10 - 1))); // [-2^63, -501] + parseFiltersAndCompare("($.apples / 50) <= -10", + List.of(Filter.range("apples", Long.MIN_VALUE, 50 * -10))); // [-2^63, -500] + + parseFiltersAndCompare("($.apples / -50) > -10", + List.of(Filter.range("apples", Long.MIN_VALUE, -50 * -10 - 1))); // [-2^63, 499] + parseFiltersAndCompare("($.apples / -50) >= -10", + List.of(Filter.range("apples", Long.MIN_VALUE, -10 * -50))); // [-2^63, 500] + parseFiltersAndCompare("($.apples / -50) < -10", + List.of(Filter.range("apples", -10 * -50 + 1, Long.MAX_VALUE))); // [501, 2^63 - 1] + parseFiltersAndCompare("($.apples / -50) <= -10", + List.of(Filter.range("apples", -10 * -50, Long.MAX_VALUE))); // [500, 2^63 - 1] + } + + @Test + void div_binIsDivided_leftNumberIsSmaller() { + parseFiltersAndCompare("($.apples / 5) > 10", + List.of(Filter.range("apples", 5 * 10 + 1, Long.MAX_VALUE))); // [51, 2^63 - 1] + parseFiltersAndCompare("($.apples / 5) >= 10", + List.of(Filter.range("apples", 5 * 10, Long.MAX_VALUE))); // [50, 2^63 - 1] + parseFiltersAndCompare("($.apples / 5) < 10", + List.of(Filter.range("apples", Long.MIN_VALUE, 5 * 10 - 1))); // [-2^63, 49] + parseFiltersAndCompare("($.apples / 5) <= 10", + List.of(Filter.range("apples", Long.MIN_VALUE, 5 * 10))); // [-2^63, 50] + + parseFiltersAndCompare("($.apples / -5) > 10", + List.of(Filter.range("apples", Long.MIN_VALUE, -5 * 10 - 1))); // [-2^63, -51] + parseFiltersAndCompare("($.apples / -5) >= 10", + List.of(Filter.range("apples", Long.MIN_VALUE, -5 * 10))); // [-2^63, -50] + parseFiltersAndCompare("($.apples / -5) < 10", + List.of(Filter.range("apples", -5 * 10 + 1, Long.MAX_VALUE))); // [-49, 2^63 - 1] + parseFiltersAndCompare("($.apples / -5) <= 10", + List.of(Filter.range("apples", -5 * 10, Long.MAX_VALUE))); // [-50, 2^63 - 1] + + parseFiltersAndCompare("($.apples / 5) > -10", + List.of(Filter.range("apples", 5 * -10 + 1, Long.MAX_VALUE))); // [-49, 2^63 - 1] + parseFiltersAndCompare("($.apples / 5) >= -10", + List.of(Filter.range("apples", 5 * -10, Long.MAX_VALUE))); // [-50, 2^63 - 1] + parseFiltersAndCompare("($.apples / 5) < -10", + List.of(Filter.range("apples", Long.MIN_VALUE, 5 * -10 - 1))); // [-2^63, -51] + parseFiltersAndCompare("($.apples / 5) <= -10", + List.of(Filter.range("apples", Long.MIN_VALUE, 5 * -10))); // [-2^63, -50] + + parseFiltersAndCompare("($.apples / -5) > -10", + List.of(Filter.range("apples", Long.MIN_VALUE, -5 * -10 - 1))); // [-2^63, 49] + parseFiltersAndCompare("($.apples / -5) >= -10", + List.of(Filter.range("apples", Long.MIN_VALUE, -10 * -5))); // [-2^63, 50] + parseFiltersAndCompare("($.apples / -5) < -10", + List.of(Filter.range("apples", -10 * -5 + 1, Long.MAX_VALUE))); // [51, 2^63 - 1] + parseFiltersAndCompare("($.apples / -5) <= -10", + List.of(Filter.range("apples", -10 * -5, Long.MAX_VALUE))); // [50, 2^63 - 1] + } + + @Test + void div_binIsDivided_leftNumberEqualsRight() { + parseFiltersAndCompare("($.apples / 5) > 5", + List.of(Filter.range("apples", 5 * 5 + 1, Long.MAX_VALUE))); // [26, 2^63 - 1] + parseFiltersAndCompare("($.apples / 5) >= 5", + List.of(Filter.range("apples", 5 * 5, Long.MAX_VALUE))); // [25, 2^63 - 1] + parseFiltersAndCompare("($.apples / 5) < 5", + List.of(Filter.range("apples", Long.MIN_VALUE, 5 * 5 - 1))); // [-2^63, 24] + parseFiltersAndCompare("($.apples / 5) <= 5", + List.of(Filter.range("apples", Long.MIN_VALUE, 5 * 5))); // [-2^63, 25] + + parseFiltersAndCompare("($.apples / -5) > -5", + List.of(Filter.range("apples", Long.MIN_VALUE, -5 * -5 - 1))); // [-2^63, 24] + parseFiltersAndCompare("($.apples / -5) >= -5", + List.of(Filter.range("apples", Long.MIN_VALUE, -5 * -5))); // [-2^63, 25] + parseFiltersAndCompare("($.apples / -5) < -5", + List.of(Filter.range("apples", -5 * -5 + 1, Long.MAX_VALUE))); // [26, 2^63 - 1] + parseFiltersAndCompare("($.apples / -5) <= -5", + List.of(Filter.range("apples", -5 * -5, Long.MAX_VALUE))); // [25, 2^63 - 1] + } + + @Test + void div_binIsDivisor_leftNumberIsLarger() { + parseFiltersAndCompare("(90 / $.bananas) > 10", + List.of(Filter.range("bananas", 1, 90 / 10 - 1))); // [1,8] + parseFiltersAndCompare("(90 / $.bananas) >= 10", + List.of(Filter.range("bananas", 1, 90 / 10))); // [1,9] + assertThatThrownBy(() -> parseFilters("(90 / $.bananas) < 10")) // maximal range is all numbers + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + assertThatThrownBy(() -> parseFilters("(90 / $.bananas) <= 10")) // maximal range is all numbers + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + + assertThatThrownBy(() -> parseFilters("(90 / $.bananas) > -10")) // maximal range is all numbers + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + assertThatThrownBy(() -> parseFilters("(90 / $.bananas) >= -10")) // maximal range is all numbers + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + parseFiltersAndCompare("(90 / $.bananas) < -10", + List.of(Filter.range("bananas", 90 / -10 + 1, -1))); // [-8, -1] + parseFiltersAndCompare("(90 / $.bananas) <= -10", + List.of(Filter.range("bananas", 90 / -10, -1))); // [-8, -1] + + parseFiltersAndCompare("(-90 / $.bananas) > 10", + List.of(Filter.range("bananas", -90 / 10 + 1, -1))); // [-8, -1] + parseFiltersAndCompare("(90 / $.bananas) >= 10", + List.of(Filter.range("bananas", 1, 90 / 10))); // [1,9] + assertThatThrownBy(() -> parseFilters("(-90 / $.bananas) < 10")) // maximal range is all numbers + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + assertThatThrownBy(() -> parseFilters("(-90 / $.bananas) <= 10")) // maximal range is all numbers + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + + assertThatThrownBy(() -> parseFilters("(-90 / $.bananas) > -10")) // maximal range is all numbers + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + assertThatThrownBy(() -> parseFilters("(-90 / $.bananas) >= -10")) // maximal range is all numbers + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + parseFiltersAndCompare("(-90 / $.bananas) < -10", + List.of(Filter.range("bananas", 1L, -90 / -10 - 1))); // [1, 8] + parseFiltersAndCompare("(-90 / $.bananas) <= -10", + List.of(Filter.range("bananas", 1L, -90 / -10))); // [1, 9] + } + + @Test + void div_binIsDivisor_leftNumberIsSmaller() { + assertThatThrownBy(() -> parseFilters("(9 / $.bananas) > 10")) // no integer numbers + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + assertThatThrownBy(() -> parseFilters("(9 / $.bananas) >= 10")) // no integer numbers + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + assertThatThrownBy(() -> parseFilters("(9 / $.bananas) < 10")) // maximal range is all numbers + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + assertThatThrownBy(() -> parseFilters("(9 / $.bananas) <= 10")) // maximal range is all numbers + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + + assertThatThrownBy(() -> parseFilters("(9 / $.bananas) > -10")) // maximal range is all numbers + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + assertThatThrownBy(() -> parseFilters("(9 / $.bananas) >= -10")) // maximal range is all numbers + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + assertThatThrownBy(() -> parseFilters("(9 / $.bananas) < -10")) // no integer numbers + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + assertThatThrownBy(() -> parseFilters("(9 / $.bananas) <= -10")) // no integer numbers + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + + assertThatThrownBy(() -> parseFilters("(-9 / $.bananas) > 10")) // no integer numbers + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + assertThatThrownBy(() -> parseFilters("(-9 / $.bananas) >= 10")) // no integer numbers + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + assertThatThrownBy(() -> parseFilters("(-9 / $.bananas) < 10")) // maximal range is all numbers + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + assertThatThrownBy(() -> parseFilters("(-9 / $.bananas) <= 10")) // maximal range is all numbers + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + + assertThatThrownBy(() -> parseFilters("(-9 / $.bananas) > -10")) // maximal range is all numbers + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + assertThatThrownBy(() -> parseFilters("(-9 / $.bananas) >= -10")) // maximal range is all numbers + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + assertThatThrownBy(() -> parseFilters("(-9 / $.bananas) < -10")) // no integer numbers + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + assertThatThrownBy(() -> parseFilters("(-9 / $.bananas) <= -10")) // no integer numbers + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + + assertThatThrownBy(() -> parseFilters("(0 / $.bananas) > 10")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + + assertThatThrownBy(() -> parseFilters("(9 / $.bananas) > 0")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("Cannot divide by zero"); + } + + @Test + void div_binIsDivisor_leftNumberEqualsRight() { + assertThatThrownBy(() -> parseFilters("(90 / $.bananas) > 90")) // no integer numbers + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + parseFiltersAndCompare("(90 / $.bananas) >= 90", + List.of(Filter.range("bananas", 90 / 90, 90 / 90))); // [1, 1] + assertThatThrownBy(() -> parseFilters("(90 / $.bananas) < 90")) // maximal range is all numbers + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + assertThatThrownBy(() -> parseFilters("(90 / $.bananas) <= 90")) // maximal range is all numbers + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + + assertThatThrownBy(() -> parseFilters("(-90 / $.bananas) > -90")) // maximal range is all numbers + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + assertThatThrownBy(() -> parseFilters("(-90 / $.bananas) >= -90")) // maximal range is all numbers + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + assertThatThrownBy(() -> parseFilters("(-90 / $.bananas) < -90")) // no integer numbers + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + parseFiltersAndCompare("(-90 / $.bananas) <= -90", + List.of(Filter.range("bananas", 1L, 90 / 90))); // [1, 1] + } +} diff --git a/src/test/java/com/aerospike/dsl/filter/BinFiltersTests.java b/src/test/java/com/aerospike/dsl/filter/BinFiltersTests.java new file mode 100644 index 0000000..1161383 --- /dev/null +++ b/src/test/java/com/aerospike/dsl/filter/BinFiltersTests.java @@ -0,0 +1,105 @@ +package com.aerospike.dsl.filter; + +import com.aerospike.client.query.Filter; +import com.aerospike.dsl.exception.AerospikeDSLException; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static com.aerospike.dsl.util.TestUtils.parseFilters; +import static com.aerospike.dsl.util.TestUtils.parseFiltersAndCompare; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class BinFiltersTests { + + @Test + void binGT() { + parseFiltersAndCompare("$.intBin1 > 100", + List.of(Filter.range("intBin1", 101, Long.MAX_VALUE))); + parseFiltersAndCompare("$.intBin1 > -100", + List.of(Filter.range("intBin1", -99, Long.MAX_VALUE))); + + assertThatThrownBy(() -> parseFilters("$.stringBin1 > 'text'")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("Operand type not supported"); + assertThatThrownBy(() -> parseFilters("$.stringBin1 > \"text\"")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("Operand type not supported"); + + // "$.intBin1 > 100" and "100 < $.intBin1" represent identical Filters + parseFiltersAndCompare("100 < $.intBin1", + List.of(Filter.range("intBin1", 101, Long.MAX_VALUE))); + + assertThatThrownBy(() -> parseFilters("'text' > $.stringBin1")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("Operand type not supported"); + assertThatThrownBy(() -> parseFilters("\"text\" > $.stringBin1")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("Operand type not supported"); + } + + @Test + void binGE() { + parseFiltersAndCompare("$.intBin1 >= 100", + List.of(Filter.range("intBin1", 100, Long.MAX_VALUE))); + + // "$.intBin1 >= 100" and "100 <= $.intBin1" represent identical Filters + parseFiltersAndCompare("100 <= $.intBin1", + List.of(Filter.range("intBin1", 100, Long.MAX_VALUE))); + } + + @Test + void binLT() { + parseFiltersAndCompare("$.intBin1 < 100", + List.of(Filter.range("intBin1", Long.MIN_VALUE, 99))); + + parseFiltersAndCompare("100 > $.intBin1", + List.of(Filter.range("intBin1", Long.MIN_VALUE, 99))); + } + + @Test + void binLE() { + parseFiltersAndCompare("$.intBin1 <= 100", + List.of(Filter.range("intBin1", Long.MIN_VALUE, 100))); + + parseFiltersAndCompare("100 >= $.intBin1", + List.of(Filter.range("intBin1", Long.MIN_VALUE, 100))); + } + + @Test + void binEQ() { + parseFiltersAndCompare("$.intBin1 == 100", + List.of(Filter.equal("intBin1", 100))); + parseFiltersAndCompare("100 == $.intBin1", + List.of(Filter.equal("intBin1", 100))); + + parseFiltersAndCompare("$.stringBin1 == 'text'", + List.of(Filter.equal("stringBin1", "text"))); + parseFiltersAndCompare("$.stringBin1 == \"text\"", + List.of(Filter.equal("stringBin1", "text"))); + } + + @Test + void binNOTEQ() { + // NOT EQUAL is not supported by secondary index filter + assertThatThrownBy(() -> parseFilters("$.intBin1 != 100")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + assertThatThrownBy(() -> parseFilters("$.stringBin1 != 'text'")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + assertThatThrownBy(() -> parseFilters("$.stringBin1 != \"text\"")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + + assertThatThrownBy(() -> parseFilters("100 != $.intBin1")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + assertThatThrownBy(() -> parseFilters("100 != 'text'")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + assertThatThrownBy(() -> parseFilters("100 != \"text\"")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessageContaining("The operation is not supported by secondary index filter"); + } +} diff --git a/src/test/java/com/aerospike/dsl/filter/ExplicitTypesFiltersTests.java b/src/test/java/com/aerospike/dsl/filter/ExplicitTypesFiltersTests.java new file mode 100644 index 0000000..8fcf9db --- /dev/null +++ b/src/test/java/com/aerospike/dsl/filter/ExplicitTypesFiltersTests.java @@ -0,0 +1,181 @@ +package com.aerospike.dsl.filter; + +import com.aerospike.client.query.Filter; +import com.aerospike.dsl.exception.AerospikeDSLException; +import org.junit.jupiter.api.Test; + +import java.util.Base64; +import java.util.List; + +import static com.aerospike.dsl.util.TestUtils.parseFilters; +import static com.aerospike.dsl.util.TestUtils.parseFiltersAndCompare; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class ExplicitTypesFiltersTests { + + @Test + void integerComparison() { + parseFiltersAndCompare("$.intBin1.get(type: INT) > 5", + List.of(Filter.range("intBin1", 6, Long.MAX_VALUE))); + + parseFiltersAndCompare("5 < $.intBin1.get(type: INT)", + List.of(Filter.range("intBin1", 6, Long.MAX_VALUE))); + } + + @Test + void stringComparison() { + parseFiltersAndCompare("$.stringBin1.get(type: STRING) == \"yes\"", + List.of(Filter.equal("stringBin1", "yes"))); + + parseFiltersAndCompare("$.stringBin1.get(type: STRING) == 'yes'", + List.of(Filter.equal("stringBin1", "yes"))); + + parseFiltersAndCompare("\"yes\" == $.stringBin1.get(type: STRING)", + List.of(Filter.equal("stringBin1", "yes"))); + + parseFiltersAndCompare("'yes' == $.stringBin1.get(type: STRING)", + List.of(Filter.equal("stringBin1", "yes"))); + } + + @Test + void stringComparisonNegativeTest() { + // A String constant must be quoted + assertThatThrownBy(() -> parseFilters("$.stringBin1.get(type: STRING) == yes")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessage("Unable to parse right operand"); + } + + @Test + void blobComparison() { + byte[] data = new byte[]{1, 2, 3}; + String encodedString = Base64.getEncoder().encodeToString(data); + parseFiltersAndCompare("$.blobBin1.get(type: BLOB) == \"" + encodedString + "\"", + List.of(Filter.equal("blobBin1", data))); + + // Reverse + parseFiltersAndCompare("\"" + encodedString + "\" == $.blobBin1.get(type: BLOB)", + List.of(Filter.equal("blobBin1", data))); + } + + @Test + void floatComparison() { + // No float support in secondary index filter + assertThatThrownBy(() -> parseFilters("$.floatBin1.get(type: FLOAT) == 1.5")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessage("Operand type not supported: FLOAT_OPERAND"); + + assertThatThrownBy(() -> parseFilters("1.5 == $.floatBin1.get(type: FLOAT)")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessage("Operand type not supported: FLOAT_OPERAND"); + } + + @Test + void booleanComparison() { + // No boolean support in secondary index filter + assertThatThrownBy(() -> parseFilters("$.boolBin1.get(type: BOOL) == true")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessage("Operand type not supported: BOOL_OPERAND"); + + assertThatThrownBy(() -> parseFilters("true == $.boolBin1.get(type: BOOL)")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessage("Operand type not supported: BOOL_OPERAND"); + } + + @Test + void negativeBooleanComparison() { + assertThatThrownBy(() -> parseFilters("$.boolBin1.get(type: BOOL) == 5")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessage("Cannot compare BOOL to INT"); + } + + @Test + void listComparison_constantOnRightSide() { + assertThatThrownBy(() -> parseFilters("$.listBin1.get(type: LIST) == [100]")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessage("Operand type not supported: LIST_OPERAND"); + } + + @Test + void listComparison_constantOnRightSide_NegativeTest() { + assertThatThrownBy(() -> parseFilters("$.listBin1.get(type: LIST) == [yes, of course]")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessage("Unable to parse list operand"); + } + + @Test + void listComparison_constantOnLeftSide() { + assertThatThrownBy(() -> parseFilters("[100] == $.listBin1.get(type: LIST)")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessage("Operand type not supported: LIST_OPERAND"); + } + + @Test + void listComparison_constantOnLeftSide_NegativeTest() { + assertThatThrownBy(() -> parseFilters("[yes, of course] == $.listBin1.get(type: LIST)")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessage("Could not parse given input, wrong syntax"); + } + + @Test + void mapComparison_constantOnRightSide() { + assertThatThrownBy(() -> parseFilters("$.mapBin1.get(type: MAP) == {100:100}")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessage("Operand type not supported: MAP_OPERAND"); + } + + @Test + void mapComparison_constantOnRightSide_NegativeTest() { + assertThatThrownBy(() -> parseFilters("$.mapBin1.get(type: MAP) == {yes, of course}")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessage("Unable to parse map operand"); + } + + @Test + void mapComparison_constantOnLeftSide() { + assertThatThrownBy(() -> parseFilters("{100:100} == $.mapBin1.get(type: MAP)")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessage("Operand type not supported: MAP_OPERAND"); + } + + @Test + void mapComparison_constantOnLeftSide_NegativeTest() { + assertThatThrownBy(() -> parseFilters("{yes, of course} == $.mapBin1.get(type: MAP)")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessage("Could not parse given input, wrong syntax"); + } + + @Test + void twoStringBinsComparison() { + assertThatThrownBy(() -> parseFilters("$.stringBin1.get(type: STRING) == $.stringBin2.get(type: STRING)")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessage("Operand type not supported: BIN_PART"); + } + + @Test + void twoIntegerBinsComparison() { + assertThatThrownBy(() -> parseFilters("$.intBin1.get(type: INT) == $.intBin2.get(type: INT)")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessage("Operand type not supported: BIN_PART"); + } + + @Test + void twoFloatBinsComparison() { + assertThatThrownBy(() -> parseFilters("$.floatBin1.get(type: FLOAT) == $.floatBin2.get(type: FLOAT)")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessage("Operand type not supported: BIN_PART"); + } + + @Test + void twoBlobBinsComparison() { + assertThatThrownBy(() -> parseFilters("$.blobBin1.get(type: BLOB) == $.blobBin2.get(type: BLOB)")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessage("Operand type not supported: BIN_PART"); + } + + @Test + void negativeTwoDifferentBinTypesComparison() { + assertThatThrownBy(() -> parseFilters("$.stringBin1.get(type: STRING) == $.floatBin2.get(type: FLOAT)")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessage("Operand type not supported: BIN_PART"); + } +} diff --git a/src/test/java/com/aerospike/dsl/filter/ImplicitTypesFiltersTests.java b/src/test/java/com/aerospike/dsl/filter/ImplicitTypesFiltersTests.java new file mode 100644 index 0000000..15f4cb5 --- /dev/null +++ b/src/test/java/com/aerospike/dsl/filter/ImplicitTypesFiltersTests.java @@ -0,0 +1,31 @@ +package com.aerospike.dsl.filter; + +import com.aerospike.dsl.exception.AerospikeDSLException; +import org.junit.jupiter.api.Test; + +import static com.aerospike.dsl.util.TestUtils.parseFilters; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class ImplicitTypesFiltersTests { + + @Test + void implicitDefaultIntComparison() { + assertThatThrownBy(() -> parseFilters("$.intBin1 < $.intBin2")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessage("Operand type not supported: BIN_PART"); + } + + @Test + void floatComparison() { + assertThatThrownBy(() -> parseFilters("$.floatBin1 >= 100.25")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessage("Operand type not supported: FLOAT_OPERAND"); + } + + @Test + void booleanComparison() { + assertThatThrownBy(() -> parseFilters("$.boolBin1 == true")) + .isInstanceOf(AerospikeDSLException.class) + .hasMessage("Operand type not supported: BOOL_OPERAND"); + } +} diff --git a/src/test/java/com/aerospike/dsl/util/TestUtils.java b/src/test/java/com/aerospike/dsl/util/TestUtils.java index a7b3882..b85a950 100644 --- a/src/test/java/com/aerospike/dsl/util/TestUtils.java +++ b/src/test/java/com/aerospike/dsl/util/TestUtils.java @@ -2,21 +2,35 @@ import com.aerospike.client.exp.Exp; import com.aerospike.client.exp.Expression; -import com.aerospike.dsl.ConditionTranslator; +import com.aerospike.client.query.Filter; +import com.aerospike.dsl.DSLParserImpl; import lombok.experimental.UtilityClass; +import java.util.List; + import static org.junit.jupiter.api.Assertions.assertEquals; @UtilityClass public class TestUtils { - public static void translate(String input) { - ConditionTranslator.translate(input); + private final DSLParserImpl parser = new DSLParserImpl(); + + public static void parseExpression(String input) { + parser.parseExpression(input); } - public static void translateAndCompare(String input, Exp expected) { - Expression actualExpression = ConditionTranslator.translate(input); + public static void parseExpressionAndCompare(String input, Exp expected) { + Expression actualExpression = parser.parseExpression(input); Expression expectedExpression = Exp.build(expected); assertEquals(actualExpression, expectedExpression); } + + public static void parseFilters(String input) { + parser.parseFilters(input); + } + + public static void parseFiltersAndCompare(String input, List expected) { + List actualFilter = parser.parseFilters(input); + assertEquals(actualFilter, expected); + } }