From ab2579c5ee9127ce8bd64b2dd34bcd5768ba2d98 Mon Sep 17 00:00:00 2001 From: Andrey G Date: Tue, 31 Oct 2023 11:06:35 +0200 Subject: [PATCH 1/2] FMWK-260 Add tests for reading arrays of primitives (#648) * add tests for reading arrays of primitives * cleanup --- .../data/aerospike/SampleClasses.java | 1 + .../core/AerospikeTemplateSaveTests.java | 23 +++++----- .../PersonRepositoryQueryTests.java | 33 +++++++++++++- .../data/aerospike/sample/Person.java | 1 + .../aerospike/sample/PersonRepository.java | 43 +++++++++++++++++++ 5 files changed, 89 insertions(+), 12 deletions(-) diff --git a/src/test/java/org/springframework/data/aerospike/SampleClasses.java b/src/test/java/org/springframework/data/aerospike/SampleClasses.java index a6e7b9d46..acbd270af 100644 --- a/src/test/java/org/springframework/data/aerospike/SampleClasses.java +++ b/src/test/java/org/springframework/data/aerospike/SampleClasses.java @@ -766,6 +766,7 @@ public static class DocumentWithBigIntegerAndNestedArray { private ObjectWithArray objectWithArray; } + @Data public static class ObjectWithArray { @Version diff --git a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateSaveTests.java b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateSaveTests.java index 7a6b519c0..b8015c5eb 100644 --- a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateSaveTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateSaveTests.java @@ -74,27 +74,30 @@ public void shouldReplaceAllBinsPresentInAerospikeWhenSavingDocument() { @Test public void shouldSaveDocumentWithArray() { - SampleClasses.DocumentWithArray doc = new SampleClasses.DocumentWithArray(id, new int[]{0, 1, 2, 3, 4, 5}); + int[] array = new int[]{0, 1, 2, 3, 4, 5}; + SampleClasses.DocumentWithArray doc = new SampleClasses.DocumentWithArray(id, array); template.save(doc); Key key = new Key(getNameSpace(), "DocumentWithArray", id); Record aeroRecord = client.get(new Policy(), key); assertThat(aeroRecord.bins.get("array")).isNotNull(); + SampleClasses.DocumentWithArray result = template.findById(id, SampleClasses.DocumentWithArray.class); + assertThat(result.getArray()).isEqualTo(array); } @Test - public void shouldSaveDocumentWithoutCustomConverter() { - SampleClasses.ObjectWithArray objectWithArray = new SampleClasses.ObjectWithArray(new Integer[]{0, 1, 2, - 3, 4}); + public void shouldSaveDocumentWithNestedArrayAndBigInteger() { + Integer[] array = new Integer[]{0, 1, 2, 3, 4}; + SampleClasses.ObjectWithArray objectWithArray = new SampleClasses.ObjectWithArray(array); + BigInteger bigInteger = new BigInteger("100"); SampleClasses.DocumentWithBigIntegerAndNestedArray doc = - new SampleClasses.DocumentWithBigIntegerAndNestedArray(id, - new BigInteger("100"), objectWithArray); + new SampleClasses.DocumentWithBigIntegerAndNestedArray(id, bigInteger, objectWithArray); template.save(doc); - Key key = new Key(getNameSpace(), - template.getSetName(SampleClasses.DocumentWithBigIntegerAndNestedArray.class), id); - Record aeroRecord = client.get(new Policy(), key); - assertThat(aeroRecord.bins).isNotEmpty(); + SampleClasses.DocumentWithBigIntegerAndNestedArray result = + template.findById(id, SampleClasses.DocumentWithBigIntegerAndNestedArray.class); + assertThat(result.getBigInteger()).isEqualTo(bigInteger); + assertThat(result.getObjectWithArray().getArray()).isEqualTo(array); } @Test diff --git a/src/test/java/org/springframework/data/aerospike/repository/PersonRepositoryQueryTests.java b/src/test/java/org/springframework/data/aerospike/repository/PersonRepositoryQueryTests.java index 33604bd54..9282b9d01 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/PersonRepositoryQueryTests.java +++ b/src/test/java/org/springframework/data/aerospike/repository/PersonRepositoryQueryTests.java @@ -62,9 +62,11 @@ public class PersonRepositoryQueryTests extends BaseBlockingIntegrationTests { .stringMap(of("key1", "val1", "key2", "val2")).address(new Address(null, null, null, null)) .build(); static final Person stefan = Person.builder().id(nextId()).firstName("Stefan").lastName("Lessard").age(34).build(); - static final Person leroi = Person.builder().id(nextId()).firstName("Leroi").lastName("Moore").age(44).build(); + static final Person leroi = Person.builder().id(nextId()).firstName("Leroi").lastName("Moore").age(44) + .intArray(new int[]{5, 6, 7, 8, 9, 10}).build(); static final Person leroi2 = Person.builder().id(nextId()).firstName("Leroi").lastName("Moore").age(25).build(); - static final Person matias = Person.builder().id(nextId()).firstName("Matias").lastName("Craft").age(24).build(); + static final Person matias = Person.builder().id(nextId()).firstName("Matias").lastName("Craft").age(24) + .intArray(new int[]{1, 2, 3, 4, 5}).build(); static final Person douglas = Person.builder().id(nextId()).firstName("Douglas").lastName("Ford").age(25).build(); public static final List allPersons = List.of(dave, donny, oliver, alicia, carter, boyd, stefan, leroi, leroi2, matias, douglas); @@ -185,6 +187,16 @@ void findByListNotContainingAddress() { repository.save(stefan); } + @Test + void findByArrayContainingInteger_forExistingResult() { + assertThat(repository.findByIntArrayContaining(1)).containsOnly(matias); + assertThat(repository.findByIntArrayContaining(5)).containsOnly(matias, leroi); + assertThat(repository.findByIntArrayContaining(10)).containsOnly(leroi); + + assertThat(repository.findByIntArrayContaining(1, 2)).containsOnly(matias); + assertThat(repository.findByIntArrayContaining(6, 7)).containsOnly(leroi); + } + @Test void findByBooleanInt() { boolean initialValue = Value.UseBoolBin; @@ -272,6 +284,23 @@ void findByListValueGreaterThanNumber() { .hasMessage("LIST_VAL_GT FilterExpression unsupported value: expected [Long.MIN_VALUE..Long.MAX_VALUE-1]"); } + @Test + void findByArrayValueGreaterThanNumber() { + List persons; + persons = repository.findByIntArrayGreaterThan(1); + assertThat(persons).containsOnly(matias, leroi); + + persons = repository.findByIntArrayGreaterThan(Long.MIN_VALUE); + assertThat(persons).containsOnly(matias, leroi); + + persons = repository.findByIntArrayGreaterThan(Long.MAX_VALUE - 1); + assertThat(persons).isEmpty(); + + assertThatThrownBy(() -> repository.findByIntArrayGreaterThan(Long.MAX_VALUE)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("LIST_VAL_GT FilterExpression unsupported value: expected [Long.MIN_VALUE..Long.MAX_VALUE-1]"); + } + @Test void findByListValueGreaterThanString() { List persons; diff --git a/src/test/java/org/springframework/data/aerospike/sample/Person.java b/src/test/java/org/springframework/data/aerospike/sample/Person.java index c7a446682..c53865b0a 100644 --- a/src/test/java/org/springframework/data/aerospike/sample/Person.java +++ b/src/test/java/org/springframework/data/aerospike/sample/Person.java @@ -62,6 +62,7 @@ public class Person { private LocalDate regDate; private List strings; private List ints; + private int[] intArray; private List> listOfIntLists; private List> listOfIntMaps; private Set intSet; diff --git a/src/test/java/org/springframework/data/aerospike/sample/PersonRepository.java b/src/test/java/org/springframework/data/aerospike/sample/PersonRepository.java index 017add0f3..d385f3995 100644 --- a/src/test/java/org/springframework/data/aerospike/sample/PersonRepository.java +++ b/src/test/java/org/springframework/data/aerospike/sample/PersonRepository.java @@ -831,6 +831,27 @@ List

findByStringMapContaining(String key1, String value1, String key2, Strin */ List

findByIntsContaining(int integer1, int integer2, int integer3); + /** + * Find all entities that satisfy the condition "have the array which contains the given integer" + *

+ * Array name in this case is IntArray + *

+ * + * @param integer number to check + */ + List

findByIntArrayContaining(int integer); + + /** + * Find all entities that satisfy the condition "have the array which contains the given integers" + *

+ * Array name in this case is IntArray + *

+ * + * @param integer1 number to check + * @param integer2 number to check + */ + List

findByIntArrayContaining(int integer1, int integer2); + /** * Find all entities that satisfy the condition "have the list which contains the given boolean" * @@ -886,6 +907,28 @@ List

findByStringMapContaining(String key1, String value1, String key2, Strin */ List

findByListOfIntListsGreaterThan(List> list); + /** + * Find all entities that satisfy the condition "have at least one array value which is greater than the given + * integer" + *

+ * Array name in this case is IntArray + *

+ * + * @param integer lower limit, exclusive, [Long.MIN_VALUE..Long.MAX_VALUE-1] + */ + List

findByIntArrayGreaterThan(int integer); + + /** + * Find all entities that satisfy the condition "have at least one array value which is greater than the given + * integer" + *

+ * Array name in this case is IntArray + *

+ * + * @param value lower limit, exclusive + */ + List

findByIntArrayGreaterThan(long value); + /** * Find all entities that satisfy the condition "have at least one map value (with the given key) which is greater * than the given parameter (list of integers)" From b507fb8fbaa784c14c84c5ea663b43f59b6350ec Mon Sep 17 00:00:00 2001 From: Andrey G Date: Tue, 31 Oct 2023 18:49:00 +0200 Subject: [PATCH 2/2] FMWK-252 Refactor query creator (#647) --- .../query/AerospikeQueryCreator.java | 364 +++++++++++------- .../core/AerospikeTemplateSaveTests.java | 4 +- 2 files changed, 220 insertions(+), 148 deletions(-) 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 9c40a7cd1..77fae5b30 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 @@ -43,15 +43,7 @@ import java.util.List; import java.util.stream.Collectors; -import static org.springframework.data.aerospike.query.FilterOperation.IS_NOT_NULL; -import static org.springframework.data.aerospike.query.FilterOperation.IS_NULL; -import static org.springframework.data.aerospike.query.FilterOperation.LIST_VAL_CONTAINING; -import static org.springframework.data.aerospike.query.FilterOperation.MAP_KEYS_CONTAIN; -import static org.springframework.data.aerospike.query.FilterOperation.MAP_KEYS_NOT_CONTAIN; -import static org.springframework.data.aerospike.query.FilterOperation.MAP_VALUES_CONTAIN; -import static org.springframework.data.aerospike.query.FilterOperation.MAP_VALUES_NOT_CONTAIN; -import static org.springframework.data.aerospike.query.FilterOperation.MAP_VAL_CONTAINING_BY_KEY; -import static org.springframework.data.aerospike.query.FilterOperation.MAP_VAL_EQ_BY_KEY; +import static org.springframework.data.aerospike.query.FilterOperation.*; import static org.springframework.data.aerospike.query.Qualifier.forId; import static org.springframework.data.aerospike.query.Qualifier.forIds; @@ -142,136 +134,217 @@ private Object convertIfNecessary(Object obj) { public AerospikeCriteria getCriteria(Part part, AerospikePersistentProperty property, Object value1, Object value2, Iterator parameters, FilterOperation op) { - Qualifier.QualifierBuilder qb = Qualifier.builder(); String fieldName = getFieldName(part.getProperty().getSegment(), property); - String dotPath = null; - Object value3 = null; + Qualifier qualifier; if (property.isIdProperty()) { - if (value1 instanceof Collection) { - return new AerospikeCriteria(forIds(((Collection) value1).toArray(String[]::new))); - } - return new AerospikeCriteria(forId((String) value1)); + qualifier = processId(value1); } else if (property.isCollectionLike()) { - List params = new ArrayList<>(); - parameters.forEachRemaining(params::add); - - if (!params.isEmpty()) { - Object nextParam = params.get(params.size() - 1); - if (op == FilterOperation.CONTAINING && !(nextParam instanceof AerospikeMapCriteria)) { - op = LIST_VAL_CONTAINING; - params.add(0, value1); // value1 stores the first parameter - return aerospikeCriteriaAndConcatenated(params, qb, part, fieldName, op, dotPath); - } - } else if (!(value1 instanceof Collection)) { // preserving the initial FilterOperation if Collection - op = getCorrespondingListFilterOperationOrFail(op); - } + qualifier = processCollection(part, value1, value2, parameters, op, fieldName); } else if (property.isMap()) { - List params = new ArrayList<>(); - parameters.forEachRemaining(params::add); - - if (params.size() == 1) { // more than 1 parameter (values) provided, the first is stored in value1 - Object nextParam = convertIfNecessary(params.get(0)); // nextParam is de facto the second - if (op == FilterOperation.CONTAINING) { - if (nextParam instanceof AerospikeMapCriteria onMap) { - switch (onMap) { - case KEY -> op = MAP_KEYS_CONTAIN; - case VALUE -> op = MAP_VALUES_CONTAIN; - } - } else { - op = MAP_VAL_EQ_BY_KEY; - dotPath = part.getProperty().toDotPath() + "." + Value.get(value1); - setQbValuesForMapByKey(qb, value1, nextParam); - } - } else if (op == FilterOperation.NOT_CONTAINING) { - if (nextParam instanceof AerospikeMapCriteria onMap) { - switch (onMap) { - case KEY -> op = MAP_KEYS_NOT_CONTAIN; - case VALUE -> op = MAP_VALUES_NOT_CONTAIN; - } - } else { - op = FilterOperation.MAP_VAL_NOTEQ_BY_KEY; - dotPath = part.getProperty().toDotPath() + "." + Value.get(value1); - setQbValuesForMapByKey(qb, value1, nextParam); - } - } else { - if (op == FilterOperation.BETWEEN) { // BETWEEN for values by a certain key - op = getCorrespondingMapValueFilterOperationOrFail(op); - qb.setValue2(Value.get(value1)); // contains key - qb.setValue1(Value.get(value2)); // contains lower limit (inclusive) - qb.setValue3(Value.get(nextParam)); // contains upper limit (inclusive) - } else { - if (op == FilterOperation.EQ) { - throw new IllegalArgumentException("Unsupported arguments '" + value1 + "' and '" + nextParam + - "', expecting Map argument in findByMapEquals queries"); - } else { - op = getCorrespondingMapValueFilterOperationOrFail(op); - setQbValuesForMapByKey(qb, value1, nextParam); - } - } - dotPath = part.getProperty().toDotPath() + "." + Value.get(value1); - } - } else if (params.isEmpty()) { - if (op != FilterOperation.BETWEEN) { // if not map in range (2 maps as parameters) - // VALUE2 contains key (field name) - value2 = Value.get(property.getFieldName()); - } - } else { - if (op == FilterOperation.CONTAINING) { - if (params.get(params.size() - 1) instanceof AerospikeMapCriteria onMap) { - switch (onMap) { - case KEY -> op = MAP_KEYS_CONTAIN; - case VALUE -> op = MAP_VALUES_CONTAIN; - case VALUE_CONTAINING -> op = MAP_VAL_CONTAINING_BY_KEY; - } - params = params.stream().limit(params.size() - 1L).collect(Collectors.toList()); - } else { - op = MAP_VAL_EQ_BY_KEY; - dotPath = part.getProperty().toDotPath() + "." + Value.get(value1); - } - - params.add(0, value1); // value1 stores the first parameter - if (op == MAP_VAL_CONTAINING_BY_KEY || op == MAP_VAL_EQ_BY_KEY) { - if (params.size() > 2) { - if ((params.size() & 1) != 0) { // if params.size() is an odd number - throw new IllegalArgumentException("FindByMapContaining: expected either 1, 2 " + - "or even number of key/value arguments, instead got " + params.size()); - } - return aerospikeCriteriaAndConcatenated(params, qb, part, fieldName, op, dotPath, true); - } else if (params.size() == 2) { - setQbValuesForMapByKey(qb, params.get(0), params.get(1)); - } - } else { - return aerospikeCriteriaAndConcatenated(params, qb, part, fieldName, op, dotPath); - } - } else { - String paramsString = params.stream().map(Object::toString).collect(Collectors.joining(", ")); - throw new IllegalArgumentException( - "Expected not more than 2 arguments (propertyType: Map, filterOperation: " + op + "), " + - " got " + (params.size() + 1) + " instead: '" + value1 + ", " + paramsString + "'"); - } - } + qualifier = processMap(part, value1, value2, parameters, op, fieldName); } else { // if it is neither a collection nor a map - if (part.getProperty().hasNext()) { // if it is a POJO field (a simple field or an inner POJO) - if (op == FilterOperation.BETWEEN) { - value3 = Value.get(value2); // contains upper limit - } else if (op == IS_NOT_NULL || op == IS_NULL) { - value1 = Value.get(property.getFieldName()); // contains key (field name) - } + qualifier = processOther(part, value1, value2, property, op, fieldName); + } + + return new AerospikeCriteria(qualifier); + } + + private Qualifier processId(Object value1) { + Qualifier qualifier; + if (value1 instanceof Collection) { + // currently id can only be a String + List ids = ((Collection) value1).stream().map(String::valueOf).toList(); + qualifier = forIds(ids.toArray(String[]::new)); + } else { + qualifier = forId((String) value1); + } + return qualifier; + } + + private Qualifier processCollection(Part part, Object value1, Object value2, Iterator parameters, + FilterOperation op, String fieldName) { + Qualifier.QualifierBuilder qb = Qualifier.builder(); + List params = new ArrayList<>(); + parameters.forEachRemaining(params::add); + + if (!params.isEmpty()) { + Object nextParam = params.get(params.size() - 1); + if (op == FilterOperation.CONTAINING && !(nextParam instanceof AerospikeMapCriteria)) { + op = LIST_VAL_CONTAINING; + params.add(0, value1); // value1 stores the first parameter + return qualifierAndConcatenated(params, qb, part, fieldName, op, null); + } + } else if (!(value1 instanceof Collection)) { + op = getCorrespondingListFilterOperationOrFail(op); + } + + return setQualifier(qb, fieldName, op, part, value1, value2, null, null); + } + + private Qualifier processMap(Part part, Object value1, Object value2, Iterator parameters, FilterOperation op, + String fieldName) { + List params = new ArrayList<>(); + parameters.forEachRemaining(params::add); + Qualifier qualifier = null; + + // the first parameter is value1, params have parameters starting from the second + if (params.size() == 1) { + qualifier = processMap2Params(part, value1, value2, params, op, fieldName); + } else if (params.isEmpty()) { + if (op != FilterOperation.BETWEEN) { // if not map in range (2 maps as parameters) + // VALUE2 contains key (field name) + qualifier = setQualifier(Qualifier.builder(), fieldName, op, part, value1, Value.get(fieldName), + null, null); + } + } else { + qualifier = processMapMultipleParams(part, value1, value2, params, op, fieldName); + } + + return qualifier; + } + + private Qualifier processMap2Params(Part part, Object value1, Object value2, List params, + FilterOperation op, String fieldName) { + Qualifier qualifier; + Object nextParam = convertIfNecessary(params.get(0)); // nextParam is de facto the second + + if (op == FilterOperation.CONTAINING) { + qualifier = processMapContaining(nextParam, part, value1, fieldName, MAP_KEYS_CONTAIN, MAP_VALUES_CONTAIN, + MAP_VAL_EQ_BY_KEY); + } else if (op == FilterOperation.NOT_CONTAINING) { + qualifier = processMapContaining(nextParam, part, value1, fieldName, MAP_KEYS_NOT_CONTAIN, + MAP_VALUES_NOT_CONTAIN, + MAP_VAL_NOTEQ_BY_KEY); + } else { + qualifier = processMapBetween(part, value1, value2, op, fieldName, nextParam); + } + + return qualifier; + } + + private Qualifier processMapContaining(Object nextParam, Part part, Object value1, String fieldName, + FilterOperation keysOp, FilterOperation valuesOp, FilterOperation byKeyOp) { + FilterOperation op; + String dotPath = null; + Qualifier.QualifierBuilder qb = Qualifier.builder(); + + if (nextParam instanceof AerospikeMapCriteria onMap) { + switch (onMap) { + case KEY -> op = keysOp; + case VALUE -> op = valuesOp; + default -> throw new UnsupportedOperationException("Unsupported parameter: " + onMap); + } + } else { + op = byKeyOp; + dotPath = part.getProperty().toDotPath() + "." + Value.get(value1); + setQbValuesForMapByKey(qb, value1, nextParam); + } + + return setQualifier(qb, fieldName, op, part, value1, null, null, dotPath); + } + + private Qualifier processMapBetween(Part part, Object value1, Object value2, FilterOperation op, String fieldName + , Object nextParam) { + Qualifier.QualifierBuilder qb = Qualifier.builder(); + String dotPath = part.getProperty().toDotPath() + "." + Value.get(value1); + + if (op == FilterOperation.BETWEEN) { // BETWEEN for values by a certain key + op = getCorrespondingMapValueFilterOperationOrFail(op); + qb.setValue2(Value.get(value1)); // contains key + qb.setValue1(Value.get(value2)); // contains lower limit (inclusive) + qb.setValue3(Value.get(nextParam)); // contains upper limit (inclusive) + } else { + if (op == FilterOperation.EQ) { + throw new IllegalArgumentException("Unsupported arguments '" + value1 + "' and '" + nextParam + + "', expecting Map argument in findByMapEquals queries"); + } else { op = getCorrespondingMapValueFilterOperationOrFail(op); + setQbValuesForMapByKey(qb, value1, nextParam); + } + } + + return setQualifier(qb, fieldName, op, part, value1, value2, null, dotPath); + } + + private Qualifier processMapMultipleParams(Part part, Object value1, Object value2, List params, + FilterOperation op, String fieldName) { + if (op == FilterOperation.CONTAINING) { + return processMapMultipleParamsContaining(part, value1, value2, params, op, fieldName); + } else { + String paramsString = params.stream().map(Object::toString).collect(Collectors.joining(", ")); + throw new IllegalArgumentException( + "Expected not more than 2 arguments (propertyType: Map, filterOperation: " + op + "), " + + " got " + (params.size() + 1) + " instead: '" + value1 + ", " + paramsString + "'"); + } + } + + private Qualifier processMapMultipleParamsContaining(Part part, Object value1, Object value2, List params, + FilterOperation op, String fieldName) { + String dotPath = null; + Qualifier.QualifierBuilder qb = Qualifier.builder(); + + if (params.get(params.size() - 1) instanceof AerospikeMapCriteria mapCriteria) { + switch (mapCriteria) { + case KEY -> op = MAP_KEYS_CONTAIN; + case VALUE -> op = MAP_VALUES_CONTAIN; + case VALUE_CONTAINING -> op = MAP_VAL_CONTAINING_BY_KEY; + } + params = params.stream().limit(params.size() - 1L).collect(Collectors.toList()); + } else { + op = MAP_VAL_EQ_BY_KEY; + dotPath = part.getProperty().toDotPath() + "." + Value.get(value1); + } + + params.add(0, value1); // value1 stores the first parameter + if (op == MAP_VAL_CONTAINING_BY_KEY || op == MAP_VAL_EQ_BY_KEY) { + return processMapMultipleParamsContainingPerSize(params, qb, part, value1, value2, fieldName, op, dotPath); + } else { + return qualifierAndConcatenated(params, qb, part, fieldName, op, dotPath); + } + } + + private Qualifier processMapMultipleParamsContainingPerSize(List params, Qualifier.QualifierBuilder qb, + Part part, Object value1, Object value2, + String fieldName, FilterOperation op, String dotPath) { + if (params.size() > 2) { + if ((params.size() & 1) != 0) { // if params.size() is an odd number + throw new IllegalArgumentException("FindByMapContaining: expected either one, two " + + "or even number of key/value arguments, instead got " + params.size()); + } + return qualifierAndConcatenated(params, qb, part, fieldName, op, dotPath, true); + } else if (params.size() == 2) { + setQbValuesForMapByKey(qb, params.get(0), params.get(1)); + return setQualifier(qb, fieldName, op, part, value1, value2, null, null); + } else { + throw new UnsupportedOperationException("Unsupported combination of operation " + op + " and " + + "parameters with size of + " + params.size()); + } + } + + private Qualifier processOther(Part part, Object value1, Object value2, AerospikePersistentProperty property, + FilterOperation op, String fieldName) { + String dotPath = null; + Object value3 = null; + Qualifier.QualifierBuilder qb = Qualifier.builder(); + + if (part.getProperty().hasNext()) { // if it is a POJO field (a simple field or an inner POJO) + if (op == FilterOperation.BETWEEN) { + value3 = Value.get(value2); // contains upper limit + } else if (op == IS_NOT_NULL || op == IS_NULL) { + value1 = Value.get(property.getFieldName()); // contains key (field name) + } + op = getCorrespondingMapValueFilterOperationOrFail(op); + value2 = Value.get(property.getFieldName()); // VALUE2 contains key (field name) + dotPath = part.getProperty().toDotPath(); + } else if (isPojo(part)) { // if it is a first level POJO + if (op != FilterOperation.BETWEEN) { + // if it is a POJO compared for equality it already has op == FilterOperation.EQ value2 = Value.get(property.getFieldName()); // VALUE2 contains key (field name) - dotPath = part.getProperty().toDotPath(); - } else if (isPojo(part)) { // if it is a first level POJO - if (op != FilterOperation.BETWEEN) { - // if it is a POJO compared for equality it already has op == FilterOperation.EQ - value2 = Value.get(property.getFieldName()); // VALUE2 contains key (field name) - } } } - return new AerospikeCriteria( - setQualifierBuilderValues(qb, fieldName, op, part, value1, value2, value3, dotPath) - ); + return setQualifier(qb, fieldName, op, part, value1, value2, value3, dotPath); } private String getFieldName(String segmentName, AerospikePersistentProperty property) { @@ -289,46 +362,45 @@ private String getFieldName(String segmentName, AerospikePersistentProperty prop return segmentName; } - private AerospikeCriteria aerospikeCriteriaAndConcatenated(List params, Qualifier.QualifierBuilder qb, - Part part, String fieldName, FilterOperation op, - String dotPath) { - return aerospikeCriteriaAndConcatenated(params, qb, part, fieldName, op, dotPath, false); + private Qualifier qualifierAndConcatenated(List params, Qualifier.QualifierBuilder qb, + Part part, String fieldName, FilterOperation op, + String dotPath) { + return qualifierAndConcatenated(params, qb, part, fieldName, op, dotPath, false); } - private AerospikeCriteria aerospikeCriteriaAndConcatenated(List params, Qualifier.QualifierBuilder qb, - Part part, String fieldName, FilterOperation op, - String dotPath, boolean containingMapKeyValuePairs) { + private Qualifier qualifierAndConcatenated(List params, Qualifier.QualifierBuilder qb, + Part part, String fieldName, FilterOperation op, + String dotPath, boolean containingMapKeyValuePairs) { Qualifier[] qualifiers; if (containingMapKeyValuePairs) { qualifiers = new Qualifier[params.size() / 2]; // keys/values qty must be even - for (int i = 0, j = 0; i < params.size(); i += 2) { + for (int i = 0, j = 0; i < params.size(); i += 2, j++) { setQbValuesForMapByKey(qb, params.get(i), params.get(i + 1)); - qualifiers[j++] = setQualifierBuilderValues(qb, fieldName, op, part, params.get(i), - null, null, dotPath).build(); + qualifiers[j] = setQualifier(qb, fieldName, op, part, params.get(i), + null, null, dotPath); } - return new AerospikeCriteria(Qualifier.and(qualifiers)); + return Qualifier.and(qualifiers); } else { qualifiers = new Qualifier[params.size()]; for (int i = 0; i < params.size(); i++) { setQbValuesForMapByKey(qb, params.get(i), params.get(i)); - qualifiers[i] = setQualifierBuilderValues(qb, fieldName, op, part, params.get(i), - null, null, dotPath).build(); + qualifiers[i] = setQualifier(qb, fieldName, op, part, params.get(i), + null, null, dotPath); } } - return new AerospikeCriteria(Qualifier.and(qualifiers)); + return Qualifier.and(qualifiers); } - private Qualifier.QualifierBuilder setQualifierBuilderValues(Qualifier.QualifierBuilder qb, String fieldName, - FilterOperation op, Part part, Object value1, - Object value2, Object value3, String dotPath) { + private Qualifier setQualifier(Qualifier.QualifierBuilder qb, String fieldName, FilterOperation op, Part part, + Object value1, Object value2, Object value3, String dotPath) { qb.setField(fieldName) .setFilterOperation(op) .setIgnoreCase(ignoreCaseToBoolean(part)) .setConverter(converter); setNotNullQbValues(qb, value1, value2, value3, dotPath); - return qb; + return qb.build(); } private FilterOperation getCorrespondingMapValueFilterOperationOrFail(FilterOperation op) { diff --git a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateSaveTests.java b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateSaveTests.java index b8015c5eb..aa60ee08c 100644 --- a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateSaveTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateSaveTests.java @@ -60,9 +60,9 @@ public void afterAll() { // test for RecordExistsAction.REPLACE_ONLY policy @Test public void shouldReplaceAllBinsPresentInAerospikeWhenSavingDocument() { - Key key = new Key(getNameSpace(), "versioned-set", id); VersionedClass first = new VersionedClass(id, "foo"); template.save(first); + Key key = new Key(getNameSpace(), template.getSetName(VersionedClass.class), id); additionalAerospikeTestOperations.addNewFieldToSavedDataInAerospike(key); template.save(new VersionedClass(id, "foo2", 2L)); @@ -78,7 +78,7 @@ public void shouldSaveDocumentWithArray() { SampleClasses.DocumentWithArray doc = new SampleClasses.DocumentWithArray(id, array); template.save(doc); - Key key = new Key(getNameSpace(), "DocumentWithArray", id); + Key key = new Key(getNameSpace(), template.getSetName(SampleClasses.DocumentWithArray.class), id); Record aeroRecord = client.get(new Policy(), key); assertThat(aeroRecord.bins.get("array")).isNotNull(); SampleClasses.DocumentWithArray result = template.findById(id, SampleClasses.DocumentWithArray.class);