Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FMWK-191 Make findByList compare only upper level when a Collection is given #587

Merged
merged 4 commits into from
Jun 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions src/main/asciidoc/preface.adoc
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
[[preface]]
== Preface

The Spring Data Aerospike project applies core Spring concepts to the development of solutions using the Aerospike key-value style data store. We provide a "template" as a high-level abstraction for storing and querying data. You will notice similarities to the JDBC support in the Spring Framework.
The Spring Data Aerospike project applies core Spring concepts and provides interface for using Aerospike key-value style data store. We provide a "repository" and a "template" as high-level abstractions for storing and querying data. You will notice similarities to the JDBC support in the Spring Framework.

This document is the reference guide for Spring Data - Aerospike Support. It explains Aerospike module concepts and semantics and the syntax for various stores namespaces.

This section provides a basic introduction to Spring and the Aerospike database. The rest of the document refers only to Spring Data Aerospike features and assumes the user is familiar with Aerospike as well as Spring concepts.
This chapter provides some basic introduction to Spring and Aerospike, it explains Aerospike concepts and syntax. The rest of the documentation refers to Spring Data Aerospike features and assumes the user is familiar with Aerospike as well as Spring concepts.

[[get-started:first-steps:spring]]
== Knowing Spring
Spring Data uses Spring framework's https://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/spring-core.html[core] functionality, such as the https://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/beans.html[IoC] container, https://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/validation.html#core-convert[type conversion system], https://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/expressions.html[expression language], https://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/jmx.html[JMX integration], and portable https://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/dao.html#dao-exceptions[DAO exception hierarchy]. While it is not important to know the Spring APIs, understanding the concepts behind them is. At a minimum, the idea behind IoC should be familiar to whatever IoC container you choose to use.
Spring Data uses Spring framework's https://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/spring-core.html[core] functionality, such as the https://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/beans.html[IoC] container, https://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/validation.html#core-convert[type conversion system], https://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/expressions.html[expression language], https://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/jmx.html[JMX integration], and portable https://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/dao.html#dao-exceptions[DAO exception hierarchy]. While it is not important to know the Spring APIs, understanding the concepts behind them is. At a minimum, the idea behind IoC should be familiar regardless of IoC container you choose to use.

The core functionality of the Aerospike support can be used directly, with no need to invoke the IoC services of the Spring Container. This is much like `JdbcTemplate` which can be used 'standalone' without any other services of the Spring container. To leverage all the features of the Spring Data document, such as the repository support, you will need to configure some parts of the library using Spring.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,22 +136,9 @@ public AerospikeCriteria getCriteria(Part part, AerospikePersistentProperty prop
op = LIST_VAL_CONTAINING;
params.add(0, value1); // value1 stores the first parameter
return aerospikeCriteriaAndConcatenated(params, qb, part, fieldName, op, dotPath);
} else if (nextParam.equals(AerospikeMapCriteria.VALUE)) {
op = getCorrespondingListFilterOperationOrFail(op);
}
} else if (!(value1 instanceof Collection<?>)) { // preserving the initial FilterOperation if Collection
switch (op) {
// for these operations a parameter without AerospikeMapCriteria.VALUE flag must be Collection type
case EQ, NOTEQ, GT, GTEQ, LT, LTEQ -> {
throw new IllegalArgumentException("Expected to receive a Collection (got " +
value1.getClass().getSimpleName() + " instead), please provide " +
"an additional AerospikeMapCriteria.VALUE parameter for applying condition at values " +
"level");
}
default -> {
op = getCorrespondingListFilterOperationOrFail(op);
}
}
op = getCorrespondingListFilterOperationOrFail(op);
}
} else if (property.isMap()) {
List<Object> params = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,7 @@ public void findByListContainingString() {

@Test
public void findByListValueLessThanOrEqual() {
Query query = QueryUtils.createQueryForMethodWithArgs("findByIntsLessThanEqual", 25,
CriteriaDefinition.AerospikeMapCriteria.VALUE);
Query query = QueryUtils.createQueryForMethodWithArgs("findByIntsLessThanEqual", 25);
Stream<Person> result = template.find(query, Person.class);

assertThat(result)
Expand All @@ -262,8 +261,7 @@ public void findByListValueLessThanOrEqual() {

@Test
public void findByListValueGreaterThan() {
Query query = QueryUtils.createQueryForMethodWithArgs("findByIntsGreaterThan", 10,
CriteriaDefinition.AerospikeMapCriteria.VALUE);
Query query = QueryUtils.createQueryForMethodWithArgs("findByIntsGreaterThan", 10);
Stream<Person> result = template.find(query, Person.class);

assertThat(result)
Expand All @@ -273,8 +271,7 @@ public void findByListValueGreaterThan() {

@Test
public void findByListValueInRange() {
Query query = QueryUtils.createQueryForMethodWithArgs("findByIntsBetween", 10, 700,
CriteriaDefinition.AerospikeMapCriteria.VALUE);
Query query = QueryUtils.createQueryForMethodWithArgs("findByIntsBetween", 10, 700);
Stream<Person> result = template.find(query, Person.class);

assertThat(result)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,8 +323,7 @@ public void findByListValueLessThanOrEqual() {
.collect(Collectors.toList());
reactiveTemplate.insertAll(persons).blockLast();

Query query = QueryUtils.createQueryForMethodWithArgs("findByIntsLessThanEqual", 500,
CriteriaDefinition.AerospikeMapCriteria.VALUE);
Query query = QueryUtils.createQueryForMethodWithArgs("findByIntsLessThanEqual", 500);

List<Person> result = reactiveTemplate.find(query, Person.class)
.subscribeOn(Schedulers.parallel())
Expand All @@ -344,8 +343,7 @@ public void findByListValueInRange() {
.collect(Collectors.toList());
reactiveTemplate.insertAll(persons).blockLast();

Query query = QueryUtils.createQueryForMethodWithArgs("findByIntsBetween", 200, 601,
CriteriaDefinition.AerospikeMapCriteria.VALUE);
Query query = QueryUtils.createQueryForMethodWithArgs("findByIntsBetween", 200, 601);

List<Person> result = reactiveTemplate.find(query, Person.class)
.subscribeOn(Schedulers.parallel())
Expand All @@ -365,8 +363,7 @@ public void findByListValueGreaterThan() {
.collect(Collectors.toList());
reactiveTemplate.insertAll(persons).blockLast();

Query query = QueryUtils.createQueryForMethodWithArgs("findByIntsGreaterThan", 549,
CriteriaDefinition.AerospikeMapCriteria.VALUE);
Query query = QueryUtils.createQueryForMethodWithArgs("findByIntsGreaterThan", 549);

List<Person> result = reactiveTemplate.find(query, Person.class)
.subscribeOn(Schedulers.parallel())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,23 +110,23 @@ public void findByListContainingInteger_forExistingResult() {

@Test
public void findByListValueGreaterThan() {
List<IndexedPerson> results = reactiveRepository.findByIntsGreaterThan(549, VALUE)
List<IndexedPerson> results = reactiveRepository.findByIntsGreaterThan(549)
.subscribeOn(Schedulers.parallel()).collectList().block();

assertThat(results).containsExactlyInAnyOrder(daniel, emilien);
}

@Test
public void findByListValueLessThanOrEqual() {
List<IndexedPerson> results = reactiveRepository.findByIntsLessThanEqual(500, VALUE)
List<IndexedPerson> results = reactiveRepository.findByIntsLessThanEqual(500)
.subscribeOn(Schedulers.parallel()).collectList().block();

assertThat(results).containsExactlyInAnyOrder(daniel, emilien);
}

@Test
public void findByListValueInRange() {
List<IndexedPerson> results = reactiveRepository.findByIntsBetween(500, 600, VALUE)
List<IndexedPerson> results = reactiveRepository.findByIntsBetween(500, 600)
.subscribeOn(Schedulers.parallel()).collectList().block();

assertThat(results).containsExactlyInAnyOrder(daniel, emilien);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,19 +147,19 @@ void findByListContainingInteger_forEmptyResult() {

@Test
void findByListValueGreaterThan() {
List<IndexedPerson> persons = repository.findByIntsGreaterThan(549, VALUE);
List<IndexedPerson> persons = repository.findByIntsGreaterThan(549);
assertThat(persons).containsExactlyInAnyOrder(jane, john);
}

@Test
void findByListValueLessThanOrEqual() {
List<IndexedPerson> persons = repository.findByIntsLessThanEqual(500, VALUE);
List<IndexedPerson> persons = repository.findByIntsLessThanEqual(500);
assertThat(persons).containsOnly(john);
}

@Test
void findByListValueInRange() {
List<IndexedPerson> persons = repository.findByIntsBetween(500, 600, VALUE);
List<IndexedPerson> persons = repository.findByIntsBetween(500, 600);
assertThat(persons).containsExactlyInAnyOrder(jane, john);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,75 +177,78 @@ void findByBoolean() {
@Test
void findByListValueLessThanOrEqualNumber() {
List<Person> persons;
persons = repository.findByIntsLessThanEqual(500, VALUE);
persons = repository.findByIntsLessThanEqual(500);
assertThat(persons).containsOnly(oliver);

persons = repository.findByIntsLessThanEqual(Long.MAX_VALUE - 1, VALUE);
persons = repository.findByIntsLessThanEqual(Long.MAX_VALUE - 1);
assertThat(persons).containsOnly(oliver, alicia);

persons = repository.findByIntsLessThanEqual(Long.MAX_VALUE, VALUE);
persons = repository.findByIntsLessThanEqual(Long.MAX_VALUE);
assertThat(persons).containsOnly(oliver, alicia);
}

@Test
void findByListValueLessThanOrEqualString() {
List<Person> persons;
persons = repository.findByStringsLessThanEqual("str4", VALUE);
persons = repository.findByStringsLessThanEqual("str4");
assertThat(persons).containsOnly(dave, donny);

persons = repository.findByStringsLessThanEqual("str3", VALUE);
persons = repository.findByStringsLessThanEqual("str3");
assertThat(persons).containsOnly(dave, donny);

persons = repository.findByStringsLessThanEqual("str2", VALUE);
persons = repository.findByStringsLessThanEqual("str2");
assertThat(persons).containsOnly(dave, donny);

persons = repository.findByStringsLessThanEqual("str0", VALUE);
persons = repository.findByStringsLessThanEqual("str0");
assertThat(persons).containsOnly(dave);

persons = repository.findByStringsLessThanEqual("str", VALUE);
persons = repository.findByStringsLessThanEqual("str");
assertThat(persons).isEmpty();
}

@Test
void findByListValueGreaterThanNumber() {
List<Person> persons;
persons = repository.findByIntsGreaterThan(549, VALUE);
persons = repository.findByIntsGreaterThan(549);
assertThat(persons).containsOnly(oliver, alicia);

persons = repository.findByIntsGreaterThan(990, VALUE);
persons = repository.findByIntsGreaterThan(990);
assertThat(persons).isEmpty();

persons = repository.findByIntsGreaterThan(Long.MIN_VALUE, VALUE);
persons = repository.findByIntsGreaterThan(Long.MIN_VALUE);
assertThat(persons).containsOnly(oliver, alicia);

persons = repository.findByIntsGreaterThan(Long.MAX_VALUE - 1, VALUE);
persons = repository.findByIntsGreaterThan(Long.MAX_VALUE - 1);
assertThat(persons).isEmpty();

assertThatThrownBy(() -> repository.findByIntsGreaterThan(Long.MAX_VALUE, VALUE))
assertThatThrownBy(() -> repository.findByIntsGreaterThan(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<Person> persons;
persons = repository.findByStringsGreaterThan("str0", VALUE);
persons = repository.findByStringsGreaterThan("str0");
assertThat(persons).containsOnly(dave, donny);

persons = repository.findByStringsGreaterThan("", VALUE);
persons = repository.findByStringsGreaterThan("");
assertThat(persons).containsOnly(dave, donny);

// ordering is by each byte in a String, so "t" > "str" because "t" > "s"
persons = repository.findByStringsGreaterThan("t", VALUE);
persons = repository.findByStringsGreaterThan("t");
assertThat(persons).isEmpty();
}

@Test
void findByListValueGreaterThanList() {
List<List<Integer>> listOfLists1 = List.of(List.of(100), List.of(200), List.of(300), List.of(400));
List<List<Integer>> listOfLists2 = List.of(List.of(101), List.of(201), List.of(301), List.of(401));
List<List<Integer>> listOfLists3 = List.of(List.of(102), List.of(202), List.of(300), List.of(400));
List<List<Integer>> listOfLists4 = List.of(List.of(1000), List.of(2000), List.of(3000), List.of(4000));
// Note: only the upper level ListOfLists will be compared even if the parameter has different number of levels
// So findByListOfListsGreaterThan(List.of(1)) and findByListOfListsGreaterThan(List.of(List.of(List.of(1))))
// will compare with the given parameter only the upper level ListOfLists itself
void findByListOfListsGreaterThan() {
List<List<Integer>> listOfLists1 = List.of(List.of(100));
List<List<Integer>> listOfLists2 = List.of(List.of(101));
List<List<Integer>> listOfLists3 = List.of(List.of(102));
List<List<Integer>> listOfLists4 = List.of(List.of(1000));
stefan.setListOfIntLists(listOfLists1);
repository.save(stefan);
douglas.setListOfIntLists(listOfLists2);
Expand All @@ -256,57 +259,24 @@ void findByListValueGreaterThanList() {
repository.save(leroi2);

List<Person> persons;
persons = repository.findByListOfIntListsGreaterThan(List.of(100), VALUE);
persons = repository.findByListOfIntListsGreaterThan(List.of(List.of(99)));
assertThat(persons).containsOnly(stefan, douglas, matias, leroi2);

persons = repository.findByListOfIntListsGreaterThan(List.of(400), VALUE);
assertThat(persons).containsOnly(douglas, leroi2);
persons = repository.findByListOfIntListsGreaterThan(List.of(List.of(100)));
assertThat(persons).containsOnly(douglas, matias, leroi2);

persons = repository.findByListOfIntListsGreaterThan(List.of(401), VALUE);
persons = repository.findByListOfIntListsGreaterThan(List.of(List.of(102)));
assertThat(persons).containsOnly(leroi2);

persons = repository.findByListOfIntListsGreaterThan(List.of(4000), VALUE);
assertThat(persons).isEmpty();
}

@Test
void findByListValueGreaterThanMap() {
List<Map<String, Integer>> listOfMaps1 = List.of(Map.of("a", 100), Map.of("b", 200), Map.of("c", 300),
Map.of("d", 400));
List<Map<String, Integer>> listOfMaps2 = List.of(Map.of("a", 101), Map.of("b", 201), Map.of("c", 301),
Map.of("d", 401));
List<Map<String, Integer>> listOfMaps3 = List.of(Map.of("a", 102), Map.of("b", 202), Map.of("c", 300),
Map.of("d", 400));
List<Map<String, Integer>> listOfMaps4 = List.of(Map.of("a", 1000), Map.of("b", 2000), Map.of("c", 3000),
Map.of("d", 4000));
stefan.setListOfIntMaps(listOfMaps1);
repository.save(stefan);
douglas.setListOfIntMaps(listOfMaps2);
repository.save(douglas);
matias.setListOfIntMaps(listOfMaps3);
repository.save(matias);
leroi2.setListOfIntMaps(listOfMaps4);
repository.save(leroi2);

List<Person> persons;
persons = repository.findByListOfIntMapsLessThan(Map.of("a", 1001), VALUE);
assertThat(persons).containsOnly(stefan, douglas, matias, leroi2);

persons = repository.findByListOfIntMapsLessThan(Map.of("a", 400), VALUE);
assertThat(persons).containsOnly(stefan, douglas, matias);

persons = repository.findByListOfIntMapsLessThan(Map.of("a", 102), VALUE);
assertThat(persons).containsOnly(stefan, douglas);

persons = repository.findByListOfIntMapsLessThan(Map.of("a", 99), VALUE);
assertThat(persons).isEmpty();
persons = repository.findByListOfIntListsGreaterThan(List.of(List.of(401)));
assertThat(persons).containsOnly(leroi2);

persons = repository.findByListOfIntMapsLessThan(Map.of("a", 0), VALUE);
persons = repository.findByListOfIntListsGreaterThan(List.of(List.of(4000)));
assertThat(persons).isEmpty();
}

@Test
void findByGreaterThanList() {
void findByListGreaterThan() {
List<Integer> listToCompare1 = List.of(100, 200, 300, 400);
List<Integer> listToCompare2 = List.of(425, 550);
List<Integer> listToCompare3 = List.of(426, 551, 991);
Expand Down Expand Up @@ -336,20 +306,20 @@ void findByGreaterThanList() {

@Test
void findByIntegerListValueInRange() {
List<Person> persons = repository.findByIntsBetween(500, 600, VALUE);
List<Person> persons = repository.findByIntsBetween(500, 600);
assertThat(persons).containsExactlyInAnyOrder(oliver, alicia);
}

@Test
void findByStringListValueInRange() {
List<Person> persons;
persons = repository.findByStringsBetween("str1", "str3", VALUE);
persons = repository.findByStringsBetween("str1", "str3");
assertThat(persons).containsExactlyInAnyOrder(donny, dave);

persons = repository.findByStringsBetween("str3", "str3", VALUE); // upper limit is exclusive
persons = repository.findByStringsBetween("str3", "str3"); // upper limit is exclusive
assertThat(persons).isEmpty();

persons = repository.findByStringsBetween("str3", "str4", VALUE);
persons = repository.findByStringsBetween("str3", "str4");
assertThat(persons).containsExactlyInAnyOrder(donny);
}

Expand Down
Loading