From 9b09dde63d6b9ba21af79b74c247cba62787aa55 Mon Sep 17 00:00:00 2001 From: Andrey G Date: Tue, 28 May 2024 12:33:02 +0300 Subject: [PATCH] FMWK-445 Refactor value setters in Qualifier builders (#746) * Make setValue() and setSecondValue() accept Object that is read into Value * Replace setValueAsObj() and setSecondValueAsObj() in MetadataQualifierBuilder with unified setValue() and setSecondValue() * Add logging whether sIndex filter and filterExp are created * Cleanup --- .../data/aerospike/annotation/Beta.java | 2 + .../aerospike/core/AerospikeTemplate.java | 10 +-- .../core/ReactiveAerospikeTemplate.java | 9 ++- .../query/FilterExpressionsBuilder.java | 15 ++-- .../data/aerospike/query/FilterOperation.java | 33 +++------ .../data/aerospike/query/QueryEngine.java | 12 ++-- .../aerospike/query/ReactorQueryEngine.java | 9 +-- .../aerospike/query/StatementBuilder.java | 22 ++++-- .../query/qualifier/BaseQualifierBuilder.java | 29 +++++++- .../ConjunctionQualifierBuilder.java | 1 + .../query/qualifier/IdQualifierBuilder.java | 1 + .../qualifier/MetadataQualifierBuilder.java | 69 ++++++------------- .../aerospike/query/qualifier/Qualifier.java | 20 +++++- .../query/qualifier/QualifierBuilder.java | 32 ++------- .../query/AerospikeQueryCreator.java | 33 --------- .../query/QueryQualifierBuilder.java | 24 +------ .../data/aerospike/util/Utils.java | 57 +++++++++++++++ .../BaseBlockingIntegrationTests.java | 2 +- .../BaseReactiveIntegrationTests.java | 2 +- .../data/aerospike/logging/LoggingTests.java | 56 +++++++-------- .../data/aerospike/query/QualifierTests.java | 2 +- .../noindex/findBy/CustomQueriesTests.java | 47 ++++++++----- .../indexed/findBy/CustomQueriesTests.java | 14 ++-- 23 files changed, 259 insertions(+), 242 deletions(-) diff --git a/src/main/java/org/springframework/data/aerospike/annotation/Beta.java b/src/main/java/org/springframework/data/aerospike/annotation/Beta.java index 48a213741..1dc7b6dbf 100644 --- a/src/main/java/org/springframework/data/aerospike/annotation/Beta.java +++ b/src/main/java/org/springframework/data/aerospike/annotation/Beta.java @@ -1,5 +1,6 @@ package org.springframework.data.aerospike.annotation; +import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -10,6 +11,7 @@ */ @Retention(RetentionPolicy.SOURCE) @Target({ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.METHOD}) +@Documented public @interface Beta { } diff --git a/src/main/java/org/springframework/data/aerospike/core/AerospikeTemplate.java b/src/main/java/org/springframework/data/aerospike/core/AerospikeTemplate.java index 95cdc03e4..8690d7db7 100644 --- a/src/main/java/org/springframework/data/aerospike/core/AerospikeTemplate.java +++ b/src/main/java/org/springframework/data/aerospike/core/AerospikeTemplate.java @@ -786,7 +786,8 @@ private Record getRecord(AerospikePersistentEntity entity, Key key, Query que private BatchPolicy getBatchPolicyFilterExp(Query query) { if (queryCriteriaIsNotNull(query)) { BatchPolicy policy = new BatchPolicy(getAerospikeClient().getBatchPolicyDefault()); - policy.filterExp = queryEngine.getFilterExpressionsBuilder().build(query); + Qualifier qualifier = query.getCriteriaObject(); + policy.filterExp = queryEngine.getFilterExpressionsBuilder().build(qualifier); return policy; } return null; @@ -815,7 +816,8 @@ private Object getRecordMapToTargetClass(AerospikePersistentEntity entity private Policy getPolicyFilterExp(Query query) { if (queryCriteriaIsNotNull(query)) { Policy policy = new Policy(getAerospikeClient().getReadPolicyDefault()); - policy.filterExp = queryEngine.getFilterExpressionsBuilder().build(query); + Qualifier qualifier = query.getCriteriaObject(); + policy.filterExp = queryEngine.getFilterExpressionsBuilder().build(qualifier); return policy; } return null; @@ -826,7 +828,8 @@ private Record getAndTouch(Key key, int expiration, String[] binNames, Query que .expiration(expiration); if (queryCriteriaIsNotNull(query)) { - writePolicyBuilder.filterExp(queryEngine.getFilterExpressionsBuilder().build(query)); + Qualifier qualifier = query.getCriteriaObject(); + writePolicyBuilder.filterExp(queryEngine.getFilterExpressionsBuilder().build(qualifier)); } WritePolicy writePolicy = writePolicyBuilder.build(); @@ -1424,7 +1427,6 @@ private Stream findRecordsUsingQuery(String setName, Class tar } KeyRecordIterator recIterator; - if (targetClass != null) { String[] binNames = getBinNamesFromTargetClass(targetClass, mappingContext); recIterator = queryEngine.select(namespace, setName, binNames, query); diff --git a/src/main/java/org/springframework/data/aerospike/core/ReactiveAerospikeTemplate.java b/src/main/java/org/springframework/data/aerospike/core/ReactiveAerospikeTemplate.java index 8df0d2c53..f4229c009 100644 --- a/src/main/java/org/springframework/data/aerospike/core/ReactiveAerospikeTemplate.java +++ b/src/main/java/org/springframework/data/aerospike/core/ReactiveAerospikeTemplate.java @@ -875,7 +875,8 @@ public Mono findByIdUsingQuery(Object id, Class entityClass, Class< Policy policy = null; if (queryCriteriaIsNotNull(query)) { policy = new Policy(reactorClient.getReadPolicyDefault()); - policy.filterExp = reactorQueryEngine.getFilterExpressionsBuilder().build(query); + Qualifier qualifier = query.getCriteriaObject(); + policy.filterExp = reactorQueryEngine.getFilterExpressionsBuilder().build(qualifier); } return reactorClient.get(policy, key, binNames) .filter(keyRecord -> Objects.nonNull(keyRecord.record)) @@ -1010,7 +1011,8 @@ public Flux findInRange(long offset, long limit, Sort sort, Class targ private BatchPolicy getBatchPolicyFilterExp(Query query) { if (queryCriteriaIsNotNull(query)) { BatchPolicy policy = new BatchPolicy(reactorClient.getAerospikeClient().getBatchPolicyDefault()); - policy.filterExp = reactorQueryEngine.getFilterExpressionsBuilder().build(query); + Qualifier qualifier = query.getCriteriaObject(); + policy.filterExp = reactorQueryEngine.getFilterExpressionsBuilder().build(qualifier); return policy; } return null; @@ -1267,7 +1269,8 @@ private Mono getAndTouch(Key key, int expiration, String[] binNames, .expiration(expiration); if (queryCriteriaIsNotNull(query)) { - writePolicyBuilder.filterExp(reactorQueryEngine.getFilterExpressionsBuilder().build(query)); + Qualifier qualifier = query.getCriteriaObject(); + writePolicyBuilder.filterExp(reactorQueryEngine.getFilterExpressionsBuilder().build(qualifier)); } WritePolicy writePolicy = writePolicyBuilder.build(); diff --git a/src/main/java/org/springframework/data/aerospike/query/FilterExpressionsBuilder.java b/src/main/java/org/springframework/data/aerospike/query/FilterExpressionsBuilder.java index 72c86f4f0..5eb1049ee 100644 --- a/src/main/java/org/springframework/data/aerospike/query/FilterExpressionsBuilder.java +++ b/src/main/java/org/springframework/data/aerospike/query/FilterExpressionsBuilder.java @@ -17,18 +17,23 @@ import com.aerospike.client.exp.Exp; import com.aerospike.client.exp.Expression; +import lombok.extern.slf4j.Slf4j; import org.springframework.data.aerospike.query.qualifier.Qualifier; -import org.springframework.data.aerospike.repository.query.Query; import static org.springframework.data.aerospike.query.FilterOperation.dualFilterOperations; -import static org.springframework.data.aerospike.query.QualifierUtils.queryCriteriaIsNotNull; +@Slf4j public class FilterExpressionsBuilder { - public Expression build(Query query) { - Qualifier qualifier = queryCriteriaIsNotNull(query) ? query.getCriteriaObject() : null; + public Expression build(Qualifier qualifier) { if (qualifier != null && requiresFilterExp(qualifier)) { - return Exp.build(qualifier.getFilterExp()); + Exp exp = qualifier.getFilterExp(); + if (exp == null) { + log.debug("Query #{}, filterExp is not set", qualifier.hashCode()); + } else { + log.debug("Query #{}, filterExp is set", qualifier.hashCode()); + } + return Exp.build(exp); } return null; } 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 756a6b97e..7f29e4ca2 100644 --- a/src/main/java/org/springframework/data/aerospike/query/FilterOperation.java +++ b/src/main/java/org/springframework/data/aerospike/query/FilterOperation.java @@ -40,7 +40,6 @@ import java.util.Optional; import java.util.function.BinaryOperator; import java.util.function.Function; -import java.util.stream.Collectors; import static com.aerospike.client.command.ParticleType.BOOL; import static com.aerospike.client.command.ParticleType.INTEGER; @@ -54,6 +53,7 @@ import static org.springframework.data.aerospike.util.FilterOperationRegexpBuilder.getNotContaining; import static org.springframework.data.aerospike.util.FilterOperationRegexpBuilder.getStartsWith; import static org.springframework.data.aerospike.util.FilterOperationRegexpBuilder.getStringEquals; +import static org.springframework.data.aerospike.util.Utils.ctxArrToString; import static org.springframework.data.aerospike.util.Utils.getExpType; import static org.springframework.data.aerospike.util.Utils.getValueExpOrFail; @@ -1266,21 +1266,21 @@ public Filter sIndexFilter(Map qualifierMap) { @SuppressWarnings("unchecked") private static Exp processMetadataFieldInOrNot(Map qualifierMap, boolean notIn) { FilterOperation filterOperation = notIn ? NOTEQ : EQ; - Object value = getValueAsObject(qualifierMap); + Object obj = getValue(qualifierMap).getObject(); Collection listOfLongs; try { - listOfLongs = (Collection) value; // previously validated + listOfLongs = (Collection) obj; // previously validated } catch (Exception e) { String operation = notIn ? "NOT_IN" : "IN"; - throw new IllegalStateException("FilterOperation." + operation + " metadata query: expecting value with " + - "type List"); + throw new IllegalStateException("FilterOperation." + operation + " metadata query: expecting value as " + + "a Collection"); } Exp[] listElementsExp = listOfLongs.stream().map(item -> Qualifier.metadataBuilder() .setMetadataField(getMetadataField(qualifierMap)) .setFilterOperation(filterOperation) - .setValueAsObj(item) + .setValue(Value.get(item)) .build() .getFilterExp() ).toArray(Exp[]::new); @@ -1331,13 +1331,13 @@ private static Optional getMetadataExp(Map qualifierM return Optional.of( operationFunction.apply( mapMetadataExp(metadataField, getServerVersionSupport(qualifierMap)), - Exp.val(getValueAsLongOrFail(getValueAsObject(qualifierMap))) + Exp.val(getValue(qualifierMap).toLong()) // previously validated ) ); } case BETWEEN -> { Exp metadata = mapMetadataExp(metadataField, getServerVersionSupport(qualifierMap)); - Exp value = Exp.val(getValue(qualifierMap).toLong()); + Exp value = Exp.val(getValue(qualifierMap).toLong()); // previously validated Exp secondValue = Exp.val(getSecondValue(qualifierMap).toLong()); return Optional.of(Exp.and(Exp.ge(metadata, value), Exp.lt(metadata, secondValue))); } @@ -1353,15 +1353,6 @@ private static Optional getMetadataExp(Map qualifierM return Optional.empty(); } - // expecting value always be of type Long - private static Long getValueAsLongOrFail(Object value) { - try { - return (Long) value; - } catch (Exception e) { - throw new IllegalArgumentException("Expecting value to be of type Long"); - } - } - private static Exp mapMetadataExp(CriteriaDefinition.AerospikeMetadata metadataField, ServerVersionSupport versionSupport) { return switch (metadataField) { @@ -1682,10 +1673,6 @@ protected static Value getValue(Map qualifierMap) { return Value.get(qualifierMap.get(VALUE)); } - protected static Object getValueAsObject(Map qualifierMap) { - return qualifierMap.get(VALUE); - } - protected static Value getSecondValue(Map qualifierMap) { return Value.get(qualifierMap.get(SECOND_VALUE)); } @@ -1706,10 +1693,6 @@ protected static String getCtxArrAsString(Map qualifierMap return ctxArrToString(ctxArr); } - private static String ctxArrToString(CTX[] ctxArr) { - return Arrays.stream(ctxArr).map(ctx -> ctx.value.toString()).collect(Collectors.joining(".")); - } - protected static ServerVersionSupport getServerVersionSupport(Map qualifierMap) { return (ServerVersionSupport) qualifierMap.get(SERVER_VERSION_SUPPORT); } diff --git a/src/main/java/org/springframework/data/aerospike/query/QueryEngine.java b/src/main/java/org/springframework/data/aerospike/query/QueryEngine.java index 5bf71225a..7b051434e 100644 --- a/src/main/java/org/springframework/data/aerospike/query/QueryEngine.java +++ b/src/main/java/org/springframework/data/aerospike/query/QueryEngine.java @@ -26,6 +26,8 @@ import com.aerospike.client.query.Statement; import lombok.Getter; import lombok.Setter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.data.aerospike.config.AerospikeDataSettings; import org.springframework.data.aerospike.query.qualifier.Qualifier; import org.springframework.data.aerospike.repository.query.Query; @@ -41,6 +43,7 @@ */ public class QueryEngine { + private static final Logger logger = LoggerFactory.getLogger(QueryEngine.class); public static final String SCANS_DISABLED_MESSAGE = "Query without a filter will initiate a scan. Since scans are potentially dangerous operations, they are " + "disabled by default in spring-data-aerospike. " + @@ -116,7 +119,7 @@ public KeyRecordIterator select(String namespace, String set, String[] binNames, } Statement statement = statementBuilder.build(namespace, set, query, binNames); statement.setMaxRecords(queryMaxRecords); - QueryPolicy localQueryPolicy = getQueryPolicy(query, true); + QueryPolicy localQueryPolicy = getQueryPolicy(qualifier, true); if (!scansEnabled && statement.getFilter() == null) { throw new IllegalStateException(SCANS_DISABLED_MESSAGE); @@ -137,7 +140,8 @@ public KeyRecordIterator select(String namespace, String set, String[] binNames, public KeyRecordIterator selectForCount(String namespace, String set, @Nullable Query query) { Statement statement = statementBuilder.build(namespace, set, query); statement.setMaxRecords(queryMaxRecords); - QueryPolicy localQueryPolicy = getQueryPolicy(query, false); + Qualifier qualifier = queryCriteriaIsNotNull(query) ? query.getCriteriaObject() : null; + QueryPolicy localQueryPolicy = getQueryPolicy(qualifier, false); if (!scansEnabled && statement.getFilter() == null) { throw new IllegalStateException(SCANS_DISABLED_MESSAGE); @@ -155,9 +159,9 @@ private Record getRecord(Policy policy, Key key, String[] binNames) { return client.get(policy, key, binNames); } - private QueryPolicy getQueryPolicy(Query query, boolean includeBins) { + private QueryPolicy getQueryPolicy(Qualifier qualifier, boolean includeBins) { QueryPolicy queryPolicy = new QueryPolicy(client.getQueryPolicyDefault()); - queryPolicy.filterExp = filterExpressionsBuilder.build(query); + queryPolicy.filterExp = filterExpressionsBuilder.build(qualifier); queryPolicy.includeBinData = includeBins; return queryPolicy; } diff --git a/src/main/java/org/springframework/data/aerospike/query/ReactorQueryEngine.java b/src/main/java/org/springframework/data/aerospike/query/ReactorQueryEngine.java index b47c3d023..6b0ebc237 100644 --- a/src/main/java/org/springframework/data/aerospike/query/ReactorQueryEngine.java +++ b/src/main/java/org/springframework/data/aerospike/query/ReactorQueryEngine.java @@ -108,7 +108,7 @@ public Flux select(String namespace, String set, String[] binNames, @ } Statement statement = statementBuilder.build(namespace, set, query, binNames); statement.setMaxRecords(queryMaxRecords); - QueryPolicy localQueryPolicy = getQueryPolicy(query, true); + QueryPolicy localQueryPolicy = getQueryPolicy(qualifier, true); if (!scansEnabled && statement.getFilter() == null) { return Flux.error(new IllegalStateException(QueryEngine.SCANS_DISABLED_MESSAGE)); @@ -128,7 +128,8 @@ public Flux select(String namespace, String set, String[] binNames, @ public Flux selectForCount(String namespace, String set, @Nullable Query query) { Statement statement = statementBuilder.build(namespace, set, query); statement.setMaxRecords(queryMaxRecords); - QueryPolicy localQueryPolicy = getQueryPolicy(query, false); + Qualifier qualifier = queryCriteriaIsNotNull(query) ? query.getCriteriaObject() : null; + QueryPolicy localQueryPolicy = getQueryPolicy(qualifier, false); if (!scansEnabled && statement.getFilter() == null) { return Flux.error(new IllegalStateException(QueryEngine.SCANS_DISABLED_MESSAGE)); @@ -137,9 +138,9 @@ public Flux selectForCount(String namespace, String set, @Nullable Qu return client.query(localQueryPolicy, statement); } - private QueryPolicy getQueryPolicy(Query query, boolean includeBins) { + private QueryPolicy getQueryPolicy(Qualifier qualifier, boolean includeBins) { QueryPolicy queryPolicy = new QueryPolicy(client.getQueryPolicyDefault()); - queryPolicy.filterExp = filterExpressionsBuilder.build(query); + queryPolicy.filterExp = filterExpressionsBuilder.build(qualifier); queryPolicy.includeBinData = includeBins; return queryPolicy; } 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 75c14e645..20754b6d5 100644 --- a/src/main/java/org/springframework/data/aerospike/query/StatementBuilder.java +++ b/src/main/java/org/springframework/data/aerospike/query/StatementBuilder.java @@ -17,8 +17,7 @@ import com.aerospike.client.query.Filter; import com.aerospike.client.query.Statement; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.data.aerospike.query.cache.IndexesCache; import org.springframework.data.aerospike.query.model.Index; import org.springframework.data.aerospike.query.model.IndexedField; @@ -32,14 +31,15 @@ import java.util.Optional; import static org.springframework.data.aerospike.query.QualifierUtils.queryCriteriaIsNotNull; +import static org.springframework.data.aerospike.util.Utils.logQualifierDetails; /** * @author peter * @author Anastasiia Smirnova */ +@Slf4j public class StatementBuilder { - private static final Logger log = LoggerFactory.getLogger(StatementBuilder.class); private final IndexesCache indexesCache; public StatementBuilder(IndexesCache indexesCache) { @@ -58,7 +58,10 @@ public Statement build(String namespace, String set, @Nullable Query query, Stri stmt.setBinNames(binNames); } if (queryCriteriaIsNotNull(query)) { - // statement's filter is set based on the first processed qualifier's filter + // logging query + logQualifierDetails(query.getCriteriaObject(), log); + // statement's filter is set based either on cardinality (the lowest bin values ratio) + // or on order (the first processed filter) setStatementFilterFromQualifiers(stmt, query.getCriteriaObject()); } return stmt; @@ -67,6 +70,7 @@ public Statement build(String namespace, String set, @Nullable Query query, Stri private void setStatementFilterFromQualifiers(Statement stmt, Qualifier qualifier) { // No qualifier, no need to set statement filter if (qualifier == null) { + log.debug("Query #{}, secondary index filter is not set", qualifier.hashCode()); return; } @@ -77,6 +81,12 @@ private void setStatementFilterFromQualifiers(Statement stmt, Qualifier qualifie } else if (isIndexedBin(stmt, qualifier)) { // Single qualifier setFilterFromSingleQualifier(stmt, qualifier); } + if (stmt.getFilter() != null) { + log.debug("Query #{}, secondary index filter is set on the bin '{}'", qualifier.hashCode(), + stmt.getFilter().getName()); + } else { + log.debug("Query #{}, secondary index filter is not set", qualifier.hashCode()); + } } private void setFilterFromMultipleQualifiers(Statement stmt, Qualifier qualifier) { @@ -130,8 +140,8 @@ private boolean isIndexedBin(Statement stmt, Qualifier qualifier) { } if (log.isDebugEnabled() && hasField) { - log.debug("Bin {}.{}.{} has secondary index: {}", - stmt.getNamespace(), stmt.getSetName(), qualifier.getBinName(), hasIndex); + log.debug("Query #{}, bin {}.{}.{} has secondary index: {}", + qualifier.hashCode(), stmt.getNamespace(), stmt.getSetName(), qualifier.getBinName(), hasIndex); } return hasIndex; } 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 c154511ad..bc1210390 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 @@ -28,13 +28,40 @@ public FilterOperation getFilterOperation() { } /** - * Set FilterOperation for qualifier. Mandatory parameter. + * Set FilterOperation. Mandatory parameter. */ public T setFilterOperation(FilterOperation operationType) { map.put(FILTER_OPERATION, operationType); return (T) this; } + /** + * Set value. Mandatory parameter for bin or metadata query for all operations + * except {@link FilterOperation#IS_NOT_NULL} and {@link FilterOperation#IS_NULL}. + *

+ * + * @param value The provided object will be read into a {@link Value}, + * so its type must be recognizable by {@link Value#get(Object)}. + */ + public T setValue(Object value) { + this.map.put(VALUE, Value.get(value)); + return (T) this; + } + + /** + * Set second value. + *

+ * Use one of the Value get() methods ({@link Value#get(int)}, {@link Value#get(String)} etc.) to firstly read the + * second value into a {@link Value} object. + * + * @param secondValue The provided object will be read into a {@link Value}, + * so its type must be recognizable by {@link Value#get(Object)}. + * */ + public T setSecondValue(Object secondValue) { + this.map.put(SECOND_VALUE, Value.get(secondValue)); + return (T) this; + } + public String getPath() { return (String) map.get(PATH); } diff --git a/src/main/java/org/springframework/data/aerospike/query/qualifier/ConjunctionQualifierBuilder.java b/src/main/java/org/springframework/data/aerospike/query/qualifier/ConjunctionQualifierBuilder.java index 71a7c8499..1c5f970cb 100644 --- a/src/main/java/org/springframework/data/aerospike/query/qualifier/ConjunctionQualifierBuilder.java +++ b/src/main/java/org/springframework/data/aerospike/query/qualifier/ConjunctionQualifierBuilder.java @@ -24,4 +24,5 @@ protected void validate() { Assert.notEmpty(this.getQualifiers(), "Qualifiers must not be empty"); Assert.isTrue(this.getQualifiers().length > 1, "There must be at least 2 qualifiers"); } + } diff --git a/src/main/java/org/springframework/data/aerospike/query/qualifier/IdQualifierBuilder.java b/src/main/java/org/springframework/data/aerospike/query/qualifier/IdQualifierBuilder.java index 3de9e297e..720376927 100644 --- a/src/main/java/org/springframework/data/aerospike/query/qualifier/IdQualifierBuilder.java +++ b/src/main/java/org/springframework/data/aerospike/query/qualifier/IdQualifierBuilder.java @@ -77,4 +77,5 @@ IdQualifierBuilder setIds(byte[]... ids) { this.map.put(MULTIPLE_IDS_FIELD, ids); return this; } + } diff --git a/src/main/java/org/springframework/data/aerospike/query/qualifier/MetadataQualifierBuilder.java b/src/main/java/org/springframework/data/aerospike/query/qualifier/MetadataQualifierBuilder.java index f5c16ef46..d40c9d33b 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 @@ -7,10 +7,7 @@ import java.util.Collection; -import static org.springframework.data.aerospike.query.qualifier.QualifierKey.KEY; import static org.springframework.data.aerospike.query.qualifier.QualifierKey.METADATA_FIELD; -import static org.springframework.data.aerospike.query.qualifier.QualifierKey.SECOND_VALUE; -import static org.springframework.data.aerospike.query.qualifier.QualifierKey.VALUE; @Beta public class MetadataQualifierBuilder extends BaseQualifierBuilder { @@ -30,72 +27,48 @@ public MetadataQualifierBuilder setMetadataField(CriteriaDefinition.AerospikeMet return this; } - public Object getKeyAsObj() { - return this.map.get(KEY); - } - - public MetadataQualifierBuilder setKeyAsObj(Object object) { - this.map.put(KEY, object); - return this; - } - - public Object getValueAsObj() { - return this.map.get(VALUE); - } - - /** - * Set value as Object. Mandatory parameter for metadata query. - */ - public MetadataQualifierBuilder setValueAsObj(Object object) { - this.map.put(VALUE, object); - return this; - } - - public Object getSecondValueAsObj() { - return this.map.get(SECOND_VALUE); - } - - public MetadataQualifierBuilder setSecondValueAsObj(Object object) { - this.map.put(SECOND_VALUE, object); - return this; - } - @Override protected void validate() { // metadata query validation if (this.getMetadataField() != null) { if (this.getPath() == null) { - if (this.getValueAsObj() != null) { - validateValueAsObj(); + if (this.getValue() != null) { + validateValues(); } else { - throw new IllegalArgumentException("Expecting valueAsObj parameter to be provided"); + throw new IllegalArgumentException("Expecting value parameter to be provided"); } } else { - throw new IllegalArgumentException("Unexpected parameter: path (unnecessary for metadata query)"); + throw new IllegalArgumentException("Unexpected parameter for metadata query: path"); } } else { throw new IllegalArgumentException("Expecting metadataField parameter to be provided"); } } - private void validateValueAsObj() { + private void validateValues() { FilterOperation operation = this.getFilterOperation(); switch (operation) { - case EQ, NOTEQ, LT, LTEQ, GT, GTEQ -> Assert.isTrue(getValueAsObj() instanceof Long, - operation.name() + ": value1 is expected to be set as Long"); + case EQ, NOTEQ, LT, LTEQ, GT, GTEQ -> Assert.isTrue(getValue().getObject() instanceof Long, + operation.name() + ": value is expected to be set as Long"); case BETWEEN -> { - Assert.isTrue(getValueAsObj() instanceof Long, - "BETWEEN: value1 is expected to be set as Long"); - Assert.isTrue(getSecondValueAsObj() instanceof Long, - "BETWEEN: value2 is expected to be set as Long"); + Assert.isTrue(getSecondValue() != null, "BETWEEN: expecting secondValue to be provided"); + Assert.isTrue(getValue().getObject() instanceof Long, + "BETWEEN: value is expected to be set as Long"); + Assert.isTrue(getSecondValue().getObject() instanceof Long, + "BETWEEN: secondValue is expected to be set as Long"); } - case NOT_IN, IN -> //noinspection unchecked - Assert.isTrue(getValueAsObj() instanceof Collection - && !((Collection) getValueAsObj()).isEmpty() - && ((Collection) getValueAsObj()).toArray()[0] instanceof Long, + case NOT_IN, IN -> + { + Object obj = getValue().getObject(); + //noinspection unchecked + Assert.isTrue(obj instanceof Collection + && !((Collection) obj).isEmpty() + && ((Collection) obj).toArray()[0] instanceof Long, operation.name() + ": value1 is expected to be a non-empty Collection"); + } default -> throw new IllegalArgumentException("Operation " + operation + " cannot be applied to " + "metadataField"); } } + } 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 735c5e49c..d04a0f74e 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 @@ -17,6 +17,7 @@ package org.springframework.data.aerospike.query.qualifier; import com.aerospike.client.Value; +import com.aerospike.client.cdt.CTX; import com.aerospike.client.command.ParticleType; import com.aerospike.client.exp.Exp; import com.aerospike.client.query.Filter; @@ -94,10 +95,18 @@ public String getBinName() { return (String) internalMap.get(BIN_NAME); } + public boolean hasPath() { + return internalMap.containsKey(PATH); + } + public String getPath() { return (String) internalMap.get(PATH); } + public boolean hasMetadataField() { + return internalMap.containsKey(METADATA_FIELD); + } + public CriteriaDefinition.AerospikeMetadata getMetadataField() { return (CriteriaDefinition.AerospikeMetadata) internalMap.get(METADATA_FIELD); } @@ -130,8 +139,11 @@ public Object getId() { return this.hasSingleId() ? internalMap.get(SINGLE_ID_FIELD) : internalMap.get(MULTIPLE_IDS_FIELD); } - public FilterOperation getFilterOperation() { - return (FilterOperation) internalMap.get(FILTER_OPERATION); + /** + * Set CTX[]. + */ + public CTX[] getCtxArray() { + return (CTX[]) internalMap.get(CTX_ARRAY); } public Qualifier[] getQualifiers() { @@ -142,6 +154,10 @@ public Value getKey() { return (Value) internalMap.get(KEY); } + public boolean hasValue() { + return internalMap.containsKey(VALUE); + } + public Value getValue() { return (Value) internalMap.get(VALUE); } 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 074c8389e..9fa54d4ca 100644 --- a/src/main/java/org/springframework/data/aerospike/query/qualifier/QualifierBuilder.java +++ b/src/main/java/org/springframework/data/aerospike/query/qualifier/QualifierBuilder.java @@ -1,6 +1,5 @@ package org.springframework.data.aerospike.query.qualifier; -import com.aerospike.client.Value; import com.aerospike.client.cdt.CTX; import org.springframework.data.aerospike.annotation.Beta; import org.springframework.data.aerospike.index.AerospikeContextDslResolverUtils; @@ -20,8 +19,6 @@ import static org.springframework.data.aerospike.index.AerospikeContextDslResolverUtils.isCtxMapValue; import static org.springframework.data.aerospike.query.qualifier.QualifierKey.IGNORE_CASE; import static org.springframework.data.aerospike.query.qualifier.QualifierKey.PATH; -import static org.springframework.data.aerospike.query.qualifier.QualifierKey.SECOND_VALUE; -import static org.springframework.data.aerospike.query.qualifier.QualifierKey.VALUE; @Beta public class QualifierBuilder extends BaseQualifierBuilder { @@ -102,29 +99,6 @@ public QualifierBuilder setPath(String path) { return this; } - /** - * Set value. Mandatory parameter for bin query for all operations except {@link FilterOperation#IS_NOT_NULL} and - * {@link FilterOperation#IS_NULL}. - *

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

- * Use one of the Value get() methods ({@link Value#get(int)}, {@link Value#get(String)} etc.) to firstly read the - * second value into a {@link Value} object. - */ - public QualifierBuilder setSecondValue(Value secondValue) { - this.map.put(SECOND_VALUE, secondValue); - return this; - } - protected void validate() { if (!StringUtils.hasText(this.getPath())) { throw new IllegalArgumentException("Expecting path parameter to be provided"); @@ -142,8 +116,9 @@ protected void validate() { List betweenList = List.of(FilterOperation.BETWEEN, FilterOperation.MAP_VAL_BETWEEN_BY_KEY, FilterOperation.MAP_VAL_BETWEEN, FilterOperation.MAP_KEYS_BETWEEN, FilterOperation.COLLECTION_VAL_BETWEEN); - if (betweenList.contains(this.getFilterOperation()) && - (this.getValue() == null || this.getSecondValue() == null)) { + if (betweenList.contains(this.getFilterOperation()) + && ((this.getValue() == null || this.getSecondValue() == null) + || (this.getValue().getObject() == null || this.getSecondValue().getObject() == null))) { throw new IllegalArgumentException(this.getFilterOperation() + ": expecting both value and secondValue " + "to be provided"); } @@ -217,4 +192,5 @@ private CTX[] resolveCtxPath(String path) { .filter(Objects::nonNull) .toArray(CTX[]::new); } + } 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 83b723fde..d14eb5723 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 @@ -15,9 +15,6 @@ */ package org.springframework.data.aerospike.repository.query; -import com.aerospike.client.Value; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.data.aerospike.convert.MappingAerospikeConverter; import org.springframework.data.aerospike.mapping.AerospikeMappingContext; import org.springframework.data.aerospike.mapping.AerospikePersistentProperty; @@ -32,7 +29,6 @@ import org.springframework.data.repository.query.parser.AbstractQueryCreator; import org.springframework.data.repository.query.parser.Part; import org.springframework.data.repository.query.parser.PartTree; -import org.springframework.util.StringUtils; import java.util.ArrayList; import java.util.Arrays; @@ -54,7 +50,6 @@ */ public class AerospikeQueryCreator extends AbstractQueryCreator { - private static final Logger LOG = LoggerFactory.getLogger(AerospikeQueryCreator.class); private final AerospikeMappingContext context; private final MappingAerospikeConverter converter; private final ServerVersionSupport versionSupport; @@ -88,11 +83,6 @@ protected CriteriaDefinition or(CriteriaDefinition base, CriteriaDefinition crit @Override protected Query complete(CriteriaDefinition criteria, Sort sort) { Query query = criteria == null ? null : new Query(criteria).with(sort); - - if (LOG.isDebugEnabled() && criteria != null) { - logQualifierDetails(criteria); - } - return query; } @@ -201,27 +191,4 @@ private List getQueryParameters(Iterator parametersIterator, FilterOp // null parameters are not allowed, instead AerospikeNullQueryCriteria.NULL_PARAM should be used return params.stream().filter(Objects::nonNull).collect(Collectors.toList()); } - - private void logQualifierDetails(CriteriaDefinition criteria) { - Qualifier qualifier = criteria.getCriteriaObject(); - Qualifier[] qualifiers = qualifier.getQualifiers(); - if (qualifiers != null && qualifiers.length > 0) { - Arrays.stream(qualifiers).forEach(this::logQualifierDetails); - } - - String binName = (StringUtils.hasLength(qualifier.getBinName()) ? qualifier.getBinName() : ""); - String operation = qualifier.getOperation().toString(); - operation = (StringUtils.hasLength(operation) ? operation : "N/A"); - String key = printValue(qualifier.getKey()); - String value = printValue(qualifier.getValue()); - String value2 = printValue(qualifier.getSecondValue()); - - LOG.debug("Created query: bin name = {}, operation = {}, key = {}, value = {}, value2 = {}", - binName, operation, key, value, value2); - } - - private String printValue(Value value) { - if (value != null && StringUtils.hasLength(value.toString())) return value.toString(); - return value == Value.getAsNull() ? "null" : ""; - } } diff --git a/src/main/java/org/springframework/data/aerospike/repository/query/QueryQualifierBuilder.java b/src/main/java/org/springframework/data/aerospike/repository/query/QueryQualifierBuilder.java index de5ba36ec..1fe3af10e 100644 --- a/src/main/java/org/springframework/data/aerospike/repository/query/QueryQualifierBuilder.java +++ b/src/main/java/org/springframework/data/aerospike/repository/query/QueryQualifierBuilder.java @@ -85,29 +85,6 @@ public QueryQualifierBuilder setNestedKey(Value key) { return this; } - /** - * Set value. Mandatory parameter for bin query for all operations except {@link FilterOperation#IS_NOT_NULL} and - * {@link FilterOperation#IS_NULL}. - *

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

- * Use one of the Value get() methods ({@link Value#get(int)}, {@link Value#get(String)} etc.) to firstly read the - * second value into a {@link Value} object. - */ - public QueryQualifierBuilder setSecondValue(Value secondValue) { - this.map.put(SECOND_VALUE, secondValue); - return this; - } - /** * For "find by one level nested map containing" queries. Set the type of the nested map value using * {@link ParticleType}. @@ -128,4 +105,5 @@ public QueryQualifierBuilder setServerVersionSupport(ServerVersionSupport server public boolean hasDotPath() { return map.get(DOT_PATH) != null; } + } diff --git a/src/main/java/org/springframework/data/aerospike/util/Utils.java b/src/main/java/org/springframework/data/aerospike/util/Utils.java index a8ec7eff0..757338507 100644 --- a/src/main/java/org/springframework/data/aerospike/util/Utils.java +++ b/src/main/java/org/springframework/data/aerospike/util/Utils.java @@ -20,11 +20,15 @@ import com.aerospike.client.Info; import com.aerospike.client.ResultCode; import com.aerospike.client.Value; +import com.aerospike.client.cdt.CTX; import com.aerospike.client.cluster.Node; import com.aerospike.client.command.ParticleType; import com.aerospike.client.exp.Exp; import lombok.experimental.UtilityClass; +import org.slf4j.Logger; import org.springframework.dao.InvalidDataAccessResourceUsageException; +import org.springframework.data.aerospike.query.qualifier.Qualifier; +import org.springframework.data.aerospike.repository.query.CriteriaDefinition; import java.io.File; import java.net.InetAddress; @@ -34,6 +38,7 @@ import java.nio.file.Path; import java.time.ZoneId; import java.time.temporal.Temporal; +import java.util.Arrays; import java.util.Currency; import java.util.Date; import java.util.List; @@ -43,6 +48,7 @@ import java.util.UUID; import java.util.concurrent.ThreadLocalRandom; import java.util.regex.Pattern; +import java.util.stream.Collectors; import static com.aerospike.client.command.ParticleType.BOOL; import static com.aerospike.client.command.ParticleType.DOUBLE; @@ -51,6 +57,7 @@ import static com.aerospike.client.command.ParticleType.MAP; import static com.aerospike.client.command.ParticleType.STRING; import static org.springframework.util.ClassUtils.isPrimitiveOrWrapper; +import static org.springframework.util.StringUtils.hasLength; /** * Utility class containing useful methods for interacting with Aerospike across the entire implementation @@ -169,4 +176,54 @@ public static boolean isSimpleValueType(Class type) { Pattern.class == type || Class.class == type)); } + + public static String ctxArrToString(CTX[] ctxArr) { + return Arrays.stream(ctxArr).map(ctx -> ctx.value.toString()).collect(Collectors.joining(".")); + } + + public static void logQualifierDetails(CriteriaDefinition criteria, Logger logger) { + if (criteria == null || !logger.isDebugEnabled()) return; + Qualifier qualifier = criteria.getCriteriaObject(); + Qualifier[] qualifiers = qualifier.getQualifiers(); + if (qualifiers != null && qualifiers.length > 0) { + Arrays.stream(qualifiers).forEach(innerQualifier -> logQualifierDetails(innerQualifier, logger)); + } + + String operation = qualifier.getOperation().toString(); + operation = (hasLength(operation) ? operation : "N/A"); + + String values = ""; + String value = valueToString(qualifier.getValue()); + String value2 = valueToString(qualifier.getSecondValue()); + values = hasLength(value) ? String.format("value = %s", value) : ""; + values = hasLength(value2) ? String.format("%s, value2 = %s", values, value2) : values; + + String path = ""; + if (isBinQualifier(qualifier)) { // bin qualifier + path = (hasLength(qualifier.getBinName()) ? qualifier.getBinName() : ""); + if (qualifier.getCtxArray() != null && qualifier.getCtxArray().length > 0) { + path += "." + ctxArrToString(qualifier.getCtxArray()); + } + if (qualifier.getKey() != null && hasLength(qualifier.getKey().getObject().toString())) { + path += "." + qualifier.getKey().getObject().toString(); + } + } else if (isMetadataQualifier(qualifier)) { + path = qualifier.getMetadataField().toString(); + } + + logger.debug("Created query #{}: path = {}, operation = {}, {}", qualifier.hashCode(), path, operation, values); + } + + private static boolean isBinQualifier(Qualifier qualifier) { + return qualifier.getBinName() != null; + } + + private static boolean isMetadataQualifier(Qualifier qualifier) { + return qualifier.getMetadataField() != null; + } + + public static String valueToString(Value value) { + if (value != null && hasLength(value.toString())) return value.toString(); + return value == Value.getAsNull() ? "null" : ""; + } } diff --git a/src/test/java/org/springframework/data/aerospike/BaseBlockingIntegrationTests.java b/src/test/java/org/springframework/data/aerospike/BaseBlockingIntegrationTests.java index a631875aa..9b4baf29a 100644 --- a/src/test/java/org/springframework/data/aerospike/BaseBlockingIntegrationTests.java +++ b/src/test/java/org/springframework/data/aerospike/BaseBlockingIntegrationTests.java @@ -77,7 +77,7 @@ protected List runLastUpdateTimeQuery(long lastUpdateTimeMillis, FilterOp Qualifier lastUpdateTimeLtMillis = Qualifier.metadataBuilder() .setMetadataField(LAST_UPDATE_TIME) .setFilterOperation(operation) - .setValueAsObj(lastUpdateTimeMillis * MILLIS_TO_NANO) + .setValue(lastUpdateTimeMillis * MILLIS_TO_NANO) .build(); return template.find(new Query(lastUpdateTimeLtMillis), entityClass).toList(); } diff --git a/src/test/java/org/springframework/data/aerospike/BaseReactiveIntegrationTests.java b/src/test/java/org/springframework/data/aerospike/BaseReactiveIntegrationTests.java index 1b0351ccd..2b8007f09 100644 --- a/src/test/java/org/springframework/data/aerospike/BaseReactiveIntegrationTests.java +++ b/src/test/java/org/springframework/data/aerospike/BaseReactiveIntegrationTests.java @@ -84,7 +84,7 @@ protected List runLastUpdateTimeQuery(long lastUpdateTimeMillis, FilterOp Qualifier lastUpdateTimeLtMillis = Qualifier.metadataBuilder() .setMetadataField(LAST_UPDATE_TIME) .setFilterOperation(operation) - .setValueAsObj(lastUpdateTimeMillis * MILLIS_TO_NANO) + .setValue(lastUpdateTimeMillis * MILLIS_TO_NANO) .build(); return reactiveTemplate.find(new Query(lastUpdateTimeLtMillis), entityClass).collectList().block(); } 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 4b41a5056..8b965f3a1 100644 --- a/src/test/java/org/springframework/data/aerospike/logging/LoggingTests.java +++ b/src/test/java/org/springframework/data/aerospike/logging/LoggingTests.java @@ -5,27 +5,18 @@ import ch.qos.logback.classic.LoggerContext; import com.aerospike.client.Value; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.slf4j.LoggerFactory; -import org.springframework.data.aerospike.config.AerospikeDataSettings; -import org.springframework.data.aerospike.convert.AerospikeCustomConversions; -import org.springframework.data.aerospike.convert.AerospikeTypeAliasAccessor; -import org.springframework.data.aerospike.convert.MappingAerospikeConverter; -import org.springframework.data.aerospike.mapping.AerospikeMappingContext; import org.springframework.data.aerospike.query.FilterOperation; import org.springframework.data.aerospike.query.StatementBuilder; import org.springframework.data.aerospike.query.cache.IndexesCache; import org.springframework.data.aerospike.query.qualifier.Qualifier; -import org.springframework.data.aerospike.repository.query.AerospikeQueryCreator; import org.springframework.data.aerospike.repository.query.Query; -import org.springframework.data.aerospike.repository.query.StubParameterAccessor; -import org.springframework.data.aerospike.sample.Person; import org.springframework.data.aerospike.server.version.ServerVersionSupport; import org.springframework.data.aerospike.util.MemoryAppender; -import org.springframework.data.repository.query.parser.PartTree; - -import java.util.Collections; +import org.springframework.data.aerospike.util.QueryUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -44,6 +35,11 @@ public static void setup() { memoryAppender.start(); } + @BeforeEach + public void beforeEach() { + memoryAppender.reset(); + } + @Test void binIsIndexed() { IndexesCache indexesCacheMock = Mockito.mock(IndexesCache.class); @@ -56,34 +52,40 @@ void binIsIndexed() { StatementBuilder statementBuilder = new StatementBuilder(indexesCacheMock); statementBuilder.build("TEST", "testSet", new Query(qualifier)); - assertThat(memoryAppender.countEventsForLogger(LOGGER_NAME)).isEqualTo(1); - String msg = "Bin TEST.testSet.testField has secondary index: false"; + // 3 events: Created query, Bin has secondary index, Secondary index filter is not set + assertThat(memoryAppender.countEventsForLogger(LOGGER_NAME)).isEqualTo(3); + String msg = "bin TEST.testSet.testField has secondary index: false"; assertThat(memoryAppender.search(msg, Level.DEBUG).size()).isEqualTo(1); assertThat(memoryAppender.contains(msg, Level.INFO)).isFalse(); } @Test - void queryIsCreated() { - AerospikeMappingContext context = new AerospikeMappingContext(); - AerospikeCustomConversions conversions = new AerospikeCustomConversions(Collections.emptyList()); - MappingAerospikeConverter converter = getMappingAerospikeConverter(conversions); + void queryIsCreated_RepositoryQuery() { + StatementBuilder statementBuilder = new StatementBuilder(Mockito.mock(IndexesCache.class)); ServerVersionSupport serverVersionSupport = Mockito.mock(ServerVersionSupport.class); - PartTree tree = new PartTree("findByFirstName", Person.class); - AerospikeQueryCreator creator = new AerospikeQueryCreator( - tree, new StubParameterAccessor("TestName"), context, converter, serverVersionSupport); - creator.createQuery(); + Query query = QueryUtils.createQueryForMethodWithArgs(serverVersionSupport, "findByFirstName", "TestName"); + statementBuilder.build("TEST", "Person", query, null); assertThat(memoryAppender.countEventsForLogger(LOGGER_NAME)).isPositive(); - String msg = "Created query: bin name = firstName, operation = EQ, key = , value = TestName, value2 = "; + String msg = "path = firstName, operation = EQ, value = TestName"; assertThat(memoryAppender.search(msg, Level.DEBUG).size()).isEqualTo(1); assertThat(memoryAppender.contains(msg, Level.INFO)).isFalse(); } - private MappingAerospikeConverter getMappingAerospikeConverter(AerospikeCustomConversions conversions) { - MappingAerospikeConverter converter = new MappingAerospikeConverter(new AerospikeMappingContext(), - conversions, new AerospikeTypeAliasAccessor(), new AerospikeDataSettings()); - converter.afterPropertiesSet(); - return converter; + @Test + void queryIsCreated_CustomQuery() { + StatementBuilder statementBuilder = new StatementBuilder(Mockito.mock(IndexesCache.class)); + Query query = new Query(Qualifier.builder() + .setPath("firstName") + .setFilterOperation(FilterOperation.EQ) + .setValue(Value.get("TestName")) + .build()); + statementBuilder.build("TEST", "Person", query, null); + + assertThat(memoryAppender.countEventsForLogger(LOGGER_NAME)).isPositive(); + String msg = "path = firstName, operation = EQ, value = TestName"; + assertThat(memoryAppender.search(msg, Level.DEBUG).size()).isEqualTo(1); + assertThat(memoryAppender.contains(msg, Level.INFO)).isFalse(); } } diff --git a/src/test/java/org/springframework/data/aerospike/query/QualifierTests.java b/src/test/java/org/springframework/data/aerospike/query/QualifierTests.java index b97c60047..e86213010 100644 --- a/src/test/java/org/springframework/data/aerospike/query/QualifierTests.java +++ b/src/test/java/org/springframework/data/aerospike/query/QualifierTests.java @@ -195,7 +195,7 @@ void metadataSinceUpdateEQQualifier() { Qualifier qualifier = Qualifier.metadataBuilder() .setMetadataField(SINCE_UPDATE_TIME) .setFilterOperation(FilterOperation.GT) - .setValueAsObj(1L) + .setValue(1L) .build(); KeyRecordIterator iterator = queryEngine.select(namespace, SET_NAME, null, new Query(qualifier)); 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 f48407a7d..c3ae53510 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 @@ -23,7 +23,7 @@ void findPersonsByMetadata() { Qualifier sinceUpdateTimeLt50Seconds = Qualifier.metadataBuilder() .setMetadataField(SINCE_UPDATE_TIME) .setFilterOperation(FilterOperation.LT) - .setValueAsObj(50000L) + .setValue(50000L) .build(); assertThat(repository.findUsingQuery(new Query(sinceUpdateTimeLt50Seconds))).containsAll(allPersons); @@ -31,8 +31,8 @@ void findPersonsByMetadata() { Qualifier sinceUpdateTimeBetween1And50000 = Qualifier.metadataBuilder() .setMetadataField(SINCE_UPDATE_TIME) .setFilterOperation(FilterOperation.BETWEEN) - .setValueAsObj(1L) - .setSecondValueAsObj(50000L) + .setValue(1L) + .setSecondValue(50000L) .build(); assertThat(repository.findUsingQuery(new Query(sinceUpdateTimeBetween1And50000))) .containsAll(repository.findUsingQuery(new Query(sinceUpdateTimeLt50Seconds))); @@ -71,14 +71,14 @@ void findPersonsByQuery() { Qualifier sinceUpdateTimeGt1 = Qualifier.metadataBuilder() .setMetadataField(SINCE_UPDATE_TIME) .setFilterOperation(FilterOperation.GT) - .setValueAsObj(1L) + .setValue(1L) .build(); // creating an expression "since_update_time metadata value is less than 50 seconds" Qualifier sinceUpdateTimeLt50Seconds = Qualifier.metadataBuilder() .setMetadataField(SINCE_UPDATE_TIME) .setFilterOperation(FilterOperation.LT) - .setValueAsObj(50000L) + .setValue(50000L) .build(); assertThat(repository.findUsingQuery(new Query(sinceUpdateTimeLt50Seconds))).containsAll(allPersons); @@ -86,8 +86,8 @@ void findPersonsByQuery() { Qualifier sinceUpdateTimeBetween1And50000 = Qualifier.metadataBuilder() .setMetadataField(SINCE_UPDATE_TIME) .setFilterOperation(FilterOperation.BETWEEN) - .setValueAsObj(1L) - .setSecondValueAsObj(50000L) + .setValue(1L) + .setSecondValue(50000L) .build(); assertThat(repository.findUsingQuery(new Query(sinceUpdateTimeBetween1And50000))).containsAll(allPersons); @@ -202,48 +202,57 @@ void findPersonsByQueryMustBeValid() { assertThatThrownBy(() -> repository.findUsingQuery(new Query(Qualifier.metadataBuilder() .setMetadataField(SINCE_UPDATE_TIME) .setFilterOperation(FilterOperation.BETWEEN) - .setValueAsObj(1L) + .setValue(1L) .build()))) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("BETWEEN: value2 is expected to be set as Long"); + .hasMessage("BETWEEN: expecting secondValue to be provided"); assertThatThrownBy(() -> repository.findUsingQuery(new Query(Qualifier.metadataBuilder() .setMetadataField(SINCE_UPDATE_TIME) .setFilterOperation(FilterOperation.BETWEEN) - .setValueAsObj("value") - .setSecondValueAsObj(1L) + .setValue(1L) + .setSecondValue(null) .build()))) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("BETWEEN: value1 is expected to be set as Long"); + .hasMessage("BETWEEN: secondValue is expected to be set as Long"); + + assertThatThrownBy(() -> repository.findUsingQuery(new Query(Qualifier.metadataBuilder() + .setMetadataField(SINCE_UPDATE_TIME) + .setFilterOperation(FilterOperation.BETWEEN) + .setValue("value") + .setSecondValue(1L) + .build()))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("BETWEEN: value is expected to be set as Long"); assertThatThrownBy(() -> repository.findUsingQuery(new Query(Qualifier.metadataBuilder() .setMetadataField(SINCE_UPDATE_TIME) .setFilterOperation(FilterOperation.GT) - .setValueAsObj(1) + .setValue(1) .build()))) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("GT: value1 is expected to be set as Long"); + .hasMessage("GT: value is expected to be set as Long"); assertThatThrownBy(() -> repository.findUsingQuery(new Query(Qualifier.metadataBuilder() .setMetadataField(SINCE_UPDATE_TIME) .setFilterOperation(FilterOperation.LT) - .setValueAsObj(1) + .setValue(1) .build()))) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("LT: value1 is expected to be set as Long"); + .hasMessage("LT: value is expected to be set as Long"); assertThatThrownBy(() -> repository.findUsingQuery(new Query(Qualifier.metadataBuilder() .setMetadataField(SINCE_UPDATE_TIME) .setFilterOperation(FilterOperation.LTEQ) - .setValueAsObj(1) + .setValue(1) .build()))) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("LTEQ: value1 is expected to be set as Long"); + .hasMessage("LTEQ: value is expected to be set as Long"); assertThatThrownBy(() -> repository.findUsingQuery(new Query(Qualifier.metadataBuilder() .setMetadataField(SINCE_UPDATE_TIME) .setFilterOperation(FilterOperation.STARTS_WITH) - .setValueAsObj(1L) + .setValue(1L) .build()))) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Operation STARTS_WITH cannot be applied to metadataField"); 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 765b7a07e..cab91960a 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 @@ -24,7 +24,7 @@ public void findPersonsByMetadata() { Qualifier sinceUpdateTimeLt50Seconds = Qualifier.metadataBuilder() .setMetadataField(SINCE_UPDATE_TIME) .setFilterOperation(FilterOperation.LT) - .setValueAsObj(50000L) + .setValue(50000L) .build(); assertThat(reactiveRepository.findUsingQuery(new Query(sinceUpdateTimeLt50Seconds)).collectList().block()) .containsAll(allIndexedPersons); @@ -33,8 +33,8 @@ public void findPersonsByMetadata() { Qualifier sinceUpdateTimeBetween1And50000 = Qualifier.metadataBuilder() .setMetadataField(SINCE_UPDATE_TIME) .setFilterOperation(FilterOperation.BETWEEN) - .setValueAsObj(1L) - .setSecondValueAsObj(50000L) + .setValue(1L) + .setSecondValue(50000L) .build(); assertThat(reactiveRepository.findUsingQuery(new Query(sinceUpdateTimeBetween1And50000)).collectList().block()) .containsAll(reactiveRepository.findUsingQuery(new Query(sinceUpdateTimeLt50Seconds)).collectList() @@ -50,14 +50,14 @@ public void findPersonsByQuery() { Qualifier sinceUpdateTimeGt1 = Qualifier.metadataBuilder() .setMetadataField(SINCE_UPDATE_TIME) .setFilterOperation(FilterOperation.GT) - .setValueAsObj(1L) + .setValue(1L) .build(); // creating an expression "since_update_time metadata value is less than 50 seconds" Qualifier sinceUpdateTimeLt50Seconds = Qualifier.metadataBuilder() .setMetadataField(SINCE_UPDATE_TIME) .setFilterOperation(FilterOperation.LT) - .setValueAsObj(50000L) + .setValue(50000L) .build(); assertThat(reactiveRepository.findUsingQuery(new Query(sinceUpdateTimeLt50Seconds)).collectList().block()) .containsAll(allIndexedPersons); @@ -66,8 +66,8 @@ public void findPersonsByQuery() { Qualifier sinceUpdateTimeBetween1And50000 = Qualifier.metadataBuilder() .setMetadataField(SINCE_UPDATE_TIME) .setFilterOperation(FilterOperation.BETWEEN) - .setValueAsObj(1L) - .setSecondValueAsObj(50000L) + .setValue(1L) + .setSecondValue(50000L) .build(); // creating an expression "firsName is equal to Petra"