Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ public class ExpressionContainer extends AbstractPart {
@Setter()
@Accessors(fluent = true)
private boolean hasSecondaryIndexFilter;
@Setter()
@Accessors(fluent = true)
private boolean isExclFromSecondaryIndexFilter;

public ExpressionContainer() {
super(PartType.EXPRESSION_CONTAINER);
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/com/aerospike/dsl/parts/cdt/CdtPart.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,9 @@ public CTX getContext() {
}

public abstract int getReturnType(PathFunction.ReturnParam returnParam);

public static boolean isCdtPart(AbstractPart part) {
return part.getPartType() == AbstractPart.PartType.LIST_PART
|| part.getPartType() == AbstractPart.PartType.MAP_PART;
}
}
11 changes: 4 additions & 7 deletions src/main/java/com/aerospike/dsl/parts/path/Path.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,8 @@

import java.util.List;

import static com.aerospike.dsl.util.PathOperandUtils.processGet;
import static com.aerospike.dsl.util.PathOperandUtils.processPathFunction;
import static com.aerospike.dsl.util.PathOperandUtils.processSize;
import static com.aerospike.dsl.util.PathOperandUtils.processValueType;
import static com.aerospike.dsl.util.PathOperandUtils.updateWithCdtTypeDesignator;
import static com.aerospike.dsl.parts.cdt.CdtPart.isCdtPart;
import static com.aerospike.dsl.util.PathOperandUtils.*;

@Getter
public class Path extends AbstractPart {
Expand All @@ -33,8 +30,8 @@ public Exp processPath(BasePath basePath, PathFunction pathFunction) {
Exp.Type valueType = processValueType(lastPathPart, pathFunction);

int cdtReturnType = 0;
if (lastPathPart instanceof CdtPart lastPart) {
cdtReturnType = lastPart.getReturnType(pathFunction.getReturnParam());
if (lastPathPart != null && isCdtPart(lastPathPart)) {
cdtReturnType = ((CdtPart) lastPathPart).getReturnType(pathFunction.getReturnParam());
}

if (lastPathPart != null) { // only if there are other parts except a bin
Expand Down
13 changes: 7 additions & 6 deletions src/main/java/com/aerospike/dsl/util/PathOperandUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -105,16 +105,16 @@ private static boolean containOnlyCdtDesignator(List<AbstractPart> parts) {
}

private static boolean isPrevCdtPartAmbiguous(AbstractPart lastPart) {
if (lastPart instanceof MapPart mapPart) { // check that lastPart is CDT Map
if (lastPart.getPartType() == MAP_PART) { // check that lastPart is CDT Map
// check relevant types
return List.of(MapPart.MapPartType.INDEX, MapPart.MapPartType.RANK, MapPart.MapPartType.KEY,
MapPart.MapPartType.VALUE)
.contains(mapPart.getMapPartType());
.contains(((MapPart) lastPart).getMapPartType());
}
if (lastPart instanceof ListPart listPart) { // check that lastPart is CDT List
if (lastPart.getPartType() == LIST_PART) { // check that lastPart is CDT List
// check relevant types
return List.of(INDEX, RANK, ListPart.ListPartType.VALUE)
.contains(listPart.getListPartType());
.contains(((ListPart) lastPart).getListPartType());
}
return false;
}
Expand Down Expand Up @@ -142,11 +142,12 @@ private static Exp doProcessCdtGet(BasePath basePath, AbstractPart lastPathPart,
}

private static boolean isListTypeDesignator(AbstractPart cdtPart) {
return cdtPart instanceof ListPart listPart && listPart.getListPartType().equals(LIST_TYPE_DESIGNATOR);
return cdtPart.getPartType() == LIST_PART
&& ((ListPart) cdtPart).getListPartType().equals(LIST_TYPE_DESIGNATOR);
}

private static boolean isMapTypeDesignator(AbstractPart cdtPart) {
return cdtPart instanceof MapPart mapPart && mapPart.getMapPartType().equals(MAP_TYPE_DESIGNATOR);
return cdtPart.getPartType() == MAP_PART && ((MapPart) cdtPart).getMapPartType().equals(MAP_TYPE_DESIGNATOR);
}

private static CTX[] getContextArray(List<AbstractPart> parts, boolean includeLast) {
Expand Down
3 changes: 1 addition & 2 deletions src/main/java/com/aerospike/dsl/util/TypeUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@

import com.aerospike.client.exp.Exp;
import com.aerospike.dsl.parts.AbstractPart;
import com.aerospike.dsl.parts.cdt.map.MapPart;
import com.aerospike.dsl.parts.cdt.map.MapTypeDesignator;
import lombok.experimental.UtilityClass;

@UtilityClass
public class TypeUtils {

public static Exp.Type getDefaultType(AbstractPart part) {
if (part instanceof MapPart
if (part.getPartType() == AbstractPart.PartType.MAP_PART
// MapTypeDesignator is usually combined with int based operations such as size
&& !(part instanceof MapTypeDesignator)) {
// For all other Map parts the default type should be STRING
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -440,20 +440,20 @@ private AbstractPart overrideType(AbstractPart part, ParseTree ctx) {
if (pathFunction != null) {
Exp.Type type = pathFunction.getBinType();
if (type != null) {
if (part instanceof BinPart binPart) {
binPart.updateExp(type);
if (part.getPartType() == AbstractPart.PartType.BIN_PART) {
((BinPart) part).updateExp(type);
} else {
part.setExpType(type);
}
}
}
} else { // Override using Implicit type detection
Exp.Type implicitType = detectImplicitTypeFromUpperTree(ctx);
if (part instanceof BinPart binPart) {
if (part.getPartType() == AbstractPart.PartType.BIN_PART) {
if (implicitType == null) {
implicitType = Exp.Type.INT;
}
binPart.updateExp(implicitType);
((BinPart) part).updateExp(implicitType);
} else { // ListPart or MapPart
if (implicitType == null) {
implicitType = TypeUtils.getDefaultType(part);
Expand Down
33 changes: 27 additions & 6 deletions src/main/java/com/aerospike/dsl/visitor/VisitorUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -1132,11 +1132,16 @@ private static ExpressionContainer chooseExprForFilter(ExpressionContainer exprC
Map<Integer, List<ExpressionContainer>> exprsPerCardinality =
getExpressionsPerCardinality(exprContainer, indexes);

// Find the entry with the largest key (cardinality)
Map<Integer, List<ExpressionContainer>> largestCardinalityMap = exprsPerCardinality.entrySet().stream()
.max(Map.Entry.comparingByKey())
.map(entry -> Map.of(entry.getKey(), entry.getValue()))
.orElse(Collections.emptyMap());
Map<Integer, List<ExpressionContainer>> largestCardinalityMap;
if (exprsPerCardinality.size() > 1) {
// Find the entry with the largest key (cardinality)
largestCardinalityMap = exprsPerCardinality.entrySet().stream()
.max(Map.Entry.comparingByKey())
.map(entry -> Map.of(entry.getKey(), entry.getValue()))
.orElse(Collections.emptyMap());
} else {
largestCardinalityMap = new HashMap<>(exprsPerCardinality);
}

List<ExpressionContainer> largestCardinalityExprs;
if (largestCardinalityMap.isEmpty()) return null;
Expand Down Expand Up @@ -1249,7 +1254,23 @@ private static BinPart getBinPart(ExpressionContainer expr, int depth) {
Predicate<AbstractPart> stopOnLogicalExpr = part -> {
if (part.getPartType() == EXPRESSION_CONTAINER) {
ExpressionContainer logicalExpr = (ExpressionContainer) part;
return logicalExpr.getOperationType() == AND || logicalExpr.getOperationType() == OR;
if (logicalExpr.isExclFromSecondaryIndexFilter()) {
// All parts of the tree branch excluded from secondary index Filter building are flagged
if (logicalExpr.getLeft().getPartType() == EXPRESSION_CONTAINER) {
((ExpressionContainer) logicalExpr.getLeft()).isExclFromSecondaryIndexFilter(true);
}
if (logicalExpr.getRight().getPartType() == EXPRESSION_CONTAINER) {
((ExpressionContainer) logicalExpr.getRight()).isExclFromSecondaryIndexFilter(true);
}
return true;
}
if (logicalExpr.getOperationType() == AND) return true;
if (logicalExpr.getOperationType() == OR) {
// Both parts of OR-combined query are excluded from secondary index Filter building
((ExpressionContainer) logicalExpr.getLeft()).isExclFromSecondaryIndexFilter(true);
((ExpressionContainer) logicalExpr.getRight()).isExclFromSecondaryIndexFilter(true);
return true;
}
}
return false;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,122 +2,123 @@

import com.aerospike.client.exp.Exp;
import com.aerospike.dsl.DslParseException;
import com.aerospike.dsl.util.TestUtils;
import org.junit.jupiter.api.Test;

import static com.aerospike.dsl.util.TestUtils.parseExp;
import static com.aerospike.dsl.util.TestUtils.parseExpAndCompare;
import static com.aerospike.dsl.util.TestUtils.parseFilterExp;
import static com.aerospike.dsl.util.TestUtils.parseDslExpressionAndCompare;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

public class ArithmeticExpressionsTests {

@Test
void add() {
parseExpAndCompare("($.apples + $.bananas) > 10",
TestUtils.parseFilterExpressionAndCompare("($.apples + $.bananas) > 10",
Exp.gt(Exp.add(Exp.intBin("apples"), Exp.intBin("bananas")), Exp.val(10)));
parseExpAndCompare("($.apples + 5) > 10",
TestUtils.parseFilterExpressionAndCompare("($.apples + 5) > 10",
Exp.gt(Exp.add(Exp.intBin("apples"), Exp.val(5)), Exp.val(10)));
parseExpAndCompare("(5 + $.bananas) > 10",
TestUtils.parseFilterExpressionAndCompare("(5 + $.bananas) > 10",
Exp.gt(Exp.add(Exp.val(5), Exp.intBin("bananas")), Exp.val(10)));

parseExpAndCompare("(5.2 + $.bananas) > 10.2",
TestUtils.parseFilterExpressionAndCompare("(5.2 + $.bananas) > 10.2",
Exp.gt(Exp.add(Exp.val(5.2), Exp.floatBin("bananas")), Exp.val(10.2)));
}

@Test
void sub() {
parseExpAndCompare("($.apples - $.bananas) == 10",
TestUtils.parseFilterExpressionAndCompare("($.apples - $.bananas) == 10",
Exp.eq(Exp.sub(Exp.intBin("apples"), Exp.intBin("bananas")), Exp.val(10)));
parseExpAndCompare("($.apples - 5) == 10",
TestUtils.parseFilterExpressionAndCompare("($.apples - 5) == 10",
Exp.eq(Exp.sub(Exp.intBin("apples"), Exp.val(5)), Exp.val(10)));
parseExpAndCompare("(15 - $.bananas) == 10",
TestUtils.parseFilterExpressionAndCompare("(15 - $.bananas) == 10",
Exp.eq(Exp.sub(Exp.val(15), Exp.intBin("bananas")), Exp.val(10)));
}

@Test
void mul() {
parseExpAndCompare("($.apples * $.bananas) != 10",
TestUtils.parseFilterExpressionAndCompare("($.apples * $.bananas) != 10",
Exp.ne(Exp.mul(Exp.intBin("apples"), Exp.intBin("bananas")), Exp.val(10)));
parseExpAndCompare("($.apples * 7) != 10",
TestUtils.parseFilterExpressionAndCompare("($.apples * 7) != 10",
Exp.ne(Exp.mul(Exp.intBin("apples"), Exp.val(7)), Exp.val(10)));
parseExpAndCompare("(3 * $.bananas) != 10",
TestUtils.parseFilterExpressionAndCompare("(3 * $.bananas) != 10",
Exp.ne(Exp.mul(Exp.val(3), Exp.intBin("bananas")), Exp.val(10)));
}

@Test
void div() {
parseExpAndCompare("($.apples / $.bananas) <= 10",
TestUtils.parseFilterExpressionAndCompare("($.apples / $.bananas) <= 10",
Exp.le(Exp.div(Exp.intBin("apples"), Exp.intBin("bananas")), Exp.val(10)));
parseExpAndCompare("($.apples / 5) <= 10",
TestUtils.parseFilterExpressionAndCompare("($.apples / 5) <= 10",
Exp.le(Exp.div(Exp.intBin("apples"), Exp.val(5)), Exp.val(10)));
parseExpAndCompare("(33 / $.bananas) <= 10",
TestUtils.parseFilterExpressionAndCompare("(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
parseExpAndCompare("($.apples / 0) <= 10",
TestUtils.parseFilterExpressionAndCompare("($.apples / 0) <= 10",
Exp.le(Exp.div(Exp.intBin("apples"), Exp.val(0)), Exp.val(10)));
}

@Test
void mod() {
parseExpAndCompare("($.apples % $.bananas) != 10",
TestUtils.parseFilterExpressionAndCompare("($.apples % $.bananas) != 10",
Exp.ne(Exp.mod(Exp.intBin("apples"), Exp.intBin("bananas")), Exp.val(10)));
parseExpAndCompare("($.apples % 7) != 10",
TestUtils.parseFilterExpressionAndCompare("($.apples % 7) != 10",
Exp.ne(Exp.mod(Exp.intBin("apples"), Exp.val(7)), Exp.val(10)));
parseExpAndCompare("(3 % $.bananas) != 10",
TestUtils.parseFilterExpressionAndCompare("(3 % $.bananas) != 10",
Exp.ne(Exp.mod(Exp.val(3), Exp.intBin("bananas")), Exp.val(10)));
}

@Test
void intAnd() {
// parseExpAndCompare("($.apples & $.bananas) != 10",
// Exp.ne(Exp.intAnd(Exp.intBin("apples"), Exp.intBin("bananas")), Exp.val(10)));
parseExpAndCompare("($.apples & 7) != 10",
TestUtils.parseFilterExpressionAndCompare("($.apples & $.bananas) != 10",
Exp.ne(Exp.intAnd(Exp.intBin("apples"), Exp.intBin("bananas")), Exp.val(10)));
TestUtils.parseFilterExpressionAndCompare("($.apples & 7) != 10",
Exp.ne(Exp.intAnd(Exp.intBin("apples"), Exp.val(7)), Exp.val(10)));
parseExpAndCompare("(3 & $.bananas) != 10",
TestUtils.parseFilterExpressionAndCompare("(3 & $.bananas) != 10",
Exp.ne(Exp.intAnd(Exp.val(3), Exp.intBin("bananas")), Exp.val(10)));
}

@Test
void intOr() {
parseExpAndCompare("($.apples | $.bananas) != 10",
TestUtils.parseFilterExpressionAndCompare("($.apples | $.bananas) != 10",
Exp.ne(Exp.intOr(Exp.intBin("apples"), Exp.intBin("bananas")), Exp.val(10)));
parseExpAndCompare("($.apples | 7) != 10",
TestUtils.parseFilterExpressionAndCompare("($.apples | 7) != 10",
Exp.ne(Exp.intOr(Exp.intBin("apples"), Exp.val(7)), Exp.val(10)));
parseExpAndCompare("(3 | $.bananas) != 10",
TestUtils.parseFilterExpressionAndCompare("(3 | $.bananas) != 10",
Exp.ne(Exp.intOr(Exp.val(3), Exp.intBin("bananas")), Exp.val(10)));
}

@Test
void intXor() {
parseExpAndCompare("($.apples ^ $.bananas) != 10",
TestUtils.parseFilterExpressionAndCompare("($.apples ^ $.bananas) != 10",
Exp.ne(Exp.intXor(Exp.intBin("apples"), Exp.intBin("bananas")), Exp.val(10)));
parseExpAndCompare("($.apples ^ 7) != 10",
TestUtils.parseFilterExpressionAndCompare("($.apples ^ 7) != 10",
Exp.ne(Exp.intXor(Exp.intBin("apples"), Exp.val(7)), Exp.val(10)));
parseExpAndCompare("(3 ^ $.bananas) != 10",
TestUtils.parseFilterExpressionAndCompare("(3 ^ $.bananas) != 10",
Exp.ne(Exp.intXor(Exp.val(3), Exp.intBin("bananas")), Exp.val(10)));
}

@Test
void intNot() {
parseExpAndCompare("(~$.apples) != 10",
TestUtils.parseFilterExpressionAndCompare("(~$.apples) != 10",
Exp.ne(Exp.intNot(Exp.intBin("apples")), Exp.val(10)));
}

@Test
void intLShift() {
parseExpAndCompare("$.visits << 1",
TestUtils.parseFilterExpressionAndCompare("$.visits << 1",
Exp.lshift(Exp.intBin("visits"), Exp.val(1)));
}

@Test
void intRShift() {
parseExpAndCompare("(($.flags >> 6) & 1) == 1",
TestUtils.parseFilterExpressionAndCompare("(($.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(() -> parseExp("($.apples.get(type: STRING) + 5) > 10"))
assertThatThrownBy(() -> parseFilterExp("($.apples.get(type: STRING) + 5) > 10"))
.isInstanceOf(DslParseException.class)
.hasMessageContaining("Cannot compare STRING to INT");

Expand Down
Loading
Loading