diff --git a/src/test/java/org/springframework/data/neo4j/integration/imperative/ScrollingIT.java b/src/test/java/org/springframework/data/neo4j/integration/imperative/ScrollingIT.java index 7bdec2625..a24990154 100644 --- a/src/test/java/org/springframework/data/neo4j/integration/imperative/ScrollingIT.java +++ b/src/test/java/org/springframework/data/neo4j/integration/imperative/ScrollingIT.java @@ -23,6 +23,8 @@ import java.util.function.Function; import org.assertj.core.data.Index; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.neo4j.driver.Driver; @@ -46,6 +48,7 @@ import org.springframework.data.neo4j.test.Neo4jExtension; import org.springframework.data.neo4j.test.Neo4jImperativeTestConfiguration; import org.springframework.data.neo4j.test.Neo4jIntegrationTest; import org.springframework.data.support.WindowIterator; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; @@ -57,174 +60,197 @@ class ScrollingIT { @SuppressWarnings("unused") private static Neo4jExtension.Neo4jConnectionSupport neo4jConnectionSupport; + @Nested + @SpringJUnitConfig(Config.class) + @DisplayName("Scroll with derived finder method") + class ScrollWithDerivedFinderMethod { - @BeforeAll - static void setupTestData(@Autowired Driver driver, @Autowired BookmarkCapture bookmarkCapture) { - try ( - var session = driver.session(bookmarkCapture.createSessionConfig()); - var transaction = session.beginTransaction() - ) { - ScrollingEntity.createTestData(transaction); - transaction.commit(); - bookmarkCapture.seedWith(session.lastBookmarks()); + @BeforeAll + static void setupTestData(@Autowired Driver driver, @Autowired BookmarkCapture bookmarkCapture) { + try ( + var session = driver.session(bookmarkCapture.createSessionConfig()); + var transaction = session.beginTransaction() + ) { + ScrollingEntity.createTestData(transaction); + transaction.commit(); + bookmarkCapture.seedWith(session.lastBookmarks()); + } + } + + @Test + void oneColumnSortNoScroll(@Autowired ScrollingRepository repository) { + + var topN = repository.findTop4ByOrderByB(); + assertThat(topN) + .hasSize(4) + .extracting(ScrollingEntity::getA) + .containsExactly("A0", "B0", "C0", "D0"); + } + + @Test + void forwardWithDuplicatesManualIteration(@Autowired ScrollingRepository repository) { + + var duplicates = repository.findAllByAOrderById("D0"); + assertThat(duplicates).hasSize(2); + + var window = repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, ScrollPosition.keyset()); + assertThat(window.hasNext()).isTrue(); + assertThat(window) + .hasSize(4) + .extracting(Function.identity()) + .satisfies(e -> assertThat(e.getId()).isEqualTo(duplicates.get(0).getId()), Index.atIndex(3)) + .extracting(ScrollingEntity::getA) + .containsExactly("A0", "B0", "C0", "D0"); + + window = repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, window.positionAt(window.size() - 1)); + assertThat(window.hasNext()).isTrue(); + assertThat(window) + .hasSize(4) + .extracting(Function.identity()) + .satisfies(e -> assertThat(e.getId()).isEqualTo(duplicates.get(1).getId()), Index.atIndex(0)) + .extracting(ScrollingEntity::getA) + .containsExactly("D0", "E0", "F0", "G0"); + + window = repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, window.positionAt(window.size() - 1)); + assertThat(window.isLast()).isTrue(); + assertThat(window).extracting(ScrollingEntity::getA) + .containsExactly("H0", "I0"); + } + + @Test + void forwardWithDuplicatesIteratorIteration(@Autowired ScrollingRepository repository) { + + var it = WindowIterator.of(pos -> repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, pos)) + .startingAt(ScrollPosition.keyset()); + var content = new ArrayList(); + while (it.hasNext()) { + var next = it.next(); + content.add(next); + } + + assertThat(content).hasSize(10); + assertThat(content.stream().map(ScrollingEntity::getId) + .distinct().toList()).hasSize(10); + } + + @Test + void backwardWithDuplicatesManualIteration(@Autowired ScrollingRepository repository) { + + // Recreate the last position + var last = repository.findFirstByA("I0"); + var keys = Map.of( + "foobar", Values.value(last.getA()), + "b", Values.value(last.getB()), + Constants.NAME_OF_ADDITIONAL_SORT, Values.value(last.getId().toString()) + ); + + var duplicates = repository.findAllByAOrderById("D0"); + assertThat(duplicates).hasSize(2); + + var window = repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, ScrollPosition.backward(keys)); + assertThat(window.hasNext()).isTrue(); + assertThat(window) + .hasSize(4) + .extracting(ScrollingEntity::getA) + .containsExactly("F0", "G0", "H0", "I0"); + + var pos = ((KeysetScrollPosition) window.positionAt(0)); + pos = ScrollPosition.backward(pos.getKeys()); + window = repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, pos); + assertThat(window.hasNext()).isTrue(); + assertThat(window) + .hasSize(4) + .extracting(Function.identity()) + .extracting(ScrollingEntity::getA) + .containsExactly("C0", "D0", "D0", "E0"); + + pos = ((KeysetScrollPosition) window.positionAt(0)); + pos = ScrollPosition.backward(pos.getKeys()); + window = repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, pos); + assertThat(window.isLast()).isTrue(); + assertThat(window).extracting(ScrollingEntity::getA) + .containsExactly("A0", "B0"); } } - @Test - void oneColumnSortNoScroll(@Autowired ScrollingRepository repository) { + @Nested + @SpringJUnitConfig(Config.class) + @DisplayName("ScrollWithExampleApi") + class ScrollWithExampleApi { - var topN = repository.findTop4ByOrderByB(); - assertThat(topN) - .hasSize(4) - .extracting(ScrollingEntity::getA) - .containsExactly("A0", "B0", "C0", "D0"); - } - - @Test - void forwardWithDuplicatesManualIteration(@Autowired ScrollingRepository repository) { - - var duplicates = repository.findAllByAOrderById("D0"); - assertThat(duplicates).hasSize(2); - - var window = repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, ScrollPosition.keyset()); - assertThat(window.hasNext()).isTrue(); - assertThat(window) - .hasSize(4) - .extracting(Function.identity()) - .satisfies(e -> assertThat(e.getId()).isEqualTo(duplicates.get(0).getId()), Index.atIndex(3)) - .extracting(ScrollingEntity::getA) - .containsExactly("A0", "B0", "C0", "D0"); - - window = repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, window.positionAt(window.size() - 1)); - assertThat(window.hasNext()).isTrue(); - assertThat(window) - .hasSize(4) - .extracting(Function.identity()) - .satisfies(e -> assertThat(e.getId()).isEqualTo(duplicates.get(1).getId()), Index.atIndex(0)) - .extracting(ScrollingEntity::getA) - .containsExactly("D0", "E0", "F0", "G0"); - - window = repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, window.positionAt(window.size() - 1)); - assertThat(window.isLast()).isTrue(); - assertThat(window).extracting(ScrollingEntity::getA) - .containsExactly("H0", "I0"); - } - - @Test - @Tag("GH-2726") - void forwardWithFluentQueryByExample(@Autowired ScrollingRepository scrollingRepository) { - ScrollingEntity scrollingEntity = new ScrollingEntity(); - Example example = Example.of(scrollingEntity, ExampleMatcher.matchingAll().withIgnoreNullValues()); - - var window = scrollingRepository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(ScrollPosition.keyset())); - assertThat(window.hasNext()).isTrue(); - assertThat(window) - .hasSize(4) - .extracting(ScrollingEntity::getA) - .containsExactly("A0", "B0", "C0", "D0"); - - ScrollPosition newPosition = ScrollPosition.forward(((KeysetScrollPosition) window.positionAt(window.size() - 1)).getKeys()); - window = scrollingRepository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(newPosition)); - assertThat(window) - .hasSize(4) - .extracting(ScrollingEntity::getA) - .containsExactly("D0", "E0", "F0", "G0"); - - window = scrollingRepository.findTop4By(ScrollingEntity.SORT_BY_C, window.positionAt(window.size() - 1)); - assertThat(window.isLast()).isTrue(); - assertThat(window).extracting(ScrollingEntity::getA) - .containsExactly("H0", "I0"); - } - - @Test - void forwardWithDuplicatesIteratorIteration(@Autowired ScrollingRepository repository) { - - var it = WindowIterator.of(pos -> repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, pos)) - .startingAt(ScrollPosition.keyset()); - var content = new ArrayList(); - while (it.hasNext()) { - var next = it.next(); - content.add(next); + @BeforeAll + static void setupTestData(@Autowired Driver driver, @Autowired BookmarkCapture bookmarkCapture) { + try ( + var session = driver.session(bookmarkCapture.createSessionConfig()); + var transaction = session.beginTransaction() + ) { + ScrollingEntity.createTestDataWithoutDuplicates(transaction); + transaction.commit(); + bookmarkCapture.seedWith(session.lastBookmarks()); + } } - assertThat(content).hasSize(10); - assertThat(content.stream().map(ScrollingEntity::getId) - .distinct().toList()).hasSize(10); - } + @Test + @Tag("GH-2726") + void forwardWithFluentQueryByExample(@Autowired ScrollingRepository scrollingRepository) { + ScrollingEntity scrollingEntity = new ScrollingEntity(); + Example example = Example.of(scrollingEntity, ExampleMatcher.matchingAll().withIgnoreNullValues()); - @Test - void backwardWithDuplicatesManualIteration(@Autowired ScrollingRepository repository) { + var window = scrollingRepository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(ScrollPosition.keyset())); + assertThat(window.hasNext()).isTrue(); + assertThat(window) + .hasSize(4) + .extracting(ScrollingEntity::getA) + .containsExactly("A0", "B0", "C0", "D0"); - // Recreate the last position - var last = repository.findFirstByA("I0"); - var keys = Map.of( - "foobar", Values.value(last.getA()), - "b", Values.value(last.getB()), - Constants.NAME_OF_ADDITIONAL_SORT, Values.value(last.getId().toString()) - ); + ScrollPosition newPosition = ScrollPosition.forward(((KeysetScrollPosition) window.positionAt(window.size() - 1)).getKeys()); + window = scrollingRepository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(newPosition)); + assertThat(window) + .hasSize(4) + .extracting(ScrollingEntity::getA) + .containsExactly("E0", "F0", "G0", "H0"); - var duplicates = repository.findAllByAOrderById("D0"); - assertThat(duplicates).hasSize(2); + window = scrollingRepository.findTop4By(ScrollingEntity.SORT_BY_C, window.positionAt(window.size() - 1)); + assertThat(window.isLast()).isTrue(); + assertThat(window).extracting(ScrollingEntity::getA) + .containsExactly("I0"); + } - var window = repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, ScrollPosition.backward(keys)); - assertThat(window.hasNext()).isTrue(); - assertThat(window) - .hasSize(4) - .extracting(ScrollingEntity::getA) - .containsExactly("F0", "G0", "H0", "I0"); + @Test + void backwardWithFluentQueryByExample(@Autowired ScrollingRepository repository) { - var pos = ((KeysetScrollPosition) window.positionAt(0)); - pos = ScrollPosition.backward(pos.getKeys()); - window = repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, pos); - assertThat(window.hasNext()).isTrue(); - assertThat(window) - .hasSize(4) - .extracting(Function.identity()) - .extracting(ScrollingEntity::getA) - .containsExactly("C0", "D0", "D0", "E0"); + ScrollingEntity scrollingEntity = new ScrollingEntity(); + Example example = Example.of(scrollingEntity, ExampleMatcher.matchingAll().withIgnoreNullValues()); - pos = ((KeysetScrollPosition) window.positionAt(0)); - pos = ScrollPosition.backward(pos.getKeys()); - window = repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, pos); - assertThat(window.isLast()).isTrue(); - assertThat(window).extracting(ScrollingEntity::getA) - .containsExactly("A0", "B0"); - } + var last = repository.findFirstByA("I0"); + var keys = Map.of( + "c", last.getC(), + Constants.NAME_OF_ADDITIONAL_SORT, Values.value(last.getId().toString()) + ); - @Test - void backwardWithFluentQueryByExample(@Autowired ScrollingRepository repository) { + var window = repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(ScrollPosition.backward(keys))); + assertThat(window.hasNext()).isTrue(); + assertThat(window) + .hasSize(4) + .extracting(ScrollingEntity::getA) + .containsExactly("F0", "G0", "H0", "I0"); - ScrollingEntity scrollingEntity = new ScrollingEntity(); - Example example = Example.of(scrollingEntity, ExampleMatcher.matchingAll().withIgnoreNullValues()); + var pos = ((KeysetScrollPosition) window.positionAt(0)); + var nextPos = ScrollPosition.backward(pos.getKeys()); + window = repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(nextPos)); + assertThat(window.hasNext()).isTrue(); + assertThat(window) + .hasSize(4) + .extracting(Function.identity()) + .extracting(ScrollingEntity::getA) + .containsExactly("B0", "C0", "D0", "E0"); - var last = repository.findFirstByA("I0"); - var keys = Map.of( - "c", last.getC(), - Constants.NAME_OF_ADDITIONAL_SORT, Values.value(last.getId().toString()) - ); - - var window = repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(ScrollPosition.backward(keys))); - assertThat(window.hasNext()).isTrue(); - assertThat(window) - .hasSize(4) - .extracting(ScrollingEntity::getA) - .containsExactly("F0", "G0", "H0", "I0"); - - var pos = ((KeysetScrollPosition) window.positionAt(0)); - var nextPos = ScrollPosition.backward(pos.getKeys()); - window = repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(nextPos)); - assertThat(window.hasNext()).isTrue(); - assertThat(window) - .hasSize(4) - .extracting(Function.identity()) - .extracting(ScrollingEntity::getA) - .containsExactly("C0", "D0", "D0", "E0"); - - var nextNextPos = ScrollPosition.backward(((KeysetScrollPosition) window.positionAt(0)).getKeys()); - window = repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(nextNextPos)); - assertThat(window.isLast()).isTrue(); - assertThat(window).extracting(ScrollingEntity::getA) - .containsExactly("A0", "B0"); + var nextNextPos = ScrollPosition.backward(((KeysetScrollPosition) window.positionAt(0)).getKeys()); + window = repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(nextNextPos)); + assertThat(window.isLast()).isTrue(); + assertThat(window).extracting(ScrollingEntity::getA) + .containsExactly("A0"); + } } @Configuration diff --git a/src/test/java/org/springframework/data/neo4j/integration/reactive/ReactiveScrollingIT.java b/src/test/java/org/springframework/data/neo4j/integration/reactive/ReactiveScrollingIT.java index 68e13b535..e67086652 100644 --- a/src/test/java/org/springframework/data/neo4j/integration/reactive/ReactiveScrollingIT.java +++ b/src/test/java/org/springframework/data/neo4j/integration/reactive/ReactiveScrollingIT.java @@ -24,8 +24,13 @@ import java.util.function.Function; import org.assertj.core.data.Index; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.Nested; import org.neo4j.driver.Driver; import org.neo4j.driver.Values; import org.springframework.beans.factory.annotation.Autowired; @@ -48,6 +53,7 @@ import org.springframework.data.neo4j.test.BookmarkCapture; import org.springframework.data.neo4j.test.Neo4jExtension; import org.springframework.data.neo4j.test.Neo4jIntegrationTest; import org.springframework.data.neo4j.test.Neo4jReactiveTestConfiguration; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import org.springframework.transaction.ReactiveTransactionManager; import reactor.test.StepVerifier; @@ -56,221 +62,252 @@ import reactor.test.StepVerifier; * @author Michael J. Simons */ @Neo4jIntegrationTest +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) class ReactiveScrollingIT { @SuppressWarnings("unused") private static Neo4jExtension.Neo4jConnectionSupport neo4jConnectionSupport; - @BeforeAll - static void setupTestData(@Autowired Driver driver, @Autowired BookmarkCapture bookmarkCapture) { - try ( - var session = driver.session(bookmarkCapture.createSessionConfig()); - var transaction = session.beginTransaction() - ) { - ScrollingEntity.createTestData(transaction); - transaction.commit(); - bookmarkCapture.seedWith(session.lastBookmarks()); + @Nested + @SpringJUnitConfig(Config.class) + @DisplayName("Scroll with derived finder method") + class ScrollWithDerivedFinderMethod { + + @BeforeAll + static void setupTestData(@Autowired Driver driver, @Autowired BookmarkCapture bookmarkCapture) { + try ( + var session = driver.session(bookmarkCapture.createSessionConfig()); + var transaction = session.beginTransaction() + ) { + ScrollingEntity.createTestData(transaction); + transaction.commit(); + bookmarkCapture.seedWith(session.lastBookmarks()); + } + } + + @Test + @Order(1) + void oneColumnSortNoScroll(@Autowired ReactiveScrollingRepository repository) { + + repository.findTop4ByOrderByB() + .map(ScrollingEntity::getA) + .as(StepVerifier::create) + .expectNext("A0", "B0", "C0", "D0") + .verifyComplete(); + } + + @Order(2) + @Test + void forwardWithDuplicatesManualIteration(@Autowired ReactiveScrollingRepository repository) { + + var duplicates = new ArrayList(); + repository.findAllByAOrderById("D0").as(StepVerifier::create) + .recordWith(() -> duplicates) + .expectNextCount(2) + .verifyComplete(); + + var windowContainer = new AtomicReference>(); + repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, ScrollPosition.keyset()) + .as(StepVerifier::create) + .consumeNextWith(windowContainer::set) + .verifyComplete(); + var window = windowContainer.get(); + assertThat(window.hasNext()).isTrue(); + assertThat(window) + .hasSize(4) + .extracting(Function.identity()) + .satisfies(e -> assertThat(e.getId()).isEqualTo(duplicates.get(0).getId()), Index.atIndex(3)) + .extracting(ScrollingEntity::getA) + .containsExactly("A0", "B0", "C0", "D0"); + + repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, window.positionAt(window.size() - 1)) + .as(StepVerifier::create) + .consumeNextWith(windowContainer::set) + .verifyComplete(); + window = windowContainer.get(); + assertThat(window.hasNext()).isTrue(); + assertThat(window) + .hasSize(4) + .extracting(Function.identity()) + .satisfies(e -> assertThat(e.getId()).isEqualTo(duplicates.get(1).getId()), Index.atIndex(0)) + .extracting(ScrollingEntity::getA) + .containsExactly("D0", "E0", "F0", "G0"); + + repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, window.positionAt(window.size() - 1)) + .as(StepVerifier::create) + .consumeNextWith(windowContainer::set) + .verifyComplete(); + window = windowContainer.get(); + assertThat(window.isLast()).isTrue(); + assertThat(window).extracting(ScrollingEntity::getA) + .containsExactly("H0", "I0"); + } + + @Test + @Order(3) + void backwardWithDuplicatesManualIteration(@Autowired ReactiveScrollingRepository repository) { + + // Recreate the last position + var last = repository.findFirstByA("I0").block(); + var keys = Map.of( + "foobar", Values.value(last.getA()), + "b", Values.value(last.getB()), + Constants.NAME_OF_ADDITIONAL_SORT, Values.value(last.getId().toString()) + ); + + var duplicates = new ArrayList(); + repository.findAllByAOrderById("D0").as(StepVerifier::create) + .recordWith(() -> duplicates) + .expectNextCount(2) + .verifyComplete(); + + var windowContainer = new AtomicReference>(); + repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, ScrollPosition.backward(keys)) + .as(StepVerifier::create) + .consumeNextWith(windowContainer::set) + .verifyComplete(); + var window = windowContainer.get(); + assertThat(window.hasNext()).isTrue(); + assertThat(window) + .hasSize(4) + .extracting(ScrollingEntity::getA) + .containsExactly("F0", "G0", "H0", "I0"); + + var pos = ((KeysetScrollPosition) window.positionAt(0)); + pos = ScrollPosition.backward(pos.getKeys()); + repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, pos) + .as(StepVerifier::create) + .consumeNextWith(windowContainer::set) + .verifyComplete(); + window = windowContainer.get(); + assertThat(window.hasNext()).isTrue(); + assertThat(window) + .hasSize(4) + .extracting(Function.identity()) + .extracting(ScrollingEntity::getA) + .containsExactly("C0", "D0", "D0", "E0"); + + pos = ((KeysetScrollPosition) window.positionAt(0)); + pos = ScrollPosition.backward(pos.getKeys()); + repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, pos) + .as(StepVerifier::create) + .consumeNextWith(windowContainer::set) + .verifyComplete(); + window = windowContainer.get(); + assertThat(window.isLast()).isTrue(); + assertThat(window).extracting(ScrollingEntity::getA) + .containsExactly("A0", "B0"); } } - @Test - void oneColumnSortNoScroll(@Autowired ReactiveScrollingRepository repository) { + @Nested + @SpringJUnitConfig(Config.class) + @DisplayName("ScrollWithExampleApi") + class ScrollWithExampleApi { - repository.findTop4ByOrderByB() - .map(ScrollingEntity::getA) - .as(StepVerifier::create) - .expectNext("A0", "B0", "C0", "D0"); - } + @BeforeAll + static void setupTestData(@Autowired Driver driver, @Autowired BookmarkCapture bookmarkCapture) { + try ( + var session = driver.session(bookmarkCapture.createSessionConfig()); + var transaction = session.beginTransaction() + ) { + ScrollingEntity.createTestDataWithoutDuplicates(transaction); + transaction.commit(); + bookmarkCapture.seedWith(session.lastBookmarks()); + } + } - @Test - void forwardWithDuplicatesManualIteration(@Autowired ReactiveScrollingRepository repository) { + @Test + @Order(4) + @Tag("GH-2726") + void forwardWithFluentQueryByExample(@Autowired ReactiveScrollingRepository repository) { + ScrollingEntity scrollingEntity = new ScrollingEntity(); + Example example = Example.of(scrollingEntity, ExampleMatcher.matchingAll().withIgnoreNullValues()); - var duplicates = new ArrayList(); - repository.findAllByAOrderById("D0").as(StepVerifier::create) - .recordWith(() -> duplicates) - .expectNextCount(2) - .verifyComplete(); + var windowContainer = new AtomicReference>(); + repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(ScrollPosition.keyset())) + .as(StepVerifier::create) + .consumeNextWith(windowContainer::set) + .verifyComplete(); + var window = windowContainer.get(); + assertThat(window.hasNext()).isTrue(); + assertThat(window) + .hasSize(4) + .extracting(ScrollingEntity::getA) + .containsExactly("A0", "B0", "C0", "D0"); - var windowContainer = new AtomicReference>(); - repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, ScrollPosition.keyset()) - .as(StepVerifier::create) - .consumeNextWith(windowContainer::set) - .verifyComplete(); - var window = windowContainer.get(); - assertThat(window.hasNext()).isTrue(); - assertThat(window) - .hasSize(4) - .extracting(Function.identity()) - .satisfies(e -> assertThat(e.getId()).isEqualTo(duplicates.get(0).getId()), Index.atIndex(3)) - .extracting(ScrollingEntity::getA) - .containsExactly("A0", "B0", "C0", "D0"); + ScrollPosition nextScrollPosition = window.positionAt(window.size() - 1); + repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(nextScrollPosition)) + .as(StepVerifier::create) + .consumeNextWith(windowContainer::set) + .verifyComplete(); + window = windowContainer.get(); + assertThat(window.hasNext()).isTrue(); + assertThat(window) + .hasSize(4) + .extracting(ScrollingEntity::getA) + .containsExactly("E0", "F0", "G0", "H0"); - repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, window.positionAt(window.size() - 1)) - .as(StepVerifier::create) - .consumeNextWith(windowContainer::set) - .verifyComplete(); - window = windowContainer.get(); - assertThat(window.hasNext()).isTrue(); - assertThat(window) - .hasSize(4) - .extracting(Function.identity()) - .satisfies(e -> assertThat(e.getId()).isEqualTo(duplicates.get(1).getId()), Index.atIndex(0)) - .extracting(ScrollingEntity::getA) - .containsExactly("D0", "E0", "F0", "G0"); + ScrollPosition nextNextScrollPosition = window.positionAt(window.size() - 1); + repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(nextNextScrollPosition)) + .as(StepVerifier::create) + .consumeNextWith(windowContainer::set) + .verifyComplete(); + window = windowContainer.get(); + assertThat(window.isLast()).isTrue(); + assertThat(window).extracting(ScrollingEntity::getA) + .containsExactly("I0"); + } - repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, window.positionAt(window.size() - 1)) - .as(StepVerifier::create) - .consumeNextWith(windowContainer::set) - .verifyComplete(); - window = windowContainer.get(); - assertThat(window.isLast()).isTrue(); - assertThat(window).extracting(ScrollingEntity::getA) - .containsExactly("H0", "I0"); - } + @Test + @Order(5) + @Tag("GH-2726") + void backwardWithFluentQueryByExample(@Autowired ReactiveScrollingRepository repository) { - @Test - void backwardWithDuplicatesManualIteration(@Autowired ReactiveScrollingRepository repository) { + Example example = Example.of(new ScrollingEntity(), ExampleMatcher.matchingAll().withIgnoreNullValues()); - // Recreate the last position - var last = repository.findFirstByA("I0").block(); - var keys = Map.of( - "foobar", Values.value(last.getA()), - "b", Values.value(last.getB()), - Constants.NAME_OF_ADDITIONAL_SORT, Values.value(last.getId().toString()) - ); + // Recreate the last position + var last = repository.findFirstByA("I0").block(); + var keys = Map.of( + "c", Values.value(last.getC()), + Constants.NAME_OF_ADDITIONAL_SORT, Values.value(last.getId().toString()) + ); - var duplicates = new ArrayList(); - repository.findAllByAOrderById("D0").as(StepVerifier::create) - .recordWith(() -> duplicates) - .expectNextCount(2) - .verifyComplete(); + var windowContainer = new AtomicReference>(); + repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(ScrollPosition.backward(keys))) + .as(StepVerifier::create) + .consumeNextWith(windowContainer::set) + .verifyComplete(); + var window = windowContainer.get(); + assertThat(window.hasNext()).isTrue(); + assertThat(window) + .hasSize(4) + .extracting(ScrollingEntity::getA) + .containsExactly("F0", "G0", "H0", "I0"); - var windowContainer = new AtomicReference>(); - repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, ScrollPosition.backward(keys)) - .as(StepVerifier::create) - .consumeNextWith(windowContainer::set) - .verifyComplete(); - var window = windowContainer.get(); - assertThat(window.hasNext()).isTrue(); - assertThat(window) - .hasSize(4) - .extracting(ScrollingEntity::getA) - .containsExactly("F0", "G0", "H0", "I0"); + var nextPos = ScrollPosition.backward(((KeysetScrollPosition) window.positionAt(0)).getKeys()); + repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(nextPos)) + .as(StepVerifier::create) + .consumeNextWith(windowContainer::set) + .verifyComplete(); + window = windowContainer.get(); + assertThat(window.hasNext()).isTrue(); + assertThat(window) + .hasSize(4) + .extracting(Function.identity()) + .extracting(ScrollingEntity::getA) + .containsExactly("B0", "C0", "D0", "E0"); - var pos = ((KeysetScrollPosition) window.positionAt(0)); - pos = ScrollPosition.backward(pos.getKeys()); - repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, pos) - .as(StepVerifier::create) - .consumeNextWith(windowContainer::set) - .verifyComplete(); - window = windowContainer.get(); - assertThat(window.hasNext()).isTrue(); - assertThat(window) - .hasSize(4) - .extracting(Function.identity()) - .extracting(ScrollingEntity::getA) - .containsExactly("C0", "D0", "D0", "E0"); - - pos = ((KeysetScrollPosition) window.positionAt(0)); - pos = ScrollPosition.backward(pos.getKeys()); - repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, pos) - .as(StepVerifier::create) - .consumeNextWith(windowContainer::set) - .verifyComplete(); - window = windowContainer.get(); - assertThat(window.isLast()).isTrue(); - assertThat(window).extracting(ScrollingEntity::getA) - .containsExactly("A0", "B0"); - } - - @Test - @Tag("GH-2726") - void forwardWithFluentQueryByExample(@Autowired ReactiveScrollingRepository repository) { - ScrollingEntity scrollingEntity = new ScrollingEntity(); - Example example = Example.of(scrollingEntity, ExampleMatcher.matchingAll().withIgnoreNullValues()); - - var windowContainer = new AtomicReference>(); - repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(ScrollPosition.keyset())) - .as(StepVerifier::create) - .consumeNextWith(windowContainer::set) - .verifyComplete(); - var window = windowContainer.get(); - assertThat(window.hasNext()).isTrue(); - assertThat(window) - .hasSize(4) - .extracting(ScrollingEntity::getA) - .containsExactly("A0", "B0", "C0", "D0"); - - ScrollPosition nextScrollPosition = window.positionAt(window.size() - 1); - repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(nextScrollPosition)) - .as(StepVerifier::create) - .consumeNextWith(windowContainer::set) - .verifyComplete(); - window = windowContainer.get(); - assertThat(window.hasNext()).isTrue(); - assertThat(window) - .hasSize(4) - .extracting(ScrollingEntity::getA) - .containsExactly("D0", "E0", "F0", "G0"); - - ScrollPosition nextNextScrollPosition = window.positionAt(window.size() - 1); - repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(nextNextScrollPosition)) - .as(StepVerifier::create) - .consumeNextWith(windowContainer::set) - .verifyComplete(); - window = windowContainer.get(); - assertThat(window.isLast()).isTrue(); - assertThat(window).extracting(ScrollingEntity::getA) - .containsExactly("H0", "I0"); - } - - @Test - @Tag("GH-2726") - void backwardWithFluentQueryByExample(@Autowired ReactiveScrollingRepository repository) { - - Example example = Example.of(new ScrollingEntity(), ExampleMatcher.matchingAll().withIgnoreNullValues()); - - // Recreate the last position - var last = repository.findFirstByA("I0").block(); - var keys = Map.of( - "c", Values.value(last.getC()), - Constants.NAME_OF_ADDITIONAL_SORT, Values.value(last.getId().toString()) - ); - - var windowContainer = new AtomicReference>(); - repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(ScrollPosition.backward(keys))) - .as(StepVerifier::create) - .consumeNextWith(windowContainer::set) - .verifyComplete(); - var window = windowContainer.get(); - assertThat(window.hasNext()).isTrue(); - assertThat(window) - .hasSize(4) - .extracting(ScrollingEntity::getA) - .containsExactly("F0", "G0", "H0", "I0"); - - var nextPos = ScrollPosition.backward(((KeysetScrollPosition) window.positionAt(0)).getKeys()); - repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(nextPos)) - .as(StepVerifier::create) - .consumeNextWith(windowContainer::set) - .verifyComplete(); - window = windowContainer.get(); - assertThat(window.hasNext()).isTrue(); - assertThat(window) - .hasSize(4) - .extracting(Function.identity()) - .extracting(ScrollingEntity::getA) - .containsExactly("C0", "D0", "D0", "E0"); - - var nextNextPos = ScrollPosition.backward(((KeysetScrollPosition) window.positionAt(0)).getKeys()); - repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(nextNextPos)) - .as(StepVerifier::create) - .consumeNextWith(windowContainer::set) - .verifyComplete(); - window = windowContainer.get(); - assertThat(window.isLast()).isTrue(); - assertThat(window).extracting(ScrollingEntity::getA) - .containsExactly("A0", "B0"); + var nextNextPos = ScrollPosition.backward(((KeysetScrollPosition) window.positionAt(0)).getKeys()); + repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(nextNextPos)) + .as(StepVerifier::create) + .consumeNextWith(windowContainer::set) + .verifyComplete(); + window = windowContainer.get(); + assertThat(window.isLast()).isTrue(); + assertThat(window).extracting(ScrollingEntity::getA) + .containsExactly("A0"); + } } @Configuration diff --git a/src/test/java/org/springframework/data/neo4j/integration/shared/common/ScrollingEntity.java b/src/test/java/org/springframework/data/neo4j/integration/shared/common/ScrollingEntity.java index b995fcd65..d15fb7d87 100644 --- a/src/test/java/org/springframework/data/neo4j/integration/shared/common/ScrollingEntity.java +++ b/src/test/java/org/springframework/data/neo4j/integration/shared/common/ScrollingEntity.java @@ -40,7 +40,7 @@ public class ScrollingEntity { public static final Sort SORT_BY_C = Sort.by(Sort.Order.asc("c")); public static void createTestData(QueryRunner queryRunner) { - queryRunner.run("MATCH (n) DETACH DELETE n"); + queryRunner.run("MATCH (n) DETACH DELETE n").consume(); queryRunner.run(""" UNWIND (range(0, 8) + [3]) AS i WITH i, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' AS letters CREATE (n:ScrollingEntity { @@ -50,7 +50,21 @@ public class ScrollingEntity { c: (localdatetime() + duration({ days: i }) + duration({ seconds: i * toInteger(rand()*10) })) }) RETURN n - """); + """).consume(); + } + + public static void createTestDataWithoutDuplicates(QueryRunner queryRunner) { + queryRunner.run("MATCH (n) DETACH DELETE n").consume(); + queryRunner.run(""" + UNWIND (range(0, 8)) AS i WITH i, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' AS letters + CREATE (n:ScrollingEntity { + id: randomUUID(), + foobar: (substring(letters, (toInteger(i) % 26), 1) + (i / 26)), + b: i, + c: (localdatetime() + duration({ days: i }) + duration({ seconds: i * toInteger(rand()*10) })) + }) + RETURN n + """).consume(); } @Id