From 91ac05f50d4ba517b25152178683eb3e5c2bdce2 Mon Sep 17 00:00:00 2001 From: agrgr Date: Sun, 5 May 2024 19:45:51 +0300 Subject: [PATCH] add support for CTX parameter in custom queries, refactoring --- .../data/aerospike/core/CoreUtils.java | 8 +- .../index/AerospikeIndexResolver.java | 103 +---- .../index/AerospikeIndexResolverUtils.java | 98 ++++ .../data/aerospike/query/ExpiryQualifier.java | 2 +- .../data/aerospike/query/FilterOperation.java | 429 +++++++++--------- .../data/aerospike/query/KeyQualifier.java | 4 +- .../query/LatestUpdateQualifier.java | 2 +- .../aerospike/query/StatementBuilder.java | 8 +- .../query/qualifier/BaseQualifierBuilder.java | 18 +- .../qualifier/MetadataQualifierBuilder.java | 2 +- .../aerospike/query/qualifier/Qualifier.java | 15 +- .../query/qualifier/QualifierBuilder.java | 53 ++- .../query/qualifier/QualifierKey.java | 6 +- .../repository/query/AerospikeCriteria.java | 2 +- .../query/AerospikeQueryCreator.java | 2 +- .../query/AerospikeQueryCreatorUtils.java | 31 +- .../BaseBlockingIntegrationTests.java | 46 +- .../BaseReactiveIntegrationTests.java | 10 +- .../core/AerospikeTemplateCountTests.java | 14 +- .../AerospikeTemplateFindByQueryTests.java | 6 +- ...iveAerospikeTemplateCountRelatedTests.java | 12 +- .../data/aerospike/logging/LoggingTests.java | 2 +- .../query/IndexedQualifierTests.java | 24 +- .../data/aerospike/query/QualifierTests.java | 78 ++-- .../data/aerospike/query/UsersTests.java | 2 +- .../ReactiveIndexedQualifierTests.java | 20 +- .../reactive/ReactiveQualifierTests.java | 62 +-- .../query/reactive/ReactiveSelectorTests.java | 18 +- .../query/reactive/ReactiveUsersTests.java | 2 +- .../IndexedPersonRepositoryQueryTests.java | 62 ++- .../blocking/indexed/findBy/BetweenTests.java | 8 +- .../indexed/findBy/ContainingTests.java | 10 +- .../indexed/findBy/CustomQueriesTests.java | 128 ++++++ .../blocking/indexed/findBy/EqualsTests.java | 71 ++- .../indexed/findBy/GreaterThanTests.java | 12 +- .../indexed/findBy/NotContainingTests.java | 4 +- .../noindex/findBy/CustomQueriesTests.java | 13 +- .../blocking/noindex/findBy/EqualsTests.java | 71 ++- .../reactive/indexed/findBy/BetweenTests.java | 2 +- .../indexed/findBy/ContainingTests.java | 12 +- .../indexed/findBy/CustomQueriesTests.java | 6 +- .../reactive/indexed/findBy/EqualsTests.java | 8 +- .../indexed/findBy/GreaterThanTests.java | 4 +- .../indexed/findBy/LessThanTests.java | 2 +- .../indexed/findBy/StartsWithTests.java | 4 +- 45 files changed, 905 insertions(+), 591 deletions(-) create mode 100644 src/main/java/org/springframework/data/aerospike/index/AerospikeIndexResolverUtils.java create mode 100644 src/test/java/org/springframework/data/aerospike/repository/query/blocking/indexed/findBy/CustomQueriesTests.java diff --git a/src/main/java/org/springframework/data/aerospike/core/CoreUtils.java b/src/main/java/org/springframework/data/aerospike/core/CoreUtils.java index ed09e33ce..030bd7140 100644 --- a/src/main/java/org/springframework/data/aerospike/core/CoreUtils.java +++ b/src/main/java/org/springframework/data/aerospike/core/CoreUtils.java @@ -95,16 +95,16 @@ static void verifyUnsortedWithOffset(Sort sort, long offset) { static Predicate getDistinctPredicate(Query query) { Predicate distinctPredicate; if (query != null && query.isDistinct()) { - List dotPathList = query.getCriteriaObject().getDotPath(); - if (dotPathList != null && dotPathList.size() > 0 && dotPathList.get(0) != null) { - String dotPathString = String.join(",", query.getCriteriaObject().getDotPath()); + List ctxPathList = query.getCriteriaObject().getCtxPath(); + if (ctxPathList != null && ctxPathList.size() > 0 && ctxPathList.get(0) != null) { + String dotPathString = String.join(",", query.getCriteriaObject().getCtxPath()); throw new UnsupportedOperationException("DISTINCT queries are currently supported only for the first " + "level objects, got a query for " + dotPathString); } final Set distinctValues = ConcurrentHashMap.newKeySet(); distinctPredicate = kr -> { - final String distinctField = query.getCriteriaObject().getField(); + final String distinctField = query.getCriteriaObject().getBinName(); if (kr.record == null || kr.record.bins == null) { return false; } 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 bf5c4983c..ec03701fa 100644 --- a/src/main/java/org/springframework/data/aerospike/index/AerospikeIndexResolver.java +++ b/src/main/java/org/springframework/data/aerospike/index/AerospikeIndexResolver.java @@ -15,7 +15,6 @@ */ package org.springframework.data.aerospike.index; -import com.aerospike.client.Value; import com.aerospike.client.cdt.CTX; import org.springframework.context.EnvironmentAware; import org.springframework.core.env.Environment; @@ -92,8 +91,11 @@ private CTX[] toCtxArray(String ctxString) { if (!StringUtils.hasLength(ctxString)) return null; String[] ctxTokens = ctxString.split("\\."); - CTX[] ctxArr = Arrays.stream(ctxTokens).filter(not(String::isEmpty)) - .map(this::toCtx).filter(Objects::nonNull).toArray(CTX[]::new); + CTX[] ctxArr = Arrays.stream(ctxTokens) + .filter(not(String::isEmpty)) + .map(AerospikeIndexResolverUtils::toCtx) + .filter(Objects::nonNull) + .toArray(CTX[]::new); if (ctxTokens.length != ctxArr.length) { throw new IllegalArgumentException("@Indexed annotation '" + ctxString + "' contains empty context"); @@ -102,10 +104,10 @@ private CTX[] toCtxArray(String ctxString) { return ctxArr; } - private enum CtxType { + protected enum CtxType { MAP('}'), LIST(']'); - private final char closingChar; + final char closingChar; CtxType(char closingChar) { this.closingChar = closingChar; @@ -116,95 +118,4 @@ public String toString() { return name().toLowerCase(); // when mentioned in exceptions } } - - private CTX toCtx(String singleCtx) { - switch (singleCtx.charAt(0)) { - case '{' -> { - return processSingleCtx(singleCtx, CtxType.MAP); - } - case '[' -> { - return processSingleCtx(singleCtx, CtxType.LIST); - } - default -> { - Object res = isInDoubleOrSingleQuotes(singleCtx) ? singleCtx.substring(1, singleCtx.length() - 1) : - parseIntOrReturnStr(singleCtx); - return CTX.mapKey(Value.get(res)); - } - } - } - - private CTX processSingleCtx(String singleCtx, CtxType ctxType) { - int length = singleCtx.length(); - if (length < 3) { - throw new IllegalArgumentException("@Indexed annotation: context string '" + singleCtx + - "' has no content"); - } - if (singleCtx.charAt(length - 1) != ctxType.closingChar) { - throw new IllegalArgumentException("@Indexed annotation: brackets mismatch, " + - "expecting '" + ctxType.closingChar + "', got '" + singleCtx.charAt(length - 1) + "' instead"); - } - - CTX result; - String substring = singleCtx.substring(2, length - 1); - if (singleCtx.charAt(1) == '=' && length > 3) { - result = processCtxValue(substring, ctxType); - } else if (singleCtx.charAt(1) == '#' && length > 3) { - result = processCtxRank(substring, ctxType); - } else { - result = processCtxIndex(singleCtx, length, ctxType); - } - - return result; - } - - private CTX processCtxValue(String substring, CtxType ctxType) { - Object result = isInDoubleOrSingleQuotes(substring) ? substring.substring(1, substring.length() - 1) : - parseIntOrReturnStr(substring); - return switch (ctxType) { - case MAP -> CTX.mapValue(Value.get(result)); - case LIST -> CTX.listValue(Value.get(result)); - }; - } - - private CTX processCtxRank(String substring, CtxType ctxType) { - int rank = parseIntOrFail(substring, ctxType, "rank"); - return switch (ctxType) { - case MAP -> CTX.mapRank(rank); - case LIST -> CTX.listRank(rank); - }; - } - - private CTX processCtxIndex(String singleCtx, int length, CtxType ctxType) { - String substring = singleCtx.substring(1, length - 1); - int idx = parseIntOrFail(substring, ctxType, "index"); - return switch (ctxType) { - case MAP -> CTX.mapIndex(idx); - case LIST -> CTX.listIndex(idx); - }; - } - - private int parseIntOrFail(String substring, CtxType ctxType, String parameterName) { - try { - return Integer.parseInt(substring); - } catch (NumberFormatException e) { - throw new IllegalArgumentException("@Indexed annotation " + ctxType + " " + parameterName + ": " + - "expecting only integer values, got '" + substring + "' instead"); - } - } - - private static Object parseIntOrReturnStr(String str) { - Object res; - try { - res = Integer.parseInt(str); - } catch (NumberFormatException e) { - res = str; - } - - return res; - } - - 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) == '\''); - } } diff --git a/src/main/java/org/springframework/data/aerospike/index/AerospikeIndexResolverUtils.java b/src/main/java/org/springframework/data/aerospike/index/AerospikeIndexResolverUtils.java new file mode 100644 index 000000000..15f5fa194 --- /dev/null +++ b/src/main/java/org/springframework/data/aerospike/index/AerospikeIndexResolverUtils.java @@ -0,0 +1,98 @@ +package org.springframework.data.aerospike.index; + +import com.aerospike.client.Value; +import com.aerospike.client.cdt.CTX; + +public class AerospikeIndexResolverUtils { + + public static CTX toCtx(String singleCtx) { + switch (singleCtx.charAt(0)) { + case '{' -> { + return processSingleCtx(singleCtx, AerospikeIndexResolver.CtxType.MAP); + } + case '[' -> { + return processSingleCtx(singleCtx, AerospikeIndexResolver.CtxType.LIST); + } + default -> { + Object res = isInDoubleOrSingleQuotes(singleCtx) ? singleCtx.substring(1, singleCtx.length() - 1) : + parseIntOrReturnStr(singleCtx); + return CTX.mapKey(Value.get(res)); + } + } + } + + 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"); + } + if (singleCtx.charAt(length - 1) != ctxType.closingChar) { + throw new IllegalArgumentException("@Indexed annotation: brackets mismatch, " + + "expecting '" + ctxType.closingChar + "', got '" + singleCtx.charAt(length - 1) + "' instead"); + } + + CTX result; + String substring = singleCtx.substring(2, length - 1); + if (singleCtx.charAt(1) == '=' && length > 3) { + result = processCtxValue(substring, ctxType); + } else if (singleCtx.charAt(1) == '#' && length > 3) { + result = processCtxRank(substring, ctxType); + } else { + result = processCtxIndex(singleCtx, length, ctxType); + } + + return result; + } + + private static CTX processCtxValue(String substring, AerospikeIndexResolver.CtxType ctxType) { + Object result = isInDoubleOrSingleQuotes(substring) ? substring.substring(1, substring.length() - 1) : + parseIntOrReturnStr(substring); + return switch (ctxType) { + case MAP -> CTX.mapValue(Value.get(result)); + case LIST -> CTX.listValue(Value.get(result)); + }; + } + + private static CTX processCtxRank(String substring, AerospikeIndexResolver.CtxType ctxType) { + int rank = parseIntOrFail(substring, ctxType, "rank"); + return switch (ctxType) { + case MAP -> CTX.mapRank(rank); + case LIST -> CTX.listRank(rank); + }; + } + + private static CTX processCtxIndex(String singleCtx, int length, AerospikeIndexResolver.CtxType ctxType) { + String substring = singleCtx.substring(1, length - 1); + int idx = parseIntOrFail(substring, ctxType, "index"); + return switch (ctxType) { + case MAP -> CTX.mapIndex(idx); + case LIST -> CTX.listIndex(idx); + }; + } + + private static int parseIntOrFail(String substring, AerospikeIndexResolver.CtxType ctxType, String parameterName) { + try { + return Integer.parseInt(substring); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("@Indexed annotation " + ctxType + " " + parameterName + ": " + + "expecting only integer values, got '" + substring + "' instead"); + } + } + + private static Object parseIntOrReturnStr(String str) { + Object res; + try { + res = Integer.parseInt(str); + } catch (NumberFormatException e) { + res = str; + } + + return res; + } + + 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) == '\''); + } +} 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 f4d61d3e7..7b175ba19 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() - .setField(QueryEngine.Meta.EXPIRATION.toString()) + .setBinName(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 eb4cca68c..dd2ca5be4 100644 --- a/src/main/java/org/springframework/data/aerospike/query/FilterOperation.java +++ b/src/main/java/org/springframework/data/aerospike/query/FilterOperation.java @@ -26,6 +26,7 @@ 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; @@ -36,16 +37,18 @@ 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; -import java.util.stream.Stream; +import java.util.stream.Collectors; import static com.aerospike.client.command.ParticleType.BOOL; import static com.aerospike.client.command.ParticleType.INTEGER; 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.util.FilterOperationRegexpBuilder.getContaining; import static org.springframework.data.aerospike.util.FilterOperationRegexpBuilder.getEndsWith; @@ -114,7 +117,7 @@ public Exp filterExp(Map qualifierMap) { Collection collection = getValueAsCollectionOrFail(qualifierMap); Exp[] arrElementsExp = collection.stream().map(item -> Qualifier.builder() - .setField(getField(qualifierMap)) + .setBinName(getBinName(qualifierMap)) .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(item)) .build() @@ -138,7 +141,7 @@ public Exp filterExp(Map qualifierMap) { Collection collection = getValueAsCollectionOrFail(qualifierMap); Exp[] arrElementsExp = collection.stream().map(item -> Qualifier.builder() - .setField(getField(qualifierMap)) + .setBinName(getBinName(qualifierMap)) .setFilterOperation(FilterOperation.NOTEQ) .setValue(Value.get(item)) .build() @@ -160,20 +163,20 @@ public Exp filterExp(Map qualifierMap) { return getMetadataExp(qualifierMap).orElseGet(() -> { Value value = getValue(qualifierMap); return switch (value.getType()) { - case INTEGER -> Exp.eq(Exp.intBin(getField(qualifierMap)), Exp.val(value.toLong())); + case INTEGER -> Exp.eq(Exp.intBin(getBinName(qualifierMap)), Exp.val(value.toLong())); case STRING -> { if (ignoreCase(qualifierMap)) { String equalsRegexp = getStringEquals(value.toString()); yield Exp.regexCompare(equalsRegexp, RegexFlag.ICASE, - Exp.stringBin(getField(qualifierMap))); + Exp.stringBin(getBinName(qualifierMap))); } else { - yield Exp.eq(Exp.stringBin(getField(qualifierMap)), Exp.val(value.toString())); + yield Exp.eq(Exp.stringBin(getBinName(qualifierMap)), Exp.val(value.toString())); } } - case BOOL -> Exp.eq(Exp.boolBin(getField(qualifierMap)), Exp.val((Boolean) value.getObject())); - case MAP -> getFilterExp(Exp.val((Map) value.getObject()), getField(qualifierMap), Exp::eq, + case BOOL -> Exp.eq(Exp.boolBin(getBinName(qualifierMap)), Exp.val((Boolean) value.getObject())); + case MAP -> getFilterExp(Exp.val((Map) value.getObject()), getBinName(qualifierMap), Exp::eq, Exp::mapBin); - case LIST -> getFilterExp(Exp.val((List) value.getObject()), getField(qualifierMap), Exp::eq, + case LIST -> getFilterExp(Exp.val((List) value.getObject()), getBinName(qualifierMap), Exp::eq, Exp::listBin); default -> throw new IllegalArgumentException("EQ FilterExpression unsupported particle type: " + value.getClass().getSimpleName()); @@ -185,13 +188,14 @@ public Exp filterExp(Map qualifierMap) { public Filter sIndexFilter(Map qualifierMap) { Value value = getValue(qualifierMap); return switch (value.getType()) { - case INTEGER -> Filter.equal(getField(qualifierMap), value.toLong()); + // No CTX here because this FilterOperation is meant for the first level objects + case INTEGER -> Filter.equal(getBinName(qualifierMap), value.toLong()); case STRING -> { // There is no case-insensitive string comparison filter. if (ignoreCase(qualifierMap)) { yield null; } - yield Filter.equal(getField(qualifierMap), value.toString()); + yield Filter.equal(getBinName(qualifierMap), value.toString()); } default -> null; }; @@ -205,27 +209,27 @@ public Exp filterExp(Map qualifierMap) { return switch (value.getType()) { // FMWK-175: Exp.ne() does not return null bins, so Exp.not(Exp.binExists()) is added case INTEGER -> { - Exp ne = Exp.ne(Exp.intBin(getField(qualifierMap)), Exp.val(value.toLong())); - yield Exp.or(Exp.not(Exp.binExists(getField(qualifierMap))), ne); + Exp ne = Exp.ne(Exp.intBin(getBinName(qualifierMap)), Exp.val(value.toLong())); + yield Exp.or(Exp.not(Exp.binExists(getBinName(qualifierMap))), ne); } case STRING -> { if (ignoreCase(qualifierMap)) { String equalsRegexp = getStringEquals(value.toString()); Exp regexCompare = Exp.not(Exp.regexCompare(equalsRegexp, RegexFlag.ICASE, - Exp.stringBin(getField(qualifierMap)))); - yield Exp.or(Exp.not(Exp.binExists(getField(qualifierMap))), regexCompare); + Exp.stringBin(getBinName(qualifierMap)))); + yield Exp.or(Exp.not(Exp.binExists(getBinName(qualifierMap))), regexCompare); } else { - Exp ne = Exp.ne(Exp.stringBin(getField(qualifierMap)), Exp.val(value.toString())); - yield Exp.or(Exp.not(Exp.binExists(getField(qualifierMap))), ne); + Exp ne = Exp.ne(Exp.stringBin(getBinName(qualifierMap)), Exp.val(value.toString())); + yield Exp.or(Exp.not(Exp.binExists(getBinName(qualifierMap))), ne); } } case BOOL -> { - Exp ne = Exp.ne(Exp.boolBin(getField(qualifierMap)), Exp.val((Boolean) value.getObject())); - yield Exp.or(Exp.not(Exp.binExists(getField(qualifierMap))), ne); + Exp ne = Exp.ne(Exp.boolBin(getBinName(qualifierMap)), Exp.val((Boolean) value.getObject())); + yield Exp.or(Exp.not(Exp.binExists(getBinName(qualifierMap))), ne); } - case MAP -> getFilterExp(Exp.val((Map) value.getObject()), getField(qualifierMap), Exp::ne, + case MAP -> getFilterExp(Exp.val((Map) value.getObject()), getBinName(qualifierMap), Exp::ne, Exp::mapBin); - case LIST -> getFilterExp(Exp.val((List) value.getObject()), getField(qualifierMap), Exp::ne, + case LIST -> getFilterExp(Exp.val((List) value.getObject()), getBinName(qualifierMap), Exp::ne, Exp::listBin); default -> throw new IllegalArgumentException("NOTEQ FilterExpression unsupported particle type: " + value.getClass().getSimpleName()); @@ -244,11 +248,11 @@ public Exp filterExp(Map qualifierMap) { return getMetadataExp(qualifierMap).orElseGet(() -> { Value value = getValue(qualifierMap); return switch (value.getType()) { - case INTEGER -> Exp.gt(Exp.intBin(getField(qualifierMap)), Exp.val(value.toLong())); - case STRING -> Exp.gt(Exp.stringBin(getField(qualifierMap)), Exp.val(value.toString())); - case MAP -> getFilterExp(Exp.val((Map) value.getObject()), getField(qualifierMap), Exp::gt, + case INTEGER -> Exp.gt(Exp.intBin(getBinName(qualifierMap)), Exp.val(value.toLong())); + case STRING -> Exp.gt(Exp.stringBin(getBinName(qualifierMap)), Exp.val(value.toString())); + case MAP -> getFilterExp(Exp.val((Map) value.getObject()), getBinName(qualifierMap), Exp::gt, Exp::mapBin); - case LIST -> getFilterExp(Exp.val((List) value.getObject()), getField(qualifierMap), Exp::gt, + case LIST -> getFilterExp(Exp.val((List) value.getObject()), getBinName(qualifierMap), Exp::gt, Exp::listBin); default -> throw new IllegalArgumentException("GT FilterExpression unsupported particle type: " + value.getClass().getSimpleName()); @@ -263,7 +267,7 @@ public Filter sIndexFilter(Map qualifierMap) { return null; } - return Filter.range(getField(qualifierMap), getValue(qualifierMap).toLong() + 1, Long.MAX_VALUE); + return Filter.range(getBinName(qualifierMap), getValue(qualifierMap).toLong() + 1, Long.MAX_VALUE); } }, GTEQ { @@ -272,11 +276,11 @@ public Exp filterExp(Map qualifierMap) { return getMetadataExp(qualifierMap).orElseGet(() -> { Value value = getValue(qualifierMap); return switch (value.getType()) { - case INTEGER -> Exp.ge(Exp.intBin(getField(qualifierMap)), Exp.val(value.toLong())); - case STRING -> Exp.ge(Exp.stringBin(getField(qualifierMap)), Exp.val(value.toString())); - case MAP -> getFilterExp(Exp.val((Map) value.getObject()), getField(qualifierMap), Exp::ge, + case INTEGER -> Exp.ge(Exp.intBin(getBinName(qualifierMap)), Exp.val(value.toLong())); + case STRING -> Exp.ge(Exp.stringBin(getBinName(qualifierMap)), Exp.val(value.toString())); + case MAP -> getFilterExp(Exp.val((Map) value.getObject()), getBinName(qualifierMap), Exp::ge, Exp::mapBin); - case LIST -> getFilterExp(Exp.val((List) value.getObject()), getField(qualifierMap), Exp::ge, + case LIST -> getFilterExp(Exp.val((List) value.getObject()), getBinName(qualifierMap), Exp::ge, Exp::listBin); default -> throw new IllegalArgumentException("GTEQ FilterExpression unsupported particle type: " + value.getClass().getSimpleName()); @@ -289,7 +293,7 @@ public Filter sIndexFilter(Map qualifierMap) { if (getValue(qualifierMap).getType() != INTEGER) { return null; } - return Filter.range(getField(qualifierMap), getValue(qualifierMap).toLong(), Long.MAX_VALUE); + return Filter.range(getBinName(qualifierMap), getValue(qualifierMap).toLong(), Long.MAX_VALUE); } }, LT { @@ -298,11 +302,11 @@ public Exp filterExp(Map qualifierMap) { return getMetadataExp(qualifierMap).orElseGet(() -> { Value value = getValue(qualifierMap); return switch (value.getType()) { - case INTEGER -> Exp.lt(Exp.intBin(getField(qualifierMap)), Exp.val(value.toLong())); - case STRING -> Exp.lt(Exp.stringBin(getField(qualifierMap)), Exp.val(value.toString())); - case MAP -> getFilterExp(Exp.val((Map) value.getObject()), getField(qualifierMap), Exp::lt, + case INTEGER -> Exp.lt(Exp.intBin(getBinName(qualifierMap)), Exp.val(value.toLong())); + case STRING -> Exp.lt(Exp.stringBin(getBinName(qualifierMap)), Exp.val(value.toString())); + case MAP -> getFilterExp(Exp.val((Map) value.getObject()), getBinName(qualifierMap), Exp::lt, Exp::mapBin); - case LIST -> getFilterExp(Exp.val((List) value.getObject()), getField(qualifierMap), Exp::lt, + case LIST -> getFilterExp(Exp.val((List) value.getObject()), getBinName(qualifierMap), Exp::lt, Exp::listBin); default -> throw new IllegalArgumentException("LT FilterExpression unsupported particle type: " + value.getClass().getSimpleName()); @@ -316,7 +320,7 @@ public Filter sIndexFilter(Map qualifierMap) { if (getValue(qualifierMap).getType() != INTEGER || getValue(qualifierMap).toLong() == Long.MIN_VALUE) { return null; } - return Filter.range(getField(qualifierMap), Long.MIN_VALUE, getValue(qualifierMap).toLong() - 1); + return Filter.range(getBinName(qualifierMap), Long.MIN_VALUE, getValue(qualifierMap).toLong() - 1); } }, LTEQ { @@ -325,11 +329,11 @@ public Exp filterExp(Map qualifierMap) { return getMetadataExp(qualifierMap).orElseGet(() -> { Value value = getValue(qualifierMap); return switch (value.getType()) { - case INTEGER -> Exp.le(Exp.intBin(getField(qualifierMap)), Exp.val(value.toLong())); - case STRING -> Exp.le(Exp.stringBin(getField(qualifierMap)), Exp.val(value.toString())); - case MAP -> getFilterExp(Exp.val((Map) value.getObject()), getField(qualifierMap), Exp::le, + case INTEGER -> Exp.le(Exp.intBin(getBinName(qualifierMap)), Exp.val(value.toLong())); + case STRING -> Exp.le(Exp.stringBin(getBinName(qualifierMap)), Exp.val(value.toString())); + case MAP -> getFilterExp(Exp.val((Map) value.getObject()), getBinName(qualifierMap), Exp::le, Exp::mapBin); - case LIST -> getFilterExp(Exp.val((List) value.getObject()), getField(qualifierMap), Exp::le, + case LIST -> getFilterExp(Exp.val((List) value.getObject()), getBinName(qualifierMap), Exp::le, Exp::listBin); default -> throw new IllegalArgumentException("LTEQ FilterExpression unsupported particle type: " + value.getClass().getSimpleName()); @@ -342,7 +346,7 @@ public Filter sIndexFilter(Map qualifierMap) { if (getValue(qualifierMap).getType() != INTEGER) { return null; } - return Filter.range(getField(qualifierMap), Long.MIN_VALUE, getValue(qualifierMap).toLong()); + return Filter.range(getBinName(qualifierMap), Long.MIN_VALUE, getValue(qualifierMap).toLong()); } }, BETWEEN { @@ -352,23 +356,23 @@ public Exp filterExp(Map qualifierMap) { Value value2 = getSecondValue(qualifierMap); return getMetadataExp(qualifierMap).orElseGet(() -> switch (value.getType()) { case INTEGER -> Exp.and( - Exp.ge(Exp.intBin(getField(qualifierMap)), Exp.val(value.toLong())), - Exp.lt(Exp.intBin(getField(qualifierMap)), Exp.val(value2.toLong())) + Exp.ge(Exp.intBin(getBinName(qualifierMap)), Exp.val(value.toLong())), + Exp.lt(Exp.intBin(getBinName(qualifierMap)), Exp.val(value2.toLong())) ); case STRING -> Exp.and( - Exp.ge(Exp.stringBin(getField(qualifierMap)), Exp.val(value.toString())), - Exp.lt(Exp.stringBin(getField(qualifierMap)), Exp.val(value2.toString())) + Exp.ge(Exp.stringBin(getBinName(qualifierMap)), Exp.val(value.toString())), + Exp.lt(Exp.stringBin(getBinName(qualifierMap)), Exp.val(value2.toString())) ); case MAP -> Exp.and( getFilterExp(Exp.val((Map) value.getObject()), - getField(qualifierMap), Exp::ge, Exp::mapBin), + getBinName(qualifierMap), Exp::ge, Exp::mapBin), getFilterExp(Exp.val((Map) value2.getObject()), - getField(qualifierMap), Exp::lt, Exp::mapBin) + getBinName(qualifierMap), Exp::lt, Exp::mapBin) ); case LIST -> Exp.and( - getFilterExp(Exp.val((List) value.getObject()), getField(qualifierMap), + getFilterExp(Exp.val((List) value.getObject()), getBinName(qualifierMap), Exp::ge, Exp::listBin), - getFilterExp(Exp.val((List) value2.getObject()), getField(qualifierMap), + getFilterExp(Exp.val((List) value2.getObject()), getBinName(qualifierMap), Exp::lt, Exp::listBin) ); default -> throw new IllegalArgumentException("BETWEEN: unexpected value of type " + value.getClass() @@ -381,7 +385,7 @@ public Filter sIndexFilter(Map qualifierMap) { if (getValue(qualifierMap).getType() != INTEGER || getSecondValue(qualifierMap).getType() != INTEGER) { return null; } - return Filter.range(getField(qualifierMap), getValue(qualifierMap).toLong(), + return Filter.range(getBinName(qualifierMap), getValue(qualifierMap).toLong(), getSecondValue(qualifierMap).toLong()); } }, @@ -389,7 +393,7 @@ public Filter sIndexFilter(Map qualifierMap) { @Override public Exp filterExp(Map qualifierMap) { String startWithRegexp = getStartsWith(getValue(qualifierMap).toString()); - return Exp.regexCompare(startWithRegexp, regexFlags(qualifierMap), Exp.stringBin(getField(qualifierMap))); + return Exp.regexCompare(startWithRegexp, regexFlags(qualifierMap), Exp.stringBin(getBinName(qualifierMap))); } @Override @@ -401,7 +405,7 @@ public Filter sIndexFilter(Map qualifierMap) { @Override public Exp filterExp(Map qualifierMap) { String endWithRegexp = getEndsWith(getValue(qualifierMap).toString()); - return Exp.regexCompare(endWithRegexp, regexFlags(qualifierMap), Exp.stringBin(getField(qualifierMap))); + return Exp.regexCompare(endWithRegexp, regexFlags(qualifierMap), Exp.stringBin(getBinName(qualifierMap))); } @Override @@ -413,7 +417,8 @@ public Filter sIndexFilter(Map qualifierMap) { @Override public Exp filterExp(Map qualifierMap) { String containingRegexp = getContaining(getValue(qualifierMap).toString()); - return Exp.regexCompare(containingRegexp, regexFlags(qualifierMap), Exp.stringBin(getField(qualifierMap))); + return Exp.regexCompare(containingRegexp, regexFlags(qualifierMap), + Exp.stringBin(getBinName(qualifierMap))); } @Override @@ -425,9 +430,9 @@ public Filter sIndexFilter(Map qualifierMap) { @Override public Exp filterExp(Map qualifierMap) { String notContainingRegexp = getNotContaining(getValue(qualifierMap).toString()); - return Exp.or(Exp.not(Exp.binExists(getField(qualifierMap))), + return Exp.or(Exp.not(Exp.binExists(getBinName(qualifierMap))), Exp.not(Exp.regexCompare(notContainingRegexp, regexFlags(qualifierMap), - Exp.stringBin(getField(qualifierMap))))); + Exp.stringBin(getBinName(qualifierMap))))); } @Override @@ -442,7 +447,7 @@ public Exp filterExp(Map qualifierMap) { if (ignoreCase(qualifierMap)) { flags = RegexFlag.EXTENDED | RegexFlag.ICASE; } - return Exp.regexCompare(getValue(qualifierMap).toString(), flags, Exp.stringBin(getField(qualifierMap))); + return Exp.regexCompare(getValue(qualifierMap).toString(), flags, Exp.stringBin(getBinName(qualifierMap))); } @Override @@ -461,30 +466,17 @@ public Exp filterExp(Map qualifierMap) { */ @Override public Filter sIndexFilter(Map qualifierMap) { - String[] dotPathArr = getDotPathArray(getDotPath(qualifierMap)); - final boolean useCtx = dotPathArr != null && dotPathArr.length > 2; - return switch (getValue(qualifierMap).getType()) { case STRING -> { if (ignoreCase(qualifierMap)) { // there is no case-insensitive string comparison filter - yield null; // MAP_VALUE_EQ_BY_KEY sIndexFilter: case-insensitive comparison is not supported - } - if (useCtx) { - yield null; // currently not supported - } else { - yield Filter.contains(getField(qualifierMap), IndexCollectionType.MAPVALUES, - getValue(qualifierMap).toString()); - } - } - case INTEGER -> { - if (useCtx) { - yield null; // currently not supported - } else { - yield Filter.range(getField(qualifierMap), IndexCollectionType.MAPVALUES, - getValue(qualifierMap).toLong(), - getValue(qualifierMap).toLong()); + yield null; } + yield Filter.contains(getBinName(qualifierMap), IndexCollectionType.MAPVALUES, + getValue(qualifierMap).toString(), getCtx(getCtxList(qualifierMap))); } + case INTEGER -> Filter.range(getBinName(qualifierMap), IndexCollectionType.MAPVALUES, + getValue(qualifierMap).toLong(), getValue(qualifierMap).toLong(), + getCtx(getCtxList(qualifierMap))); default -> null; }; } @@ -507,7 +499,7 @@ public Exp filterExp(Map qualifierMap) { Collection collection = getValueAsCollectionOrFail(qualifierMap); Exp[] arrElementsExp = collection.stream().map(item -> Qualifier.builder() - .setField(getField(qualifierMap)) + .setBinName(getBinName(qualifierMap)) .setFilterOperation(FilterOperation.MAP_VAL_EQ_BY_KEY) .setKey(getKey(qualifierMap)) .setValue(Value.get(item)) @@ -530,7 +522,7 @@ public Exp filterExp(Map qualifierMap) { Collection collection = getValueAsCollectionOrFail(qualifierMap); Exp[] arrElementsExp = collection.stream().map(item -> Qualifier.builder() - .setField(getField(qualifierMap)) + .setBinName(getBinName(qualifierMap)) .setFilterOperation(FilterOperation.MAP_VAL_NOTEQ_BY_KEY) .setKey(getKey(qualifierMap)) .setValue(Value.get(item)) @@ -562,14 +554,9 @@ public Filter sIndexFilter(Map qualifierMap) { return null; } - String[] dotPathArr = getDotPathArray(getDotPath(qualifierMap)); - if (dotPathArr != null && dotPathArr.length > 2) { - return null; // currently not supported - } else { - return Filter.range(getField(qualifierMap), IndexCollectionType.MAPVALUES, - getKey(qualifierMap).toLong() + 1, - Long.MAX_VALUE); - } + return Filter.range(getBinName(qualifierMap), IndexCollectionType.MAPVALUES, + getKey(qualifierMap).toLong() + 1, + Long.MAX_VALUE, getCtx(getCtxList(qualifierMap))); } }, MAP_VAL_GTEQ_BY_KEY { @@ -586,15 +573,8 @@ public Filter sIndexFilter(Map qualifierMap) { if (getKey(qualifierMap).getType() != INTEGER) { return null; } - - String[] dotPathArr = getDotPathArray(getDotPath(qualifierMap)); - if (dotPathArr != null && dotPathArr.length > 2) { - return null; // currently not supported - } else { - return Filter.range(getField(qualifierMap), IndexCollectionType.MAPVALUES, - getKey(qualifierMap).toLong(), - Long.MAX_VALUE); - } + return Filter.range(getBinName(qualifierMap), IndexCollectionType.MAPVALUES, + getKey(qualifierMap).toLong(), Long.MAX_VALUE, getCtx(getCtxList(qualifierMap))); } }, MAP_VAL_LT_BY_KEY { @@ -612,14 +592,8 @@ public Filter sIndexFilter(Map qualifierMap) { if (getKey(qualifierMap).getType() != INTEGER || getKey(qualifierMap).toLong() == Long.MIN_VALUE) { return null; } - - String[] dotPathArr = getDotPathArray(getDotPath(qualifierMap)); - if (dotPathArr != null && dotPathArr.length > 2) { - return null; // currently not supported - } else { - return Filter.range(getField(qualifierMap), IndexCollectionType.MAPVALUES, Long.MIN_VALUE, - getKey(qualifierMap).toLong() - 1); - } + return Filter.range(getBinName(qualifierMap), IndexCollectionType.MAPVALUES, Long.MIN_VALUE, + getKey(qualifierMap).toLong() - 1, getCtx(getCtxList(qualifierMap))); } }, MAP_VAL_LTEQ_BY_KEY { @@ -636,20 +610,14 @@ public Filter sIndexFilter(Map qualifierMap) { if (getValue(qualifierMap).getType() != INTEGER) { return null; } - - String[] dotPathArr = getDotPathArray(getDotPath(qualifierMap)); - if (dotPathArr != null && dotPathArr.length > 2) { - return null; // currently not supported - } else { - return Filter.range(getField(qualifierMap), IndexCollectionType.MAPVALUES, Long.MIN_VALUE, - getValue(qualifierMap).toLong()); - } + return Filter.range(getBinName(qualifierMap), IndexCollectionType.MAPVALUES, Long.MIN_VALUE, + getValue(qualifierMap).toLong(), getCtx(getCtxList(qualifierMap))); } }, MAP_VAL_BETWEEN_BY_KEY { @Override public Exp filterExp(Map qualifierMap) { - String[] dotPathArr = getDotPathArray(getDotPath(qualifierMap)); + List ctxList = getCtxList(qualifierMap); Exp lowerLimit; Exp upperLimit; Exp.Type type; @@ -679,20 +647,20 @@ public Exp filterExp(Map qualifierMap) { getValue(qualifierMap).getClass().getSimpleName()); } - return mapValBetweenByKey(qualifierMap, dotPathArr, type, lowerLimit, upperLimit); + return mapValBetweenByKey(qualifierMap, ctxList, type, lowerLimit, upperLimit); } - private static Exp mapValBetweenByKey(Map qualifierMap, String[] dotPathArr, + private static Exp mapValBetweenByKey(Map qualifierMap, List ctxList, Exp.Type type, Exp lowerLimit, Exp upperLimit) { Exp mapExp; - if (dotPathArr != null && dotPathArr.length > 2) { + if (ctxList != null && ctxList.size() > 2) { mapExp = MapExp.getByKey(MapReturnType.VALUE, type, Exp.val(getKey(qualifierMap).toString()), - Exp.mapBin(getField(qualifierMap)), dotPathToCtxMapKeys(dotPathArr)); + Exp.mapBin(getBinName(qualifierMap)), resolveCtxList(ctxList)); } else { mapExp = MapExp.getByKey(MapReturnType.VALUE, type, Exp.val(getKey(qualifierMap).toString()), - Exp.mapBin(getField(qualifierMap))); + Exp.mapBin(getBinName(qualifierMap))); } return Exp.and( @@ -709,15 +677,9 @@ public Filter sIndexFilter(Map qualifierMap) { if (getValue(qualifierMap).getType() != INTEGER || getSecondValue(qualifierMap).getType() != INTEGER) { return null; } - - String[] dotPathArr = getDotPathArray(getDotPath(qualifierMap)); - if (dotPathArr != null && dotPathArr.length > 2) { - return null; // currently not supported - } else { - return Filter.range(getField(qualifierMap), IndexCollectionType.MAPVALUES, - getValue(qualifierMap).toLong(), - getSecondValue(qualifierMap).toLong()); - } + return Filter.range(getBinName(qualifierMap), IndexCollectionType.MAPVALUES, + getValue(qualifierMap).toLong(), getSecondValue(qualifierMap).toLong(), + getCtx(getCtxList(qualifierMap))); } }, MAP_VAL_STARTS_WITH_BY_KEY { @@ -727,7 +689,7 @@ public Exp filterExp(Map qualifierMap) { return Exp.regexCompare(startWithRegexp, regexFlags(qualifierMap), MapExp.getByKey(MapReturnType.VALUE, Exp.Type.STRING, Exp.val(getKey(qualifierMap).toString()), - Exp.mapBin(getField(qualifierMap))) + Exp.mapBin(getBinName(qualifierMap))) ); } @@ -745,7 +707,7 @@ public Exp filterExp(Map qualifierMap) { } return Exp.regexCompare(getValue(qualifierMap).toString(), flags, MapExp.getByKey(MapReturnType.VALUE, Exp.Type.STRING, Exp.val(getKey(qualifierMap).toString()), - Exp.mapBin(getField(qualifierMap))) + Exp.mapBin(getBinName(qualifierMap))) ); } @@ -761,7 +723,7 @@ public Exp filterExp(Map qualifierMap) { return Exp.regexCompare(endWithRegexp, regexFlags(qualifierMap), MapExp.getByKey(MapReturnType.VALUE, Exp.Type.STRING, Exp.val(getValue(qualifierMap).toString()), - Exp.mapBin(getField(qualifierMap))) + Exp.mapBin(getBinName(qualifierMap))) ); } @@ -780,7 +742,7 @@ public Exp filterExp(Map qualifierMap) { // Out of simple properties only a String is validated for CONTAINING String containingRegexp = getContaining(getValue(qualifierMap).toString()); Exp nestedString = MapExp.getByKey(MapReturnType.VALUE, Exp.Type.STRING, - Exp.val(getKey(qualifierMap).toString()), Exp.mapBin(getField(qualifierMap))); + Exp.val(getKey(qualifierMap).toString()), Exp.mapBin(getBinName(qualifierMap))); return Exp.regexCompare(containingRegexp, regexFlags(qualifierMap), nestedString); } case LIST -> { @@ -789,7 +751,7 @@ public Exp filterExp(Map qualifierMap) { // Map value is a List Exp nestedList = MapExp.getByKey(MapReturnType.VALUE, Exp.Type.LIST, key, - Exp.mapBin(getField(qualifierMap))); + Exp.mapBin(getBinName(qualifierMap))); // Check whether List contains the value return Exp.gt( ListExp.getByValue(ListReturnType.COUNT, value, nestedList), @@ -802,7 +764,7 @@ public Exp filterExp(Map qualifierMap) { Exp secondKey = getExpOrFail(getNestedKey(qualifierMap), "MAP_VAL_CONTAINING_BY_KEY"); Exp nestedMap = MapExp.getByKey(MapReturnType.VALUE, Exp.Type.MAP, key, - Exp.mapBin(getField(qualifierMap))); + Exp.mapBin(getBinName(qualifierMap))); return Exp.eq( MapExp.getByKey(MapReturnType.VALUE, getExpType(val), secondKey, nestedMap), value); @@ -820,10 +782,10 @@ public Filter sIndexFilter(Map qualifierMap) { @Override public Exp filterExp(Map qualifierMap) { Integer nestedType = getNestedType(qualifierMap); - Exp mapBinDoesNotExist = Exp.not(Exp.binExists(getField(qualifierMap))); + Exp mapBinDoesNotExist = Exp.not(Exp.binExists(getBinName(qualifierMap))); Exp key = getExpOrFail(getKey(qualifierMap), "MAP_VAL_NOT_CONTAINING_BY_KEY"); Exp mapNotContainingKey = Exp.eq( - MapExp.getByKey(MapReturnType.COUNT, Exp.Type.INT, key, Exp.mapBin(getField(qualifierMap))), + MapExp.getByKey(MapReturnType.COUNT, Exp.Type.INT, key, Exp.mapBin(getBinName(qualifierMap))), Exp.val(0)); if (nestedType == null) throw new IllegalStateException("Expecting valid nestedType, got null"); @@ -833,7 +795,7 @@ public Exp filterExp(Map qualifierMap) { String containingRegexp = getContaining(getValue(qualifierMap).toString()); Exp nestedString = MapExp.getByKey(MapReturnType.VALUE, Exp.Type.STRING, Exp.val(getKey(qualifierMap).toString()), - Exp.mapBin(getField(qualifierMap))); + Exp.mapBin(getBinName(qualifierMap))); return Exp.or(mapBinDoesNotExist, mapNotContainingKey, Exp.not(Exp.regexCompare(containingRegexp, regexFlags(qualifierMap), nestedString))); } @@ -841,7 +803,7 @@ public Exp filterExp(Map qualifierMap) { Exp value = getExpOrFail(getValue(qualifierMap), "MAP_VAL_NOT_CONTAINING_BY_KEY"); Exp nestedList = MapExp.getByKey(MapReturnType.VALUE, Exp.Type.LIST, key, - Exp.mapBin(getField(qualifierMap))); + Exp.mapBin(getBinName(qualifierMap))); Exp nestedListNotContainingValue = Exp.eq(ListExp.getByValue(ListReturnType.COUNT, value, nestedList), Exp.val(0)); return Exp.or(mapBinDoesNotExist, mapNotContainingKey, nestedListNotContainingValue); @@ -852,7 +814,7 @@ public Exp filterExp(Map qualifierMap) { Exp secondKey = getExpOrFail(getNestedKey(qualifierMap), "MAP_VAL_NOT_CONTAINING_BY_KEY"); Exp nestedMap = MapExp.getByKey(MapReturnType.VALUE, Exp.Type.MAP, key, - Exp.mapBin(getField(qualifierMap))); + Exp.mapBin(getBinName(qualifierMap))); Exp nestedMapNotContainingValueByKey = Exp.ne( MapExp.getByKey(MapReturnType.VALUE, getExpType(val), secondKey, nestedMap), value); @@ -892,8 +854,8 @@ public Filter sIndexFilter(Map qualifierMap) { MAP_VAL_IS_NOT_NULL_BY_KEY { @Override public Exp filterExp(Map qualifierMap) { - String[] dotPathArray = getDotPathArray(getDotPath(qualifierMap)); - if (dotPathArray != null && dotPathArray.length > 1) { + List ctx = getCtxList(qualifierMap); + if (ctx != null) { // in case it is a field of an object set to null the key does not get added to a Map, // so it is enough to look for Maps with the given key return mapKeysContain(qualifierMap); @@ -912,8 +874,8 @@ public Filter sIndexFilter(Map qualifierMap) { MAP_VAL_IS_NULL_BY_KEY { @Override public Exp filterExp(Map qualifierMap) { - String[] dotPathArray = getDotPathArray(getDotPath(qualifierMap)); - if (dotPathArray != null && dotPathArray.length > 1) { + List ctx = getCtxList(qualifierMap); + if (ctx != null) { // in case it is a field of an object set to null the key does not get added to a Map, // so it is enough to look for Maps without the given key return mapKeysNotContain(qualifierMap); @@ -992,7 +954,7 @@ public Exp filterExp(Map qualifierMap) { return Exp.gt( MapExp.getByKeyRange(MapReturnType.COUNT, twoValues.getFirst(), twoValues.getSecond(), - Exp.mapBin(getField(qualifierMap))), + Exp.mapBin(getBinName(qualifierMap))), Exp.val(0)); } @@ -1020,7 +982,7 @@ public Exp filterExp(Map qualifierMap) { return Exp.gt( MapExp.getByValueRange(MapReturnType.COUNT, twoValues.getFirst(), twoValues.getSecond(), - Exp.mapBin(getField(qualifierMap))), + Exp.mapBin(getBinName(qualifierMap))), Exp.val(0)); } @@ -1035,7 +997,7 @@ public Filter sIndexFilter(Map qualifierMap) { GEO_WITHIN { @Override public Exp filterExp(Map qualifierMap) { - return Exp.geoCompare(Exp.geoBin(getField(qualifierMap)), Exp.geo(getValue(qualifierMap).toString())); + return Exp.geoCompare(Exp.geoBin(getBinName(qualifierMap)), Exp.geo(getValue(qualifierMap).toString())); } @Override @@ -1056,7 +1018,7 @@ public Exp filterExp(Map qualifierMap) { val.getClass().getSimpleName(); Exp value = getValueExpOrFail(val, errMsg); return Exp.gt( - ListExp.getByValue(ListReturnType.COUNT, value, Exp.listBin(getField(qualifierMap))), + ListExp.getByValue(ListReturnType.COUNT, value, Exp.listBin(getBinName(qualifierMap))), Exp.val(0)); } @@ -1082,9 +1044,9 @@ public Exp filterExp(Map qualifierMap) { val.getClass().getSimpleName(); Exp value = getValueExpOrFail(val, errMsg); - Exp binIsNull = Exp.not(Exp.binExists(getField(qualifierMap))); + Exp binIsNull = Exp.not(Exp.binExists(getBinName(qualifierMap))); Exp listNotContaining = Exp.eq( - ListExp.getByValue(ListReturnType.COUNT, value, Exp.listBin(getField(qualifierMap))), + ListExp.getByValue(ListReturnType.COUNT, value, Exp.listBin(getBinName(qualifierMap))), Exp.val(0)); return Exp.or(binIsNull, listNotContaining); } @@ -1113,7 +1075,7 @@ public Exp filterExp(Map qualifierMap) { return Exp.gt( ListExp.getByValueRange(ListReturnType.COUNT, twoValues.getFirst(), twoValues.getSecond(), - Exp.listBin(getField(qualifierMap))), + Exp.listBin(getBinName(qualifierMap))), Exp.val(0)); } @@ -1138,7 +1100,7 @@ public Exp filterExp(Map qualifierMap) { return Exp.gt( ListExp.getByValueRange(ListReturnType.COUNT, Exp.val(getValue(qualifierMap).toLong() + 1L), - null, Exp.listBin(getField(qualifierMap))), + null, Exp.listBin(getBinName(qualifierMap))), Exp.val(0) ); } else { @@ -1152,8 +1114,8 @@ public Exp filterExp(Map qualifierMap) { }; Exp rangeIncludingValue = ListExp.getByValueRange(ListReturnType.COUNT, value, null, - Exp.listBin(getField(qualifierMap))); - Exp valueOnly = ListExp.getByValue(ListReturnType.COUNT, value, Exp.listBin(getField(qualifierMap))); + Exp.listBin(getBinName(qualifierMap))); + Exp valueOnly = ListExp.getByValue(ListReturnType.COUNT, value, Exp.listBin(getBinName(qualifierMap))); return Exp.gt(Exp.sub(rangeIncludingValue, valueOnly), Exp.val(0)); } } @@ -1165,7 +1127,7 @@ public Filter sIndexFilter(Map qualifierMap) { return null; } - return Filter.range(getField(qualifierMap), IndexCollectionType.LIST, + return Filter.range(getBinName(qualifierMap), IndexCollectionType.LIST, getValue(qualifierMap).toLong() + 1, Long.MAX_VALUE); } }, @@ -1183,7 +1145,7 @@ public Exp filterExp(Map qualifierMap) { }; return Exp.gt( - ListExp.getByValueRange(ListReturnType.COUNT, value, null, Exp.listBin(getField(qualifierMap))), + ListExp.getByValueRange(ListReturnType.COUNT, value, null, Exp.listBin(getBinName(qualifierMap))), Exp.val(0)); } @@ -1193,7 +1155,7 @@ public Filter sIndexFilter(Map qualifierMap) { return null; } - return Filter.range(getField(qualifierMap), IndexCollectionType.LIST, getValue(qualifierMap).toLong(), + return Filter.range(getBinName(qualifierMap), IndexCollectionType.LIST, getValue(qualifierMap).toLong(), Long.MAX_VALUE); } }, @@ -1219,7 +1181,7 @@ public Exp filterExp(Map qualifierMap) { }; return Exp.gt( - ListExp.getByValueRange(ListReturnType.COUNT, null, value, Exp.listBin(getField(qualifierMap))), + ListExp.getByValueRange(ListReturnType.COUNT, null, value, Exp.listBin(getBinName(qualifierMap))), Exp.val(0)); } @@ -1230,7 +1192,7 @@ public Filter sIndexFilter(Map qualifierMap) { return null; } - return Filter.range(getField(qualifierMap), IndexCollectionType.LIST, Long.MIN_VALUE, + return Filter.range(getBinName(qualifierMap), IndexCollectionType.LIST, Long.MIN_VALUE, getValue(qualifierMap).toLong() - 1); } }, @@ -1247,7 +1209,7 @@ public Exp filterExp(Map qualifierMap) { return Exp.gt( ListExp.getByValueRange(ListReturnType.COUNT, Exp.val(Long.MIN_VALUE), - upperLimit, Exp.listBin(getField(qualifierMap))), + upperLimit, Exp.listBin(getBinName(qualifierMap))), Exp.val(0)); } else { Exp value = switch (getValue(qualifierMap).getType()) { @@ -1260,8 +1222,8 @@ public Exp filterExp(Map qualifierMap) { }; Exp rangeIncludingValue = ListExp.getByValueRange(ListReturnType.COUNT, null, value, - Exp.listBin(getField(qualifierMap))); - Exp valueOnly = ListExp.getByValue(ListReturnType.COUNT, value, Exp.listBin(getField(qualifierMap))); + Exp.listBin(getBinName(qualifierMap))); + Exp valueOnly = ListExp.getByValue(ListReturnType.COUNT, value, Exp.listBin(getBinName(qualifierMap))); return Exp.gt(Exp.add(rangeIncludingValue, valueOnly), Exp.val(0)); } } @@ -1272,13 +1234,13 @@ public Filter sIndexFilter(Map qualifierMap) { return null; } - return Filter.range(getField(qualifierMap), IndexCollectionType.LIST, Long.MIN_VALUE, + return Filter.range(getBinName(qualifierMap), IndexCollectionType.LIST, Long.MIN_VALUE, getValue(qualifierMap).toLong()); } }, IS_NOT_NULL { @Override public Exp filterExp(Map qualifierMap) { - return Exp.binExists(getField(qualifierMap)); + return Exp.binExists(getBinName(qualifierMap)); } @Override @@ -1288,7 +1250,8 @@ public Filter sIndexFilter(Map qualifierMap) { }, IS_NULL { @Override public Exp filterExp(Map qualifierMap) { - return Exp.not(Exp.binExists(getField(qualifierMap))); // with value set to null a bin becomes non-existing + // with value set to null a bin becomes non-existing + return Exp.not(Exp.binExists(getBinName(qualifierMap))); } @Override @@ -1298,7 +1261,7 @@ public Filter sIndexFilter(Map qualifierMap) { }, NOT_NULL { @Override public Exp filterExp(Map qualifierMap) { - return Exp.binExists(getField(qualifierMap)); // if a bin exists its value is not null + return Exp.binExists(getBinName(qualifierMap)); // if a bin exists its value is not null } @Override @@ -1373,7 +1336,7 @@ private static Collection getValueAsCollectionOrFail(Map getMetadataExp(Map qualifierMap) { CriteriaDefinition.AerospikeMetadata metadataField = getMetadataField(qualifierMap); - String field = getField(qualifierMap); + String field = getBinName(qualifierMap); if (metadataField != null && (field == null || field.isEmpty())) { FilterOperation operation = getOperation(qualifierMap); @@ -1442,7 +1405,7 @@ private static Exp mapKeysNotContain(Map qualifierMap) { String errMsg = "MAP_KEYS_NOT_CONTAIN FilterExpression unsupported type: got " + getKey(qualifierMap).getClass().getSimpleName(); Exp mapKeysNotContain = mapKeysCount(qualifierMap, Exp::eq, errMsg); - Exp binDoesNotExist = Exp.not(Exp.binExists(getField(qualifierMap))); + Exp binDoesNotExist = Exp.not(Exp.binExists(getBinName(qualifierMap))); return Exp.or(binDoesNotExist, mapKeysNotContain); } @@ -1455,7 +1418,7 @@ private static Exp mapKeysContain(Map qualifierMap) { private static Exp mapValuesNotContain(Map qualifierMap) { String errMsg = "MAP_VALUES_NOT_CONTAIN FilterExpression unsupported type: got " + getValue(qualifierMap).getClass().getSimpleName(); - Exp binDoesNotExist = Exp.not(Exp.binExists(getField(qualifierMap))); + Exp binDoesNotExist = Exp.not(Exp.binExists(getBinName(qualifierMap))); Exp mapValuesNotContain = mapValuesCountComparedToZero(qualifierMap, Exp::eq, errMsg); return Exp.or(binDoesNotExist, mapValuesNotContain); } @@ -1470,7 +1433,7 @@ private static Exp mapValuesContain(Map qualifierMap) { private static Exp mapKeysCount(Map qualifierMap, BinaryOperator operator, String errMsg) { Exp key = getValueExpOrFail(getValue(qualifierMap), errMsg); - Exp map = Exp.mapBin(getField(qualifierMap)); + Exp map = Exp.mapBin(getBinName(qualifierMap)); Value mapBinKey = getKey(qualifierMap); if (!mapBinKey.equals(Value.NullValue.INSTANCE)) { @@ -1480,7 +1443,8 @@ private static Exp mapKeysCount(Map qualifierMap, BinaryOp Exp nestedMapKey = getValueExpOrFail(mapBinKey, err); // locate a Map within its parent bin - map = MapExp.getByKey(MapReturnType.VALUE, Exp.Type.MAP, nestedMapKey, Exp.mapBin(getField(qualifierMap))); + map = MapExp.getByKey(MapReturnType.VALUE, Exp.Type.MAP, nestedMapKey, + Exp.mapBin(getBinName(qualifierMap))); } return operator.apply( @@ -1493,7 +1457,7 @@ private static Exp mapValuesCountComparedToZero(Map qualif BinaryOperator operator, String errMsg) { Exp value = getValueExpOrFail(getValue(qualifierMap), errMsg); - Exp map = Exp.mapBin(getField(qualifierMap)); + Exp map = Exp.mapBin(getBinName(qualifierMap)); Value mapBinKey = getKey(qualifierMap); if (!mapBinKey.equals(Value.NullValue.INSTANCE)) { @@ -1503,7 +1467,8 @@ private static Exp mapValuesCountComparedToZero(Map qualif Exp nestedMapKey = getValueExpOrFail(mapBinKey, err); // if it is a nested query we need to locate a Map within its parent bin - map = MapExp.getByKey(MapReturnType.VALUE, Exp.Type.MAP, nestedMapKey, Exp.mapBin(getField(qualifierMap))); + map = MapExp.getByKey(MapReturnType.VALUE, Exp.Type.MAP, nestedMapKey, + Exp.mapBin(getBinName(qualifierMap))); } return operator.apply( @@ -1513,30 +1478,30 @@ private static Exp mapValuesCountComparedToZero(Map qualif private static Exp getFilterExpMapValOrFail(Map qualifierMap, BinaryOperator operator, String opName) { - String[] dotPathArr = getDotPathArray(getDotPath(qualifierMap)); + List ctxList = getCtxList(qualifierMap); return switch (getValue(qualifierMap).getType()) { - case INTEGER -> operator.apply(getMapExp(qualifierMap, dotPathArr, Exp.Type.INT), + case INTEGER -> operator.apply(getMapExp(qualifierMap, ctxList, Exp.Type.INT), Exp.val(getValue(qualifierMap).toLong())); - case STRING -> operator.apply(getMapExp(qualifierMap, dotPathArr, Exp.Type.STRING), + case STRING -> operator.apply(getMapExp(qualifierMap, ctxList, Exp.Type.STRING), Exp.val(getValue(qualifierMap).toString())); - case LIST -> operator.apply(getMapExp(qualifierMap, dotPathArr, Exp.Type.LIST), + case LIST -> operator.apply(getMapExp(qualifierMap, ctxList, Exp.Type.LIST), Exp.val((List) getValue(qualifierMap).getObject())); - case MAP -> operator.apply(getMapExp(qualifierMap, dotPathArr, Exp.Type.MAP), + case MAP -> operator.apply(getMapExp(qualifierMap, ctxList, 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, String[] dotPathArr, Exp.Type expType) { + private static Exp getMapExp(Map qualifierMap, List ctxList, Exp.Type expType) { Exp mapKeyExp = getMapKeyExp(getKey(qualifierMap).getObject(), keepOriginalKeyTypes(qualifierMap)); - if (dotPathArr != null && dotPathArr.length > 2) { + if (ctxList != null && ctxList.size() > 2) { return MapExp.getByKey(MapReturnType.VALUE, expType, mapKeyExp, - Exp.mapBin(getField(qualifierMap)), dotPathToCtxMapKeys(dotPathArr)); + Exp.mapBin(getBinName(qualifierMap)), resolveCtxList(ctxList)); } else { return MapExp.getByKey(MapReturnType.VALUE, expType, mapKeyExp, - Exp.mapBin(getField(qualifierMap))); + Exp.mapBin(getBinName(qualifierMap))); } } @@ -1580,8 +1545,7 @@ private static Exp getFilterExpMapValNotEqOrFail(Map quali private static Exp getMapValEqOrFail(Map qualifierMap, BinaryOperator operator, String opName) { - String[] dotPathArr = getDotPathArray(getDotPath(qualifierMap)); - final boolean useCtx = dotPathArr != null && dotPathArr.length > 2; + List ctxList = getCtxList(qualifierMap); // boolean values are read as BoolIntValue (INTEGER ParticleType) if Value.UseBoolBin == false // so converting to BooleanValue to process correctly @@ -1591,67 +1555,66 @@ private static Exp getMapValEqOrFail(Map qualifierMap, Bin Value value = getValue(qualifierMap); return switch (value.getType()) { - case INTEGER -> getMapValEqExp(qualifierMap, Exp.Type.INT, value.toLong(), dotPathArr, operator, - useCtx); + case INTEGER -> getMapValEqExp(qualifierMap, Exp.Type.INT, value.toLong(), ctxList, 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(), dotPathArr, operator, - useCtx); + yield getMapValEqExp(qualifierMap, Exp.Type.STRING, value.toString(), ctxList, operator); } - case BOOL -> getMapValEqExp(qualifierMap, Exp.Type.BOOL, value.getObject(), dotPathArr, operator, - useCtx); - case LIST -> getMapValEqExp(qualifierMap, Exp.Type.LIST, value.getObject(), dotPathArr, operator, - useCtx); - case MAP -> getMapValEqExp(qualifierMap, Exp.Type.MAP, value.getObject(), dotPathArr, operator, useCtx); + 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); default -> throw new UnsupportedOperationException( opName + " FilterExpression unsupported type: " + value.getClass().getSimpleName()); }; } private static Exp getMapValEqExp(Map qualifierMap, Exp.Type expType, Object value, - String[] dotPathArr, - BinaryOperator operator, boolean useCtx) { - Exp mapExp = getMapValEq(qualifierMap, expType, dotPathArr, useCtx); + List ctxList, + BinaryOperator operator) { + Exp mapExp = getMapValEq(qualifierMap, expType, ctxList); return operator.apply(mapExp, toExp(value)); } - private static Exp getMapValEq(Map qualifierMap, Exp.Type expType, String[] dotPathArr, - boolean useCtx) { - if (useCtx) { + private static Exp getMapValEq(Map qualifierMap, Exp.Type expType, List ctxList) { + Exp bin = getBin(getBinName(qualifierMap), getBinType(qualifierMap), "MAP_VAL_EQ"); + if (ctxList != null) { return MapExp.getByKey(MapReturnType.VALUE, expType, - getValueExpOrFail(getKey(qualifierMap), "MAP_VAL_EQ: unsupported type"), // key (field name) - Exp.mapBin(getField(qualifierMap)), dotPathToCtxMapKeys(dotPathArr)); + getValueExpOrFail(getKey(qualifierMap), "MAP_VAL_EQ: unsupported type"), + bin, resolveCtxList(ctxList)); } else { return MapExp.getByKey(MapReturnType.VALUE, expType, getValueExpOrFail(getKey(qualifierMap), "MAP_VAL_EQ: unsupported type"), - Exp.mapBin(getField(qualifierMap))); + bin); } } + private static Exp getBin(String binName, Exp.Type binType, String filterOperation) { + if (binType == null) { + return Exp.mapBin(binName); + } + + return switch (binType) { + case INT -> Exp.intBin(binName); + case STRING -> Exp.stringBin(binName); + case BOOL -> Exp.boolBin(binName); + case LIST -> Exp.listBin(binName); + default -> Exp.mapBin(binName); // currently mapBin is the default + }; + } + private static Exp getFilterExp(Exp exp, String field, BinaryOperator operator, Function binExp) { return operator.apply(binExp.apply(field), exp); } - private static String[] getDotPathArray(List dotPathList) { - if (dotPathList != null && !dotPathList.isEmpty()) { - // the first element of dotPath is part.getProperty().toDotPath() - // the second element of dotPath, if present, is a value - Stream valueStream = dotPathList.size() == 1 || dotPathList.get(1) == null ? Stream.empty() - : Stream.of(dotPathList.get(1)); - return Stream.concat(Arrays.stream(dotPathList.get(0).split("\\.")), valueStream) - .toArray(String[]::new); - } - return null; - } - - private static CTX[] dotPathToCtxMapKeys(String[] dotPathArray) { - return Arrays.stream(dotPathArray).map(str -> CTX.mapKey(Value.get(str))) - .skip(1) // first element is bin name - .limit(dotPathArray.length - 2L) // last element is the key we already have + private static CTX[] resolveCtxList(List ctxList) { + return ctxList.stream() + .filter(not(String::isEmpty)) + .map(AerospikeIndexResolverUtils::toCtx) + .filter(Objects::nonNull) .toArray(CTX[]::new); } @@ -1683,8 +1646,12 @@ private static Exp toExp(Object value) { return res; } - protected static String getField(Map qualifierMap) { - return (String) qualifierMap.get(FIELD); + protected static String getBinName(Map qualifierMap) { + return (String) qualifierMap.get(BIN_NAME); + } + + protected static Exp.Type getBinType(Map qualifierMap) { + return (Exp.Type) qualifierMap.get(BIN_TYPE); } protected static CriteriaDefinition.AerospikeMetadata getMetadataField(Map qualifierMap) { @@ -1733,8 +1700,22 @@ protected static Integer getNestedType(Map qualifierMap) { } @SuppressWarnings("unchecked") - protected static List getDotPath(Map qualifierMap) { - return (List) qualifierMap.get(DOT_PATH); + 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 List getCtxList(String ctxPath) { + if (ctxPath == null) return null; + + return Arrays.stream(ctxPath.split("\\.")) + .filter(not(String::isEmpty)) + .collect(Collectors.toList()); + } + + private static CTX[] getCtx(List ctxList) { + return ctxList != null ? resolveCtxList(ctxList) : null; } public abstract Exp filterExp(Map qualifierMap); @@ -1746,19 +1727,23 @@ protected Filter collectionContains(IndexCollectionType collectionType, Map Filter.contains(getField(qualifierMap), collectionType, val.toLong()); - case STRING -> Filter.contains(getField(qualifierMap), collectionType, val.toString()); + case INTEGER -> + Filter.contains(getBinName(qualifierMap), collectionType, val.toLong(), + getCtx(getCtxList(qualifierMap))); + case STRING -> + Filter.contains(getBinName(qualifierMap), collectionType, val.toString(), + getCtx(getCtxList(qualifierMap))); default -> null; }; } protected Filter collectionRange(IndexCollectionType collectionType, Map qualifierMap) { - return Filter.range(getField(qualifierMap), collectionType, getValue(qualifierMap).toLong(), + return Filter.range(getBinName(qualifierMap), collectionType, getValue(qualifierMap).toLong(), getSecondValue(qualifierMap).toLong()); } @SuppressWarnings("SameParameterValue") protected Filter geoWithinRadius(IndexCollectionType collectionType, Map qualifierMap) { - return Filter.geoContains(getField(qualifierMap), getValue(qualifierMap).toString()); + return Filter.geoContains(getBinName(qualifierMap), getValue(qualifierMap).toString()); } } 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 744f2c5b4..4b62309c2 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() - .setField(QueryEngine.Meta.KEY.toString()) + .setBinName(QueryEngine.Meta.KEY.toString()) .setFilterOperation(FilterOperation.EQ) .setValue(value) ); @@ -52,7 +52,7 @@ public KeyQualifier(Value value) { public KeyQualifier(byte[] digest) { super(Qualifier.builder() - .setField(QueryEngine.Meta.KEY.toString()) + .setBinName(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 e52a76b47..a1f4b409d 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() - .setField("latest_update_time") + .setBinName("latest_update_time") .setFilterOperation(op) .setValue(value) ); diff --git a/src/main/java/org/springframework/data/aerospike/query/StatementBuilder.java b/src/main/java/org/springframework/data/aerospike/query/StatementBuilder.java index fef06979b..75c14e645 100644 --- a/src/main/java/org/springframework/data/aerospike/query/StatementBuilder.java +++ b/src/main/java/org/springframework/data/aerospike/query/StatementBuilder.java @@ -122,16 +122,16 @@ private void setFilterFromSingleQualifier(Statement stmt, Qualifier qualifier) { private boolean isIndexedBin(Statement stmt, Qualifier qualifier) { boolean hasIndex = false, hasField = false; - if (StringUtils.hasLength(qualifier.getField())) { + if (StringUtils.hasLength(qualifier.getBinName())) { hasField = true; hasIndex = indexesCache.hasIndexFor( - new IndexedField(stmt.getNamespace(), stmt.getSetName(), qualifier.getField()) + new IndexedField(stmt.getNamespace(), stmt.getSetName(), qualifier.getBinName()) ); } if (log.isDebugEnabled() && hasField) { log.debug("Bin {}.{}.{} has secondary index: {}", - stmt.getNamespace(), stmt.getSetName(), qualifier.getField(), hasIndex); + stmt.getNamespace(), stmt.getSetName(), qualifier.getBinName(), hasIndex); } return hasIndex; } @@ -139,7 +139,7 @@ private boolean isIndexedBin(Statement stmt, Qualifier qualifier) { private int getMinBinValuesRatioForQualifier(Statement stmt, Qualifier qualifier) { // Get all indexes for field List indexList = indexesCache.getAllIndexesForField( - new IndexedField(stmt.getNamespace(), stmt.getSetName(), qualifier.getField())); + new IndexedField(stmt.getNamespace(), stmt.getSetName(), qualifier.getBinName())); // Return the lowest bin values ratio of the indexes in indexList Optional minBinValuesRatio = indexList.stream() 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 7e8a038f4..dc125e8f6 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 @@ -1,19 +1,13 @@ package org.springframework.data.aerospike.query.qualifier; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.data.aerospike.config.AerospikeDataConfigurationSupport; -import org.springframework.data.aerospike.config.AerospikeSettings; -import org.springframework.data.aerospike.convert.MappingAerospikeConverter; import org.springframework.data.aerospike.query.FilterOperation; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import static org.springframework.data.aerospike.query.qualifier.QualifierKey.DOT_PATH; -import static org.springframework.data.aerospike.query.qualifier.QualifierKey.FIELD; +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.KEY; import static org.springframework.data.aerospike.query.qualifier.QualifierKey.OPERATION; import static org.springframework.data.aerospike.query.qualifier.QualifierKey.SECOND_VALUE; @@ -33,8 +27,8 @@ public T setFilterOperation(FilterOperation filterOperation) { return (T) this; } - public String getField() { - return (String) map.get(FIELD); + public String getBinName() { + return (String) map.get(BIN_NAME); } public boolean hasKey() { @@ -49,8 +43,8 @@ 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 Qualifier build() { 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 f535cb3b8..23f7f08d7 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 @@ -57,7 +57,7 @@ public MetadataQualifierBuilder setSecondValueAsObj(Object object) { protected void validate() { // metadata query if (this.getMetadataField() != null) { - if (this.getField() == null) { + if (this.getBinName() == null) { FilterOperation operation = this.getFilterOperation(); switch (operation) { case EQ, NOTEQ, LT, LTEQ, GT, GTEQ -> Assert.isTrue(getValueAsObj() instanceof Long, 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 41f17000f..18d08432e 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 @@ -68,7 +68,7 @@ public Qualifier getCriteriaObject() { @Override public String getCriteriaField() { - return this.getField(); + return this.getBinName(); } private Map getMap() { @@ -87,8 +87,8 @@ public FilterOperation getOperation() { return (FilterOperation) internalMap.get(OPERATION); } - public String getField() { - return (String) internalMap.get(FIELD); + public String getBinName() { + return (String) internalMap.get(BIN_NAME); } public CriteriaDefinition.AerospikeMetadata getMetadataField() { @@ -140,8 +140,8 @@ public Value getSecondValue() { } @SuppressWarnings("unchecked") - public List getDotPath() { - return (List) internalMap.get(DOT_PATH); + public List getCtxPath() { + return (List) internalMap.get(CTX_PATH); } public Filter getSecondaryIndexFilter() { @@ -227,8 +227,9 @@ public Set> entrySet() { @Override public String toString() { - if (!StringUtils.hasLength(getField()) && StringUtils.hasLength(getMetadataField().toString())) { - return String.format("%s:%s:%s:%s:%s", getField(), getOperation(), getKey(), getValue(), getSecondValue()); + if (StringUtils.hasLength(getBinName()) && getMetadataField() == null) { + return String.format("%s:%s:%s:%s:%s", getBinName(), getOperation(), getKey(), getValue(), + getSecondValue()); } return String.format("(metadata) %s:%s:%s:%s:%s", getMetadataField().toString(), getOperation(), getKey(), getValue(), getSecondValue()); 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 4d3b1080f..0f704e68a 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 @@ -2,17 +2,11 @@ import com.aerospike.client.Value; import com.aerospike.client.command.ParticleType; +import com.aerospike.client.exp.Exp; import java.util.List; -import static org.springframework.data.aerospike.query.qualifier.QualifierKey.DOT_PATH; -import static org.springframework.data.aerospike.query.qualifier.QualifierKey.FIELD; -import static org.springframework.data.aerospike.query.qualifier.QualifierKey.IGNORE_CASE; -import static org.springframework.data.aerospike.query.qualifier.QualifierKey.KEY; -import static org.springframework.data.aerospike.query.qualifier.QualifierKey.NESTED_KEY; -import static org.springframework.data.aerospike.query.qualifier.QualifierKey.NESTED_TYPE; -import static org.springframework.data.aerospike.query.qualifier.QualifierKey.SECOND_VALUE; -import static org.springframework.data.aerospike.query.qualifier.QualifierKey.VALUE; +import static org.springframework.data.aerospike.query.qualifier.QualifierKey.*; public class QualifierBuilder extends BaseQualifierBuilder { @@ -27,8 +21,32 @@ public QualifierBuilder setIgnoreCase(boolean ignoreCase) { /** * Set bin name. */ - public QualifierBuilder setField(String field) { - this.map.put(FIELD, field); + public QualifierBuilder setBinName(String field) { + this.map.put(BIN_NAME, field); + return this; + } + + /** + * Set bin name. + */ + public QualifierBuilder setBinType(Exp.Type type) { + this.map.put(BIN_TYPE, type); + 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; } @@ -44,8 +62,7 @@ public QualifierBuilder setKey(Value key) { } /** - * For "find by one level nested map containing" queries. - * Set nested Map key. + * 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. @@ -78,19 +95,11 @@ public QualifierBuilder setSecondValue(Value secondValue) { } /** - * For "find by one level nested map containing" queries. - * Set the type of the nested map value using {@link ParticleType}. + * 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; } - - /** - * Required only for a nested value query (e.g. find by a POJO field). - */ - public QualifierBuilder setDotPath(List dotPath) { - this.map.put(DOT_PATH, dotPath); - return this; - } } 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 16bf2cd15..808a218a5 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,17 +5,19 @@ */ public enum QualifierKey { - FIELD, + BIN_NAME, + BIN_TYPE, METADATA_FIELD, SINGLE_ID_FIELD, MULTIPLE_IDS_FIELD, + CTX_PATH, + CTX_LIST, IGNORE_CASE, KEY, NESTED_KEY, VALUE, SECOND_VALUE, NESTED_TYPE, - DOT_PATH, DATA_SETTINGS, QUALIFIERS, OPERATION, diff --git a/src/main/java/org/springframework/data/aerospike/repository/query/AerospikeCriteria.java b/src/main/java/org/springframework/data/aerospike/repository/query/AerospikeCriteria.java index 990c8e985..9aaace9a9 100644 --- a/src/main/java/org/springframework/data/aerospike/repository/query/AerospikeCriteria.java +++ b/src/main/java/org/springframework/data/aerospike/repository/query/AerospikeCriteria.java @@ -42,6 +42,6 @@ public Qualifier getCriteriaObject() { @Override public String getCriteriaField() { - return this.getField(); + return this.getBinName(); } } 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 a354d8dfe..7b1ad0913 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 @@ -204,7 +204,7 @@ private void logQualifierDetails(CriteriaDefinition criteria) { Arrays.stream(qualifiers).forEach(this::logQualifierDetails); } - String field = (StringUtils.hasLength(qualifier.getField()) ? qualifier.getField() : ""); + String field = (StringUtils.hasLength(qualifier.getBinName()) ? qualifier.getBinName() : ""); String operation = qualifier.getOperation().toString(); operation = (StringUtils.hasLength(operation) ? operation : "N/A"); Value k = qualifier.getKey(); 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 61c8c543e..f7ae3e3b4 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 @@ -11,11 +11,14 @@ import org.springframework.data.mapping.PropertyPath; import org.springframework.data.repository.query.parser.Part; import org.springframework.data.util.TypeInformation; +import org.springframework.lang.NonNull; import org.springframework.util.StringUtils; +import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.TreeMap; +import java.util.stream.Collectors; import java.util.stream.Stream; import static org.springframework.data.aerospike.convert.AerospikeConverter.CLASS_KEY; @@ -28,14 +31,38 @@ public class AerospikeQueryCreatorUtils { protected static Qualifier setQualifier(QualifierBuilder qb, String fieldName, FilterOperation op, Part part, List dotPath) { - qb.setField(fieldName) + qb.setBinName(fieldName) .setFilterOperation(op) .setIgnoreCase(ignoreCaseToBoolean(part)); - if (dotPath != null && !qb.hasDotPath()) qb.setDotPath(dotPath); + if (dotPath != null && !qb.hasCtxPath()) { + String[] dotPathArr = getDotPathArray(dotPath); + if (dotPathArr != null && dotPathArr.length > 2) { + qb.setCtxList(getCtxFromDotPathArray(dotPathArr)); + } + } return qb.build(); } + protected static String[] getDotPathArray(List dotPathList) { + if (dotPathList != null && !dotPathList.isEmpty()) { + // the first element of dotPath is part.getProperty().toDotPath() + // the second element of dotPath, if present, is a value + Stream valueStream = dotPathList.size() == 1 || dotPathList.get(1) == null ? Stream.empty() + : Stream.of(dotPathList.get(1)); + return Stream.concat(Arrays.stream(dotPathList.get(0).split("\\.")), valueStream) + .toArray(String[]::new); + } + return null; + } + + protected static List getCtxFromDotPathArray(@NonNull String[] dotPathArr) { + return Arrays.stream(dotPathArr) + .skip(1) // first element is bin name + .limit(dotPathArr.length - 2L) // last element is the key we already have + .collect(Collectors.toList()); + } + protected static Object convertNullParameter(Object value) { return (value == NULL_PARAM) ? Value.getAsNull() : value; } diff --git a/src/test/java/org/springframework/data/aerospike/BaseBlockingIntegrationTests.java b/src/test/java/org/springframework/data/aerospike/BaseBlockingIntegrationTests.java index f2dcf447c..f4d30e6d4 100644 --- a/src/test/java/org/springframework/data/aerospike/BaseBlockingIntegrationTests.java +++ b/src/test/java/org/springframework/data/aerospike/BaseBlockingIntegrationTests.java @@ -22,10 +22,12 @@ import org.springframework.data.aerospike.server.version.ServerVersionSupport; import org.springframework.data.aerospike.util.QueryUtils; import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.util.TypeInformation; import org.springframework.util.StringUtils; import java.util.Collection; import java.util.List; +import java.util.Map; import static java.util.function.Predicate.not; import static org.assertj.core.api.Assertions.assertThat; @@ -116,26 +118,54 @@ protected void assertBinsAreIndexed(TestInfo testInfo) { } /** - * Assert that the given query statement contains secondary index filter + * Assert that the given query's statement contains secondary index filter + * + * @param query Query to be performed + * @param returnEntityClass Class of Query return entity + */ + protected void assertQueryHasSecIndexFilter(Query query, Class returnEntityClass) { + String setName = template.getSetName(returnEntityClass); + String[] binNames = getBinNamesFromTargetClass(returnEntityClass, mappingContext); + + assertThat(queryHasSecIndexFilter(namespace, setName, query, binNames)) + .as(String.format("Expecting the query '%s' statement to have secondary index filter", + query.getCriteriaObject())).isTrue(); + } + + /** + * Assert that the given method's query statement contains secondary index filter * * @param methodName Query method to be performed * @param returnEntityClass Class of Query return entity * @param methodParams Query parameters */ - protected void assertStmtHasSecIndexFilter(String methodName, Class returnEntityClass, - Object... methodParams) { - assertThat(stmtHasSecIndexFilter(methodName, returnEntityClass, methodParams)) + protected void assertQueryHasSecIndexFilter(String methodName, Class returnEntityClass, + Object... methodParams) { + assertThat(queryHasSecIndexFilter(methodName, returnEntityClass, methodParams)) .as(String.format("Expecting the query %s statement to have secondary index filter", methodName)).isTrue(); } - protected boolean stmtHasSecIndexFilter(String methodName, Class returnTypeClass, - Object... methodParams) { - String setName = template.getSetName(returnTypeClass); - String[] binNames = getBinNamesFromTargetClass(returnTypeClass, mappingContext); + protected boolean queryHasSecIndexFilter(String methodName, Class returnEntityClass, + Object... methodParams) { + String setName = template.getSetName(returnEntityClass); + String[] binNames = getBinNamesFromTargetClass(returnEntityClass, mappingContext); Query query = QueryUtils.createQueryForMethodWithArgs(methodName, methodParams); + return queryHasSecIndexFilter(namespace, setName, query, binNames); + } + + protected boolean queryHasSecIndexFilter(String namespace, String setName, Query query, String[] binNames) { Statement statement = queryEngine.getStatementBuilder().build(namespace, setName, query, binNames); // Checking that the statement has secondary index filter (which means it will be used) return statement.getFilter() != null; } + + protected Map pojoToMap(Object pojo) { + Object result = template.getAerospikeConverter().toWritableValue(pojo, TypeInformation.of(pojo.getClass())); + if (result instanceof Map) { + return (Map) result; + } + + throw new IllegalArgumentException("The result of conversion is not a Map, expecting only a POJO argument"); + } } diff --git a/src/test/java/org/springframework/data/aerospike/BaseReactiveIntegrationTests.java b/src/test/java/org/springframework/data/aerospike/BaseReactiveIntegrationTests.java index 02b8414cc..c453c5daa 100644 --- a/src/test/java/org/springframework/data/aerospike/BaseReactiveIntegrationTests.java +++ b/src/test/java/org/springframework/data/aerospike/BaseReactiveIntegrationTests.java @@ -133,14 +133,14 @@ protected void assertBinsAreIndexed(TestInfo testInfo) { * @param returnEntityClass Class of Query return entity * @param methodParams Query parameters */ - protected void assertStmtHasSecIndexFilter(String methodName, Class returnEntityClass, - Object... methodParams) { - assertThat(stmtHasSecIndexFilter(methodName, returnEntityClass, methodParams)) + protected void assertQueryHasSecIndexFilter(String methodName, Class returnEntityClass, + Object... methodParams) { + assertThat(queryHasSecIndexFilter(methodName, returnEntityClass, methodParams)) .as(String.format("Expecting the query %s statement to have secondary index filter", methodName)).isTrue(); } - protected boolean stmtHasSecIndexFilter(String methodName, Class returnEntityClass, - Object... methodParams) { + protected boolean queryHasSecIndexFilter(String methodName, Class returnEntityClass, + Object... methodParams) { String setName = reactiveTemplate.getSetName(returnEntityClass); String[] binNames = getBinNamesFromTargetClass(returnEntityClass, mappingContext); Query query = QueryUtils.createQueryForMethodWithArgs(ReactiveIndexedPersonRepository.class, returnEntityClass, 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 28a8a163d..8f2789111 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) - .setField("firstName") + .setBinName("firstName") .setValue(Value.get("vasili")) .build() ), @@ -73,13 +73,13 @@ public void countFindsAllItemsByGivenCriteria() { Qualifier qbIs1 = Qualifier.builder() .setFilterOperation(FilterOperation.EQ) - .setField("firstName") + .setBinName("firstName") .setValue(Value.get("vasili")) .build(); Qualifier qbIs2 = Qualifier.builder() .setFilterOperation(FilterOperation.EQ) - .setField("age") + .setBinName("age") .setValue(Value.get(51)) .build(); @@ -92,7 +92,7 @@ public void countFindsAllItemsByGivenCriteria() { long petyaCount = template.count( new Query(Qualifier.builder() .setFilterOperation(FilterOperation.EQ) - .setField("firstName") + .setBinName("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() - .setField("firstName") + .setBinName("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() - .setField("firstName") + .setBinName("firstName") .setValue(Value.get("VaS")) .setFilterOperation(FilterOperation.STARTS_WITH) .setIgnoreCase(false) @@ -145,7 +145,7 @@ public void countFindsAllItemsByGivenCriteriaAndRespectsIgnoreCase() { public void countReturnsZeroIfNoDocumentsByProvidedCriteriaIsFound() { Query query1 = new Query (Qualifier.builder() - .setField("firstName") + .setBinName("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 3690a4815..950708304 100644 --- a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateFindByQueryTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateFindByQueryTests.java @@ -455,7 +455,7 @@ public void findAllUsingQuery_shouldRunWithDifferentArgumentsCombinations() { // find by query Qualifier qualifier = Qualifier.builder() - .setField(fieldName) + .setBinName(fieldName) .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(fieldValue1)) .build(); @@ -466,12 +466,12 @@ public void findAllUsingQuery_shouldRunWithDifferentArgumentsCombinations() { // find by query with a complex qualifier Qualifier dataEqFieldValue1 = Qualifier.builder() .setFilterOperation(FilterOperation.EQ) - .setField(fieldName) + .setBinName(fieldName) .setValue(Value.get(fieldValue1)) .build(); Qualifier dataEqFieldValue2 = Qualifier.builder() .setFilterOperation(FilterOperation.EQ) - .setField(fieldName) + .setBinName(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 b404d5bd9..89628ade5 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() - .setField("firstName") + .setBinName("firstName") .setValue(Value.get("vasili")) .setFilterOperation(FilterOperation.EQ); @@ -60,7 +60,7 @@ void count_shouldFindAllItemsByGivenCriteria() { assertThat(vasyaCount).isEqualTo(3); QualifierBuilder qbVasya2 = Qualifier.builder() - .setField("age") + .setBinName("age") .setValue(Value.get(51)) .setFilterOperation(FilterOperation.EQ); @@ -72,7 +72,7 @@ void count_shouldFindAllItemsByGivenCriteria() { assertThat(vasya51Count).isEqualTo(1); QualifierBuilder qbPetya = Qualifier.builder() - .setField("firstName") + .setBinName("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() - .setField("firstName") + .setBinName("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() - .setField("firstName") + .setBinName("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() - .setField("firstName") + .setBinName("firstName") .setValue(Value.get("nastyushka")) .setFilterOperation(FilterOperation.EQ); 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 c1bf949b9..587ad1e1d 100644 --- a/src/test/java/org/springframework/data/aerospike/logging/LoggingTests.java +++ b/src/test/java/org/springframework/data/aerospike/logging/LoggingTests.java @@ -47,7 +47,7 @@ public static void setup() { void binIsIndexed() { IndexesCache indexesCacheMock = Mockito.mock(IndexesCache.class); Qualifier qualifier = Qualifier.builder() - .setField("testField") + .setBinName("testField") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get("testValue1")) .build(); 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 529c5f7dd..23bbfe15a 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() - .setField("age") + .setBinName("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() - .setField("age") + .setBinName("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() - .setField("age") + .setBinName("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() - .setField("color") + .setBinName("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() - .setField("age") + .setBinName("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() - .setField("age") + .setBinName("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() - .setField("color") + .setBinName("color") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(ORANGE)) .build(); @@ -208,7 +208,7 @@ void selectWithGeoWithin() { lon, lat, radius); Qualifier qualifier = Qualifier.builder() - .setField(GEO_BIN_NAME) + .setBinName(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() - .setField("color") + .setBinName("color") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(GREEN)) .build(); Qualifier qual2 = Qualifier.builder() - .setField("age") + .setBinName("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() - .setField("color") + .setBinName("color") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(GREEN)) .build(); Qualifier ageBetween28And29 = Qualifier.builder() - .setField("age") + .setBinName("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 eb2496bef..b7fe295fd 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() - .setField("age") + .setBinName("age") .setFilterOperation(FilterOperation.LT) .setValue(Value.get(26)).build(); @@ -103,7 +103,7 @@ void selectAll() { void lTQualifier() { // Ages range from 25 -> 29. We expected to only get back values with age < 26 Qualifier qualifier = Qualifier.builder() - .setField("age") + .setBinName("age") .setFilterOperation(FilterOperation.LT) .setValue(Value.get(26)) .build(); @@ -121,7 +121,7 @@ void lTQualifier() { void numericLTEQQualifier() { // Ages range from 25 -> 29. We expected to only get back values with age <= 26 Qualifier qualifier = Qualifier.builder() - .setField("age") + .setBinName("age") .setFilterOperation(FilterOperation.LTEQ) .setValue(Value.get(26)) .build(); @@ -142,7 +142,7 @@ void numericLTEQQualifier() { void numericEQQualifier() { // Ages range from 25 -> 29. We expected to only get back values with age == 26 Qualifier qualifier = Qualifier.builder() - .setField("age") + .setBinName("age") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(26)) .build(); @@ -160,7 +160,7 @@ void numericEQQualifier() { void numericGTEQQualifier() { // Ages range from 25 -> 29. We expected to only get back values with age >= 28 Qualifier qualifier = Qualifier.builder() - .setField("age") + .setBinName("age") .setFilterOperation(FilterOperation.GTEQ) .setValue(Value.get(28)) .build(); @@ -181,7 +181,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() - .setField("age") + .setBinName("age") .setFilterOperation(FilterOperation.GT) .setValue(Value.get(28)) .build(); @@ -215,7 +215,7 @@ void metadataSinceUpdateEQQualifier() { @Test void stringEQQualifier() { Qualifier qualifier = Qualifier.builder() - .setField("color") + .setBinName("color") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(ORANGE)) .build(); @@ -232,7 +232,7 @@ void stringEQQualifier() { @Test void stringEQIgnoreCaseQualifier() { Qualifier qualifier = Qualifier.builder() - .setField("color") + .setBinName("color") .setFilterOperation(FilterOperation.EQ) .setIgnoreCase(true) .setValue(Value.get(ORANGE.toUpperCase())) @@ -251,7 +251,7 @@ void stringEQIgnoreCaseQualifier() { void stringEqualIgnoreCaseWorksOnUnindexedBin() { boolean ignoreCase = true; Qualifier qualifier = Qualifier.builder() - .setField("color") + .setBinName("color") .setFilterOperation(FilterOperation.EQ) .setIgnoreCase(ignoreCase) .setValue(Value.get("BlUe")) @@ -271,7 +271,7 @@ void stringEqualIgnoreCaseWorksOnIndexedBin() { withIndex(namespace, SET_NAME, "color_index_selector", "color", IndexType.STRING, () -> { boolean ignoreCase = true; Qualifier qualifier = Qualifier.builder() - .setField("color") + .setBinName("color") .setFilterOperation(FilterOperation.EQ) .setIgnoreCase(ignoreCase) .setValue(Value.get("BlUe")) @@ -294,7 +294,7 @@ void stringEqualIgnoreCaseWorksOnIndexedBin() { void stringEqualIgnoreCaseWorksRequiresFullMatch() { boolean ignoreCase = true; Qualifier qualifier = Qualifier.builder() - .setField("color") + .setBinName("color") .setFilterOperation(FilterOperation.EQ) .setIgnoreCase(ignoreCase) .setValue(Value.get("lue")) @@ -308,7 +308,7 @@ void stringEqualIgnoreCaseWorksRequiresFullMatch() { @Test void stringStartWithQualifier() { Qualifier qualifier = Qualifier.builder() - .setField("color") + .setBinName("color") .setFilterOperation(FilterOperation.STARTS_WITH) .setValue(Value.get(BLUE.substring(0, 2))) .build(); @@ -325,7 +325,7 @@ void stringStartWithQualifier() { @Test void stringStartWithEntireWordQualifier() { Qualifier qualifier = Qualifier.builder() - .setField("color") + .setBinName("color") .setFilterOperation(FilterOperation.STARTS_WITH) .setValue(Value.get(BLUE)) .build(); @@ -342,7 +342,7 @@ void stringStartWithEntireWordQualifier() { @Test void stringStartWithICASEQualifier() { Qualifier qualifier = Qualifier.builder() - .setField("color") + .setBinName("color") .setFilterOperation(FilterOperation.STARTS_WITH) .setIgnoreCase(true) .setValue(Value.get("BLU")) @@ -360,7 +360,7 @@ void stringStartWithICASEQualifier() { @Test void stringEndsWithQualifier() { Qualifier qualifier = Qualifier.builder() - .setField("color") + .setBinName("color") .setFilterOperation(FilterOperation.ENDS_WITH) .setValue(Value.get(GREEN.substring(2))) .build(); @@ -377,7 +377,7 @@ void stringEndsWithQualifier() { @Test void selectEndsWith() { Qualifier qualifier = Qualifier.builder() - .setField("color") + .setBinName("color") .setFilterOperation(FilterOperation.ENDS_WITH) .setValue(Value.get("e")) .build(); @@ -394,7 +394,7 @@ void selectEndsWith() { @Test void stringEndsWithEntireWordQualifier() { Qualifier qualifier = Qualifier.builder() - .setField("color") + .setBinName("color") .setFilterOperation(FilterOperation.ENDS_WITH) .setValue(Value.get(GREEN)) .build(); @@ -412,7 +412,7 @@ void stringEndsWithEntireWordQualifier() { void betweenQualifier() { // Ages range from 25 -> 29. Get back age between 26 and 28 inclusive Qualifier qualifier = Qualifier.builder() - .setField("age") + .setBinName("age") .setFilterOperation(FilterOperation.BETWEEN) .setValue(Value.get(26)) .setSecondValue(Value.get(29)) // + 1 as upper limit is exclusive @@ -438,7 +438,7 @@ void containingQualifier() { .collect(Collectors.toMap(color -> color, color -> queryEngineTestDataPopulator.colourCounts.get(color))); Qualifier qualifier = Qualifier.builder() - .setField("color") + .setBinName("color") .setFilterOperation(FilterOperation.CONTAINING) .setValue(Value.get("l")) .build(); @@ -458,7 +458,7 @@ void inQualifier() { .collect(Collectors.toMap(color -> color, color -> queryEngineTestDataPopulator.colourCounts.get(color))); Qualifier qualifier = Qualifier.builder() - .setField("color") + .setBinName("color") .setFilterOperation(FilterOperation.IN) .setValue(Value.get(inColors)) .build(); @@ -477,7 +477,7 @@ void listContainsQualifier() { String binName = "colorList"; Qualifier qualifier = Qualifier.builder() - .setField(binName) + .setBinName(binName) .setFilterOperation(FilterOperation.COLLECTION_VAL_CONTAINING) .setValue(Value.get(searchColor)) .build(); @@ -501,7 +501,7 @@ void mapKeysContainQualifier() { String binName = "colorAgeMap"; Qualifier qualifier = Qualifier.builder() - .setField(binName) + .setBinName(binName) .setFilterOperation(FilterOperation.MAP_KEYS_CONTAIN) .setValue(Value.get(searchColor)) .build(); @@ -524,7 +524,7 @@ void mapValuesContainQualifier() { String binName = "ageColorMap"; Qualifier qualifier = Qualifier.builder() - .setField(binName) + .setBinName(binName) .setFilterOperation(FilterOperation.MAP_VALUES_CONTAIN) .setValue(Value.get(searchColor)) .build(); @@ -544,7 +544,7 @@ void mapValuesContainQualifier() { @Test void containingDoesNotUseSpecialCharacterQualifier() { Qualifier qualifier = Qualifier.builder() - .setField(SPECIAL_CHAR_BIN) + .setBinName(SPECIAL_CHAR_BIN) .setFilterOperation(FilterOperation.CONTAINING) .setValue(Value.get(".*")) .build(); @@ -560,7 +560,7 @@ void containingDoesNotUseSpecialCharacterQualifier() { @Test void startWithDoesNotUseSpecialCharacterQualifier() { Qualifier qualifier = Qualifier.builder() - .setField(SPECIAL_CHAR_BIN) + .setBinName(SPECIAL_CHAR_BIN) .setFilterOperation(FilterOperation.STARTS_WITH) .setValue(Value.get(".*")) .build(); @@ -576,7 +576,7 @@ void startWithDoesNotUseSpecialCharacterQualifier() { @Test void endWithDoesNotUseSpecialCharacterQualifier() { Qualifier qualifier = Qualifier.builder() - .setField(SPECIAL_CHAR_BIN) + .setBinName(SPECIAL_CHAR_BIN) .setFilterOperation(FilterOperation.ENDS_WITH) .setValue(Value.get(".*")) .build(); @@ -592,7 +592,7 @@ void endWithDoesNotUseSpecialCharacterQualifier() { @Test void eQIcaseDoesNotUseSpecialCharacter() { Qualifier qualifier = Qualifier.builder() - .setField(SPECIAL_CHAR_BIN) + .setBinName(SPECIAL_CHAR_BIN) .setFilterOperation(FilterOperation.EQ) .setIgnoreCase(true) .setValue(Value.get(".*")) @@ -607,7 +607,7 @@ void eQIcaseDoesNotUseSpecialCharacter() { @ValueSource(strings = {"[", "$", "\\", "^"}) void containingFindsSquareBracket(String specialString) { Qualifier qualifier = Qualifier.builder() - .setField(SPECIAL_CHAR_BIN) + .setBinName(SPECIAL_CHAR_BIN) .setFilterOperation(FilterOperation.CONTAINING) .setIgnoreCase(true) .setValue(Value.get(specialString)) @@ -630,7 +630,7 @@ void selectWithGeoWithin() { + "\"coordinates\": [[%.8f, %.8f], %f] }", lon, lat, radius); Qualifier qualifier = Qualifier.builder() - .setField(GEO_BIN_NAME) + .setBinName(GEO_BIN_NAME) .setFilterOperation(FilterOperation.GEO_WITHIN) .setValue(Value.getAsGeoJSON(rgnstr)) .build(); @@ -646,14 +646,14 @@ void selectWithGeoWithin() { void startWithAndEqualIgnoreCaseReturnsAllItems() { boolean ignoreCase = true; Qualifier qual1 = Qualifier.builder() - .setField("color") + .setBinName("color") .setFilterOperation(FilterOperation.EQ) .setIgnoreCase(ignoreCase) .setValue(Value.get(BLUE.toUpperCase())) .build(); Qualifier qual2 = Qualifier.builder() - .setField("name") + .setBinName("name") .setFilterOperation(FilterOperation.STARTS_WITH) .setIgnoreCase(ignoreCase) .setValue(Value.get("NA")) @@ -671,7 +671,7 @@ void startWithAndEqualIgnoreCaseReturnsAllItems() { void equalIgnoreCaseReturnsNoItemsIfNoneMatched() { boolean ignoreCase = false; Qualifier qual1 = Qualifier.builder() - .setField("color") + .setBinName("color") .setFilterOperation(FilterOperation.EQ) .setIgnoreCase(ignoreCase) .setValue(Value.get(BLUE.toUpperCase())) @@ -686,7 +686,7 @@ void equalIgnoreCaseReturnsNoItemsIfNoneMatched() { void startWithIgnoreCaseReturnsNoItemsIfNoneMatched() { boolean ignoreCase = false; Qualifier qual1 = Qualifier.builder() - .setField("name") + .setBinName("name") .setFilterOperation(FilterOperation.STARTS_WITH) .setIgnoreCase(ignoreCase) .setValue(Value.get("NA")) @@ -700,23 +700,23 @@ void startWithIgnoreCaseReturnsNoItemsIfNoneMatched() { @Test void selectWithBetweenAndOrQualifiers() { Qualifier colorIsGreen = Qualifier.builder() - .setField("color") + .setBinName("color") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(GREEN)) .build(); Qualifier ageBetween28And29 = Qualifier.builder() - .setField("age") + .setBinName("age") .setFilterOperation(FilterOperation.BETWEEN) .setKey(Value.get(28)) .setValue(Value.get(29)) .build(); Qualifier ageIs25 = Qualifier.builder() - .setField("age") + .setBinName("age") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(25)) .build(); Qualifier nameIs696 = Qualifier.builder() - .setField("name") + .setBinName("name") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get("name:696")) .build(); @@ -749,12 +749,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() - .setField("color") + .setBinName("color") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(BLUE)) .build(); Qualifier ageBetween28And29 = Qualifier.builder() - .setField("age") + .setBinName("age") .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 0bd461374..c1fb0f2bb 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() - .setField("region") + .setBinName("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 1b684444e..d9acc8653 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() - .setField("age") + .setBinName("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() - .setField("age") + .setBinName("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() - .setField("age") + .setBinName("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() - .setField("age") + .setBinName("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() - .setField("age") + .setBinName("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() - .setField("color") + .setBinName("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() - .setField("color") + .setBinName("color") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(BLUE)) .build(); @@ -254,12 +254,12 @@ public void selectWithBlueColorQuery() { public void selectWithQualifiersOnly() { withIndex(namespace, INDEXED_SET_NAME, "color_index", "color", IndexType.STRING, () -> { Qualifier qual1 = Qualifier.builder() - .setField("color") + .setBinName("color") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(GREEN)) .build(); Qualifier qual2 = Qualifier.builder() - .setField("age") + .setBinName("age") .setFilterOperation(FilterOperation.BETWEEN) .setValue(Value.get(28)) .setValue(Value.get(29)) @@ -291,7 +291,7 @@ public void selectWithGeoWithin() { + "\"coordinates\": [[%.8f, %.8f], %f] }", lon, lat, radius); Qualifier qualifier = Qualifier.builder() - .setField(GEO_BIN_NAME) + .setBinName(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 cd26f4add..82413f395 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() - .setField("age") + .setBinName("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() - .setField("age") + .setBinName("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() - .setField("age") + .setBinName("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() - .setField("age") + .setBinName("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() - .setField("age") + .setBinName("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() - .setField("age") + .setBinName("age") .setFilterOperation(FilterOperation.GT) .setValue(Value.get(28)) .build(); @@ -211,7 +211,7 @@ public void numericGTQualifier() { @Test public void stringEQQualifier() { Qualifier stringEqQualifier = Qualifier.builder() - .setField("color") + .setBinName("color") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(ORANGE)) .build(); @@ -230,7 +230,7 @@ public void stringEQQualifier() { @Test public void stringEQQualifierCaseSensitive() { Qualifier stringEqQualifier = Qualifier.builder() - .setField("color") + .setBinName("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() - .setField("color") + .setBinName("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() - .setField("color") + .setBinName("color") .setFilterOperation(FilterOperation.STARTS_WITH) .setValue(Value.get(BLUE)) .build(); @@ -292,7 +292,7 @@ public void stringStartWithICASEQualifier() { String blue = "blu"; Qualifier stringEqQualifier = Qualifier.builder() - .setField("color") + .setBinName("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() - .setField("color") + .setBinName("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() - .setField("color") + .setBinName("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() - .setField("age") + .setBinName("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() - .setField("color") + .setBinName("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() - .setField("color") + .setBinName("color") .setFilterOperation(FilterOperation.IN) .setValue(Value.get(inColours)) .build(); @@ -439,7 +439,7 @@ public void listContainsQualifier() { String binName = "colorList"; Qualifier ageRangeQualifier = Qualifier.builder() - .setField(binName) + .setBinName(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() - .setField(binName) + .setBinName(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() - .setField(binName) + .setBinName(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() - .setField(SPECIAL_CHAR_BIN) + .setBinName(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() - .setField(SPECIAL_CHAR_BIN) + .setBinName(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() - .setField(SPECIAL_CHAR_BIN) + .setBinName(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() - .setField(SPECIAL_CHAR_BIN) + .setBinName(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() - .setField(SPECIAL_CHAR_BIN) + .setBinName(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() - .setField("color") + .setBinName("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() - .setField("color") + .setBinName("color") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(expectedColor)) .build(); Qualifier qual2 = Qualifier.builder() - .setField("age") + .setBinName("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() - .setField("color") + .setBinName("color") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get("green")) .build(); Qualifier qualAgeBetween28And29 = Qualifier.builder() - .setField("age") + .setBinName("age") .setFilterOperation(FilterOperation.BETWEEN) .setValue(Value.get(28)) .setSecondValue(Value.get(30)) // + 1 as upper limit is exclusive .build(); Qualifier qualAgeIs25 = Qualifier.builder() - .setField("age") + .setBinName("age") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(25)) .build(); Qualifier qualNameIs696 = Qualifier.builder() - .setField("name") + .setBinName("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 500af9eee..baa3685fb 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 @@ -55,7 +55,7 @@ public void selectAll() { @Test public void selectEndsWith() { Qualifier qual1 = Qualifier.builder() - .setField("color") + .setBinName("color") .setFilterOperation(ENDS_WITH) .setValue(Value.get("e")) .build(); @@ -74,7 +74,7 @@ public void selectEndsWith() { @Test public void selectStartsWith() { Qualifier startsWithQual = Qualifier.builder() - .setField("color") + .setBinName("color") .setFilterOperation(STARTS_WITH) .setValue(Value.get("bl")) .build(); @@ -94,14 +94,14 @@ public void selectStartsWith() { public void startWithAndEqualIgnoreCaseReturnsAllItems() { boolean ignoreCase = true; Qualifier qual1 = Qualifier.builder() - .setField("color") + .setBinName("color") .setFilterOperation(EQ) .setIgnoreCase(ignoreCase) .setValue(Value.get("BLUE")) .build(); Qualifier qual2 = Qualifier.builder() - .setField("name") + .setBinName("name") .setFilterOperation(STARTS_WITH) .setIgnoreCase(ignoreCase) .setValue(Value.get("NA")) @@ -118,7 +118,7 @@ public void startWithAndEqualIgnoreCaseReturnsAllItems() { public void equalIgnoreCaseReturnsNoItemsIfNoneMatched() { boolean ignoreCase = false; Qualifier qual1 = Qualifier.builder() - .setField("color") + .setBinName("color") .setFilterOperation(EQ) .setIgnoreCase(ignoreCase) .setValue(Value.get("BLUE")) @@ -134,7 +134,7 @@ public void equalIgnoreCaseReturnsNoItemsIfNoneMatched() { public void startWithIgnoreCaseReturnsNoItemsIfNoneMatched() { boolean ignoreCase = false; Qualifier qual1 = Qualifier.builder() - .setField("name") + .setBinName("name") .setFilterOperation(STARTS_WITH) .setIgnoreCase(ignoreCase) .setValue(Value.get("NA")) @@ -152,7 +152,7 @@ public void stringEqualIgnoreCaseWorksOnUnindexedBin() { String expectedColor = "blue"; Qualifier caseInsensitiveQual = Qualifier.builder() - .setField("color") + .setBinName("color") .setFilterOperation(EQ) .setIgnoreCase(ignoreCase) .setValue(Value.get("BlUe")) @@ -173,7 +173,7 @@ public void stringEqualIgnoreCaseWorksOnUnindexedBin() { public void stringEqualIgnoreCaseWorksRequiresFullMatch() { boolean ignoreCase = true; Qualifier caseInsensitiveQual = Qualifier.builder() - .setField("color") + .setBinName("color") .setFilterOperation(EQ) .setIgnoreCase(ignoreCase) .setValue(Value.get("lue")) @@ -195,7 +195,7 @@ public void selectWithGeoWithin() { + "\"coordinates\": [[%.8f, %.8f], %f] }", lon, lat, radius); Qualifier qual1 = Qualifier.builder() - .setField(GEO_BIN_NAME) + .setBinName(GEO_BIN_NAME) .setFilterOperation(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 02496c5c4..e1b4947e2 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() - .setField("region") + .setBinName("region") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get("n")) .build(); diff --git a/src/test/java/org/springframework/data/aerospike/repository/query/blocking/indexed/IndexedPersonRepositoryQueryTests.java b/src/test/java/org/springframework/data/aerospike/repository/query/blocking/indexed/IndexedPersonRepositoryQueryTests.java index 32e30b60a..2475e2074 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/query/blocking/indexed/IndexedPersonRepositoryQueryTests.java +++ b/src/test/java/org/springframework/data/aerospike/repository/query/blocking/indexed/IndexedPersonRepositoryQueryTests.java @@ -149,13 +149,13 @@ public void beforeAll() { .indexType(NUMERIC) .indexCollectionType(MAPVALUES) .build()); - newIndexes.add(Index.builder() - .set(setName) - .name("indexed_person_address_keys_index") - .bin("address") - .indexType(STRING) - .indexCollectionType(MAPKEYS) - .build()); +// newIndexes.add(Index.builder() +// .set(setName) +// .name("indexed_person_address_keys_index") +// .bin("address") +// .indexType(STRING) +// .indexCollectionType(MAPKEYS) +// .build()); newIndexes.add(Index.builder() .set(setName) .name("indexed_person_address_values_index") @@ -163,37 +163,61 @@ public void beforeAll() { .indexType(STRING) .indexCollectionType(MAPVALUES) .build()); +// newIndexes.add(Index.builder() +// .set(setName) +// .name("indexed_person_friend_address_keys_index") +// .bin("friend") +// .indexType(STRING) +// .indexCollectionType(MAPKEYS) +// .ctx(new CTX[]{CTX.mapKey(Value.get("address"))}) +// .build()); newIndexes.add(Index.builder() .set(setName) - .name("indexed_person_friend_address_keys_index") + .name("indexed_person_friend_address_values_index") .bin("friend") .indexType(STRING) - .indexCollectionType(MAPKEYS) + .indexCollectionType(MAPVALUES) .ctx(new CTX[]{CTX.mapKey(Value.get("address"))}) .build()); newIndexes.add(Index.builder() .set(setName) - .name("indexed_person_friend_address_values_index") - .bin("friend") + .name("indexed_person_addressesList_0_values_index") + .bin("addressesList") .indexType(STRING) .indexCollectionType(MAPVALUES) - .ctx(new CTX[]{CTX.mapValue(Value.get("address"))}) + .ctx(new CTX[]{CTX.listIndex(0)}) .build()); +// newIndexes.add(Index.builder() +// .set(setName) +// .name("indexed_person_friend_bestFriend_address_keys_index") +// .bin("friend") +// .indexType(STRING) +// .indexCollectionType(MAPKEYS) +// .ctx(new CTX[]{CTX.mapKey(Value.get("bestFriend")), CTX.mapKey(Value.get("address"))}) +// .build()); +// newIndexes.add(Index.builder() +// .set(setName) +// .name("indexed_person_bestFriend_friend_address_keys_index") +// .bin("bestFriend") +// .indexType(STRING) +// .indexCollectionType(MAPKEYS) +// .ctx(new CTX[]{CTX.mapKey(Value.get("friend")), CTX.mapKey(Value.get("address"))}) +// .build()); newIndexes.add(Index.builder() .set(setName) - .name("indexed_person_friend_bestFriend_address_keys_index") + .name("indexed_person_friend_bestFriend_address_values_index") .bin("friend") .indexType(STRING) - .indexCollectionType(MAPKEYS) + .indexCollectionType(MAPVALUES) .ctx(new CTX[]{CTX.mapKey(Value.get("bestFriend")), CTX.mapKey(Value.get("address"))}) .build()); newIndexes.add(Index.builder() .set(setName) - .name("indexed_person_bestFriend_friend_address_keys_index") - .bin("bestFriend") - .indexType(STRING) - .indexCollectionType(MAPKEYS) - .ctx(new CTX[]{CTX.mapKey(Value.get("friend")), CTX.mapKey(Value.get("address"))}) + .name("indexed_person_friend_bestFriend_address_values_index_num") + .bin("friend") + .indexType(NUMERIC) + .indexCollectionType(MAPVALUES) + .ctx(new CTX[]{CTX.mapKey(Value.get("bestFriend")), CTX.mapKey(Value.get("address"))}) .build()); newIndexes.add(Index.builder() .set(setName) 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 7a02e639d..52f738886 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 @@ -19,7 +19,7 @@ public class BetweenTests extends IndexedPersonRepositoryQueryTests { @Test @AssertBinsAreIndexed(binNames = "age", entityClass = IndexedPerson.class) public void findBySimplePropertyBetween_Integer() { - assertStmtHasSecIndexFilter("findByAgeBetween", IndexedPerson.class, 40, 45); + assertQueryHasSecIndexFilter("findByAgeBetween", IndexedPerson.class, 40, 45); Iterable it = repository.findByAgeBetween(40, 45); assertThat(it).hasSize(2).contains(john, peter); } @@ -27,7 +27,7 @@ public void findBySimplePropertyBetween_Integer() { @Test @AssertBinsAreIndexed(binNames = "age", entityClass = IndexedPerson.class) public void findBySimplePropertyBetween_Integer_OrderBySimpleProperty() { - assertStmtHasSecIndexFilter("findByAgeBetweenOrderByLastName", IndexedPerson.class, 30, 45); + assertQueryHasSecIndexFilter("findByAgeBetweenOrderByLastName", IndexedPerson.class, 30, 45); Iterable it = repository.findByAgeBetweenOrderByLastName(30, 45); assertThat(it).hasSize(3); } @@ -37,13 +37,13 @@ public void findBySimplePropertyBetween_Integer_OrderBySimpleProperty() { public void findBySimplePropertyBetween_Integer_AND_SimplePropertyEquals_String() { QueryParam ageBetween = QueryParam.of(40, 45); QueryParam lastName = QueryParam.of("Matthews"); - assertStmtHasSecIndexFilter("findByAgeBetweenAndLastName", IndexedPerson.class, ageBetween, lastName); + assertQueryHasSecIndexFilter("findByAgeBetweenAndLastName", IndexedPerson.class, ageBetween, lastName); Iterable it = repository.findByAgeBetweenAndLastName(ageBetween, lastName); assertThat(it).hasSize(0); ageBetween = QueryParam.of(20, 26); lastName = QueryParam.of("Smith"); - assertStmtHasSecIndexFilter("findByAgeBetweenAndLastName", IndexedPerson.class, ageBetween, lastName); + assertQueryHasSecIndexFilter("findByAgeBetweenAndLastName", IndexedPerson.class, ageBetween, lastName); Iterable result = repository.findByAgeBetweenAndLastName(ageBetween, lastName); assertThat(result).hasSize(1).contains(billy); } diff --git a/src/test/java/org/springframework/data/aerospike/repository/query/blocking/indexed/findBy/ContainingTests.java b/src/test/java/org/springframework/data/aerospike/repository/query/blocking/indexed/findBy/ContainingTests.java index 81f6d75ac..d4b7d54dd 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/query/blocking/indexed/findBy/ContainingTests.java +++ b/src/test/java/org/springframework/data/aerospike/repository/query/blocking/indexed/findBy/ContainingTests.java @@ -20,7 +20,7 @@ public class ContainingTests extends IndexedPersonRepositoryQueryTests { @Test @AssertBinsAreIndexed(binNames = "strings", entityClass = IndexedPerson.class) void findByCollectionContaining_String() { - assertStmtHasSecIndexFilter("findByStringsContaining", IndexedPerson.class, "str1"); + assertQueryHasSecIndexFilter("findByStringsContaining", IndexedPerson.class, "str1"); assertThat(repository.findByStringsContaining("str1")).containsOnly(john, peter); assertThat(repository.findByStringsContaining("str2")).containsOnly(john, peter); assertThat(repository.findByStringsContaining("str3")).containsOnly(peter); @@ -30,7 +30,7 @@ void findByCollectionContaining_String() { @Test @AssertBinsAreIndexed(binNames = "ints", entityClass = IndexedPerson.class) void findByCollectionContaining_Integer() { - assertStmtHasSecIndexFilter("findByIntsContaining", IndexedPerson.class, 550); + assertQueryHasSecIndexFilter("findByIntsContaining", IndexedPerson.class, 550); assertThat(repository.findByIntsContaining(550)).containsOnly(john, jane); assertThat(repository.findByIntsContaining(990)).containsOnly(john, jane); assertThat(repository.findByIntsContaining(600)).containsOnly(jane); @@ -41,7 +41,7 @@ void findByCollectionContaining_Integer() { @AssertBinsAreIndexed(binNames = "stringMap", entityClass = IndexedPerson.class) void findByMapKeysContaining_String() { assertThat(billy.getStringMap()).containsKey("key1"); - assertStmtHasSecIndexFilter("findByStringMapContaining", IndexedPerson.class, KEY, "key1"); + assertQueryHasSecIndexFilter("findByStringMapContaining", IndexedPerson.class, KEY, "key1"); List persons = repository.findByStringMapContaining(KEY, "key1"); assertThat(persons).contains(billy); @@ -51,7 +51,7 @@ void findByMapKeysContaining_String() { @AssertBinsAreIndexed(binNames = "stringMap", entityClass = IndexedPerson.class) void findByMapValuesContaining_String() { assertThat(billy.getStringMap()).containsValue("val1"); - assertStmtHasSecIndexFilter("findByStringMapContaining", IndexedPerson.class, VALUE, "key1"); + assertQueryHasSecIndexFilter("findByStringMapContaining", IndexedPerson.class, VALUE, "key1"); List persons = repository.findByStringMapContaining(VALUE, "val1"); assertThat(persons).contains(billy); @@ -62,7 +62,7 @@ void findByMapValuesContaining_String() { void findByExactMapKeyAndValue_Integer() { assertThat(tricia.getIntMap()).containsKey("key1"); assertThat(tricia.getIntMap().get("key1")).isEqualTo(0); - assertStmtHasSecIndexFilter("findByIntMapContaining", IndexedPerson.class, KEY_VALUE_PAIR, "key1", 0); + assertQueryHasSecIndexFilter("findByIntMapContaining", IndexedPerson.class, KEY_VALUE_PAIR, "key1", 0); Iterable result = repository.findByIntMapContaining(KEY_VALUE_PAIR, "key1", 0); assertThat(result).contains(tricia); 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 new file mode 100644 index 000000000..4f751b51c --- /dev/null +++ b/src/test/java/org/springframework/data/aerospike/repository/query/blocking/indexed/findBy/CustomQueriesTests.java @@ -0,0 +1,128 @@ +package org.springframework.data.aerospike.repository.query.blocking.indexed.findBy; + +import com.aerospike.client.Value; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.data.aerospike.config.AssertBinsAreIndexed; +import org.springframework.data.aerospike.query.FilterOperation; +import org.springframework.data.aerospike.query.qualifier.Qualifier; +import org.springframework.data.aerospike.repository.query.Query; +import org.springframework.data.aerospike.repository.query.blocking.indexed.IndexedPersonRepositoryQueryTests; +import org.springframework.data.aerospike.sample.Address; +import org.springframework.data.aerospike.sample.IndexedPerson; +import org.springframework.data.aerospike.util.TestUtils; + +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 { + + @Test + @AssertBinsAreIndexed(binNames = "friend", entityClass = IndexedPerson.class) + void findByNestedSimpleProperty_String_map_in_map() { + String zipCode = "C0123"; + assertThat(john.getAddress().getZipCode()).isEqualTo(zipCode); + jane.setFriend(john); + repository.save(jane); + + Qualifier nestedZipCodeEq = Qualifier.builder() + // 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 + .setValue(Value.get(zipCode)) // value of the nested key + .build(); + + assertQueryHasSecIndexFilter(new Query(nestedZipCodeEq), IndexedPerson.class); + Iterable result = repository.findUsingQuery(new Query(nestedZipCodeEq)); + assertThat(result).contains(jane); + TestUtils.setFriendsToNull(repository, jane); + } + + @Test + @AssertBinsAreIndexed(binNames = "friend", entityClass = IndexedPerson.class) + void findByNestedSimpleProperty_String_map_in_list() { + String zipCode = "ZipCode"; + john.setAddressesList(List.of(new Address("Street", 100, zipCode, "City"))); + repository.save(john); + assertThat(john.getAddressesList().get(0).getZipCode()).isEqualTo(zipCode); + + Qualifier nestedZipCodeEq = Qualifier.builder() + // 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 + .setValue(Value.get(zipCode)) // value of the nested key + .build(); + + assertQueryHasSecIndexFilter(new Query(nestedZipCodeEq), IndexedPerson.class); + Iterable resultTest2 = repository.findUsingQuery(new Query(nestedZipCodeEq)); + Assertions.assertThat(resultTest2).contains(john); + john.setAddressesList(null); + repository.save(john); + } + + + @Test + @AssertBinsAreIndexed(binNames = "friend", entityClass = IndexedPerson.class) + void findByNestedSimpleProperty_String_3_levels() { + String zipCode = "C0123"; + assertThat(john.getAddress().getZipCode()).isEqualTo(zipCode); + jane.setBestFriend(john); + repository.save(jane); + peter.setFriend(jane); + repository.save(peter); + + Qualifier nestedZipCodeEq = Qualifier.builder() + // 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 + .setValue(Value.get(zipCode)) // value of the nested key + .build(); + + assertQueryHasSecIndexFilter(new Query(nestedZipCodeEq), IndexedPerson.class); + Iterable result = repository.findUsingQuery(new Query(nestedZipCodeEq)); + assertThat(result).contains(peter); + TestUtils.setFriendsToNull(repository, jane, peter); + } + + @Test + @AssertBinsAreIndexed(binNames = "friend", entityClass = IndexedPerson.class) + void findByNestedSimpleProperty_Integer_3_levels() { + int apartment = 1; + assertThat(john.getAddress().getApartment()).isEqualTo(apartment); + jane.setBestFriend(john); + repository.save(jane); + peter.setFriend(jane); + repository.save(peter); + + 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 + .setValue(Value.get(apartment)) // value of the nested key + .build(); + + assertQueryHasSecIndexFilter(new Query(nestedApartmentEq), IndexedPerson.class); + Iterable result = repository.findUsingQuery(new Query(nestedApartmentEq)); + assertThat(result).contains(peter); + TestUtils.setFriendsToNull(repository, jane, peter); + } +} + 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 a2497562e..f6e486cf4 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 @@ -4,7 +4,10 @@ import org.junit.jupiter.api.Test; import org.springframework.data.aerospike.BaseIntegrationTests; import org.springframework.data.aerospike.config.AssertBinsAreIndexed; +import org.springframework.data.aerospike.query.FilterOperation; import org.springframework.data.aerospike.query.QueryParam; +import org.springframework.data.aerospike.query.qualifier.Qualifier; +import org.springframework.data.aerospike.repository.query.Query; import org.springframework.data.aerospike.repository.query.blocking.indexed.IndexedPersonRepositoryQueryTests; import org.springframework.data.aerospike.sample.IndexedPerson; import org.springframework.data.aerospike.sample.Person; @@ -12,6 +15,7 @@ import java.util.List; +import static com.aerospike.client.exp.Exp.Type.MAP; import static org.assertj.core.api.Assertions.assertThat; /** @@ -22,11 +26,11 @@ public class EqualsTests extends IndexedPersonRepositoryQueryTests { @Test @AssertBinsAreIndexed(binNames = "lastName", entityClass = IndexedPerson.class) public void findBySimplePropertyEquals_String() { - assertStmtHasSecIndexFilter("findByLastName", IndexedPerson.class, "Gillaham"); + assertQueryHasSecIndexFilter("findByLastName", IndexedPerson.class, "Gillaham"); List result = repository.findByLastName("Gillaham"); assertThat(result).containsOnly(jane); - assertStmtHasSecIndexFilter("findByFirstName", IndexedPerson.class, "Tricia"); + assertQueryHasSecIndexFilter("findByFirstName", IndexedPerson.class, "Tricia"); assertBinIsIndexed("firstName", IndexedPerson.class); List result2 = repository.findByFirstName("Tricia"); assertThat(result2).containsOnly(tricia); @@ -47,8 +51,7 @@ void findBySimplePropertyEquals_Boolean_NoSecondaryIndexFilter() { ); repository.save(boolBinPerson); - // Secondary index filter for a boolean value is not supported - assertThat(stmtHasSecIndexFilter("findByIsActive", IndexedPerson.class, true)).isFalse(); + assertThat(queryHasSecIndexFilter("findByIsActive", IndexedPerson.class, true)).isFalse(); assertThat(repository.findByIsActive(true)).contains(boolBinPerson); Value.UseBoolBin = initialValue; // set back to the default value @@ -62,7 +65,7 @@ public void findByTwoSimplePropertiesEqual_BooleanAndString() { QueryParam paramFalse = QueryParam.of(false); QueryParam paramTricia = QueryParam.of("Tricia"); - assertStmtHasSecIndexFilter("findByIsActiveAndFirstName", IndexedPerson.class, paramFalse, paramTricia); + assertQueryHasSecIndexFilter("findByIsActiveAndFirstName", IndexedPerson.class, paramFalse, paramTricia); List result = repository.findByIsActiveAndFirstName(paramFalse, paramTricia); assertThat(result).containsOnly(tricia); @@ -73,13 +76,13 @@ public void findByTwoSimplePropertiesEqual_BooleanAndString() { public void findByTwoSimplePropertiesEqual_StringAndInteger() { QueryParam firstName = QueryParam.of("Billy"); QueryParam age = QueryParam.of(25); - assertStmtHasSecIndexFilter("findByFirstNameAndAge", IndexedPerson.class, firstName, age); + assertQueryHasSecIndexFilter("findByFirstNameAndAge", IndexedPerson.class, firstName, age); List result = repository.findByFirstNameAndAge(firstName, age); assertThat(result).containsOnly(billy); firstName = QueryParam.of("Peter"); age = QueryParam.of(41); - assertStmtHasSecIndexFilter("findByFirstNameAndAge", IndexedPerson.class, firstName, age); + assertQueryHasSecIndexFilter("findByFirstNameAndAge", IndexedPerson.class, firstName, age); result = repository.findByFirstNameAndAge(firstName, age); assertThat(result).containsOnly(peter); } @@ -89,7 +92,7 @@ public void findByTwoSimplePropertiesEqual_StringAndInteger() { void findByNestedSimpleProperty_String() { String zipCode = "C0123"; assertThat(john.getAddress().getZipCode()).isEqualTo(zipCode); - assertStmtHasSecIndexFilter("findByAddressZipCode", IndexedPerson.class, zipCode); + assertQueryHasSecIndexFilter("findByAddressZipCode", IndexedPerson.class, zipCode); List result = repository.findByAddressZipCode(zipCode); assertThat(result).contains(john); } @@ -102,10 +105,25 @@ void findByNestedSimpleProperty_String_2_levels() { jane.setFriend(john); repository.save(jane); - // Currently nested queries don't have secondary index filter -// assertStmtHasSecIndexFilter("findByFriendAddressZipCode", IndexedPerson.class, zipCode); + assertQueryHasSecIndexFilter("findByFriendAddressZipCode", IndexedPerson.class, zipCode); List result = repository.findByFriendAddressZipCode(zipCode); assertThat(result).contains(jane); + + // An alternative way to perform the same using a custom query + Qualifier nestedZipCodeEq = Qualifier.builder() + // 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 + .setValue(Value.get(zipCode)) // value of the nested key + .build(); + + assertQueryHasSecIndexFilter(new Query(nestedZipCodeEq), IndexedPerson.class); + Iterable result2 = repository.findUsingQuery(new Query(nestedZipCodeEq)); + assertThat(result).isEqualTo(result2); TestUtils.setFriendsToNull(repository, jane); } @@ -119,10 +137,23 @@ void findByNestedSimpleProperty_String_3_levels() { peter.setFriend(jane); repository.save(peter); - // Currently deeply nested queries don't have secondary index filter -// assertStmtHasSecIndexFilter("findByFriendBestFriendAddressZipCode", IndexedPerson.class, zipCode); + assertQueryHasSecIndexFilter("findByFriendBestFriendAddressZipCode", IndexedPerson.class, zipCode); List result = repository.findByFriendBestFriendAddressZipCode(zipCode); assertThat(result).contains(peter); + + // 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 + .setValue(Value.get(zipCode)) // value of the nested key + .build(); + + assertQueryHasSecIndexFilter(new Query(nestedZipCodeEq), IndexedPerson.class); + Iterable result2 = repository.findUsingQuery(new Query(nestedZipCodeEq)); + assertThat(result).isEqualTo(result2); TestUtils.setFriendsToNull(repository, jane, peter); } @@ -136,10 +167,22 @@ void findByNestedSimpleProperty_Integer_3_levels() { peter.setFriend(jane); repository.save(peter); - // Currently deeply nested queries don't have secondary index filter -// assertStmtHasSecIndexFilter("findByFriendBestFriendAddressApartment", IndexedPerson.class, apartment); + assertQueryHasSecIndexFilter("findByFriendBestFriendAddressApartment", IndexedPerson.class, apartment); List result = repository.findByFriendBestFriendAddressApartment(apartment); assertThat(result).contains(peter); + + // 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 + .setValue(Value.get(apartment)) // value of the nested key + .build(); + + Iterable result2 = repository.findUsingQuery(new Query(nestedApartmentEq)); + assertThat(result).isEqualTo(result2); TestUtils.setFriendsToNull(repository, jane, peter); } } diff --git a/src/test/java/org/springframework/data/aerospike/repository/query/blocking/indexed/findBy/GreaterThanTests.java b/src/test/java/org/springframework/data/aerospike/repository/query/blocking/indexed/findBy/GreaterThanTests.java index 7f6bdf1cc..64d5ebeea 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/query/blocking/indexed/findBy/GreaterThanTests.java +++ b/src/test/java/org/springframework/data/aerospike/repository/query/blocking/indexed/findBy/GreaterThanTests.java @@ -23,7 +23,7 @@ public class GreaterThanTests extends IndexedPersonRepositoryQueryTests { @AssertBinsAreIndexed(binNames = "firstName", entityClass = IndexedPerson.class) public void findBySimplePropertyGreaterThan_String_NoSecondaryIndexFilter() { // "Greater than a String" has no secondary index Filter - assertThat(stmtHasSecIndexFilter("findByFirstNameGreaterThan", IndexedPerson.class, "Bill")).isFalse(); + assertThat(queryHasSecIndexFilter("findByFirstNameGreaterThan", IndexedPerson.class, "Bill")).isFalse(); List result = repository.findByFirstNameGreaterThan("Bill"); assertThat(result).containsAll(allIndexedPersons); } @@ -31,7 +31,7 @@ public void findBySimplePropertyGreaterThan_String_NoSecondaryIndexFilter() { @Test @AssertBinsAreIndexed(binNames = "age", entityClass = IndexedPerson.class) public void findBySimplePropertyGreaterThan_Integer_Paginated() { - assertStmtHasSecIndexFilter("findByAgeGreaterThan", IndexedPerson.class, 40); + assertQueryHasSecIndexFilter("findByAgeGreaterThan", IndexedPerson.class, 40); Slice slice = repository.findByAgeGreaterThan(40, PageRequest.of(0, 10)); assertThat(slice.hasContent()).isTrue(); assertThat(slice.hasNext()).isFalse(); @@ -41,7 +41,7 @@ public void findBySimplePropertyGreaterThan_Integer_Paginated() { @Test @AssertBinsAreIndexed(binNames = "age", entityClass = IndexedPerson.class) public void findBySimplePropertyGreaterThan_Integer_Paginated_respectsLimitAndOffsetAndSort() { - assertStmtHasSecIndexFilter("findByAgeGreaterThan", IndexedPerson.class, 40); + assertQueryHasSecIndexFilter("findByAgeGreaterThan", IndexedPerson.class, 40); List result = IntStream.range(0, 4) .mapToObj(index -> repository.findByAgeGreaterThan(40, PageRequest.of(index, 1, Sort.by("age")))) .flatMap(slice -> slice.getContent().stream()) @@ -55,7 +55,7 @@ public void findBySimplePropertyGreaterThan_Integer_Paginated_respectsLimitAndOf @Test @AssertBinsAreIndexed(binNames = "age", entityClass = IndexedPerson.class) public void findBySimplePropertyGreaterThan_Integer_Paginated_validHasPrevAndHasNext() { - assertStmtHasSecIndexFilter("findByAgeGreaterThan", IndexedPerson.class, 40); + assertQueryHasSecIndexFilter("findByAgeGreaterThan", IndexedPerson.class, 40); Slice first = repository.findByAgeGreaterThan(40, PageRequest.of(0, 1, Sort.by("age"))); assertThat(first.hasContent()).isTrue(); assertThat(first.getNumberOfElements()).isEqualTo(1); @@ -63,14 +63,14 @@ public void findBySimplePropertyGreaterThan_Integer_Paginated_validHasPrevAndHas assertThat(first.isFirst()).isTrue(); assertThat(first.isLast()).isFalse(); - assertStmtHasSecIndexFilter("findByAgeGreaterThan", IndexedPerson.class, 40); + assertQueryHasSecIndexFilter("findByAgeGreaterThan", IndexedPerson.class, 40); Slice last = repository.findByAgeGreaterThan(40, PageRequest.of(2, 1, Sort.by("age"))); assertThat(last.hasContent()).isTrue(); assertThat(last.getNumberOfElements()).isEqualTo(1); assertThat(last.hasNext()).isFalse(); assertThat(last.isLast()).isTrue(); - assertStmtHasSecIndexFilter("findByAgeGreaterThan", IndexedPerson.class, 100); + assertQueryHasSecIndexFilter("findByAgeGreaterThan", IndexedPerson.class, 100); Slice slice = repository.findByAgeGreaterThan(100, PageRequest.of(0, 10)); assertThat(slice.hasContent()).isFalse(); assertThat(slice.hasNext()).isFalse(); diff --git a/src/test/java/org/springframework/data/aerospike/repository/query/blocking/indexed/findBy/NotContainingTests.java b/src/test/java/org/springframework/data/aerospike/repository/query/blocking/indexed/findBy/NotContainingTests.java index 55223247b..eac0a98c7 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/query/blocking/indexed/findBy/NotContainingTests.java +++ b/src/test/java/org/springframework/data/aerospike/repository/query/blocking/indexed/findBy/NotContainingTests.java @@ -21,7 +21,7 @@ public class NotContainingTests extends IndexedPersonRepositoryQueryTests { void findByMapKeysNotContaining_String_NoSecondaryIndexFilter() { assertThat(billy.getStringMap()).containsKey("key1"); // "Not containing" has no secondary index Filter - assertThat(stmtHasSecIndexFilter("findByStringMapNotContaining", IndexedPerson.class, KEY, "key3")).isFalse(); + assertThat(queryHasSecIndexFilter("findByStringMapNotContaining", IndexedPerson.class, KEY, "key3")).isFalse(); List persons = repository.findByStringMapNotContaining(KEY, "key3"); assertThat(persons).contains(billy); @@ -32,7 +32,7 @@ void findByMapKeysNotContaining_String_NoSecondaryIndexFilter() { void findByMapValuesNotContaining_String() { assertThat(billy.getStringMap()).containsValue("val1"); // "Not containing" has no secondary index Filter - assertThat(stmtHasSecIndexFilter("findByStringMapNotContaining", IndexedPerson.class, VALUE, "val3")).isFalse(); + assertThat(queryHasSecIndexFilter("findByStringMapNotContaining", IndexedPerson.class, VALUE, "val3")).isFalse(); List persons = repository.findByStringMapNotContaining(VALUE, "val3"); assertThat(persons).contains(billy); 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 e907694da..a94f99810 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 @@ -14,7 +14,6 @@ import org.springframework.data.aerospike.sample.Person; import org.springframework.data.domain.Sort; -import java.util.Collections; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @@ -74,14 +73,14 @@ void findPersonsByQuery() { // creating an expression "firstName is equal to Carter" Qualifier firstNameEqCarter = Qualifier.builder() - .setField("firstName") + .setBinName("firstName") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get("Carter")) .build(); // creating an expression "age is equal to 49" Qualifier ageEq49 = Qualifier.builder() - .setField("age") + .setBinName("age") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get(49)) .build(); @@ -90,7 +89,7 @@ void findPersonsByQuery() { // creating an expression "firstName is equal to Leroi" with sorting by age and limiting by 1 row Qualifier firstNameEqLeroi = Qualifier.builder() - .setField("firstName") + .setBinName("firstName") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get("Leroi")) .build(); @@ -103,7 +102,7 @@ void findPersonsByQuery() { // creating an expression "age is greater than 49" Qualifier ageGt49 = Qualifier.builder() .setFilterOperation(FilterOperation.GT) - .setField("age") + .setBinName("age") .setValue(Value.get(49)) .build(); result = repository.findUsingQuery(new Query(ageGt49)); @@ -257,7 +256,7 @@ void mapValuesTest() { assertThat(boyd.getStringMap().get("key1")).isEqualTo(valueToSearch); Qualifier stringMapValuesContainString = Qualifier.builder() - .setField("stringMap") + .setBinName("stringMap") .setFilterOperation(FilterOperation.MAP_VALUES_CONTAIN) .setValue(Value.get(valueToSearch)) .build(); @@ -270,7 +269,7 @@ void mapValuesTest() { // 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 Qualifier intMapWithExactKeyAndValueLt100 = Qualifier.builder() - .setField("intMap") // Map bin name + .setBinName("intMap") // 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 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 149588ab5..b7a9632dc 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 @@ -4,7 +4,10 @@ import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.data.aerospike.BaseIntegrationTests; +import org.springframework.data.aerospike.query.FilterOperation; import org.springframework.data.aerospike.query.QueryParam; +import org.springframework.data.aerospike.query.qualifier.Qualifier; +import org.springframework.data.aerospike.repository.query.Query; import org.springframework.data.aerospike.repository.query.blocking.noindex.PersonRepositoryQueryTests; import org.springframework.data.aerospike.sample.Address; import org.springframework.data.aerospike.sample.Person; @@ -14,6 +17,7 @@ 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; @@ -210,8 +214,22 @@ void findByNestedSimplePropertyEquals() { repository.save(carter); List result = repository.findByFriendAddressZipCode(zipCode); - assertThat(result).containsExactly(carter); + + // An alternative to "findByFriendAddressZipCode" is using a custom query + Qualifier nestedZipCodeEq = Qualifier.builder() + // 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 + .setValue(Value.get(zipCode)) // value of the nested key + .build(); + + Iterable result2 = repository.findUsingQuery(new Query(nestedZipCodeEq)); + assertThat(result).isEqualTo(result2); TestUtils.setFriendsToNull(repository, carter); } @@ -259,9 +277,24 @@ void findByDeeplyNestedSimplePropertyEquals_PojoField_String_10_levels() { List result = repository.findByFriendFriendFriendFriendFriendFriendFriendFriendBestFriendAddressZipCode(zipCode); - assertThat(result).containsExactly(douglas); + // An alternative way to perform the same using a custom query + Qualifier nestedZipCodeEq = Qualifier.builder() + // 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 + .setValue(Value.get(zipCode)) // value of the nested key + .build(); + + Iterable result2 = repository.findUsingQuery(new Query(nestedZipCodeEq)); + assertThat(result).isEqualTo(result2); + // cleanup TestUtils.setFriendsToNull(repository, allPersons.toArray(Person[]::new)); alicia.setAddress(null); @@ -280,9 +313,24 @@ void findByDeeplyNestedSimplePropertyEquals_PojoField_Integer_10_levels() { List result = repository.findByFriendFriendFriendFriendFriendFriendFriendFriendBestFriendAddressApartment(apartment); - assertThat(result).containsExactly(douglas); + // An alternative way to perform the same using a custom query + Qualifier nestedApartmentEq = Qualifier.builder() + // 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 + .setValue(Value.get(apartment)) // value of the nested key + .build(); + + Iterable result2 = repository.findUsingQuery(new Query(nestedApartmentEq)); + assertThat(result).isEqualTo(result2); + // cleanup TestUtils.setFriendsToNull(repository, allPersons.toArray(Person[]::new)); alicia.setAddress(null); @@ -301,9 +349,24 @@ void findByDeeplyNestedSimplePropertyEquals_Pojo_9_levels() { List result = repository.findByFriendFriendFriendFriendFriendFriendFriendFriendBestFriendAddress(address); - assertThat(result).containsExactly(douglas); + // An alternative way to perform the same using a custom query + Qualifier nestedAddressEq = Qualifier.builder() + // 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 + .setValue(Value.get(pojoToMap(address))) // value of the nested key + .build(); + + Iterable result2 = repository.findUsingQuery(new Query(nestedAddressEq)); + assertThat(result).isEqualTo(result2); + // cleanup TestUtils.setFriendsToNull(repository, allPersons.toArray(Person[]::new)); alicia.setAddress(null); diff --git a/src/test/java/org/springframework/data/aerospike/repository/query/reactive/indexed/findBy/BetweenTests.java b/src/test/java/org/springframework/data/aerospike/repository/query/reactive/indexed/findBy/BetweenTests.java index 7702bacc4..6db789518 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/query/reactive/indexed/findBy/BetweenTests.java +++ b/src/test/java/org/springframework/data/aerospike/repository/query/reactive/indexed/findBy/BetweenTests.java @@ -18,7 +18,7 @@ public class BetweenTests extends ReactiveIndexedPersonRepositoryQueryTests { @Test @AssertBinsAreIndexed(binNames = "age", entityClass = IndexedPerson.class) public void findBySimplePropertyBetween_Integer() { - assertStmtHasSecIndexFilter("findByAgeBetween", IndexedPerson.class, 39, 45); + assertQueryHasSecIndexFilter("findByAgeBetween", IndexedPerson.class, 39, 45); List results = reactiveRepository.findByAgeBetween(39, 45) .subscribeOn(Schedulers.parallel()).collectList().block(); assertThat(results).hasSize(2).contains(alain, luc); diff --git a/src/test/java/org/springframework/data/aerospike/repository/query/reactive/indexed/findBy/ContainingTests.java b/src/test/java/org/springframework/data/aerospike/repository/query/reactive/indexed/findBy/ContainingTests.java index ad3af6592..37de61c88 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/query/reactive/indexed/findBy/ContainingTests.java +++ b/src/test/java/org/springframework/data/aerospike/repository/query/reactive/indexed/findBy/ContainingTests.java @@ -21,7 +21,7 @@ public class ContainingTests extends ReactiveIndexedPersonRepositoryQueryTests { @Test @AssertBinsAreIndexed(binNames = "strings", entityClass = IndexedPerson.class) public void findByCollectionContaining_String() { - assertStmtHasSecIndexFilter("findByStringsContaining", IndexedPerson.class, "str1"); + assertQueryHasSecIndexFilter("findByStringsContaining", IndexedPerson.class, "str1"); List results = reactiveRepository.findByStringsContaining("str1") .subscribeOn(Schedulers.parallel()).collectList().block(); assertThat(results).containsExactlyInAnyOrder(alain); @@ -30,7 +30,7 @@ public void findByCollectionContaining_String() { @Test @AssertBinsAreIndexed(binNames = "ints", entityClass = IndexedPerson.class) public void findByCollectionContaining_Integer() { - assertStmtHasSecIndexFilter("findByIntsContaining", IndexedPerson.class, 550); + assertQueryHasSecIndexFilter("findByIntsContaining", IndexedPerson.class, 550); List results = reactiveRepository.findByIntsContaining(550) .subscribeOn(Schedulers.parallel()).collectList().block(); assertThat(results).containsExactlyInAnyOrder(daniel, emilien); @@ -39,7 +39,7 @@ public void findByCollectionContaining_Integer() { @Test @AssertBinsAreIndexed(binNames = "stringMap", entityClass = IndexedPerson.class) public void findByMapKeysContaining_String() { - assertStmtHasSecIndexFilter("findByStringMapContaining", IndexedPerson.class, KEY, "key1"); + assertQueryHasSecIndexFilter("findByStringMapContaining", IndexedPerson.class, KEY, "key1"); List results = reactiveRepository.findByStringMapContaining(KEY, "key1") .subscribeOn(Schedulers.parallel()).collectList().block(); @@ -49,7 +49,7 @@ public void findByMapKeysContaining_String() { @Test @AssertBinsAreIndexed(binNames = "stringMap", entityClass = IndexedPerson.class) public void findByMapValuesContaining_String() { - assertStmtHasSecIndexFilter("findByStringMapContaining", IndexedPerson.class, VALUE, "val1"); + assertQueryHasSecIndexFilter("findByStringMapContaining", IndexedPerson.class, VALUE, "val1"); List results = reactiveRepository.findByStringMapContaining(VALUE, "val1") .subscribeOn(Schedulers.parallel()).collectList().block(); assertThat(results).contains(luc, petra); @@ -62,7 +62,7 @@ public void findByExactMapKeyAndValue_String() { assertThat(petra.getStringMap().containsValue("val1")).isTrue(); assertThat(luc.getStringMap().containsKey("key1")).isTrue(); assertThat(luc.getStringMap().containsValue("val1")).isTrue(); - assertStmtHasSecIndexFilter("findByStringMapContaining", IndexedPerson.class, KEY_VALUE_PAIR, "key1", "val1"); + assertQueryHasSecIndexFilter("findByStringMapContaining", IndexedPerson.class, KEY_VALUE_PAIR, "key1", "val1"); List results = reactiveRepository.findByStringMapContaining(KEY_VALUE_PAIR, "key1", "val1") .subscribeOn(Schedulers.parallel()).collectList().block(); @@ -76,7 +76,7 @@ public void findByExactMapKeyAndValue_Integer() { assertThat(emilien.getIntMap().get("key1")).isZero(); assertThat(lilly.getIntMap().containsKey("key1")).isTrue(); assertThat(lilly.getIntMap().get("key1")).isNotZero(); - assertStmtHasSecIndexFilter("findByIntMapContaining", IndexedPerson.class, KEY_VALUE_PAIR, "key1", 0); + assertQueryHasSecIndexFilter("findByIntMapContaining", IndexedPerson.class, KEY_VALUE_PAIR, "key1", 0); List results = reactiveRepository.findByIntMapContaining(KEY_VALUE_PAIR, "key1", 0) .subscribeOn(Schedulers.parallel()).collectList().block(); 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 1e9586528..581774014 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() - .setField("firstName") + .setBinName("firstName") .setFilterOperation(FilterOperation.EQ) .setValue(Value.get("Petra")) .build(); // creating an expression "age is equal to 34" Qualifier ageEq34 = Qualifier.builder() - .setField("age") + .setBinName("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) - .setField("age") + .setBinName("age") .setValue(Value.get(34)) .build(); result = reactiveRepository.findUsingQuery(new Query(ageGt34)).collectList().block(); diff --git a/src/test/java/org/springframework/data/aerospike/repository/query/reactive/indexed/findBy/EqualsTests.java b/src/test/java/org/springframework/data/aerospike/repository/query/reactive/indexed/findBy/EqualsTests.java index 609155694..cd054b81b 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/query/reactive/indexed/findBy/EqualsTests.java +++ b/src/test/java/org/springframework/data/aerospike/repository/query/reactive/indexed/findBy/EqualsTests.java @@ -19,12 +19,12 @@ public class EqualsTests extends ReactiveIndexedPersonRepositoryQueryTests { @Test @AssertBinsAreIndexed(binNames = {"lastName", "firstName"}, entityClass = IndexedPerson.class) public void findBySimpleProperty_String() { - assertStmtHasSecIndexFilter("findByLastName", IndexedPerson.class, "Coutant-Kerbalec"); + assertQueryHasSecIndexFilter("findByLastName", IndexedPerson.class, "Coutant-Kerbalec"); List results = reactiveRepository.findByLastName("Coutant-Kerbalec") .subscribeOn(Schedulers.parallel()).collectList().block(); assertThat(results).containsOnly(petra, emilien); - assertStmtHasSecIndexFilter("findByFirstName", IndexedPerson.class, "Lilly"); + assertQueryHasSecIndexFilter("findByFirstName", IndexedPerson.class, "Lilly"); List results2 = reactiveRepository.findByFirstName("Lilly") .subscribeOn(Schedulers.parallel()).collectList().block(); assertThat(results2).containsExactlyInAnyOrder(lilly); @@ -36,7 +36,7 @@ public void findBySimpleProperty_String_AND_SimpleProperty_Integer() { QueryParam firstName = QueryParam.of("Lilly"); QueryParam age = QueryParam.of(28); - assertStmtHasSecIndexFilter("findByFirstNameAndAge", IndexedPerson.class, firstName, age); + assertQueryHasSecIndexFilter("findByFirstNameAndAge", IndexedPerson.class, firstName, age); List results = reactiveRepository.findByFirstNameAndAge(firstName, age) .subscribeOn(Schedulers.parallel()).collectList().block(); assertThat(results).containsOnly(lilly); @@ -47,7 +47,7 @@ public void findBySimpleProperty_String_AND_SimpleProperty_Integer() { public void findByNestedSimpleProperty_String() { String zipCode = "C0123"; assertThat(alain.getAddress().getZipCode()).isEqualTo(zipCode); - assertStmtHasSecIndexFilter("findByAddressZipCode", IndexedPerson.class, zipCode); + assertQueryHasSecIndexFilter("findByAddressZipCode", IndexedPerson.class, zipCode); List results = reactiveRepository.findByAddressZipCode(zipCode) .subscribeOn(Schedulers.parallel()).collectList().block(); assertThat(results).contains(alain); diff --git a/src/test/java/org/springframework/data/aerospike/repository/query/reactive/indexed/findBy/GreaterThanTests.java b/src/test/java/org/springframework/data/aerospike/repository/query/reactive/indexed/findBy/GreaterThanTests.java index d5dbb7a50..6841bfcee 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/query/reactive/indexed/findBy/GreaterThanTests.java +++ b/src/test/java/org/springframework/data/aerospike/repository/query/reactive/indexed/findBy/GreaterThanTests.java @@ -22,7 +22,7 @@ public class GreaterThanTests extends ReactiveIndexedPersonRepositoryQueryTests @Test @AssertBinsAreIndexed(binNames = "age", entityClass = IndexedPerson.class) public void findBySimplePropertyGreaterThan_Integer_Paginated() { - assertStmtHasSecIndexFilter("findByAgeGreaterThan", IndexedPerson.class, 1, PageRequest.of(0, 1)); + assertQueryHasSecIndexFilter("findByAgeGreaterThan", IndexedPerson.class, 1, PageRequest.of(0, 1)); Page page = reactiveRepository.findByAgeGreaterThan(1, PageRequest.of(0, 1)) .subscribeOn(Schedulers.parallel()).block(); assertThat(page).containsAnyElementsOf(allIndexedPersons); @@ -44,7 +44,7 @@ public void findBySimplePropertyGreaterThan_Integer_Paginated() { @Test @AssertBinsAreIndexed(binNames = "age", entityClass = IndexedPerson.class) public void findBySimplePropertyGreaterThan_Integer_Unpaged() { - assertStmtHasSecIndexFilter("findByAgeGreaterThan", IndexedPerson.class, 40, Pageable.unpaged()); + assertQueryHasSecIndexFilter("findByAgeGreaterThan", IndexedPerson.class, 40, Pageable.unpaged()); Slice slice = reactiveRepository.findByAgeGreaterThan(40, Pageable.unpaged()) .subscribeOn(Schedulers.parallel()).block(); assertThat(slice.hasContent()).isTrue(); diff --git a/src/test/java/org/springframework/data/aerospike/repository/query/reactive/indexed/findBy/LessThanTests.java b/src/test/java/org/springframework/data/aerospike/repository/query/reactive/indexed/findBy/LessThanTests.java index aa43cc564..2323601cc 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/query/reactive/indexed/findBy/LessThanTests.java +++ b/src/test/java/org/springframework/data/aerospike/repository/query/reactive/indexed/findBy/LessThanTests.java @@ -18,7 +18,7 @@ public class LessThanTests extends ReactiveIndexedPersonRepositoryQueryTests { @Test @AssertBinsAreIndexed(binNames = "age", entityClass = IndexedPerson.class) public void findBySimplePropertyLessThan_Integer_Unpaged() { - assertStmtHasSecIndexFilter("findByAgeLessThan", IndexedPerson.class, 40, Pageable.unpaged()); + assertQueryHasSecIndexFilter("findByAgeLessThan", IndexedPerson.class, 40, Pageable.unpaged()); Page page = reactiveRepository.findByAgeLessThan(40, Pageable.unpaged()) .subscribeOn(Schedulers.parallel()).block(); assertThat(page.hasContent()).isTrue(); diff --git a/src/test/java/org/springframework/data/aerospike/repository/query/reactive/indexed/findBy/StartsWithTests.java b/src/test/java/org/springframework/data/aerospike/repository/query/reactive/indexed/findBy/StartsWithTests.java index 97bb59bb6..1ee5865bc 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/query/reactive/indexed/findBy/StartsWithTests.java +++ b/src/test/java/org/springframework/data/aerospike/repository/query/reactive/indexed/findBy/StartsWithTests.java @@ -22,13 +22,13 @@ public class StartsWithTests extends ReactiveIndexedPersonRepositoryQueryTests { @AssertBinsAreIndexed(binNames = "lastName", entityClass = IndexedPerson.class) void findBySimplePropertyStartingWith_String_Distinct_NoSecondaryIndexFilter() { // There is no secondary index filter for "starts with" - assertThat(stmtHasSecIndexFilter("findDistinctByLastNameStartingWith", IndexedPerson.class, "Coutant-Kerbalec")).isFalse(); + assertThat(queryHasSecIndexFilter("findDistinctByLastNameStartingWith", IndexedPerson.class, "Coutant-Kerbalec")).isFalse(); List persons = reactiveRepository.findDistinctByLastNameStartingWith("Coutant-Kerbalec") .subscribeOn(Schedulers.parallel()).collectList().block(); assertThat(persons).hasSize(1); // There is no secondary index filter for "starts with" - assertThat(stmtHasSecIndexFilter("findByLastNameStartingWith", IndexedPerson.class, "Coutant-Kerbalec")).isFalse(); + assertThat(queryHasSecIndexFilter("findByLastNameStartingWith", IndexedPerson.class, "Coutant-Kerbalec")).isFalse(); List persons2 = reactiveRepository.findByLastNameStartingWith("Coutant-Kerbalec") .subscribeOn(Schedulers.parallel()).collectList().block(); assertThat(persons2).hasSize(2);