From 05c38b819f3c8eedad8388d6c377e85a5db610ca Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 13 Jun 2023 10:55:52 +0200 Subject: [PATCH] Retain scroll direction across keyset scroll requests. Closes #4413 --- .../data/mongodb/core/ScrollUtils.java | 5 +- .../core/MongoTemplateScrollTests.java | 27 ++++++++- .../mongodb/core/ScrollUtilsUnitTests.java | 56 +++++++++++++++++++ 3 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ScrollUtilsUnitTests.java diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ScrollUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ScrollUtils.java index d568d6e5d..1f193f2e7 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ScrollUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ScrollUtils.java @@ -61,7 +61,8 @@ class ScrollUtils { Document sortObject = query.getSortObject(); KeysetScrollPosition keyset = query.getKeyset(); - KeysetScrollDirector director = KeysetScrollDirector.of(keyset.getDirection()); + Direction direction = keyset.getDirection(); + KeysetScrollDirector director = KeysetScrollDirector.of(direction); List resultsToUse = director.postPostProcessResults(result, query.getLimit()); @@ -71,7 +72,7 @@ class ScrollUtils { Entity entity = operations.forEntity(last); Map keys = entity.extractKeys(sortObject, sourceType); - return ScrollPosition.forward(keys); + return ScrollPosition.of(keys, direction); }; return Window.from(resultsToUse, positionFunction, hasMoreElements(result, query.getLimit())); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateScrollTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateScrollTests.java index 1c36ece2b..cd5d5a0dd 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateScrollTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateScrollTests.java @@ -190,9 +190,30 @@ class MongoTemplateScrollTests { window = template.scroll(q.with(window.positionAt(0)).limit(2), Person.class); assertThat(window).hasSize(2); - assertThat(window).containsOnly(john20, john40_1); - assertThat(window.hasNext()).isTrue(); - assertThat(window.isLast()).isFalse(); + assertThat(window).containsOnly(jane_20, jane_40); + assertThat(window.hasNext()).isFalse(); + assertThat(window.isLast()).isTrue(); + } + + @Test // GH-4413 + void shouldAllowInitialBackwardSort() { + + Person jane_20 = new Person("Jane", 20); + Person jane_40 = new Person("Jane", 40); + Person jane_42 = new Person("Jane", 42); + Person john20 = new Person("John", 20); + Person john40_1 = new Person("John", 40); + Person john40_2 = new Person("John", 40); + + template.insertAll(Arrays.asList(john20, john40_1, john40_2, jane_20, jane_40, jane_42)); + Query q = new Query(where("firstName").regex("J.*")).with(Sort.by("firstName", "age")); + q.with(ScrollPosition.keyset().backward()).limit(3); + + Window window = template.scroll(q, Person.class); + assertThat(window).containsExactly(john20, john40_1, john40_2); + + window = template.scroll(q.with(window.positionAt(0)).limit(3), Person.class); + assertThat(window).containsExactly(jane_20, jane_40, jane_42); } @ParameterizedTest // GH-4308 diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ScrollUtilsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ScrollUtilsUnitTests.java new file mode 100644 index 000000000..79afc5d22 --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ScrollUtilsUnitTests.java @@ -0,0 +1,56 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.core; + +import static org.assertj.core.api.AssertionsForClassTypes.*; +import static org.mockito.Mockito.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.KeysetScrollPosition; +import org.springframework.data.domain.ScrollPosition; +import org.springframework.data.domain.Window; +import org.springframework.data.mongodb.core.EntityOperations.Entity; +import org.springframework.data.mongodb.core.query.Query; + +/** + * Unit tests for {@link ScrollUtils}. + * + * @author Mark Paluch + */ +class ScrollUtilsUnitTests { + + @Test // GH-4413 + void positionShouldRetainScrollDirection() { + + Query query = new Query(); + query.with(ScrollPosition.keyset().backward()); + EntityOperations entityOperationsMock = mock(EntityOperations.class); + Entity entityMock = mock(Entity.class); + + when(entityOperationsMock.forEntity(any())).thenReturn(entityMock); + when(entityMock.extractKeys(any(), any())).thenReturn(Map.of("k", "v")); + + Window window = ScrollUtils.createWindow(query, new ArrayList<>(List.of(1, 2, 3)), Integer.class, + entityOperationsMock); + + assertThat(window.positionAt(0)).isInstanceOf(KeysetScrollPosition.class); + assertThat(((KeysetScrollPosition) window.positionAt(0)).scrollsBackward()).isTrue(); + } +}