From 82fe83793249c216993e8f088029fcede21915ef Mon Sep 17 00:00:00 2001 From: agrgr Date: Sun, 29 Oct 2023 15:00:17 +0200 Subject: [PATCH 1/4] refactor query creator --- .../query/AerospikeQueryCreator.java | 311 ++++++++++-------- .../core/AerospikeTemplateSaveTests.java | 4 +- 2 files changed, 183 insertions(+), 132 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..7ce8a53dc 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; @@ -144,114 +136,17 @@ public AerospikeCriteria getCriteria(Part part, AerospikePersistentProperty prop 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 + String dotPath = null; + Object value3 = null; 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 @@ -267,11 +162,168 @@ public AerospikeCriteria getCriteria(Part part, AerospikePersistentProperty prop value2 = Value.get(property.getFieldName()); // VALUE2 contains key (field name) } } + qualifier = setQualifier(qb, fieldName, op, part, value1, value2, value3, dotPath); + } + + return new AerospikeCriteria(qualifier); + } + + private Qualifier processId(Object value1) { + Qualifier qualifier; + if (value1 instanceof Collection) { + qualifier = forIds(((Collection) value1).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) { + String dotPath = null; + 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 aerospikeCriteriaAndConcatenated(params, qb, part, fieldName, op, dotPath); + } + } else if (!(value1 instanceof Collection)) { + op = getCorrespondingListFilterOperationOrFail(op); + } + + return setQualifier(qb, fieldName, op, part, value1, value2, null, dotPath); + } + + 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) { + String dotPath = null; + Qualifier.QualifierBuilder qb = Qualifier.builder(); + + 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 one, two " + + "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 + "'"); } - return new AerospikeCriteria( - setQualifierBuilderValues(qb, fieldName, op, part, value1, value2, value3, dotPath) - ); + return setQualifier(qb, fieldName, op, part, value1, value2, null, dotPath); } private String getFieldName(String segmentName, AerospikePersistentProperty property) { @@ -289,22 +341,22 @@ 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) { + private Qualifier aerospikeCriteriaAndConcatenated(List params, Qualifier.QualifierBuilder qb, + Part part, String fieldName, FilterOperation op, + String dotPath) { return aerospikeCriteriaAndConcatenated(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 aerospikeCriteriaAndConcatenated(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) { 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)); @@ -312,23 +364,22 @@ private AerospikeCriteria aerospikeCriteriaAndConcatenated(List params, 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 50a75652f..bd757d815 100644 --- a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateSaveTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateSaveTests.java @@ -59,9 +59,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)); @@ -76,7 +76,7 @@ public void shouldSaveDocumentWithArray() { SampleClasses.DocumentWithArray doc = new SampleClasses.DocumentWithArray(id, new int[]{0, 1, 2, 3, 4, 5}); 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(); } From e84a76664c0148f5f9e1ab6db32711cca71cc62c Mon Sep 17 00:00:00 2001 From: agrgr Date: Sun, 29 Oct 2023 15:12:20 +0200 Subject: [PATCH 2/4] refactor query creator --- .../query/AerospikeQueryCreator.java | 45 +++++++++++-------- 1 file changed, 26 insertions(+), 19 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 7ce8a53dc..d786ffea6 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 @@ -134,7 +134,6 @@ 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); Qualifier qualifier; @@ -145,24 +144,7 @@ public AerospikeCriteria getCriteria(Part part, AerospikePersistentProperty prop } else if (property.isMap()) { qualifier = processMap(part, value1, value2, parameters, op, fieldName); } else { // if it is neither a collection nor a map - String dotPath = null; - Object value3 = null; - 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) - } - } - qualifier = setQualifier(qb, fieldName, op, part, value1, value2, value3, dotPath); + qualifier = processOther(part, value1, value2, property, op, fieldName); } return new AerospikeCriteria(qualifier); @@ -326,6 +308,31 @@ private Qualifier processMapMultipleParams(Part part, Object value1, Object valu return setQualifier(qb, fieldName, op, part, value1, value2, null, dotPath); } + 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) + } + } + + return setQualifier(qb, fieldName, op, part, value1, value2, value3, dotPath); + } + private String getFieldName(String segmentName, AerospikePersistentProperty property) { org.springframework.data.aerospike.mapping.Field annotation = property.findAnnotation(org.springframework.data.aerospike.mapping.Field.class); From c383f943b73e16d79a825a9690ba778eddadc438 Mon Sep 17 00:00:00 2001 From: agrgr Date: Tue, 31 Oct 2023 16:19:00 +0200 Subject: [PATCH 3/4] cleanup --- .../query/AerospikeQueryCreator.java | 98 +++++++++++-------- 1 file changed, 56 insertions(+), 42 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 d786ffea6..6c7b95a50 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 @@ -153,7 +153,9 @@ public AerospikeCriteria getCriteria(Part part, AerospikePersistentProperty prop private Qualifier processId(Object value1) { Qualifier qualifier; if (value1 instanceof Collection) { - qualifier = forIds(((Collection) value1).toArray(String[]::new)); + // 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); } @@ -162,7 +164,6 @@ private Qualifier processId(Object value1) { private Qualifier processCollection(Part part, Object value1, Object value2, Iterator parameters, FilterOperation op, String fieldName) { - String dotPath = null; Qualifier.QualifierBuilder qb = Qualifier.builder(); List params = new ArrayList<>(); parameters.forEachRemaining(params::add); @@ -172,13 +173,13 @@ private Qualifier processCollection(Part part, Object value1, Object value2, Ite 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); + 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, dotPath); + return setQualifier(qb, fieldName, op, part, value1, value2, null, null); } private Qualifier processMap(Part part, Object value1, Object value2, Iterator parameters, FilterOperation op, @@ -268,44 +269,57 @@ private Qualifier processMapBetween(Part part, Object value1, Object value2, Fil private Qualifier processMapMultipleParams(Part part, Object value1, Object value2, List params, FilterOperation op, String fieldName) { - String dotPath = null; - Qualifier.QualifierBuilder qb = Qualifier.builder(); - 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 one, two " + - "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); - } + 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 + "'"); } + } - return setQualifier(qb, fieldName, op, part, value1, value2, null, dotPath); + 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, @@ -348,15 +362,15 @@ private String getFieldName(String segmentName, AerospikePersistentProperty prop return segmentName; } - private Qualifier 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 Qualifier 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 @@ -366,7 +380,7 @@ private Qualifier aerospikeCriteriaAndConcatenated(List params, Qualifie 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++) { From ccf13c517cc44ecdeff8be827af61e9329a26eb4 Mon Sep 17 00:00:00 2001 From: agrgr Date: Tue, 31 Oct 2023 16:21:05 +0200 Subject: [PATCH 4/4] cleanup --- .../aerospike/repository/query/AerospikeQueryCreator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 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 6c7b95a50..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 @@ -374,9 +374,9 @@ private Qualifier qualifierAndConcatenated(List params, Qualifier.Qualif 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++] = setQualifier(qb, fieldName, op, part, params.get(i), + qualifiers[j] = setQualifier(qb, fieldName, op, part, params.get(i), null, null, dotPath); }