diff --git a/src/main/java/org/springframework/data/aerospike/annotation/Beta.java b/src/main/java/org/springframework/data/aerospike/annotation/Beta.java new file mode 100644 index 000000000..48a213741 --- /dev/null +++ b/src/main/java/org/springframework/data/aerospike/annotation/Beta.java @@ -0,0 +1,15 @@ +package org.springframework.data.aerospike.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Marks public API as potentially volatile, i.e. it may be a subject of incompatible changes in future releases. + */ +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.METHOD}) +public @interface Beta { + +} diff --git a/src/main/java/org/springframework/data/aerospike/annotation/Indexed.java b/src/main/java/org/springframework/data/aerospike/annotation/Indexed.java index d423c1bd4..6971c4780 100644 --- a/src/main/java/org/springframework/data/aerospike/annotation/Indexed.java +++ b/src/main/java/org/springframework/data/aerospike/annotation/Indexed.java @@ -112,10 +112,10 @@ * a.55 [mapKey("a"), mapKey(55)] * * - * a.aa.{=222} [mapKey("a"), mapKey("aa"),mapValue(222)] + * a.aa.{=222} [mapKey("a"), mapKey("aa"), mapValue(222)] * * - * ab.cd.[-1]."10" [mapKey("ab"), mapKey("cd"),listIndex(-1), mapKey("10")] + * ab.cd.[-1]."10" [mapKey("ab"), mapKey("cd"), listIndex(-1), mapKey("10")] * * */ diff --git a/src/main/java/org/springframework/data/aerospike/index/AerospikeIndexResolverUtils.java b/src/main/java/org/springframework/data/aerospike/index/AerospikeContextDslResolverUtils.java similarity index 54% rename from src/main/java/org/springframework/data/aerospike/index/AerospikeIndexResolverUtils.java rename to src/main/java/org/springframework/data/aerospike/index/AerospikeContextDslResolverUtils.java index 15f5fa194..16253fa5c 100644 --- a/src/main/java/org/springframework/data/aerospike/index/AerospikeIndexResolverUtils.java +++ b/src/main/java/org/springframework/data/aerospike/index/AerospikeContextDslResolverUtils.java @@ -2,8 +2,19 @@ import com.aerospike.client.Value; import com.aerospike.client.cdt.CTX; +import com.aerospike.client.exp.Exp; -public class AerospikeIndexResolverUtils { +import java.util.List; + +import static org.springframework.data.aerospike.index.AerospikeContextDslResolverUtils.CtxType.LIST_INDEX; +import static org.springframework.data.aerospike.index.AerospikeContextDslResolverUtils.CtxType.LIST_RANK; +import static org.springframework.data.aerospike.index.AerospikeContextDslResolverUtils.CtxType.LIST_VALUE; +import static org.springframework.data.aerospike.index.AerospikeContextDslResolverUtils.CtxType.MAP_INDEX; +import static org.springframework.data.aerospike.index.AerospikeContextDslResolverUtils.CtxType.MAP_KEY; +import static org.springframework.data.aerospike.index.AerospikeContextDslResolverUtils.CtxType.MAP_RANK; +import static org.springframework.data.aerospike.index.AerospikeContextDslResolverUtils.CtxType.MAP_VALUE; + +public class AerospikeContextDslResolverUtils { public static CTX toCtx(String singleCtx) { switch (singleCtx.charAt(0)) { @@ -24,12 +35,11 @@ public static CTX toCtx(String singleCtx) { private static CTX processSingleCtx(String singleCtx, AerospikeIndexResolver.CtxType ctxType) { int length = singleCtx.length(); if (length < 3) { - throw new IllegalArgumentException("@Indexed annotation: context string '" + singleCtx + - "' has no content"); + throw new IllegalArgumentException(String.format("Context DSL: string '%s' has no content", singleCtx)); } if (singleCtx.charAt(length - 1) != ctxType.closingChar) { - throw new IllegalArgumentException("@Indexed annotation: brackets mismatch, " + - "expecting '" + ctxType.closingChar + "', got '" + singleCtx.charAt(length - 1) + "' instead"); + throw new IllegalArgumentException(String.format("Context DSL: brackets mismatch, expecting '%s', " + + "got '%s' instead", ctxType.closingChar, singleCtx.charAt(length - 1))); } CTX result; @@ -75,8 +85,8 @@ private static int parseIntOrFail(String substring, AerospikeIndexResolver.CtxTy try { return Integer.parseInt(substring); } catch (NumberFormatException e) { - throw new IllegalArgumentException("@Indexed annotation " + ctxType + " " + parameterName + ": " + - "expecting only integer values, got '" + substring + "' instead"); + throw new IllegalArgumentException(String.format("Context DSL %s %s: expecting only integer values, " + + "got '%s' instead", ctxType, parameterName, substring)); } } @@ -95,4 +105,53 @@ private static boolean isInDoubleOrSingleQuotes(String str) { return str.length() > 2 && (str.charAt(0) == '"' || str.charAt(0) == '\'') && (str.charAt(str.length() - 1) == '"' || str.charAt(str.length() - 1) == '\''); } + + /** + * Check whether context element's id is the same as in {@link CTX#mapKey(Value)} + */ + public static boolean isCtxMapKey(CTX ctx) { + return ctx.id == MAP_KEY.getId(); + } + + /** + * Check whether context element's id is the same as in {@link CTX#mapValue(Value)} + */ + public static boolean isCtxMapValue(CTX ctx) { + return ctx.id == MAP_VALUE.getId(); + } + + /** + * Check whether context element's id is the same as in {@link CTX#mapKey(Value)} + */ + public static Exp.Type getCtxType(CTX ctx) { + List listIds = List.of(LIST_INDEX.getId(), LIST_RANK.getId(), LIST_VALUE.getId()); + List mapIds = List.of(MAP_INDEX.getId(), MAP_RANK.getId(), MAP_KEY.getId(), MAP_VALUE.getId()); + if (listIds.contains(ctx.id)) { + return Exp.Type.LIST; + } else if (mapIds.contains(ctx.id)) { + return Exp.Type.MAP; + } else { + throw new IllegalStateException("Unexpected CTX element id"); + } + } + + enum CtxType { + LIST_INDEX(0x10), + LIST_RANK(0x11), + LIST_VALUE(0x13), + MAP_INDEX(0x20), + MAP_RANK(0x21), + MAP_KEY(0x22), + MAP_VALUE(0x23); + + private final int id; + + CtxType(int id) { + this.id = id; + } + + public int getId() { + return id; + } + } } diff --git a/src/main/java/org/springframework/data/aerospike/index/AerospikeIndexResolver.java b/src/main/java/org/springframework/data/aerospike/index/AerospikeIndexResolver.java index ec03701fa..117ee35f2 100644 --- a/src/main/java/org/springframework/data/aerospike/index/AerospikeIndexResolver.java +++ b/src/main/java/org/springframework/data/aerospike/index/AerospikeIndexResolver.java @@ -93,7 +93,7 @@ private CTX[] toCtxArray(String ctxString) { String[] ctxTokens = ctxString.split("\\."); CTX[] ctxArr = Arrays.stream(ctxTokens) .filter(not(String::isEmpty)) - .map(AerospikeIndexResolverUtils::toCtx) + .map(AerospikeContextDslResolverUtils::toCtx) .filter(Objects::nonNull) .toArray(CTX[]::new); diff --git a/src/main/java/org/springframework/data/aerospike/query/ExpiryQualifier.java b/src/main/java/org/springframework/data/aerospike/query/ExpiryQualifier.java index 7b175ba19..44ea73fac 100644 --- a/src/main/java/org/springframework/data/aerospike/query/ExpiryQualifier.java +++ b/src/main/java/org/springframework/data/aerospike/query/ExpiryQualifier.java @@ -38,7 +38,7 @@ public class ExpiryQualifier extends Qualifier { public ExpiryQualifier(FilterOperation op, Value value) { super(Qualifier.builder() - .setBinName(QueryEngine.Meta.EXPIRATION.toString()) + .setPath(QueryEngine.Meta.EXPIRATION.toString()) .setFilterOperation(op) .setValue(value) ); diff --git a/src/main/java/org/springframework/data/aerospike/query/FilterOperation.java b/src/main/java/org/springframework/data/aerospike/query/FilterOperation.java index e5376084d..756a6b97e 100644 --- a/src/main/java/org/springframework/data/aerospike/query/FilterOperation.java +++ b/src/main/java/org/springframework/data/aerospike/query/FilterOperation.java @@ -26,7 +26,6 @@ import com.aerospike.client.query.IndexCollectionType; import com.aerospike.client.query.RegexFlag; import org.springframework.data.aerospike.config.AerospikeDataSettings; -import org.springframework.data.aerospike.index.AerospikeIndexResolverUtils; import org.springframework.data.aerospike.query.qualifier.Qualifier; import org.springframework.data.aerospike.query.qualifier.QualifierKey; import org.springframework.data.aerospike.repository.query.CriteriaDefinition; @@ -38,7 +37,6 @@ import java.util.Collection; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Optional; import java.util.function.BinaryOperator; import java.util.function.Function; @@ -49,7 +47,6 @@ import static com.aerospike.client.command.ParticleType.LIST; import static com.aerospike.client.command.ParticleType.MAP; import static com.aerospike.client.command.ParticleType.STRING; -import static java.util.function.Predicate.not; import static org.springframework.data.aerospike.query.qualifier.QualifierKey.*; import static org.springframework.data.aerospike.repository.query.AerospikeQueryCreatorUtils.getDotPathArray; import static org.springframework.data.aerospike.util.FilterOperationRegexpBuilder.getContaining; @@ -119,7 +116,7 @@ public Exp filterExp(Map qualifierMap) { Collection collection = getValueAsCollectionOrFail(qualifierMap); Exp[] arrElementsExp = collection.stream().map(item -> Qualifier.builder() - .setBinName(getBinName(qualifierMap)) + .setPath(getBinName(qualifierMap)) .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(item)) .build() @@ -143,7 +140,7 @@ public Exp filterExp(Map qualifierMap) { Collection collection = getValueAsCollectionOrFail(qualifierMap); Exp[] arrElementsExp = collection.stream().map(item -> Qualifier.builder() - .setBinName(getBinName(qualifierMap)) + .setPath(getBinName(qualifierMap)) .setFilterOperation(FilterOperation.NOTEQ) .setValue(Value.get(item)) .build() @@ -474,11 +471,11 @@ public Filter sIndexFilter(Map qualifierMap) { yield null; } yield Filter.contains(getBinName(qualifierMap), IndexCollectionType.MAPVALUES, - getValue(qualifierMap).toString(), getCtx(getCtxList(qualifierMap))); + getValue(qualifierMap).toString(), getCtxArr(qualifierMap)); } case INTEGER -> Filter.range(getBinName(qualifierMap), IndexCollectionType.MAPVALUES, getValue(qualifierMap).toLong(), getValue(qualifierMap).toLong(), - getCtx(getCtxList(qualifierMap))); + getCtxArr(qualifierMap)); default -> null; }; } @@ -499,11 +496,14 @@ public Filter sIndexFilter(Map qualifierMap) { public Exp filterExp(Map qualifierMap) { // Convert IN to EQ with logical OR as there is no direct support for IN query Collection collection = getValueAsCollectionOrFail(qualifierMap); + String pathPart = getKey(qualifierMap).toString(); + if (getCtxArr(qualifierMap) != null) pathPart = getCtxArrAsString(qualifierMap) + pathPart; + String path = String.join(".", getBinName(qualifierMap), pathPart); + Exp[] arrElementsExp = collection.stream().map(item -> Qualifier.builder() - .setBinName(getBinName(qualifierMap)) .setFilterOperation(FilterOperation.MAP_VAL_EQ_BY_KEY) - .setKey(getKey(qualifierMap)) + .setPath(path) .setValue(Value.get(item)) .build() .getFilterExp() @@ -522,11 +522,13 @@ public Filter sIndexFilter(Map qualifierMap) { public Exp filterExp(Map qualifierMap) { // Convert NOT_IN to NOTEQ with logical AND as there is no direct support for NOT_IN query Collection collection = getValueAsCollectionOrFail(qualifierMap); + String pathPart = getKey(qualifierMap).toString(); + if (getCtxArr(qualifierMap) != null) pathPart = getCtxArrAsString(qualifierMap) + pathPart; + String path = String.join(".", getBinName(qualifierMap), pathPart); Exp[] arrElementsExp = collection.stream().map(item -> Qualifier.builder() - .setBinName(getBinName(qualifierMap)) .setFilterOperation(FilterOperation.MAP_VAL_NOTEQ_BY_KEY) - .setKey(getKey(qualifierMap)) + .setPath(path) .setValue(Value.get(item)) .build() .getFilterExp() @@ -558,7 +560,7 @@ public Filter sIndexFilter(Map qualifierMap) { return Filter.range(getBinName(qualifierMap), IndexCollectionType.MAPVALUES, getKey(qualifierMap).toLong() + 1, - Long.MAX_VALUE, getCtx(getCtxList(qualifierMap))); + Long.MAX_VALUE, getCtxArr(qualifierMap)); } }, MAP_VAL_GTEQ_BY_KEY { @@ -576,7 +578,7 @@ public Filter sIndexFilter(Map qualifierMap) { return null; } return Filter.range(getBinName(qualifierMap), IndexCollectionType.MAPVALUES, - getKey(qualifierMap).toLong(), Long.MAX_VALUE, getCtx(getCtxList(qualifierMap))); + getKey(qualifierMap).toLong(), Long.MAX_VALUE, getCtxArr(qualifierMap)); } }, MAP_VAL_LT_BY_KEY { @@ -595,7 +597,7 @@ public Filter sIndexFilter(Map qualifierMap) { return null; } return Filter.range(getBinName(qualifierMap), IndexCollectionType.MAPVALUES, Long.MIN_VALUE, - getKey(qualifierMap).toLong() - 1, getCtx(getCtxList(qualifierMap))); + getKey(qualifierMap).toLong() - 1, getCtxArr(qualifierMap)); } }, MAP_VAL_LTEQ_BY_KEY { @@ -613,13 +615,13 @@ public Filter sIndexFilter(Map qualifierMap) { return null; } return Filter.range(getBinName(qualifierMap), IndexCollectionType.MAPVALUES, Long.MIN_VALUE, - getValue(qualifierMap).toLong(), getCtx(getCtxList(qualifierMap))); + getValue(qualifierMap).toLong(), getCtxArr(qualifierMap)); } }, MAP_VAL_BETWEEN_BY_KEY { @Override public Exp filterExp(Map qualifierMap) { - List ctxList = getCtxList(qualifierMap); + CTX[] ctxArr = getCtxArr(qualifierMap); Exp lowerLimit; Exp upperLimit; Exp.Type type; @@ -649,7 +651,7 @@ public Exp filterExp(Map qualifierMap) { getValue(qualifierMap).getClass().getSimpleName()); } - return mapValBetweenByKey(qualifierMap, ctxList, type, lowerLimit, upperLimit); + return mapValBetweenByKey(qualifierMap, ctxArr, type, lowerLimit, upperLimit); } /** @@ -662,7 +664,7 @@ public Filter sIndexFilter(Map qualifierMap) { } return Filter.range(getBinName(qualifierMap), IndexCollectionType.MAPVALUES, getValue(qualifierMap).toLong(), getSecondValue(qualifierMap).toLong(), - getCtx(getCtxList(qualifierMap))); + getCtxArr(qualifierMap)); } }, MAP_VAL_STARTS_WITH_BY_KEY { @@ -1385,14 +1387,14 @@ private static BinaryOperator mapOperation(FilterOperation operation) { }; } - private static Exp mapValBetweenByKey(Map qualifierMap, List ctxList, + private static Exp mapValBetweenByKey(Map qualifierMap, CTX[] ctxArr, Exp.Type type, Exp lowerLimit, Exp upperLimit) { Exp mapExp; - if (ctxList != null) { + if (ctxArr != null && ctxArr.length > 0) { mapExp = MapExp.getByKey(MapReturnType.VALUE, type, Exp.val(getKey(qualifierMap).toString()), - Exp.mapBin(getBinName(qualifierMap)), resolveCtxList(ctxList)); + Exp.mapBin(getBinName(qualifierMap)), ctxArr); } else { mapExp = MapExp.getByKey(MapReturnType.VALUE, type, Exp.val(getKey(qualifierMap).toString()), Exp.mapBin(getBinName(qualifierMap))); @@ -1481,27 +1483,27 @@ private static Exp mapValuesCountComparedToZero(Map qualif private static Exp getFilterExpMapValOrFail(Map qualifierMap, BinaryOperator operator, String opName) { - List ctxList = getCtxList(qualifierMap); + CTX[] ctxArr = getCtxArr(qualifierMap); return switch (getValue(qualifierMap).getType()) { - case INTEGER -> operator.apply(getMapExp(qualifierMap, ctxList, Exp.Type.INT), + case INTEGER -> operator.apply(getMapExp(qualifierMap, ctxArr, Exp.Type.INT), Exp.val(getValue(qualifierMap).toLong())); - case STRING -> operator.apply(getMapExp(qualifierMap, ctxList, Exp.Type.STRING), + case STRING -> operator.apply(getMapExp(qualifierMap, ctxArr, Exp.Type.STRING), Exp.val(getValue(qualifierMap).toString())); - case LIST -> operator.apply(getMapExp(qualifierMap, ctxList, Exp.Type.LIST), + case LIST -> operator.apply(getMapExp(qualifierMap, ctxArr, Exp.Type.LIST), Exp.val((List) getValue(qualifierMap).getObject())); - case MAP -> operator.apply(getMapExp(qualifierMap, ctxList, Exp.Type.MAP), + case MAP -> operator.apply(getMapExp(qualifierMap, ctxArr, Exp.Type.MAP), Exp.val((Map) getValue(qualifierMap).getObject())); default -> throw new UnsupportedOperationException( opName + " FilterExpression unsupported type: " + getValue(qualifierMap).getClass().getSimpleName()); }; } - private static Exp getMapExp(Map qualifierMap, List ctxList, Exp.Type expType) { + private static Exp getMapExp(Map qualifierMap, CTX[] ctxArr, Exp.Type expType) { Exp mapKeyExp = getMapKeyExp(getKey(qualifierMap).getObject(), keepOriginalKeyTypes(qualifierMap)); - if (ctxList != null) { + if (ctxArr != null) { return MapExp.getByKey(MapReturnType.VALUE, expType, mapKeyExp, - Exp.mapBin(getBinName(qualifierMap)), resolveCtxList(ctxList)); + Exp.mapBin(getBinName(qualifierMap)), ctxArr); } else { return MapExp.getByKey(MapReturnType.VALUE, expType, mapKeyExp, Exp.mapBin(getBinName(qualifierMap))); @@ -1548,7 +1550,7 @@ private static Exp getFilterExpMapValNotEqOrFail(Map quali private static Exp getMapValEqOrFail(Map qualifierMap, BinaryOperator operator, String opName) { - List ctxList = getCtxList(qualifierMap); + CTX[] ctxArr = getCtxArr(qualifierMap); // boolean values are read as BoolIntValue (INTEGER ParticleType) if Value.UseBoolBin == false // so converting to BooleanValue to process correctly @@ -1558,35 +1560,34 @@ private static Exp getMapValEqOrFail(Map qualifierMap, Bin Value value = getValue(qualifierMap); return switch (value.getType()) { - case INTEGER -> getMapValEqExp(qualifierMap, Exp.Type.INT, value.toLong(), ctxList, operator); + case INTEGER -> getMapValEqExp(qualifierMap, Exp.Type.INT, value.toLong(), ctxArr, operator); case STRING -> { if (ignoreCase(qualifierMap)) { throw new UnsupportedOperationException( opName + " FilterExpression: case insensitive comparison is not supported"); } - yield getMapValEqExp(qualifierMap, Exp.Type.STRING, value.toString(), ctxList, operator); + yield getMapValEqExp(qualifierMap, Exp.Type.STRING, value.toString(), ctxArr, operator); } - case BOOL -> getMapValEqExp(qualifierMap, Exp.Type.BOOL, value.getObject(), ctxList, operator); - case LIST -> getMapValEqExp(qualifierMap, Exp.Type.LIST, value.getObject(), ctxList, operator); - case MAP -> getMapValEqExp(qualifierMap, Exp.Type.MAP, value.getObject(), ctxList, operator); + case BOOL -> getMapValEqExp(qualifierMap, Exp.Type.BOOL, value.getObject(), ctxArr, operator); + case LIST -> getMapValEqExp(qualifierMap, Exp.Type.LIST, value.getObject(), ctxArr, operator); + case MAP -> getMapValEqExp(qualifierMap, Exp.Type.MAP, value.getObject(), ctxArr, operator); default -> throw new UnsupportedOperationException( opName + " FilterExpression unsupported type: " + value.getClass().getSimpleName()); }; } private static Exp getMapValEqExp(Map qualifierMap, Exp.Type expType, Object value, - List ctxList, - BinaryOperator operator) { - Exp mapExp = getMapValEq(qualifierMap, expType, ctxList); + CTX[] ctxArr, BinaryOperator operator) { + Exp mapExp = getMapValEq(qualifierMap, expType, ctxArr); return operator.apply(mapExp, toExp(value)); } - private static Exp getMapValEq(Map qualifierMap, Exp.Type expType, List ctxList) { + private static Exp getMapValEq(Map qualifierMap, Exp.Type expType, CTX[] ctxArr) { Exp bin = getBin(getBinName(qualifierMap), getBinType(qualifierMap), "MAP_VAL_EQ"); - if (ctxList != null) { + if (ctxArr != null && ctxArr.length > 0) { return MapExp.getByKey(MapReturnType.VALUE, expType, getValueExpOrFail(getKey(qualifierMap), "MAP_VAL_EQ: unsupported type"), - bin, resolveCtxList(ctxList)); + bin, ctxArr); } else { return MapExp.getByKey(MapReturnType.VALUE, expType, getValueExpOrFail(getKey(qualifierMap), "MAP_VAL_EQ: unsupported type"), @@ -1613,14 +1614,6 @@ private static Exp getFilterExp(Exp exp, String field, return operator.apply(binExp.apply(field), exp); } - private static CTX[] resolveCtxList(List ctxList) { - return ctxList.stream() - .filter(not(String::isEmpty)) - .map(AerospikeIndexResolverUtils::toCtx) - .filter(Objects::nonNull) - .toArray(CTX[]::new); - } - private static Exp toExp(Object value) { Exp res; if (value instanceof Byte || value instanceof Short || value instanceof Integer || value instanceof Long) { @@ -1662,7 +1655,7 @@ protected static CriteriaDefinition.AerospikeMetadata getMetadataField(Map qualifierMap) { - return (FilterOperation) qualifierMap.get(OPERATION); + return (FilterOperation) qualifierMap.get(FILTER_OPERATION); } protected static Boolean ignoreCase(Map qualifierMap) { @@ -1677,8 +1670,8 @@ protected static Value getKey(Map qualifierMap) { return Value.get(qualifierMap.get(KEY)); } - protected static Object getKeyAsObject(Map qualifierMap) { - return qualifierMap.get(KEY); + protected static String getKeyAsString(Map qualifierMap) { + return (String) qualifierMap.get(KEY); } protected static Value getNestedKey(Map qualifierMap) { @@ -1703,22 +1696,18 @@ protected static Integer getNestedType(Map qualifierMap) { } @SuppressWarnings("unchecked") - protected static List getCtxList(Map qualifierMap) { - String ctxPath = (String) qualifierMap.get(CTX_PATH); - List ctxList = (List) qualifierMap.get(CTX_LIST); - return (ctxList == null) ? getCtxList(ctxPath) : ctxList; + protected static CTX[] getCtxArr(Map qualifierMap) { + return (CTX[]) qualifierMap.get(CTX_ARRAY); } - protected static List getCtxList(String ctxPath) { - if (ctxPath == null) return null; - - return Arrays.stream(ctxPath.split("\\.")) - .filter(not(String::isEmpty)) - .collect(Collectors.toList()); + @SuppressWarnings("unchecked") + protected static String getCtxArrAsString(Map qualifierMap) { + CTX[] ctxArr = (CTX[]) qualifierMap.get(CTX_ARRAY); + return ctxArrToString(ctxArr); } - private static CTX[] getCtx(List ctxList) { - return ctxList != null ? resolveCtxList(ctxList) : null; + private static String ctxArrToString(CTX[] ctxArr) { + return Arrays.stream(ctxArr).map(ctx -> ctx.value.toString()).collect(Collectors.joining(".")); } protected static ServerVersionSupport getServerVersionSupport(Map qualifierMap) { @@ -1735,9 +1724,9 @@ protected Filter collectionContains(IndexCollectionType collectionType, Map Filter.contains(getBinName(qualifierMap), collectionType, val.toLong(), - getCtx(getCtxList(qualifierMap))); + getCtxArr(qualifierMap)); case STRING -> Filter.contains(getBinName(qualifierMap), collectionType, val.toString(), - getCtx(getCtxList(qualifierMap))); + getCtxArr(qualifierMap)); default -> null; }; } diff --git a/src/main/java/org/springframework/data/aerospike/query/KeyQualifier.java b/src/main/java/org/springframework/data/aerospike/query/KeyQualifier.java index 4b62309c2..65cc7f95c 100644 --- a/src/main/java/org/springframework/data/aerospike/query/KeyQualifier.java +++ b/src/main/java/org/springframework/data/aerospike/query/KeyQualifier.java @@ -44,7 +44,7 @@ public class KeyQualifier extends Qualifier { public KeyQualifier(Value value) { super(Qualifier.builder() - .setBinName(QueryEngine.Meta.KEY.toString()) + .setPath(QueryEngine.Meta.KEY.toString()) .setFilterOperation(FilterOperation.EQ) .setValue(value) ); @@ -52,7 +52,7 @@ public KeyQualifier(Value value) { public KeyQualifier(byte[] digest) { super(Qualifier.builder() - .setBinName(QueryEngine.Meta.KEY.toString()) + .setPath(QueryEngine.Meta.KEY.toString()) .setFilterOperation(FilterOperation.EQ) .setValue(null) ); diff --git a/src/main/java/org/springframework/data/aerospike/query/LatestUpdateQualifier.java b/src/main/java/org/springframework/data/aerospike/query/LatestUpdateQualifier.java index a1f4b409d..43cdee000 100644 --- a/src/main/java/org/springframework/data/aerospike/query/LatestUpdateQualifier.java +++ b/src/main/java/org/springframework/data/aerospike/query/LatestUpdateQualifier.java @@ -38,7 +38,7 @@ public class LatestUpdateQualifier extends Qualifier { public LatestUpdateQualifier(FilterOperation op, Value value) { super(Qualifier.builder() - .setBinName("latest_update_time") + .setPath("latest_update_time") .setFilterOperation(op) .setValue(value) ); diff --git a/src/main/java/org/springframework/data/aerospike/query/qualifier/BaseQualifierBuilder.java b/src/main/java/org/springframework/data/aerospike/query/qualifier/BaseQualifierBuilder.java index ce8c65ff3..c154511ad 100644 --- a/src/main/java/org/springframework/data/aerospike/query/qualifier/BaseQualifierBuilder.java +++ b/src/main/java/org/springframework/data/aerospike/query/qualifier/BaseQualifierBuilder.java @@ -7,62 +7,49 @@ import java.util.HashMap; import java.util.Map; -import static org.springframework.data.aerospike.query.qualifier.QualifierKey.BIN_NAME; -import static org.springframework.data.aerospike.query.qualifier.QualifierKey.CTX_PATH; -import static org.springframework.data.aerospike.query.qualifier.QualifierKey.DOT_PATH; -import static org.springframework.data.aerospike.query.qualifier.QualifierKey.KEY; -import static org.springframework.data.aerospike.query.qualifier.QualifierKey.OPERATION; +import static org.springframework.data.aerospike.query.qualifier.QualifierKey.FILTER_OPERATION; +import static org.springframework.data.aerospike.query.qualifier.QualifierKey.IGNORE_CASE; +import static org.springframework.data.aerospike.query.qualifier.QualifierKey.PATH; import static org.springframework.data.aerospike.query.qualifier.QualifierKey.SECOND_VALUE; import static org.springframework.data.aerospike.query.qualifier.QualifierKey.VALUE; @SuppressWarnings("unchecked") -abstract class BaseQualifierBuilder> implements IQualifierBuilder { +public abstract class BaseQualifierBuilder> implements IQualifierBuilder { protected final Map map = new HashMap<>(); + public boolean getIgnoreCase() { + Object ignoreCase = map.get(IGNORE_CASE); + return ignoreCase != null && Boolean.parseBoolean(ignoreCase.toString()); + } + public FilterOperation getFilterOperation() { - return (FilterOperation) map.get(OPERATION); + return (FilterOperation) map.get(FILTER_OPERATION); } /** - * Set filter operation. Mandatory parameter. + * Set FilterOperation for qualifier. Mandatory parameter. */ - public T setFilterOperation(FilterOperation filterOperation) { - map.put(OPERATION, filterOperation); + public T setFilterOperation(FilterOperation operationType) { + map.put(FILTER_OPERATION, operationType); return (T) this; } - public String getBinName() { - return (String) map.get(BIN_NAME); - } - - public boolean hasKey() { - return map.get(KEY) != null; - } - - public boolean hasValue() { - return map.get(VALUE) != null; + public String getPath() { + return (String) map.get(PATH); } public Value getValue() { return (Value) map.get(VALUE); } - public boolean hasSecondValue() { - return map.get(SECOND_VALUE) != null; - } - - public boolean hasDotPath() { - return map.get(DOT_PATH) != null; - } - - public boolean hasCtxPath() { - return map.get(CTX_PATH) != null; + public Value getSecondValue() { + return (Value) map.get(SECOND_VALUE); } public Qualifier build() { validate(); - return new Qualifier(this); + return new Qualifier(process(this)); } public Map getMap() { @@ -72,4 +59,9 @@ public Map getMap() { protected void validate() { // do nothing } + + protected IQualifierBuilder process(BaseQualifierBuilder builder) { + // do nothing + return this; + } } diff --git a/src/main/java/org/springframework/data/aerospike/query/qualifier/MetadataQualifierBuilder.java b/src/main/java/org/springframework/data/aerospike/query/qualifier/MetadataQualifierBuilder.java index 06c39c4cc..f5c16ef46 100644 --- a/src/main/java/org/springframework/data/aerospike/query/qualifier/MetadataQualifierBuilder.java +++ b/src/main/java/org/springframework/data/aerospike/query/qualifier/MetadataQualifierBuilder.java @@ -1,5 +1,6 @@ package org.springframework.data.aerospike.query.qualifier; +import org.springframework.data.aerospike.annotation.Beta; import org.springframework.data.aerospike.query.FilterOperation; import org.springframework.data.aerospike.repository.query.CriteriaDefinition; import org.springframework.util.Assert; @@ -11,6 +12,7 @@ import static org.springframework.data.aerospike.query.qualifier.QualifierKey.SECOND_VALUE; import static org.springframework.data.aerospike.query.qualifier.QualifierKey.VALUE; +@Beta public class MetadataQualifierBuilder extends BaseQualifierBuilder { MetadataQualifierBuilder() { @@ -62,14 +64,14 @@ public MetadataQualifierBuilder setSecondValueAsObj(Object object) { protected void validate() { // metadata query validation if (this.getMetadataField() != null) { - if (this.getBinName() == null) { + if (this.getPath() == null) { if (this.getValueAsObj() != null) { validateValueAsObj(); } else { throw new IllegalArgumentException("Expecting valueAsObj parameter to be provided"); } } else { - throw new IllegalArgumentException("Unexpected parameter: bin name (unnecessary for metadata query)"); + throw new IllegalArgumentException("Unexpected parameter: path (unnecessary for metadata query)"); } } else { throw new IllegalArgumentException("Expecting metadataField parameter to be provided"); diff --git a/src/main/java/org/springframework/data/aerospike/query/qualifier/Qualifier.java b/src/main/java/org/springframework/data/aerospike/query/qualifier/Qualifier.java index 36325d674..735c5e49c 100644 --- a/src/main/java/org/springframework/data/aerospike/query/qualifier/Qualifier.java +++ b/src/main/java/org/springframework/data/aerospike/query/qualifier/Qualifier.java @@ -20,6 +20,7 @@ import com.aerospike.client.command.ParticleType; import com.aerospike.client.exp.Exp; import com.aerospike.client.query.Filter; +import org.springframework.data.aerospike.annotation.Beta; import org.springframework.data.aerospike.config.AerospikeDataSettings; import org.springframework.data.aerospike.query.FilterOperation; import org.springframework.data.aerospike.repository.query.CriteriaDefinition; @@ -56,8 +57,8 @@ protected Qualifier(IQualifierBuilder builder) { } protected Qualifier(Qualifier qualifier) { - if (!qualifier.getMap().isEmpty()) { - internalMap.putAll(qualifier.getMap()); + if (!qualifier.getImmutableMap().isEmpty()) { + internalMap.putAll(qualifier.getImmutableMap()); } } @@ -71,26 +72,32 @@ public String getCriteriaField() { return this.getBinName(); } - private Map getMap() { + private Map getImmutableMap() { return Collections.unmodifiableMap(this.internalMap); } + @Beta public static QualifierBuilder builder() { return new QualifierBuilder(); } + @Beta public static MetadataQualifierBuilder metadataBuilder() { return new MetadataQualifierBuilder(); } public FilterOperation getOperation() { - return (FilterOperation) internalMap.get(OPERATION); + return (FilterOperation) internalMap.get(FILTER_OPERATION); } public String getBinName() { return (String) internalMap.get(BIN_NAME); } + public String getPath() { + return (String) internalMap.get(PATH); + } + public CriteriaDefinition.AerospikeMetadata getMetadataField() { return (CriteriaDefinition.AerospikeMetadata) internalMap.get(METADATA_FIELD); } @@ -123,6 +130,10 @@ public Object getId() { return this.hasSingleId() ? internalMap.get(SINGLE_ID_FIELD) : internalMap.get(MULTIPLE_IDS_FIELD); } + public FilterOperation getFilterOperation() { + return (FilterOperation) internalMap.get(FILTER_OPERATION); + } + public Qualifier[] getQualifiers() { return (Qualifier[]) internalMap.get(QUALIFIERS); } @@ -144,11 +155,6 @@ public List getDotPath() { return (List) internalMap.get(DOT_PATH); } - @SuppressWarnings("unchecked") - public List getCtxPath() { - return (List) internalMap.get(CTX_PATH); - } - public Filter getSecondaryIndexFilter() { return FilterOperation.valueOf(getOperation().toString()).sIndexFilter(internalMap); } diff --git a/src/main/java/org/springframework/data/aerospike/query/qualifier/QualifierBuilder.java b/src/main/java/org/springframework/data/aerospike/query/qualifier/QualifierBuilder.java index eff32692b..074c8389e 100644 --- a/src/main/java/org/springframework/data/aerospike/query/qualifier/QualifierBuilder.java +++ b/src/main/java/org/springframework/data/aerospike/query/qualifier/QualifierBuilder.java @@ -1,16 +1,29 @@ package org.springframework.data.aerospike.query.qualifier; import com.aerospike.client.Value; -import com.aerospike.client.command.ParticleType; -import com.aerospike.client.exp.Exp; +import com.aerospike.client.cdt.CTX; +import org.springframework.data.aerospike.annotation.Beta; +import org.springframework.data.aerospike.index.AerospikeContextDslResolverUtils; import org.springframework.data.aerospike.query.FilterOperation; -import org.springframework.data.aerospike.server.version.ServerVersionSupport; +import org.springframework.data.aerospike.repository.query.QueryQualifierBuilder; import org.springframework.util.StringUtils; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; import java.util.List; - -import static org.springframework.data.aerospike.query.qualifier.QualifierKey.*; - +import java.util.Objects; + +import static java.util.function.Predicate.not; +import static org.springframework.data.aerospike.index.AerospikeContextDslResolverUtils.getCtxType; +import static org.springframework.data.aerospike.index.AerospikeContextDslResolverUtils.isCtxMapKey; +import static org.springframework.data.aerospike.index.AerospikeContextDslResolverUtils.isCtxMapValue; +import static org.springframework.data.aerospike.query.qualifier.QualifierKey.IGNORE_CASE; +import static org.springframework.data.aerospike.query.qualifier.QualifierKey.PATH; +import static org.springframework.data.aerospike.query.qualifier.QualifierKey.SECOND_VALUE; +import static org.springframework.data.aerospike.query.qualifier.QualifierKey.VALUE; + +@Beta public class QualifierBuilder extends BaseQualifierBuilder { QualifierBuilder() { @@ -22,64 +35,70 @@ public QualifierBuilder setIgnoreCase(boolean ignoreCase) { } /** - * Set bin name. Mandatory parameter for bin query. - */ - public QualifierBuilder setBinName(String field) { - this.map.put(BIN_NAME, field); - return this; - } - - /** - * Set bin type. - */ - public QualifierBuilder setBinType(Exp.Type type) { - this.map.put(BIN_TYPE, type); - return this; - } - - /** - * Set full path from bin name to required element. + * Dot separated path consisting of bin name and optional context. Mandatory parameter for bin query. Current + * limitation: if context is given, its last element must be either a List, a Map or a Map key, not Map value. + *

+ * Context is provided using the following DSL. + *
+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
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
+ *
+ * Examples: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
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")]
*/ - public QualifierBuilder setDotPath(List dotPath) { - this.map.put(DOT_PATH, dotPath); - return this; - } - - /** - * Set context path. - */ - public QualifierBuilder setCtx(String ctx) { - this.map.put(CTX_PATH, ctx); - return this; - } - - /** - * Set context path. - */ - public QualifierBuilder setCtxList(List ctxList) { - this.map.put(CTX_LIST, ctxList); - return this; - } - - /** - * Set Map key. - *

- * Use one of the Value get() methods ({@link Value#get(int)}, {@link Value#get(String)} etc.) to firstly read the - * key into a {@link Value} object. - */ - public QualifierBuilder setKey(Value key) { - this.map.put(KEY, key); - return this; - } - - /** - * For "find by one level nested map containing" queries. Set nested Map key. - *

- * Use one of the Value get() methods ({@link Value#get(int)}, {@link Value#get(String)} etc.) to firstly read the - * key into a {@link Value} object. - */ - public QualifierBuilder setNestedKey(Value key) { - this.map.put(NESTED_KEY, key); + public QualifierBuilder setPath(String path) { + this.map.put(PATH, path); return this; } @@ -106,30 +125,13 @@ public QualifierBuilder setSecondValue(Value secondValue) { return this; } - /** - * For "find by one level nested map containing" queries. Set the type of the nested map value using - * {@link ParticleType}. - */ - public QualifierBuilder setNestedType(int type) { - this.map.put(NESTED_TYPE, type); - return this; - } - - /** - * Set server version support. - */ - public QualifierBuilder setServerVersionSupport(ServerVersionSupport serverVersionSupport) { - this.map.put(SERVER_VERSION_SUPPORT, serverVersionSupport); - return this; - } - protected void validate() { - if (!StringUtils.hasText(this.getBinName())) { - throw new IllegalArgumentException("Expecting bin name parameter to be provided"); + if (!StringUtils.hasText(this.getPath())) { + throw new IllegalArgumentException("Expecting path parameter to be provided"); } if (this.getFilterOperation() == null) { - throw new IllegalArgumentException("Expecting filter operation parameter to be provided"); + throw new IllegalArgumentException("Expecting operation type parameter to be provided"); } if (this.getValue() == null @@ -137,5 +139,82 @@ protected void validate() { && this.getFilterOperation() != FilterOperation.IS_NOT_NULL) { throw new IllegalArgumentException("Expecting value parameter to be provided"); } + + List betweenList = List.of(FilterOperation.BETWEEN, FilterOperation.MAP_VAL_BETWEEN_BY_KEY, + FilterOperation.MAP_VAL_BETWEEN, FilterOperation.MAP_KEYS_BETWEEN, FilterOperation.COLLECTION_VAL_BETWEEN); + if (betweenList.contains(this.getFilterOperation()) && + (this.getValue() == null || this.getSecondValue() == null)) { + throw new IllegalArgumentException(this.getFilterOperation() + ": expecting both value and secondValue " + + "to be provided"); + } + } + + protected IQualifierBuilder process(BaseQualifierBuilder externalQb) { + return getInnerQb(externalQb); + } + + private QueryQualifierBuilder getInnerQb(BaseQualifierBuilder externalBuilder) { + QueryQualifierBuilder innerQb; + try { + innerQb = getInnerQualifierBuilderInstance(); + } catch (Exception e) { + throw new IllegalStateException("Cannot initialize QueryQualifierBuilder", e.getCause()); + } + setInnerQbOrFail(innerQb, externalBuilder.getPath()); + innerQb.setInnerQbFilterOperation(externalBuilder.getFilterOperation()); + innerQb.setValue(externalBuilder.getValue()); + if (externalBuilder.getSecondValue() != null) innerQb.setSecondValue(externalBuilder.getSecondValue()); + if (externalBuilder.getIgnoreCase()) innerQb.setIgnoreCase(true); + return innerQb; + } + + protected QueryQualifierBuilder getInnerQualifierBuilderInstance() throws ClassNotFoundException, + NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { + Class qbClass = Class.forName("org.springframework.data.aerospike.repository.query.QueryQualifierBuilder"); + Constructor ctor = qbClass.getDeclaredConstructor(); + ctor.setAccessible(true); + return (QueryQualifierBuilder) ctor.newInstance(); + } + + private void setInnerQbOrFail(QueryQualifierBuilder innerQb, String path) { + CTX[] ctxArr = resolveCtxPath(path); + + if (ctxArr.length >= 1) { + if (!isCtxMapKey(ctxArr[0])) { + throw new IllegalArgumentException(String.format("Cannot resolve the given path '%s', expecting the " + + "first element as a " + + "simple String", path)); + } + innerQb.setBinName(ctxArr[0].value.toString()); + if (ctxArr.length == 1) return; // length == 1 + + CTX lastElement = ctxArr[ctxArr.length - 1]; + if (isCtxMapValue(lastElement)) { + throw new UnsupportedOperationException(String.format("Unsupported path '%s', expecting the last " + + "element not to be a Map value", path)); + } + innerQb.setKey(lastElement.value); // the last element, if present, is map key + + if (ctxArr.length > 2) { + // CTX path: elements between the first and the last + innerQb.setCtxArray(Arrays.copyOfRange(ctxArr, 1, ctxArr.length - 1)); + CTX secondElement = ctxArr[1]; + innerQb.setBinType(getCtxType(secondElement)); // finding bin type by looking at the first CTX element + } else { // length == 2 + innerQb.setBinType(getCtxType(lastElement)); // finding bin type + } + } else { + throw new IllegalArgumentException(String.format("Cannot resolve the given path '%s'", path)); + } + } + + private CTX[] resolveCtxPath(String path) { + if (path == null) return null; + + return Arrays.stream(path.split("\\.")) + .filter(not(String::isEmpty)) + .map(AerospikeContextDslResolverUtils::toCtx) + .filter(Objects::nonNull) + .toArray(CTX[]::new); } } diff --git a/src/main/java/org/springframework/data/aerospike/query/qualifier/QualifierKey.java b/src/main/java/org/springframework/data/aerospike/query/qualifier/QualifierKey.java index 19d33a2d1..d193ed40a 100644 --- a/src/main/java/org/springframework/data/aerospike/query/qualifier/QualifierKey.java +++ b/src/main/java/org/springframework/data/aerospike/query/qualifier/QualifierKey.java @@ -5,14 +5,14 @@ */ public enum QualifierKey { + PATH, BIN_NAME, BIN_TYPE, METADATA_FIELD, SINGLE_ID_FIELD, MULTIPLE_IDS_FIELD, DOT_PATH, - CTX_PATH, - CTX_LIST, + CTX_ARRAY, IGNORE_CASE, KEY, NESTED_KEY, @@ -21,7 +21,7 @@ public enum QualifierKey { NESTED_TYPE, DATA_SETTINGS, QUALIFIERS, - OPERATION, + FILTER_OPERATION, DIGEST_KEY, HAS_SINDEX_FILTER, SERVER_VERSION_SUPPORT diff --git a/src/main/java/org/springframework/data/aerospike/repository/query/AerospikeQueryCreator.java b/src/main/java/org/springframework/data/aerospike/repository/query/AerospikeQueryCreator.java index 085990496..83b723fde 100644 --- a/src/main/java/org/springframework/data/aerospike/repository/query/AerospikeQueryCreator.java +++ b/src/main/java/org/springframework/data/aerospike/repository/query/AerospikeQueryCreator.java @@ -209,18 +209,15 @@ private void logQualifierDetails(CriteriaDefinition criteria) { Arrays.stream(qualifiers).forEach(this::logQualifierDetails); } - String field = (StringUtils.hasLength(qualifier.getBinName()) ? qualifier.getBinName() : ""); + String binName = (StringUtils.hasLength(qualifier.getBinName()) ? qualifier.getBinName() : ""); String operation = qualifier.getOperation().toString(); operation = (StringUtils.hasLength(operation) ? operation : "N/A"); - Value k = qualifier.getKey(); - String key = printValue(k); - Value val = qualifier.getValue(); - String value = printValue(val); - Value val2 = qualifier.getSecondValue(); - String value2 = printValue(val2); - - LOG.debug("Created query: field = {}, operation = {}, key = {}, value = {}, value2 = {}", - field, operation, key, value, value2); + String key = printValue(qualifier.getKey()); + String value = printValue(qualifier.getValue()); + String value2 = printValue(qualifier.getSecondValue()); + + LOG.debug("Created query: bin name = {}, operation = {}, key = {}, value = {}, value2 = {}", + binName, operation, key, value, value2); } private String printValue(Value value) { diff --git a/src/main/java/org/springframework/data/aerospike/repository/query/AerospikeQueryCreatorUtils.java b/src/main/java/org/springframework/data/aerospike/repository/query/AerospikeQueryCreatorUtils.java index 998e69422..2b533716a 100644 --- a/src/main/java/org/springframework/data/aerospike/repository/query/AerospikeQueryCreatorUtils.java +++ b/src/main/java/org/springframework/data/aerospike/repository/query/AerospikeQueryCreatorUtils.java @@ -1,11 +1,12 @@ package org.springframework.data.aerospike.repository.query; import com.aerospike.client.Value; +import com.aerospike.client.cdt.CTX; import org.springframework.data.aerospike.convert.MappingAerospikeConverter; +import org.springframework.data.aerospike.index.AerospikeContextDslResolverUtils; import org.springframework.data.aerospike.mapping.AerospikePersistentProperty; import org.springframework.data.aerospike.query.FilterOperation; import org.springframework.data.aerospike.query.qualifier.Qualifier; -import org.springframework.data.aerospike.query.qualifier.QualifierBuilder; import org.springframework.data.aerospike.repository.query.CriteriaDefinition.AerospikeQueryCriterion; import org.springframework.data.aerospike.server.version.ServerVersionSupport; import org.springframework.data.aerospike.util.Utils; @@ -18,10 +19,12 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.Objects; import java.util.TreeMap; import java.util.stream.Collectors; import java.util.stream.Stream; +import static java.util.function.Predicate.not; import static org.springframework.data.aerospike.convert.AerospikeConverter.CLASS_KEY; import static org.springframework.data.aerospike.repository.query.CriteriaDefinition.AerospikeNullQueryCriterion; import static org.springframework.data.aerospike.repository.query.CriteriaDefinition.AerospikeNullQueryCriterion.NULL_PARAM; @@ -30,22 +33,31 @@ public class AerospikeQueryCreatorUtils { - protected static Qualifier setQualifier(QualifierBuilder qb, String fieldName, FilterOperation op, Part part, + protected static Qualifier setQualifier(QueryQualifierBuilder qb, String binName, FilterOperation op, Part part, List dotPath, ServerVersionSupport versionSupport) { - qb.setBinName(fieldName) - .setFilterOperation(op) + qb.setBinName(binName) + .setInnerQbFilterOperation(op) .setIgnoreCase(ignoreCaseToBoolean(part)); if (dotPath != null && !qb.hasDotPath()) { qb.setDotPath(dotPath); String[] dotPathArr = getDotPathArray(dotPath); if (dotPathArr != null && dotPathArr.length > 2) { - qb.setCtxList(getCtxFromDotPathArray(dotPathArr)); + List ctxList = getCtxFromDotPathArray(dotPathArr); + qb.setCtxArray(resolveCtxList(ctxList)); } } qb.setServerVersionSupport(versionSupport); return qb.build(); } + private static CTX[] resolveCtxList(List ctxList) { + return ctxList.stream() + .filter(not(String::isEmpty)) + .map(AerospikeContextDslResolverUtils::toCtx) + .filter(Objects::nonNull) + .toArray(CTX[]::new); + } + public static String[] getDotPathArray(List dotPathList) { if (dotPathList != null && !dotPathList.isEmpty()) { // the first element of dotPath is part.getProperty().toDotPath() @@ -97,14 +109,14 @@ protected static Class getCollectionElementsClass(PropertyPath property) { } protected static Qualifier qualifierAndConcatenated(ServerVersionSupport versionSupport, List params, - QualifierBuilder qb, + QueryQualifierBuilder qb, Part part, String fieldName, FilterOperation op, List dotPath) { return qualifierAndConcatenated(versionSupport, params, qb, part, fieldName, op, dotPath, false); } protected static Qualifier qualifierAndConcatenated(ServerVersionSupport versionSupport, List params, - QualifierBuilder qb, + QueryQualifierBuilder qb, Part part, String fieldName, FilterOperation op, List dotPath, boolean containingMapKeyValuePairs) { Qualifier[] qualifiers; @@ -139,7 +151,7 @@ protected static String getFieldName(String segmentName, AerospikePersistentProp return segmentName; } - protected static void setQbValuesForMapByKey(QualifierBuilder qb, Object key, Object value) { + protected static void setQbValuesForMapByKey(QueryQualifierBuilder qb, Object key, Object value) { qb.setKey(Value.get(value)); // contains value qb.setValue(Value.get(key)); // contains key } @@ -164,19 +176,19 @@ protected static Value getValueOfQueryParameter(Object queryParameter) { return Value.get(convertNullParameter(queryParameter)); } - protected static void setQualifierBuilderKey(QualifierBuilder qb, Object key) { + protected static void setQualifierBuilderKey(QueryQualifierBuilder qb, Object key) { qb.setKey(getValueOfQueryParameter(key)); } - protected static void setQualifierBuilderSecondKey(QualifierBuilder qb, Object key) { + protected static void setQualifierBuilderSecondKey(QueryQualifierBuilder qb, Object key) { qb.setNestedKey(getValueOfQueryParameter(key)); } - protected static void setQualifierBuilderValue(QualifierBuilder qb, Object value) { + protected static void setQualifierBuilderValue(QueryQualifierBuilder qb, Object value) { qb.setValue(getValueOfQueryParameter(value)); } - protected static void setQualifierBuilderSecondValue(QualifierBuilder qb, Object value) { + protected static void setQualifierBuilderSecondValue(QueryQualifierBuilder qb, Object value) { qb.setSecondValue(getValueOfQueryParameter(value)); } diff --git a/src/main/java/org/springframework/data/aerospike/repository/query/CollectionQueryCreator.java b/src/main/java/org/springframework/data/aerospike/repository/query/CollectionQueryCreator.java index 78862c256..49d79f2af 100644 --- a/src/main/java/org/springframework/data/aerospike/repository/query/CollectionQueryCreator.java +++ b/src/main/java/org/springframework/data/aerospike/repository/query/CollectionQueryCreator.java @@ -5,7 +5,6 @@ import org.springframework.data.aerospike.mapping.AerospikePersistentProperty; import org.springframework.data.aerospike.query.FilterOperation; import org.springframework.data.aerospike.query.qualifier.Qualifier; -import org.springframework.data.aerospike.query.qualifier.QualifierBuilder; import org.springframework.data.aerospike.server.version.ServerVersionSupport; import org.springframework.data.mapping.PropertyPath; import org.springframework.data.repository.query.parser.Part; @@ -132,7 +131,7 @@ private void validateCollectionContainingTypes(PropertyPath property, List dotPath = null; FilterOperation op = filterOperation; @@ -244,7 +243,7 @@ public Qualifier process() { return qualifier; } - private Qualifier processMapTwoParams(QualifierBuilder qb, Part part, List params, FilterOperation op, + private Qualifier processMapTwoParams(QueryQualifierBuilder qb, Part part, List params, FilterOperation op, String fieldName) { Qualifier qualifier; if (op == FilterOperation.CONTAINING) { @@ -260,7 +259,7 @@ private Qualifier processMapTwoParams(QualifierBuilder qb, Part part, List queryParameters, + private Qualifier processMapOtherThanContaining(QueryQualifierBuilder qb, Part part, List queryParameters, FilterOperation op, String fieldName) { Object param1 = queryParameters.get(0); @@ -294,7 +293,7 @@ private Qualifier processMapOtherThanContaining(QualifierBuilder qb, Part part, return setQualifier(qb, fieldName, op, part, dotPath, versionSupport); } - private Qualifier processMapMultipleParams(QualifierBuilder qb) { + private Qualifier processMapMultipleParams(QueryQualifierBuilder qb) { if (filterOperation == FilterOperation.CONTAINING || filterOperation == FilterOperation.NOT_CONTAINING) { return processMapMultipleParamsContaining(qb, part, queryParameters, filterOperation, fieldName, isNested); } else { @@ -302,7 +301,7 @@ private Qualifier processMapMultipleParams(QualifierBuilder qb) { } } - private Qualifier processMapMultipleParamsContaining(QualifierBuilder qb, Part part, List params, + private Qualifier processMapMultipleParamsContaining(QueryQualifierBuilder qb, Part part, List params, FilterOperation op, String fieldName, boolean isNested) { List dotPath; AerospikeQueryCriterion queryCriterion; diff --git a/src/main/java/org/springframework/data/aerospike/repository/query/PojoQueryCreator.java b/src/main/java/org/springframework/data/aerospike/repository/query/PojoQueryCreator.java index 5ec9c561f..576c519c4 100644 --- a/src/main/java/org/springframework/data/aerospike/repository/query/PojoQueryCreator.java +++ b/src/main/java/org/springframework/data/aerospike/repository/query/PojoQueryCreator.java @@ -4,7 +4,6 @@ import org.springframework.data.aerospike.mapping.AerospikePersistentProperty; import org.springframework.data.aerospike.query.FilterOperation; import org.springframework.data.aerospike.query.qualifier.Qualifier; -import org.springframework.data.aerospike.query.qualifier.QualifierBuilder; import org.springframework.data.aerospike.server.version.ServerVersionSupport; import org.springframework.data.mapping.PropertyPath; import org.springframework.data.repository.query.parser.Part; @@ -88,7 +87,7 @@ private void validatePojoQueryBetween(List queryParameters, String query @Override public Qualifier process() { Qualifier qualifier; - QualifierBuilder qb = Qualifier.builder(); + QueryQualifierBuilder qb = new QueryQualifierBuilder(); FilterOperation op = filterOperation; List dotPath = null; diff --git a/src/main/java/org/springframework/data/aerospike/repository/query/QueryQualifierBuilder.java b/src/main/java/org/springframework/data/aerospike/repository/query/QueryQualifierBuilder.java new file mode 100644 index 000000000..de5ba36ec --- /dev/null +++ b/src/main/java/org/springframework/data/aerospike/repository/query/QueryQualifierBuilder.java @@ -0,0 +1,131 @@ +package org.springframework.data.aerospike.repository.query; + +import com.aerospike.client.Value; +import com.aerospike.client.cdt.CTX; +import com.aerospike.client.command.ParticleType; +import com.aerospike.client.exp.Exp; +import org.springframework.data.aerospike.query.FilterOperation; +import org.springframework.data.aerospike.query.qualifier.BaseQualifierBuilder; +import org.springframework.data.aerospike.query.qualifier.QualifierBuilder; +import org.springframework.data.aerospike.server.version.ServerVersionSupport; + +import java.util.List; + +import static org.springframework.data.aerospike.query.qualifier.QualifierKey.*; + +public class QueryQualifierBuilder extends BaseQualifierBuilder { + + QueryQualifierBuilder() { + } + + /** + * Set FilterOperation for qualifier. Mandatory parameter. + */ + public QueryQualifierBuilder setInnerQbFilterOperation(FilterOperation operationType) { + map.put(FILTER_OPERATION, operationType); + return this; + } + + public QueryQualifierBuilder setIgnoreCase(boolean ignoreCase) { + this.map.put(IGNORE_CASE, ignoreCase); + return this; + } + + /** + * Set bin name. Mandatory parameter for bin query. + */ + public QueryQualifierBuilder setBinName(String field) { + this.map.put(BIN_NAME, field); + return this; + } + + /** + * Set bin type. + */ + public QueryQualifierBuilder setBinType(Exp.Type type) { + this.map.put(BIN_TYPE, type); + return this; + } + + /** + * Set full path from bin name to required element. + */ + public QueryQualifierBuilder setDotPath(List dotPath) { + this.map.put(DOT_PATH, dotPath); + return this; + } + + /** + * Set CTX[]. + */ + public QueryQualifierBuilder setCtxArray(CTX[] ctxArray) { + this.map.put(CTX_ARRAY, ctxArray); + return this; + } + + /** + * Set Map key. + *

+ * Use one of the Value get() methods ({@link Value#get(int)}, {@link Value#get(String)} etc.) to firstly read the + * key into a {@link Value} object. + */ + public QueryQualifierBuilder setKey(Value key) { + this.map.put(KEY, key); + return this; + } + + /** + * For "find by one level nested map containing" queries. Set nested Map key. + *

+ * Use one of the Value get() methods ({@link Value#get(int)}, {@link Value#get(String)} etc.) to firstly read the + * key into a {@link Value} object. + */ + public QueryQualifierBuilder setNestedKey(Value key) { + this.map.put(NESTED_KEY, key); + return this; + } + + /** + * Set value. Mandatory parameter for bin query for all operations except {@link FilterOperation#IS_NOT_NULL} and + * {@link FilterOperation#IS_NULL}. + *

+ * Use one of the Value get() methods ({@link Value#get(int)}, {@link Value#get(String)} etc.) to firstly read the + * value into a {@link Value} object. + */ + public QueryQualifierBuilder setValue(Value value) { + this.map.put(VALUE, value); + return this; + } + + /** + * Set second value. + *

+ * Use one of the Value get() methods ({@link Value#get(int)}, {@link Value#get(String)} etc.) to firstly read the + * second value into a {@link Value} object. + */ + public QueryQualifierBuilder setSecondValue(Value secondValue) { + this.map.put(SECOND_VALUE, secondValue); + return this; + } + + /** + * For "find by one level nested map containing" queries. Set the type of the nested map value using + * {@link ParticleType}. + */ + public QueryQualifierBuilder setNestedType(int type) { + this.map.put(NESTED_TYPE, type); + return this; + } + + /** + * Set server version support. + */ + public QueryQualifierBuilder setServerVersionSupport(ServerVersionSupport serverVersionSupport) { + this.map.put(SERVER_VERSION_SUPPORT, serverVersionSupport); + return this; + } + + public boolean hasDotPath() { + return map.get(DOT_PATH) != null; + } +} diff --git a/src/main/java/org/springframework/data/aerospike/repository/query/SimplePropertyQueryCreator.java b/src/main/java/org/springframework/data/aerospike/repository/query/SimplePropertyQueryCreator.java index 59a704c96..486fbcf36 100644 --- a/src/main/java/org/springframework/data/aerospike/repository/query/SimplePropertyQueryCreator.java +++ b/src/main/java/org/springframework/data/aerospike/repository/query/SimplePropertyQueryCreator.java @@ -6,7 +6,6 @@ import org.springframework.data.aerospike.mapping.AerospikePersistentProperty; import org.springframework.data.aerospike.query.FilterOperation; import org.springframework.data.aerospike.query.qualifier.Qualifier; -import org.springframework.data.aerospike.query.qualifier.QualifierBuilder; import org.springframework.data.aerospike.server.version.ServerVersionSupport; import org.springframework.data.mapping.PropertyPath; import org.springframework.data.repository.query.parser.Part; @@ -115,7 +114,7 @@ private void validateSimplePropertyQueryBetween(String queryPartDescription, Lis @Override public Qualifier process() { - QualifierBuilder qb = Qualifier.builder(); + QueryQualifierBuilder qb = new QueryQualifierBuilder(); if (isBooleanQuery) { // setting the value for a boolean query without arguments diff --git a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateCountTests.java b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateCountTests.java index 8f2789111..6f72ecd49 100644 --- a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateCountTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateCountTests.java @@ -62,7 +62,7 @@ public void countFindsAllItemsByGivenCriteria() { long vasyaCount = template.count( new Query(Qualifier.builder() .setFilterOperation(FilterOperation.EQ) - .setBinName("firstName") + .setPath("firstName") .setValue(Value.get("vasili")) .build() ), @@ -73,13 +73,13 @@ public void countFindsAllItemsByGivenCriteria() { Qualifier qbIs1 = Qualifier.builder() .setFilterOperation(FilterOperation.EQ) - .setBinName("firstName") + .setPath("firstName") .setValue(Value.get("vasili")) .build(); Qualifier qbIs2 = Qualifier.builder() .setFilterOperation(FilterOperation.EQ) - .setBinName("age") + .setPath("age") .setValue(Value.get(51)) .build(); @@ -92,7 +92,7 @@ public void countFindsAllItemsByGivenCriteria() { long petyaCount = template.count( new Query(Qualifier.builder() .setFilterOperation(FilterOperation.EQ) - .setBinName("firstName") + .setPath("firstName") .setValue(Value.get("petya")) .build() ), @@ -116,7 +116,7 @@ public void countFindsAllItemsByGivenCriteriaAndRespectsIgnoreCase() { template.insert(new Person(id3, "vasili", 52)); Query query1 = new Query(Qualifier.builder() - .setBinName("firstName") + .setPath("firstName") .setValue(Value.get("vas")) .setFilterOperation(FilterOperation.STARTS_WITH) .setIgnoreCase(true) @@ -125,7 +125,7 @@ public void countFindsAllItemsByGivenCriteriaAndRespectsIgnoreCase() { assertThat(template.count(query1, Person.class)).isEqualTo(3); Query query2 = new Query(Qualifier.builder() - .setBinName("firstName") + .setPath("firstName") .setValue(Value.get("VaS")) .setFilterOperation(FilterOperation.STARTS_WITH) .setIgnoreCase(false) @@ -133,7 +133,6 @@ public void countFindsAllItemsByGivenCriteriaAndRespectsIgnoreCase() { ); assertThat(template.count(query2, Person.class)).isEqualTo(1); - assertThat(template.count(null, Person.class)).isEqualTo(3); template.delete(template.findById(id, Person.class)); @@ -145,7 +144,7 @@ public void countFindsAllItemsByGivenCriteriaAndRespectsIgnoreCase() { public void countReturnsZeroIfNoDocumentsByProvidedCriteriaIsFound() { Query query1 = new Query (Qualifier.builder() - .setBinName("firstName") + .setPath("firstName") .setValue(Value.get("nastyushka")) .setFilterOperation(FilterOperation.STARTS_WITH) .build() diff --git a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateFindByQueryTests.java b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateFindByQueryTests.java index 633d73830..c3f51c189 100644 --- a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateFindByQueryTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateFindByQueryTests.java @@ -468,7 +468,7 @@ public void findAllUsingQuery_shouldRunWithDifferentArgumentsCombinations() { // find by query Qualifier qualifier = Qualifier.builder() - .setBinName(fieldName) + .setPath(fieldName) .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(fieldValue1)) .build(); @@ -479,12 +479,12 @@ public void findAllUsingQuery_shouldRunWithDifferentArgumentsCombinations() { // find by query with a complex qualifier Qualifier dataEqFieldValue1 = Qualifier.builder() .setFilterOperation(FilterOperation.EQ) - .setBinName(fieldName) + .setPath(fieldName) .setValue(Value.get(fieldValue1)) .build(); Qualifier dataEqFieldValue2 = Qualifier.builder() .setFilterOperation(FilterOperation.EQ) - .setBinName(fieldName) + .setPath(fieldName) .setValue(Value.get(fieldValue2)) .build(); Qualifier qualifierOr = Qualifier.or(dataEqFieldValue1, dataEqFieldValue2); diff --git a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateCountRelatedTests.java b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateCountRelatedTests.java index 89628ade5..a45c8dd2f 100644 --- a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateCountRelatedTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateCountRelatedTests.java @@ -48,7 +48,7 @@ void count_shouldFindAllItemsByGivenCriteria() { reactiveTemplate.insert(new Person(id4, "petya", 52)).block(); QualifierBuilder qbVasya1 = Qualifier.builder() - .setBinName("firstName") + .setPath("firstName") .setValue(Value.get("vasili")) .setFilterOperation(FilterOperation.EQ); @@ -60,7 +60,7 @@ void count_shouldFindAllItemsByGivenCriteria() { assertThat(vasyaCount).isEqualTo(3); QualifierBuilder qbVasya2 = Qualifier.builder() - .setBinName("age") + .setPath("age") .setValue(Value.get(51)) .setFilterOperation(FilterOperation.EQ); @@ -72,7 +72,7 @@ void count_shouldFindAllItemsByGivenCriteria() { assertThat(vasya51Count).isEqualTo(1); QualifierBuilder qbPetya = Qualifier.builder() - .setBinName("firstName") + .setPath("firstName") .setValue(Value.get("petya")) .setFilterOperation(FilterOperation.EQ); Long petyaCount = reactiveTemplate.count(new Query(qbPetya.build()), Person.class) @@ -94,7 +94,7 @@ void count_shouldFindAllItemsByGivenCriteriaAndRespectsIgnoreCase() { reactiveTemplate.insert(new Person(id3, "vasili", 52)).block(); QualifierBuilder qbVasya1 = Qualifier.builder() - .setBinName("firstName") + .setPath("firstName") .setValue(Value.get("vas")) .setIgnoreCase(true) .setFilterOperation(FilterOperation.STARTS_WITH); @@ -103,7 +103,7 @@ void count_shouldFindAllItemsByGivenCriteriaAndRespectsIgnoreCase() { assertThat(reactiveTemplate.count(query1, Person.class).block()).isEqualTo(3); QualifierBuilder qbVasya2 = Qualifier.builder() - .setBinName("firstName") + .setPath("firstName") .setValue(Value.get("VaS")) .setIgnoreCase(false) .setFilterOperation(FilterOperation.STARTS_WITH); @@ -117,7 +117,7 @@ void count_shouldFindAllItemsByGivenCriteriaAndRespectsIgnoreCase() { @Test void count_shouldReturnZeroIfNoDocumentsByProvidedCriteriaIsFound() { QualifierBuilder qb1 = Qualifier.builder() - .setBinName("firstName") + .setPath("firstName") .setValue(Value.get("nastyushka")) .setFilterOperation(FilterOperation.EQ); diff --git a/src/test/java/org/springframework/data/aerospike/index/IndexedAnnotationTests.java b/src/test/java/org/springframework/data/aerospike/index/IndexedAnnotationTests.java index 45aa36935..01a5c7aca 100644 --- a/src/test/java/org/springframework/data/aerospike/index/IndexedAnnotationTests.java +++ b/src/test/java/org/springframework/data/aerospike/index/IndexedAnnotationTests.java @@ -32,7 +32,7 @@ class TestPerson { @Id String id; - @Indexed(type = IndexType.STRING, name = "test_person_friend_address_keys_index", + @Indexed(type = IndexType.STRING, name = "test_person_friend_address_keys_index", bin = "friend", collectionType = IndexCollectionType.MAPKEYS, ctx = "address") // CTX.mapKey(Value.get("address")) TestFriend friend; } @@ -44,8 +44,7 @@ class TestPerson { .equals("test_person_friend_address_keys_index") && CTX.toBase64(index.getCtx()).equals(CTX.toBase64(new CTX[]{CTX.mapKey(Value.get("address"))})) - ) - .count() + ).count() ).isEqualTo(1L); assertThat(indexesCache.hasIndexFor(new IndexedField(namespace, template.getSetName(TestPerson.class), "friend"))).isTrue(); @@ -59,8 +58,7 @@ class TestPerson { .equals("test_person_friend_address_keys_index") && CTX.toBase64(index.getCtx()).equals(CTX.toBase64(new CTX[]{CTX.mapKey(Value.get("address"))})) - ) - .count() + ).count() ).isZero(); assertThat(indexesCache.hasIndexFor(new IndexedField(namespace, template.getSetName(TestPerson.class), "friend"))).isFalse(); @@ -90,8 +88,7 @@ class TestPerson { .equals("test_person_friend_address_keys_index") && CTX.toBase64(index.getCtx()).equals(CTX.toBase64(new CTX[]{CTX.mapKey(Value.get("address"))})) - ) - .count() + ).count() ).isEqualTo(1L); assertThat(indexesCache.hasIndexFor(new IndexedField(namespace, template.getSetName(TestPerson.class), "friend"))).isTrue(); @@ -105,8 +102,7 @@ class TestPerson { .equals("test_person_friend_address_keys_index") && CTX.toBase64(index.getCtx()).equals(CTX.toBase64(new CTX[]{CTX.mapKey(Value.get("address"))})) - ) - .count() + ).count() ).isZero(); assertThat(indexesCache.hasIndexFor(new IndexedField(namespace, template.getSetName(TestPerson.class), "friend"))).isFalse(); @@ -124,7 +120,7 @@ class TestPerson { @Id String id; - @Indexed(type = IndexType.STRING, name = "test_person_friend_address_keys_index", + @Indexed(type = IndexType.STRING, name = "test_person_friend_address_keys_index", bin = "friend", collectionType = IndexCollectionType.MAPKEYS, ctx = "ab.cd.'10'.{#5}.{='1'}.[-1].[#100].[=20]") // CTX.mapKey(Value.get("ab")), CTX.mapKey(Value.get("cd")), CTX.mapKey(Value.get("10")), CTX.mapRank(5), // CTX.mapValue(Value.get("1")), CTX.listIndex(-1), CTX.listRank(100), CTX.listValue(Value.get(20)) @@ -141,14 +137,12 @@ class TestPerson { CTX.mapKey(Value.get("cd")), CTX.mapKey(Value.get("10")), CTX.mapRank(5), CTX.mapValue(Value.get("1")), CTX.listIndex(-1), CTX.listRank(100), CTX.listValue(Value.get(20))})) - ) - .count() + ).count() ).isEqualTo(1L); assertThat(indexesCache.hasIndexFor(new IndexedField(namespace, template.getSetName(TestPerson.class), "friend"))).isTrue(); - additionalAerospikeTestOperations.dropIndex(IndexedPerson.class, - "test_person_friend_address_keys_index"); + additionalAerospikeTestOperations.dropIndex(IndexedPerson.class, "test_person_friend_address_keys_index"); } @Test @@ -163,7 +157,7 @@ class TestPerson { @Id String id; - @Indexed(type = IndexType.STRING, name = "test_person_friend_address_keys_index", + @Indexed(type = IndexType.STRING, name = "test_person_friend_address_keys_index", bin = "friend", collectionType = IndexCollectionType.MAPKEYS, ctx = "\"10\".{=\"1\"}") // CTX.mapKey(Value.get("10")), CTX.mapValue(Value.get("1")) TestFriend friend; @@ -177,14 +171,12 @@ class TestPerson { && CTX.toBase64(index.getCtx()).equals(CTX.toBase64(new CTX[]{CTX.mapKey(Value.get("10")), CTX.mapValue(Value.get("1"))})) - ) - .count() + ).count() ).isEqualTo(1L); assertThat(indexesCache.hasIndexFor(new IndexedField(namespace, template.getSetName(TestPerson.class), "friend"))).isTrue(); - additionalAerospikeTestOperations.dropIndex(IndexedPerson.class, - "test_person_friend_address_keys_index"); + additionalAerospikeTestOperations.dropIndex(IndexedPerson.class, "test_person_friend_address_keys_index"); } @Test @@ -193,7 +185,7 @@ class TestPerson { @Id String id; - @Indexed(type = IndexType.STRING, name = "test_person_many_dots_index", + @Indexed(type = IndexType.STRING, name = "test_person_many_dots_index", bin = "friend", collectionType = IndexCollectionType.MAPKEYS, ctx = "ab....cd..") String someField; } @@ -210,7 +202,7 @@ class TestPerson { @Id String id; - @Indexed(type = IndexType.STRING, name = "test_person_too_small_context_length_index", + @Indexed(type = IndexType.STRING, name = "test_person_too_small_context_length_index", bin = "friend", collectionType = IndexCollectionType.MAPKEYS, ctx = "ab.[]") String someField; } @@ -218,7 +210,7 @@ class TestPerson { assertThatThrownBy(() -> additionalAerospikeTestOperations.getIndexes(template.getSetName(TestPerson.class))) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("@Indexed annotation: context string '[]' has no content"); + .hasMessage("Context DSL: string '[]' has no content"); } @Test @@ -227,7 +219,7 @@ class TestPerson { @Id String id; - @Indexed(type = IndexType.STRING, name = "test_person_wrong_closing_bracket_index", + @Indexed(type = IndexType.STRING, name = "test_person_wrong_closing_bracket_index", bin = "friend", collectionType = IndexCollectionType.MAPKEYS, ctx = "ab.{cd]") String someField; } @@ -235,7 +227,7 @@ class TestPerson { assertThatThrownBy(() -> additionalAerospikeTestOperations.getIndexes(template.getSetName(TestPerson.class))) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("@Indexed annotation: brackets mismatch, expecting '}', got ']' instead"); + .hasMessage("Context DSL: brackets mismatch, expecting '}', got ']' instead"); } @Test @@ -244,7 +236,7 @@ class TestPerson { @Id String id; - @Indexed(type = IndexType.STRING, name = "test_person_friend_address_keys_index", + @Indexed(type = IndexType.STRING, name = "test_person_friend_address_keys_index", bin = "friend", collectionType = IndexCollectionType.MAPKEYS, ctx = "{#address}") // rank must be integer String someField; } @@ -252,7 +244,7 @@ class TestPerson { assertThatThrownBy(() -> additionalAerospikeTestOperations.getIndexes(template.getSetName(TestPerson.class))) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("@Indexed annotation map rank: expecting only integer values, got 'address' instead"); + .hasMessage("Context DSL map rank: expecting only integer values, got 'address' instead"); } @Test @@ -261,7 +253,7 @@ class TestPerson { @Id String id; - @Indexed(type = IndexType.STRING, name = "test_person_friend_address_keys_index", + @Indexed(type = IndexType.STRING, name = "test_person_friend_address_keys_index", bin = "friend", collectionType = IndexCollectionType.MAPKEYS, ctx = "[#address]") // rank must be integer String someField; } @@ -269,7 +261,7 @@ class TestPerson { assertThatThrownBy(() -> additionalAerospikeTestOperations.getIndexes(template.getSetName(TestPerson.class))) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("@Indexed annotation list rank: expecting only integer values, got 'address' instead"); + .hasMessage("Context DSL list rank: expecting only integer values, got 'address' instead"); } @Test @@ -278,7 +270,7 @@ class TestPerson { @Id String id; - @Indexed(type = IndexType.STRING, name = "test_person_friend_address_keys_index", + @Indexed(type = IndexType.STRING, name = "test_person_friend_address_keys_index", bin = "friend", collectionType = IndexCollectionType.MAPKEYS, ctx = "{address}") // index must be integer String someField; } @@ -286,7 +278,7 @@ class TestPerson { assertThatThrownBy(() -> additionalAerospikeTestOperations.getIndexes(template.getSetName(TestPerson.class))) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("@Indexed annotation map index: expecting only integer values, got 'address' instead"); + .hasMessage("Context DSL map index: expecting only integer values, got 'address' instead"); } @Test @@ -295,7 +287,7 @@ class TestPerson { @Id String id; - @Indexed(type = IndexType.STRING, name = "test_person_friend_address_keys_index", + @Indexed(type = IndexType.STRING, name = "test_person_friend_address_keys_index", bin = "friend", collectionType = IndexCollectionType.MAPKEYS, ctx = "[address]") // index must be integer String someField; } @@ -303,6 +295,6 @@ class TestPerson { assertThatThrownBy(() -> additionalAerospikeTestOperations.getIndexes(template.getSetName(TestPerson.class))) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("@Indexed annotation list index: expecting only integer values, got 'address' instead"); + .hasMessage("Context DSL list index: expecting only integer values, got 'address' instead"); } } diff --git a/src/test/java/org/springframework/data/aerospike/logging/LoggingTests.java b/src/test/java/org/springframework/data/aerospike/logging/LoggingTests.java index 96c692324..4b41a5056 100644 --- a/src/test/java/org/springframework/data/aerospike/logging/LoggingTests.java +++ b/src/test/java/org/springframework/data/aerospike/logging/LoggingTests.java @@ -48,7 +48,7 @@ public static void setup() { void binIsIndexed() { IndexesCache indexesCacheMock = Mockito.mock(IndexesCache.class); Qualifier qualifier = Qualifier.builder() - .setBinName("testField") + .setPath("testField") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get("testValue1")) .build(); @@ -75,7 +75,7 @@ void queryIsCreated() { creator.createQuery(); assertThat(memoryAppender.countEventsForLogger(LOGGER_NAME)).isPositive(); - String msg = "Created query: field = firstName, operation = EQ, key = , value = TestName, value2 = "; + String msg = "Created query: bin name = firstName, operation = EQ, key = , value = TestName, value2 = "; assertThat(memoryAppender.search(msg, Level.DEBUG).size()).isEqualTo(1); assertThat(memoryAppender.contains(msg, Level.INFO)).isFalse(); } diff --git a/src/test/java/org/springframework/data/aerospike/query/IndexedQualifierTests.java b/src/test/java/org/springframework/data/aerospike/query/IndexedQualifierTests.java index 23bbfe15a..e2c66ae9f 100644 --- a/src/test/java/org/springframework/data/aerospike/query/IndexedQualifierTests.java +++ b/src/test/java/org/springframework/data/aerospike/query/IndexedQualifierTests.java @@ -52,7 +52,7 @@ void selectOnIndexedLTQualifier() { withIndex(namespace, INDEXED_SET_NAME, "age_index", "age", IndexType.NUMERIC, () -> { // Ages range from 25 -> 29. We expected to only get back values with age < 26 Qualifier qualifier = Qualifier.builder() - .setBinName("age") + .setPath("age") .setFilterOperation(FilterOperation.LT) .setValue(Value.get(26)) .build(); @@ -73,7 +73,7 @@ void selectOnIndexedLTEQQualifier() { withIndex(namespace, INDEXED_SET_NAME, "age_index", "age", IndexType.NUMERIC, () -> { // Ages range from 25 -> 29. We expected to only get back values with age <= 26 Qualifier qualifier = Qualifier.builder() - .setBinName("age") + .setPath("age") .setFilterOperation(FilterOperation.LTEQ) .setValue(Value.get(26)) .build(); @@ -97,7 +97,7 @@ void selectOnIndexedNumericEQQualifier() { withIndex(namespace, INDEXED_SET_NAME, "age_index", "age", IndexType.NUMERIC, () -> { // Ages range from 25 -> 29. We expected to only get back values with age == 26 Qualifier qualifier = Qualifier.builder() - .setBinName("age") + .setPath("age") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(26)) .build(); @@ -117,7 +117,7 @@ void selectOnIndexedNumericEQQualifier() { void selectWithBlueColorQuery() { withIndex(namespace, INDEXED_SET_NAME, "age_index", "age", IndexType.NUMERIC, () -> { Qualifier qual1 = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(BLUE)) .build(); @@ -137,7 +137,7 @@ void selectOnIndexedGTEQQualifier() { withIndex(namespace, INDEXED_SET_NAME, "age_index", "age", IndexType.NUMERIC, () -> { // Ages range from 25 -> 29. We expected to only get back values with age >= 28 Qualifier qualifier = Qualifier.builder() - .setBinName("age") + .setPath("age") .setFilterOperation(FilterOperation.GTEQ) .setValue(Value.get(28)) .build(); @@ -160,7 +160,7 @@ void selectOnIndexedGTEQQualifier() { void selectOnIndexedGTQualifier() { withIndex(namespace, INDEXED_SET_NAME, "age_index", "age", IndexType.NUMERIC, () -> { Qualifier qualifier = Qualifier.builder() - .setBinName("age") + .setPath("age") .setFilterOperation(FilterOperation.GT) .setValue(Value.get(28)) .build(); @@ -180,7 +180,7 @@ void selectOnIndexedGTQualifier() { void selectOnIndexedStringEQQualifier() { withIndex(namespace, INDEXED_SET_NAME, "color_index", "color", IndexType.STRING, () -> { Qualifier qualifier = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(ORANGE)) .build(); @@ -208,7 +208,7 @@ void selectWithGeoWithin() { lon, lat, radius); Qualifier qualifier = Qualifier.builder() - .setBinName(GEO_BIN_NAME) + .setPath(GEO_BIN_NAME) .setFilterOperation(FilterOperation.GEO_WITHIN) .setValue(Value.getAsGeoJSON(rgnstr)) .build(); @@ -243,12 +243,12 @@ void selectWithoutQuery() { @Test void selectWithQualifiersOnly() { Qualifier qual1 = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(GREEN)) .build(); Qualifier qual2 = Qualifier.builder() - .setBinName("age") + .setPath("age") .setFilterOperation(FilterOperation.BETWEEN) .setValue(Value.get(28)) .setSecondValue(Value.get(29)) @@ -268,12 +268,12 @@ void selectWithQualifiersOnly() { @Test void selectWithAndQualifier() { Qualifier colorIsGreen = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(GREEN)) .build(); Qualifier ageBetween28And29 = Qualifier.builder() - .setBinName("age") + .setPath("age") .setFilterOperation(FilterOperation.BETWEEN) .setValue(Value.get(28)) .setSecondValue(Value.get(29)) diff --git a/src/test/java/org/springframework/data/aerospike/query/QualifierTests.java b/src/test/java/org/springframework/data/aerospike/query/QualifierTests.java index b7fe295fd..b97c60047 100644 --- a/src/test/java/org/springframework/data/aerospike/query/QualifierTests.java +++ b/src/test/java/org/springframework/data/aerospike/query/QualifierTests.java @@ -59,7 +59,7 @@ void throwsExceptionWhenScansDisabled() { queryEngine.setScansEnabled(false); try { Qualifier qualifier = Qualifier.builder() - .setBinName("age") + .setPath("age") // bin name .setFilterOperation(FilterOperation.LT) .setValue(Value.get(26)).build(); @@ -76,9 +76,7 @@ void throwsExceptionWhenScansDisabled() { @Test void selectOneWitKey() { KeyQualifier kq = new KeyQualifier(Value.get("selector-test:3")); - KeyRecordIterator iterator = queryEngine.select(namespace, SET_NAME, null, new Query(kq)); - assertThat(iterator).toIterable().hasSize(1); } @@ -86,16 +84,13 @@ void selectOneWitKey() { @Test void selectOneWitKeyNonExisting() { KeyQualifier kq = new KeyQualifier(Value.get("selector-test:unknown")); - KeyRecordIterator iterator = queryEngine.select(namespace, SET_NAME, null, new Query(kq)); - assertThat(iterator).toIterable().isEmpty(); } @Test void selectAll() { KeyRecordIterator iterator = queryEngine.select(namespace, SET_NAME, null, null); - assertThat(iterator).toIterable().hasSize(RECORD_COUNT); } @@ -103,7 +98,7 @@ void selectAll() { void lTQualifier() { // Ages range from 25 -> 29. We expected to only get back values with age < 26 Qualifier qualifier = Qualifier.builder() - .setBinName("age") + .setPath("age") // bin name .setFilterOperation(FilterOperation.LT) .setValue(Value.get(26)) .build(); @@ -121,7 +116,7 @@ void lTQualifier() { void numericLTEQQualifier() { // Ages range from 25 -> 29. We expected to only get back values with age <= 26 Qualifier qualifier = Qualifier.builder() - .setBinName("age") + .setPath("age") // bin name .setFilterOperation(FilterOperation.LTEQ) .setValue(Value.get(26)) .build(); @@ -142,7 +137,7 @@ void numericLTEQQualifier() { void numericEQQualifier() { // Ages range from 25 -> 29. We expected to only get back values with age == 26 Qualifier qualifier = Qualifier.builder() - .setBinName("age") + .setPath("age") // bin name .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(26)) .build(); @@ -160,7 +155,7 @@ void numericEQQualifier() { void numericGTEQQualifier() { // Ages range from 25 -> 29. We expected to only get back values with age >= 28 Qualifier qualifier = Qualifier.builder() - .setBinName("age") + .setPath("age") // bin name .setFilterOperation(FilterOperation.GTEQ) .setValue(Value.get(28)) .build(); @@ -181,7 +176,7 @@ void numericGTEQQualifier() { void numericGTQualifier() { // Ages range from 25 -> 29. We expected to only get back values with age > 28 or equivalently == 29 Qualifier qualifier = Qualifier.builder() - .setBinName("age") + .setPath("age") // bin name .setFilterOperation(FilterOperation.GT) .setValue(Value.get(28)) .build(); @@ -215,7 +210,7 @@ void metadataSinceUpdateEQQualifier() { @Test void stringEQQualifier() { Qualifier qualifier = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(ORANGE)) .build(); @@ -232,7 +227,7 @@ void stringEQQualifier() { @Test void stringEQIgnoreCaseQualifier() { Qualifier qualifier = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.EQ) .setIgnoreCase(true) .setValue(Value.get(ORANGE.toUpperCase())) @@ -251,7 +246,7 @@ void stringEQIgnoreCaseQualifier() { void stringEqualIgnoreCaseWorksOnUnindexedBin() { boolean ignoreCase = true; Qualifier qualifier = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.EQ) .setIgnoreCase(ignoreCase) .setValue(Value.get("BlUe")) @@ -271,7 +266,7 @@ void stringEqualIgnoreCaseWorksOnIndexedBin() { withIndex(namespace, SET_NAME, "color_index_selector", "color", IndexType.STRING, () -> { boolean ignoreCase = true; Qualifier qualifier = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.EQ) .setIgnoreCase(ignoreCase) .setValue(Value.get("BlUe")) @@ -294,7 +289,7 @@ void stringEqualIgnoreCaseWorksOnIndexedBin() { void stringEqualIgnoreCaseWorksRequiresFullMatch() { boolean ignoreCase = true; Qualifier qualifier = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.EQ) .setIgnoreCase(ignoreCase) .setValue(Value.get("lue")) @@ -308,7 +303,7 @@ void stringEqualIgnoreCaseWorksRequiresFullMatch() { @Test void stringStartWithQualifier() { Qualifier qualifier = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.STARTS_WITH) .setValue(Value.get(BLUE.substring(0, 2))) .build(); @@ -325,7 +320,7 @@ void stringStartWithQualifier() { @Test void stringStartWithEntireWordQualifier() { Qualifier qualifier = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.STARTS_WITH) .setValue(Value.get(BLUE)) .build(); @@ -342,7 +337,7 @@ void stringStartWithEntireWordQualifier() { @Test void stringStartWithICASEQualifier() { Qualifier qualifier = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.STARTS_WITH) .setIgnoreCase(true) .setValue(Value.get("BLU")) @@ -360,7 +355,7 @@ void stringStartWithICASEQualifier() { @Test void stringEndsWithQualifier() { Qualifier qualifier = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.ENDS_WITH) .setValue(Value.get(GREEN.substring(2))) .build(); @@ -377,7 +372,7 @@ void stringEndsWithQualifier() { @Test void selectEndsWith() { Qualifier qualifier = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.ENDS_WITH) .setValue(Value.get("e")) .build(); @@ -394,7 +389,7 @@ void selectEndsWith() { @Test void stringEndsWithEntireWordQualifier() { Qualifier qualifier = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.ENDS_WITH) .setValue(Value.get(GREEN)) .build(); @@ -412,7 +407,7 @@ void stringEndsWithEntireWordQualifier() { void betweenQualifier() { // Ages range from 25 -> 29. Get back age between 26 and 28 inclusive Qualifier qualifier = Qualifier.builder() - .setBinName("age") + .setPath("age") .setFilterOperation(FilterOperation.BETWEEN) .setValue(Value.get(26)) .setSecondValue(Value.get(29)) // + 1 as upper limit is exclusive @@ -438,7 +433,7 @@ void containingQualifier() { .collect(Collectors.toMap(color -> color, color -> queryEngineTestDataPopulator.colourCounts.get(color))); Qualifier qualifier = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.CONTAINING) .setValue(Value.get("l")) .build(); @@ -458,7 +453,7 @@ void inQualifier() { .collect(Collectors.toMap(color -> color, color -> queryEngineTestDataPopulator.colourCounts.get(color))); Qualifier qualifier = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.IN) .setValue(Value.get(inColors)) .build(); @@ -477,7 +472,7 @@ void listContainsQualifier() { String binName = "colorList"; Qualifier qualifier = Qualifier.builder() - .setBinName(binName) + .setPath(binName) .setFilterOperation(FilterOperation.COLLECTION_VAL_CONTAINING) .setValue(Value.get(searchColor)) .build(); @@ -501,7 +496,7 @@ void mapKeysContainQualifier() { String binName = "colorAgeMap"; Qualifier qualifier = Qualifier.builder() - .setBinName(binName) + .setPath(binName) .setFilterOperation(FilterOperation.MAP_KEYS_CONTAIN) .setValue(Value.get(searchColor)) .build(); @@ -524,7 +519,7 @@ void mapValuesContainQualifier() { String binName = "ageColorMap"; Qualifier qualifier = Qualifier.builder() - .setBinName(binName) + .setPath(binName) .setFilterOperation(FilterOperation.MAP_VALUES_CONTAIN) .setValue(Value.get(searchColor)) .build(); @@ -544,7 +539,7 @@ void mapValuesContainQualifier() { @Test void containingDoesNotUseSpecialCharacterQualifier() { Qualifier qualifier = Qualifier.builder() - .setBinName(SPECIAL_CHAR_BIN) + .setPath(SPECIAL_CHAR_BIN) .setFilterOperation(FilterOperation.CONTAINING) .setValue(Value.get(".*")) .build(); @@ -560,7 +555,7 @@ void containingDoesNotUseSpecialCharacterQualifier() { @Test void startWithDoesNotUseSpecialCharacterQualifier() { Qualifier qualifier = Qualifier.builder() - .setBinName(SPECIAL_CHAR_BIN) + .setPath(SPECIAL_CHAR_BIN) .setFilterOperation(FilterOperation.STARTS_WITH) .setValue(Value.get(".*")) .build(); @@ -576,7 +571,7 @@ void startWithDoesNotUseSpecialCharacterQualifier() { @Test void endWithDoesNotUseSpecialCharacterQualifier() { Qualifier qualifier = Qualifier.builder() - .setBinName(SPECIAL_CHAR_BIN) + .setPath(SPECIAL_CHAR_BIN) .setFilterOperation(FilterOperation.ENDS_WITH) .setValue(Value.get(".*")) .build(); @@ -592,7 +587,7 @@ void endWithDoesNotUseSpecialCharacterQualifier() { @Test void eQIcaseDoesNotUseSpecialCharacter() { Qualifier qualifier = Qualifier.builder() - .setBinName(SPECIAL_CHAR_BIN) + .setPath(SPECIAL_CHAR_BIN) .setFilterOperation(FilterOperation.EQ) .setIgnoreCase(true) .setValue(Value.get(".*")) @@ -607,7 +602,7 @@ void eQIcaseDoesNotUseSpecialCharacter() { @ValueSource(strings = {"[", "$", "\\", "^"}) void containingFindsSquareBracket(String specialString) { Qualifier qualifier = Qualifier.builder() - .setBinName(SPECIAL_CHAR_BIN) + .setPath(SPECIAL_CHAR_BIN) .setFilterOperation(FilterOperation.CONTAINING) .setIgnoreCase(true) .setValue(Value.get(specialString)) @@ -630,7 +625,7 @@ void selectWithGeoWithin() { + "\"coordinates\": [[%.8f, %.8f], %f] }", lon, lat, radius); Qualifier qualifier = Qualifier.builder() - .setBinName(GEO_BIN_NAME) + .setPath(GEO_BIN_NAME) .setFilterOperation(FilterOperation.GEO_WITHIN) .setValue(Value.getAsGeoJSON(rgnstr)) .build(); @@ -646,14 +641,14 @@ void selectWithGeoWithin() { void startWithAndEqualIgnoreCaseReturnsAllItems() { boolean ignoreCase = true; Qualifier qual1 = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.EQ) .setIgnoreCase(ignoreCase) .setValue(Value.get(BLUE.toUpperCase())) .build(); Qualifier qual2 = Qualifier.builder() - .setBinName("name") + .setPath("name") .setFilterOperation(FilterOperation.STARTS_WITH) .setIgnoreCase(ignoreCase) .setValue(Value.get("NA")) @@ -671,7 +666,7 @@ void startWithAndEqualIgnoreCaseReturnsAllItems() { void equalIgnoreCaseReturnsNoItemsIfNoneMatched() { boolean ignoreCase = false; Qualifier qual1 = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.EQ) .setIgnoreCase(ignoreCase) .setValue(Value.get(BLUE.toUpperCase())) @@ -686,7 +681,7 @@ void equalIgnoreCaseReturnsNoItemsIfNoneMatched() { void startWithIgnoreCaseReturnsNoItemsIfNoneMatched() { boolean ignoreCase = false; Qualifier qual1 = Qualifier.builder() - .setBinName("name") + .setPath("name") .setFilterOperation(FilterOperation.STARTS_WITH) .setIgnoreCase(ignoreCase) .setValue(Value.get("NA")) @@ -700,23 +695,23 @@ void startWithIgnoreCaseReturnsNoItemsIfNoneMatched() { @Test void selectWithBetweenAndOrQualifiers() { Qualifier colorIsGreen = Qualifier.builder() - .setBinName("color") + .setPath("color") // bin name .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(GREEN)) .build(); Qualifier ageBetween28And29 = Qualifier.builder() - .setBinName("age") + .setPath("age") // bin name .setFilterOperation(FilterOperation.BETWEEN) - .setKey(Value.get(28)) - .setValue(Value.get(29)) + .setValue(Value.get(28)) + .setSecondValue(Value.get(29)) .build(); Qualifier ageIs25 = Qualifier.builder() - .setBinName("age") + .setPath("age") // bin name .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(25)) .build(); Qualifier nameIs696 = Qualifier.builder() - .setBinName("name") + .setPath("name") // bin name .setFilterOperation(FilterOperation.EQ) .setValue(Value.get("name:696")) .build(); @@ -749,12 +744,12 @@ void selectWithBetweenAndOrQualifiers() { void selectWithOrQualifiers() { // We are expecting to get back all records where color == blue or (age == 28 || age == 29) Qualifier colorIsBlue = Qualifier.builder() - .setBinName("color") + .setPath("color") // bin name .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(BLUE)) .build(); Qualifier ageBetween28And29 = Qualifier.builder() - .setBinName("age") + .setPath("age") // bin name .setFilterOperation(FilterOperation.BETWEEN) .setValue(Value.get(28)) .setSecondValue(Value.get(30)) // + 1 as upper limit is exclusive diff --git a/src/test/java/org/springframework/data/aerospike/query/UsersTests.java b/src/test/java/org/springframework/data/aerospike/query/UsersTests.java index c1fb0f2bb..b1396bc3f 100644 --- a/src/test/java/org/springframework/data/aerospike/query/UsersTests.java +++ b/src/test/java/org/springframework/data/aerospike/query/UsersTests.java @@ -34,7 +34,7 @@ void usersInterrupted() { @Test void usersInNorthRegion() { Qualifier qualifier = Qualifier.builder() - .setBinName("region") + .setPath("region") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get("n")) .build(); diff --git a/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveIndexedQualifierTests.java b/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveIndexedQualifierTests.java index d9acc8653..57bc1325b 100644 --- a/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveIndexedQualifierTests.java +++ b/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveIndexedQualifierTests.java @@ -58,7 +58,7 @@ public void selectOnIndexedLTQualifier() { // Ages range from 25 -> 29. We expected to only get back values with age < 26 withIndex(namespace, INDEXED_SET_NAME, "age_index", "age", IndexType.NUMERIC, () -> { Qualifier qualifier = Qualifier.builder() - .setBinName("age") + .setPath("age") .setFilterOperation(LT) .setValue(Value.get(26)) .build(); @@ -90,7 +90,7 @@ public void selectOnIndexedLTEQQualifier() { withIndex(namespace, INDEXED_SET_NAME, "age_index", "age", IndexType.NUMERIC, () -> { // Ages range from 25 -> 29. We expected to only get back values with age <= 26 Qualifier qualifier = Qualifier.builder() - .setBinName("age") + .setPath("age") .setFilterOperation(LTEQ) .setValue(Value.get(26)) .build(); @@ -125,7 +125,7 @@ public void selectOnIndexedNumericEQQualifier() { withIndex(namespace, INDEXED_SET_NAME, "age_index", "age", IndexType.NUMERIC, () -> { // Ages range from 25 -> 29. We expected to only get back values with age == 26 Qualifier qualifier = Qualifier.builder() - .setBinName("age") + .setPath("age") .setFilterOperation(EQ) .setValue(Value.get(26)) .build(); @@ -149,7 +149,7 @@ public void selectOnIndexedGTEQQualifier() { withIndex(namespace, INDEXED_SET_NAME, "age_index", "age", IndexType.NUMERIC, () -> { // Ages range from 25 -> 29. We expected to only get back values with age >= 28 Qualifier qualifier = Qualifier.builder() - .setBinName("age") + .setPath("age") .setFilterOperation(GTEQ) .setValue(Value.get(28)) .build(); @@ -184,7 +184,7 @@ public void selectOnIndexedGTQualifier() { withIndex(namespace, INDEXED_SET_NAME, "age_index", "age", IndexType.NUMERIC, () -> { // Ages range from 25 -> 29. We expected to only get back values with age > 28 or equivalently == 29 Qualifier qualifier = Qualifier.builder() - .setBinName("age") + .setPath("age") .setFilterOperation(GT) .setValue(Value.get(28)) .build(); @@ -207,7 +207,7 @@ public void selectOnIndexedGTQualifier() { public void selectOnIndexedStringEQQualifier() { withIndex(namespace, INDEXED_SET_NAME, "color_index", "color", IndexType.STRING, () -> { Qualifier qualifier = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(EQ) .setValue(Value.get(ORANGE)) .build(); @@ -230,7 +230,7 @@ public void selectOnIndexedStringEQQualifier() { public void selectWithBlueColorQuery() { withIndex(namespace, INDEXED_SET_NAME, "age_index", "age", IndexType.NUMERIC, () -> { Qualifier qual1 = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(BLUE)) .build(); @@ -254,15 +254,15 @@ public void selectWithBlueColorQuery() { public void selectWithQualifiersOnly() { withIndex(namespace, INDEXED_SET_NAME, "color_index", "color", IndexType.STRING, () -> { Qualifier qual1 = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(GREEN)) .build(); Qualifier qual2 = Qualifier.builder() - .setBinName("age") + .setPath("age") .setFilterOperation(FilterOperation.BETWEEN) .setValue(Value.get(28)) - .setValue(Value.get(29)) + .setSecondValue(Value.get(29)) .build(); Flux flux = reactiveQueryEngine.select(namespace, INDEXED_SET_NAME, null, @@ -291,7 +291,7 @@ public void selectWithGeoWithin() { + "\"coordinates\": [[%.8f, %.8f], %f] }", lon, lat, radius); Qualifier qualifier = Qualifier.builder() - .setBinName(GEO_BIN_NAME) + .setPath(GEO_BIN_NAME) .setFilterOperation(GEO_WITHIN) .setValue(Value.getAsGeoJSON(rgnstr)) .build(); diff --git a/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveQualifierTests.java b/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveQualifierTests.java index 82413f395..40d0d02c8 100644 --- a/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveQualifierTests.java +++ b/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveQualifierTests.java @@ -66,7 +66,7 @@ void throwsExceptionWhenScansDisabled() { reactiveQueryEngine.setScansEnabled(false); try { Qualifier qualifier = Qualifier.builder() - .setBinName("age") + .setPath("age") .setFilterOperation(FilterOperation.LT) .setValue(Value.get(26)) .build(); @@ -85,7 +85,7 @@ void throwsExceptionWhenScansDisabled() { public void lTQualifier() { // Ages range from 25 -> 29. We expected to only get back values with age < 26 Qualifier ageRangeQualifier = Qualifier.builder() - .setBinName("age") + .setPath("age") .setFilterOperation(FilterOperation.LT) .setValue(Value.get(26)) .build(); @@ -109,7 +109,7 @@ public void lTQualifier() { public void numericLTEQQualifier() { // Ages range from 25 -> 29. We expected to only get back values with age <= 26 Qualifier ageRangeQualifier = Qualifier.builder() - .setBinName("age") + .setPath("age") .setFilterOperation(FilterOperation.LTEQ) .setValue(Value.get(26)) .build(); @@ -140,7 +140,7 @@ public void numericLTEQQualifier() { public void numericEQQualifier() { // Ages range from 25 -> 29. We expected to only get back values with age == 26 Qualifier ageRangeQualifier = Qualifier.builder() - .setBinName("age") + .setPath("age") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(26)) .build(); @@ -161,7 +161,7 @@ public void numericEQQualifier() { public void numericGTEQQualifier() { // Ages range from 25 -> 29. We expected to only get back values with age >= 28 Qualifier ageRangeQualifier = Qualifier.builder() - .setBinName("age") + .setPath("age") .setFilterOperation(FilterOperation.GTEQ) .setValue(Value.get(28)) .build(); @@ -192,7 +192,7 @@ public void numericGTEQQualifier() { public void numericGTQualifier() { // Ages range from 25 -> 29. We expected to only get back values with age > 28 or equivalently == 29 Qualifier ageRangeQualifier = Qualifier.builder() - .setBinName("age") + .setPath("age") .setFilterOperation(FilterOperation.GT) .setValue(Value.get(28)) .build(); @@ -211,7 +211,7 @@ public void numericGTQualifier() { @Test public void stringEQQualifier() { Qualifier stringEqQualifier = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(ORANGE)) .build(); @@ -230,7 +230,7 @@ public void stringEQQualifier() { @Test public void stringEQQualifierCaseSensitive() { Qualifier stringEqQualifier = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.EQ) .setIgnoreCase(true) .setValue(Value.get(ORANGE.toUpperCase())) @@ -252,7 +252,7 @@ public void stringStartWithQualifier() { String bluePrefix = "blu"; Qualifier stringEqQualifier = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.STARTS_WITH) .setValue(Value.get("blu")) .build(); @@ -271,7 +271,7 @@ public void stringStartWithQualifier() { @Test public void stringStartWithEntireWordQualifier() { Qualifier stringEqQualifier = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.STARTS_WITH) .setValue(Value.get(BLUE)) .build(); @@ -292,7 +292,7 @@ public void stringStartWithICASEQualifier() { String blue = "blu"; Qualifier stringEqQualifier = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.STARTS_WITH) .setIgnoreCase(true) .setValue(Value.get("BLU")) @@ -314,7 +314,7 @@ public void stringEndsWithQualifier() { String greenEnding = GREEN.substring(2); Qualifier stringEqQualifier = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.ENDS_WITH) .setValue(Value.get(greenEnding)) .build(); @@ -333,7 +333,7 @@ public void stringEndsWithQualifier() { @Test public void stringEndsWithEntireWordQualifier() { Qualifier stringEqQualifier = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.ENDS_WITH) .setValue(Value.get(GREEN)) .build(); @@ -353,7 +353,7 @@ public void stringEndsWithEntireWordQualifier() { public void betweenQualifier() { // Ages range from 25 -> 29. Get back age between 26 and 28 inclusive Qualifier ageRangeQualifier = Qualifier.builder() - .setBinName("age") + .setPath("age") .setFilterOperation(FilterOperation.BETWEEN) .setValue(Value.get(26)) .setSecondValue(Value.get(29)) // + 1 as upper limit is exclusive @@ -391,7 +391,7 @@ public void containingQualifier() { .collect(Collectors.toMap(c -> c, color -> queryEngineTestDataPopulator.colourCounts.get(color))); Qualifier ageRangeQualifier = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.CONTAINING) .setValue(Value.get("l")) .build(); @@ -415,7 +415,7 @@ public void inQualifier() { .collect(Collectors.toMap(c -> c, color -> queryEngineTestDataPopulator.colourCounts.get(color))); Qualifier qualifier = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.IN) .setValue(Value.get(inColours)) .build(); @@ -439,7 +439,7 @@ public void listContainsQualifier() { String binName = "colorList"; Qualifier ageRangeQualifier = Qualifier.builder() - .setBinName(binName) + .setPath(binName) .setFilterOperation(FilterOperation.COLLECTION_VAL_CONTAINING) .setValue(Value.get(searchColor)) .build(); @@ -469,7 +469,7 @@ public void mapKeysContainQualifier() { String binName = "colorAgeMap"; Qualifier ageRangeQualifier = Qualifier.builder() - .setBinName(binName) + .setPath(binName) .setFilterOperation(FilterOperation.MAP_KEYS_CONTAIN) .setValue(Value.get(searchColor)) .build(); @@ -498,7 +498,7 @@ public void testMapValuesContainQualifier() { String binName = "ageColorMap"; Qualifier ageRangeQualifier = Qualifier.builder() - .setBinName(binName) + .setPath(binName) .setFilterOperation(FilterOperation.MAP_VALUES_CONTAIN) .setValue(Value.get(searchColor)) .build(); @@ -523,7 +523,7 @@ public void testMapValuesContainQualifier() { @Test public void testContainingDoesNotUseSpecialCharacterQualifier() { Qualifier ageRangeQualifier = Qualifier.builder() - .setBinName(SPECIAL_CHAR_BIN) + .setPath(SPECIAL_CHAR_BIN) .setFilterOperation(FilterOperation.CONTAINING) .setValue(Value.get(".*")) .build(); @@ -543,7 +543,7 @@ public void testContainingDoesNotUseSpecialCharacterQualifier() { @Test public void testStartWithDoesNotUseSpecialCharacterQualifier() { Qualifier ageRangeQualifier = Qualifier.builder() - .setBinName(SPECIAL_CHAR_BIN) + .setPath(SPECIAL_CHAR_BIN) .setFilterOperation(FilterOperation.STARTS_WITH) .setValue(Value.get(".*")) .build(); @@ -566,7 +566,7 @@ public void testStartWithDoesNotUseSpecialCharacterQualifier() { @Test public void testEndWithDoesNotUseSpecialCharacterQualifier() { Qualifier ageRangeQualifier = Qualifier.builder() - .setBinName(SPECIAL_CHAR_BIN) + .setPath(SPECIAL_CHAR_BIN) .setFilterOperation(FilterOperation.ENDS_WITH) .setValue(Value.get(".*")) .build(); @@ -589,7 +589,7 @@ public void testEndWithDoesNotUseSpecialCharacterQualifier() { @Test public void testEQICaseDoesNotUseSpecialCharacter() { Qualifier ageRangeQualifier = Qualifier.builder() - .setBinName(SPECIAL_CHAR_BIN) + .setPath(SPECIAL_CHAR_BIN) .setFilterOperation(FilterOperation.EQ) .setIgnoreCase(true) .setValue(Value.get(".*")) @@ -606,7 +606,7 @@ public void testContainingFindsSquareBracket() { String[] specialStrings = new String[]{"[", "$", "\\", "^"}; for (String specialString : specialStrings) { Qualifier ageRangeQualifier = Qualifier.builder() - .setBinName(SPECIAL_CHAR_BIN) + .setPath(SPECIAL_CHAR_BIN) .setFilterOperation(FilterOperation.CONTAINING) .setIgnoreCase(true) .setValue(Value.get(specialString)) @@ -636,7 +636,7 @@ public void stringEqualIgnoreCaseWorksOnIndexedBin() { String expectedColor = "blue"; Qualifier caseInsensitiveQual = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.EQ) .setIgnoreCase(ignoreCase) .setValue(Value.get("BlUe")) @@ -663,12 +663,12 @@ public void selectWithOrQualifiers() { // We are expecting to get back all records where color == blue or (age == 28 || age == 29) Qualifier qual1 = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(expectedColor)) .build(); Qualifier qual2 = Qualifier.builder() - .setBinName("age") + .setPath("age") .setFilterOperation(FilterOperation.BETWEEN) .setValue(Value.get(28)) .setSecondValue(Value.get(30)) // + 1 as upper limit is exclusive @@ -706,23 +706,23 @@ public void selectWithOrQualifiers() { @Test public void selectWithBetweenAndOrQualifiers() { Qualifier qualColorIsGreen = Qualifier.builder() - .setBinName("color") + .setPath("color") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get("green")) .build(); Qualifier qualAgeBetween28And29 = Qualifier.builder() - .setBinName("age") + .setPath("age") .setFilterOperation(FilterOperation.BETWEEN) .setValue(Value.get(28)) .setSecondValue(Value.get(30)) // + 1 as upper limit is exclusive .build(); Qualifier qualAgeIs25 = Qualifier.builder() - .setBinName("age") + .setPath("age") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(25)) .build(); Qualifier qualNameIs696 = Qualifier.builder() - .setBinName("name") + .setPath("name") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get("name:696")) .build(); diff --git a/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveSelectorTests.java b/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveSelectorTests.java index baa3685fb..638919781 100644 --- a/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveSelectorTests.java +++ b/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveSelectorTests.java @@ -3,6 +3,7 @@ import com.aerospike.client.Value; import com.aerospike.client.query.KeyRecord; import org.junit.jupiter.api.Test; +import org.springframework.data.aerospike.query.FilterOperation; import org.springframework.data.aerospike.query.KeyQualifier; import org.springframework.data.aerospike.query.qualifier.Qualifier; import org.springframework.data.aerospike.repository.query.Query; @@ -10,10 +11,6 @@ import reactor.test.StepVerifier; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.data.aerospike.query.FilterOperation.ENDS_WITH; -import static org.springframework.data.aerospike.query.FilterOperation.EQ; -import static org.springframework.data.aerospike.query.FilterOperation.GEO_WITHIN; -import static org.springframework.data.aerospike.query.FilterOperation.STARTS_WITH; import static org.springframework.data.aerospike.query.QueryEngineTestDataPopulator.BLUE; import static org.springframework.data.aerospike.query.QueryEngineTestDataPopulator.GEO_BIN_NAME; import static org.springframework.data.aerospike.query.QueryEngineTestDataPopulator.GEO_SET; @@ -55,8 +52,8 @@ public void selectAll() { @Test public void selectEndsWith() { Qualifier qual1 = Qualifier.builder() - .setBinName("color") - .setFilterOperation(ENDS_WITH) + .setPath("color") + .setFilterOperation(FilterOperation.ENDS_WITH) .setValue(Value.get("e")) .build(); @@ -74,8 +71,8 @@ public void selectEndsWith() { @Test public void selectStartsWith() { Qualifier startsWithQual = Qualifier.builder() - .setBinName("color") - .setFilterOperation(STARTS_WITH) + .setPath("color") + .setFilterOperation(FilterOperation.STARTS_WITH) .setValue(Value.get("bl")) .build(); @@ -94,15 +91,15 @@ public void selectStartsWith() { public void startWithAndEqualIgnoreCaseReturnsAllItems() { boolean ignoreCase = true; Qualifier qual1 = Qualifier.builder() - .setBinName("color") - .setFilterOperation(EQ) + .setPath("color") + .setFilterOperation(FilterOperation.EQ) .setIgnoreCase(ignoreCase) .setValue(Value.get("BLUE")) .build(); Qualifier qual2 = Qualifier.builder() - .setBinName("name") - .setFilterOperation(STARTS_WITH) + .setPath("name") + .setFilterOperation(FilterOperation.STARTS_WITH) .setIgnoreCase(ignoreCase) .setValue(Value.get("NA")) .build(); @@ -118,8 +115,8 @@ public void startWithAndEqualIgnoreCaseReturnsAllItems() { public void equalIgnoreCaseReturnsNoItemsIfNoneMatched() { boolean ignoreCase = false; Qualifier qual1 = Qualifier.builder() - .setBinName("color") - .setFilterOperation(EQ) + .setPath("color") + .setFilterOperation(FilterOperation.EQ) .setIgnoreCase(ignoreCase) .setValue(Value.get("BLUE")) .build(); @@ -134,8 +131,8 @@ public void equalIgnoreCaseReturnsNoItemsIfNoneMatched() { public void startWithIgnoreCaseReturnsNoItemsIfNoneMatched() { boolean ignoreCase = false; Qualifier qual1 = Qualifier.builder() - .setBinName("name") - .setFilterOperation(STARTS_WITH) + .setPath("name") + .setFilterOperation(FilterOperation.STARTS_WITH) .setIgnoreCase(ignoreCase) .setValue(Value.get("NA")) .build(); @@ -152,8 +149,8 @@ public void stringEqualIgnoreCaseWorksOnUnindexedBin() { String expectedColor = "blue"; Qualifier caseInsensitiveQual = Qualifier.builder() - .setBinName("color") - .setFilterOperation(EQ) + .setPath("color") + .setFilterOperation(FilterOperation.EQ) .setIgnoreCase(ignoreCase) .setValue(Value.get("BlUe")) .build(); @@ -173,8 +170,8 @@ public void stringEqualIgnoreCaseWorksOnUnindexedBin() { public void stringEqualIgnoreCaseWorksRequiresFullMatch() { boolean ignoreCase = true; Qualifier caseInsensitiveQual = Qualifier.builder() - .setBinName("color") - .setFilterOperation(EQ) + .setPath("color") + .setFilterOperation(FilterOperation.EQ) .setIgnoreCase(ignoreCase) .setValue(Value.get("lue")) .build(); @@ -195,8 +192,8 @@ public void selectWithGeoWithin() { + "\"coordinates\": [[%.8f, %.8f], %f] }", lon, lat, radius); Qualifier qual1 = Qualifier.builder() - .setBinName(GEO_BIN_NAME) - .setFilterOperation(GEO_WITHIN) + .setPath(GEO_BIN_NAME) + .setFilterOperation(FilterOperation.GEO_WITHIN) .setValue(Value.getAsGeoJSON(rgnstr)) .build(); diff --git a/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveUsersTests.java b/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveUsersTests.java index e1b4947e2..dfd068999 100644 --- a/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveUsersTests.java +++ b/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveUsersTests.java @@ -17,7 +17,7 @@ public class ReactiveUsersTests extends BaseReactiveQueryEngineTests { @Test public void usersInNorthRegion() { Qualifier qualifier = Qualifier.builder() - .setBinName("region") + .setPath("region") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get("n")) .build(); diff --git a/src/test/java/org/springframework/data/aerospike/repository/query/blocking/indexed/findBy/BetweenTests.java b/src/test/java/org/springframework/data/aerospike/repository/query/blocking/indexed/findBy/BetweenTests.java index f0b9cd2c0..1aa0379bd 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/query/blocking/indexed/findBy/BetweenTests.java +++ b/src/test/java/org/springframework/data/aerospike/repository/query/blocking/indexed/findBy/BetweenTests.java @@ -13,7 +13,6 @@ import java.util.List; -import static com.aerospike.client.exp.Exp.Type.MAP; import static org.assertj.core.api.Assertions.assertThat; /** @@ -91,10 +90,7 @@ void findByNestedSimplePropertyBetween_Integer_3_levels() { // find records having a map with a key between given values // POJOs are saved as Maps .setFilterOperation(FilterOperation.MAP_VAL_BETWEEN_BY_KEY) // POJOs are saved as Maps - .setBinName("bestFriend") // bin name - .setBinType(MAP) // bin type - .setCtx("friend.address") // context path from the bin to the nested map, exclusive - .setKey(Value.get("apartment")) // nested key + .setPath("bestFriend.friend.address.apartment") // path includes bin name, context and the required map key .setValue(Value.get(1)) // lower limit for the value of the nested key .setSecondValue(Value.get(3)) // lower limit for the value of the nested key .build(); diff --git a/src/test/java/org/springframework/data/aerospike/repository/query/blocking/indexed/findBy/CustomQueriesTests.java b/src/test/java/org/springframework/data/aerospike/repository/query/blocking/indexed/findBy/CustomQueriesTests.java index de6f1a2a5..b2421477f 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/query/blocking/indexed/findBy/CustomQueriesTests.java +++ b/src/test/java/org/springframework/data/aerospike/repository/query/blocking/indexed/findBy/CustomQueriesTests.java @@ -14,8 +14,6 @@ import java.util.List; -import static com.aerospike.client.exp.Exp.Type.LIST; -import static com.aerospike.client.exp.Exp.Type.MAP; import static org.assertj.core.api.Assertions.assertThat; public class CustomQueriesTests extends IndexedPersonRepositoryQueryTests { @@ -32,10 +30,7 @@ void findByNestedSimpleProperty_String_map_in_map() { // find records having a nested map with a key that equals a value // POJOs are saved as Maps .setFilterOperation(FilterOperation.MAP_VAL_EQ_BY_KEY) // POJOs are saved as Maps - .setBinName("friend") // bin name - .setBinType(MAP) // bin type - .setCtx("address") // context path from the bin to the nested map, exclusive - .setKey(Value.get("zipCode")) // nested key + .setPath("friend.address.zipCode") // path includes bin name, context and the required map key .setValue(Value.get(zipCode)) // value of the nested key .build(); @@ -57,11 +52,7 @@ void findByNestedSimpleProperty_String_map_in_list() { // find records having a nested map with a key that equals a value // POJOs are saved as Maps .setFilterOperation(FilterOperation.MAP_VAL_EQ_BY_KEY) - .setBinName("addressesList") // bin name - .setBinType(LIST) // bin type - .setCtx("[0]") // context path from the bin to the nested map, exclusive // list index - // to a string - .setKey(Value.get("zipCode")) // nested key + .setPath("addressesList.[0].zipCode") // path: bin name, context (list index) and the required map key .setValue(Value.get(zipCode)) // value of the nested key .build(); @@ -87,10 +78,7 @@ void findByNestedSimpleProperty_String_3_levels() { // find records having a nested map with a key that equals a value // POJOs are saved as Maps .setFilterOperation(FilterOperation.MAP_VAL_EQ_BY_KEY) // POJOs are saved as Maps - .setBinName("friend") // bin name - .setBinType(MAP) // bin type - .setCtx("bestFriend.address") // context path from the bin to the nested map, exclusive - .setKey(Value.get("zipCode")) // nested key + .setPath("friend.bestFriend.address.zipCode") // path includes bin name, context and the required map key .setValue(Value.get(zipCode)) // value of the nested key .build(); @@ -112,10 +100,7 @@ void findByNestedSimpleProperty_Integer_3_levels() { Qualifier nestedApartmentEq = Qualifier.builder() .setFilterOperation(FilterOperation.MAP_VAL_EQ_BY_KEY) // POJOs are saved as Maps - .setBinName("friend") // bin name - .setBinType(MAP) // bin type - .setCtx("bestFriend.address") // context path from the bin to the nested map, exclusive - .setKey(Value.get("apartment")) // nested key + .setPath("friend.bestFriend.address.apartment") // path includes bin name, context and the required map key .setValue(Value.get(apartment)) // value of the nested key .build(); @@ -141,10 +126,7 @@ void findByNestedSimplePropertyBetween_Integer_3_levels() { // find records having a map with a key between given values // POJOs are saved as Maps .setFilterOperation(FilterOperation.MAP_VAL_BETWEEN_BY_KEY) // POJOs are saved as Maps - .setBinName("bestFriend") // bin name - .setBinType(MAP) // bin type - .setCtx("friend.address") // context path from the bin to the nested map, exclusive - .setKey(Value.get("apartment")) // nested key + .setPath("bestFriend.friend.address.apartment") // path includes bin name, context and the required map key .setValue(Value.get(1)) // lower limit for the value of the nested key .setSecondValue(Value.get(3)) // lower limit for the value of the nested key .build(); diff --git a/src/test/java/org/springframework/data/aerospike/repository/query/blocking/indexed/findBy/EqualsTests.java b/src/test/java/org/springframework/data/aerospike/repository/query/blocking/indexed/findBy/EqualsTests.java index f6e486cf4..620336106 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/query/blocking/indexed/findBy/EqualsTests.java +++ b/src/test/java/org/springframework/data/aerospike/repository/query/blocking/indexed/findBy/EqualsTests.java @@ -15,7 +15,6 @@ import java.util.List; -import static com.aerospike.client.exp.Exp.Type.MAP; import static org.assertj.core.api.Assertions.assertThat; /** @@ -114,10 +113,7 @@ void findByNestedSimpleProperty_String_2_levels() { // find records having a map with a key that equals a value // POJOs are saved as Maps .setFilterOperation(FilterOperation.MAP_VAL_EQ_BY_KEY) // POJOs are saved as Maps - .setBinName("friend") // bin name - .setBinType(MAP) // bin type - .setCtx("address") // context path from the bin to the nested map, exclusive - .setKey(Value.get("zipCode")) // nested key + .setPath("friend.address.zipCode") // path .setValue(Value.get(zipCode)) // value of the nested key .build(); @@ -144,10 +140,7 @@ void findByNestedSimpleProperty_String_3_levels() { // An alternative way to perform the same using a custom query Qualifier nestedZipCodeEq = Qualifier.builder() .setFilterOperation(FilterOperation.MAP_VAL_EQ_BY_KEY) // POJOs are saved as Maps - .setBinName("friend") // bin name - .setBinType(MAP) // bin type - .setCtx("bestFriend.address") // context path from the bin to the nested map, exclusive - .setKey(Value.get("zipCode")) // nested key + .setPath("friend.bestFriend.address.zipCode") // path .setValue(Value.get(zipCode)) // value of the nested key .build(); @@ -174,10 +167,7 @@ void findByNestedSimpleProperty_Integer_3_levels() { // An alternative way to perform the same using a custom query Qualifier nestedApartmentEq = Qualifier.builder() .setFilterOperation(FilterOperation.MAP_VAL_EQ_BY_KEY) // POJOs are saved as Maps - .setBinName("friend") // bin name - .setBinType(MAP) // bin type - .setCtx("bestFriend.address") // context - path from the bin to the nested map, exclusive - .setKey(Value.get("apartment")) // nested key + .setPath("friend.bestFriend.address.apartment") // path .setValue(Value.get(apartment)) // value of the nested key .build(); diff --git a/src/test/java/org/springframework/data/aerospike/repository/query/blocking/noindex/findBy/CustomQueriesTests.java b/src/test/java/org/springframework/data/aerospike/repository/query/blocking/noindex/findBy/CustomQueriesTests.java index 91eb3ef30..f48407a7d 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/query/blocking/noindex/findBy/CustomQueriesTests.java +++ b/src/test/java/org/springframework/data/aerospike/repository/query/blocking/noindex/findBy/CustomQueriesTests.java @@ -2,11 +2,6 @@ import com.aerospike.client.Value; import org.junit.jupiter.api.Test; -import org.springframework.data.aerospike.config.AerospikeDataSettings; -import org.springframework.data.aerospike.convert.AerospikeCustomConversions; -import org.springframework.data.aerospike.convert.AerospikeTypeAliasAccessor; -import org.springframework.data.aerospike.convert.MappingAerospikeConverter; -import org.springframework.data.aerospike.mapping.AerospikeMappingContext; import org.springframework.data.aerospike.query.FilterOperation; import org.springframework.data.aerospike.query.qualifier.Qualifier; import org.springframework.data.aerospike.repository.query.Query; @@ -43,6 +38,31 @@ void findPersonsByMetadata() { .containsAll(repository.findUsingQuery(new Query(sinceUpdateTimeLt50Seconds))); } + @Test + void findBySimplePropertyEquals_Enum() { + Qualifier genderEqFemale = Qualifier.builder() + .setPath("gender") + .setFilterOperation(FilterOperation.EQ) + .setValue(Value.get(Person.Gender.FEMALE)) + .build(); + assertThat(repository.findUsingQuery(new Query(genderEqFemale))).containsOnly(alicia); + } + + @Test + void findBySimplePropertyEquals_String() { + String email = "alicia@test.com"; + alicia.setEmailAddress(email); + repository.save(alicia); + + Qualifier genderEqFemale = Qualifier.builder() + // custom bin name has been set to "email" via @Field annotation + .setPath("email") + .setFilterOperation(FilterOperation.EQ) + .setValue(Value.get(email)) + .build(); + assertThat(repository.findUsingQuery(new Query(genderEqFemale))).containsOnly(alicia); + } + @Test void findPersonsByQuery() { Iterable result; @@ -73,14 +93,14 @@ void findPersonsByQuery() { // creating an expression "firstName is equal to Carter" Qualifier firstNameEqCarter = Qualifier.builder() - .setBinName("firstName") + .setPath("firstName") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get("Carter")) .build(); // creating an expression "age is equal to 49" Qualifier ageEq49 = Qualifier.builder() - .setBinName("age") + .setPath("age") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(49)) .build(); @@ -89,7 +109,7 @@ void findPersonsByQuery() { // creating an expression "firstName is equal to Leroi" with sorting by age and limiting by 1 row Qualifier firstNameEqLeroi = Qualifier.builder() - .setBinName("firstName") + .setPath("firstName") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get("Leroi")) .build(); @@ -102,7 +122,7 @@ void findPersonsByQuery() { // creating an expression "age is greater than 49" Qualifier ageGt49 = Qualifier.builder() .setFilterOperation(FilterOperation.GT) - .setBinName("age") + .setPath("age") .setValue(Value.get(49)) .build(); result = repository.findUsingQuery(new Query(ageGt49)); @@ -257,7 +277,7 @@ void mapValuesTest() { assertThat(boyd.getStringMap().get("key1")).isEqualTo(valueToSearch); Qualifier stringMapValuesContainString = Qualifier.builder() - .setBinName("stringMap") + .setPath("stringMap") .setFilterOperation(FilterOperation.MAP_VALUES_CONTAIN) .setValue(Value.get(valueToSearch)) .build(); @@ -267,32 +287,13 @@ void mapValuesTest() { assertThat(carter.getIntMap().get(keyExactMatch)).isLessThan(valueToSearchLessThan); // it cannot be easily combined using boolean logic - // because in fact it is a "less than" Exp that uses the result of another Exp "MapExp.getByKey", - // so it requires new logic if exposed to users + // because in fact it is a "less than" Exp that uses the result of another Exp "MapExp.getByKey" Qualifier intMapWithExactKeyAndValueLt100 = Qualifier.builder() - .setBinName("intMap") // Map bin name + .setPath("intMap." + keyExactMatch) // Map bin name .setFilterOperation(FilterOperation.MAP_VAL_LT_BY_KEY) - .setKey(Value.get(keyExactMatch)) // Map key .setValue(Value.get(valueToSearchLessThan)) // Map value to compare with .build(); assertThat(repository.findUsingQuery(new Query(intMapWithExactKeyAndValueLt100))).containsOnly(carter); } - - private MappingAerospikeConverter getMappingAerospikeConverter(AerospikeCustomConversions conversions) { - MappingAerospikeConverter converter = new MappingAerospikeConverter(new AerospikeMappingContext(), - conversions, new AerospikeTypeAliasAccessor(), new AerospikeDataSettings()); - converter.afterPropertiesSet(); - return converter; - } - - @Test - void findBySimplePropertyEquals_Enum() { - Qualifier genderEqFemale = Qualifier.builder() - .setBinName("gender") - .setFilterOperation(FilterOperation.EQ) - .setValue(Value.get(Person.Gender.FEMALE)) - .build(); - assertThat(repository.findUsingQuery(new Query(genderEqFemale))).containsOnly(alicia); - } } diff --git a/src/test/java/org/springframework/data/aerospike/repository/query/blocking/noindex/findBy/EqualsTests.java b/src/test/java/org/springframework/data/aerospike/repository/query/blocking/noindex/findBy/EqualsTests.java index 213a131c7..835e119d9 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/query/blocking/noindex/findBy/EqualsTests.java +++ b/src/test/java/org/springframework/data/aerospike/repository/query/blocking/noindex/findBy/EqualsTests.java @@ -17,7 +17,6 @@ import java.util.List; import java.util.Map; -import static com.aerospike.client.exp.Exp.Type.MAP; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.springframework.data.aerospike.query.QueryParam.of; @@ -227,10 +226,7 @@ void findByNestedSimplePropertyEquals() { // find records having a map with a key that equals a value // POJOs are saved as Maps .setFilterOperation(FilterOperation.MAP_VAL_EQ_BY_KEY) // POJOs are saved as Maps - .setBinName("friend") // bin name - .setBinType(MAP) // bin type - .setCtx("address") // context path from the bin to the nested map, exclusive - .setKey(Value.get("zipCode")) // nested key + .setPath("friend.address.zipCode") // path includes bin name, context and the required map key .setValue(Value.get(zipCode)) // value of the nested key .build(); @@ -290,11 +286,8 @@ void findByDeeplyNestedSimplePropertyEquals_PojoField_String_10_levels() { // find records having a map with a key that equals a value // POJOs are saved as Maps .setFilterOperation(FilterOperation.MAP_VAL_EQ_BY_KEY) // POJOs are saved as Maps - .setBinName("friend") // bin name - .setBinType(MAP) // bin type - // context path from the bin to the nested map, exclusive - .setCtx("friend.friend.friend.friend.friend.friend.friend.bestFriend.address") - .setKey(Value.get("zipCode")) // nested key + // path includes bin name, context and the required map key + .setPath("friend.friend.friend.friend.friend.friend.friend.friend.bestFriend.address.zipCode") .setValue(Value.get(zipCode)) // value of the nested key .build(); @@ -326,11 +319,7 @@ void findByDeeplyNestedSimplePropertyEquals_PojoField_Integer_10_levels() { // find records having a map with a key that equals a value // POJOs are saved as Maps .setFilterOperation(FilterOperation.MAP_VAL_EQ_BY_KEY) // POJOs are saved as Maps - .setBinName("friend") // bin name - .setBinType(MAP) // bin type - // context path from the bin to the nested map, exclusive - .setCtx("friend.friend.friend.friend.friend.friend.friend.bestFriend.address") - .setKey(Value.get("apartment")) // nested key + .setPath("friend.friend.friend.friend.friend.friend.friend.friend.bestFriend.address.apartment") // path .setValue(Value.get(apartment)) // value of the nested key .build(); @@ -362,11 +351,7 @@ void findByDeeplyNestedSimplePropertyEquals_Pojo_9_levels() { // find records having a map with a key that equals a value // POJOs are saved as Maps .setFilterOperation(FilterOperation.MAP_VAL_EQ_BY_KEY) // POJOs are saved as Maps - .setBinName("friend") // bin name - .setBinType(MAP) // bin type - // context path from the bin to the nested map, exclusive - .setCtx("friend.friend.friend.friend.friend.friend.friend.bestFriend") - .setKey(Value.get("address")) // nested key + .setPath("friend.friend.friend.friend.friend.friend.friend.friend.bestFriend.address") // path .setValue(Value.get(pojoToMap(address))) // value of the nested key .build(); diff --git a/src/test/java/org/springframework/data/aerospike/repository/query/reactive/indexed/findBy/CustomQueriesTests.java b/src/test/java/org/springframework/data/aerospike/repository/query/reactive/indexed/findBy/CustomQueriesTests.java index 581774014..765b7a07e 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/query/reactive/indexed/findBy/CustomQueriesTests.java +++ b/src/test/java/org/springframework/data/aerospike/repository/query/reactive/indexed/findBy/CustomQueriesTests.java @@ -72,14 +72,14 @@ public void findPersonsByQuery() { // creating an expression "firsName is equal to Petra" Qualifier firstNameEqPetra = Qualifier.builder() - .setBinName("firstName") + .setPath("firstName") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get("Petra")) .build(); // creating an expression "age is equal to 34" Qualifier ageEq34 = Qualifier.builder() - .setBinName("age") + .setPath("age") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(34)) .build(); @@ -89,7 +89,7 @@ public void findPersonsByQuery() { // creating an expression "age is greater than 34" Qualifier ageGt34 = Qualifier.builder() .setFilterOperation(FilterOperation.GT) - .setBinName("age") + .setPath("age") .setValue(Value.get(34)) .build(); result = reactiveRepository.findUsingQuery(new Query(ageGt34)).collectList().block();