Skip to content

Commit ab93e61

Browse files
committed
Retain scroll direction across keyset scroll requests.
Closes #4413
1 parent 0c9e9c6 commit ab93e61

File tree

3 files changed

+84
-7
lines changed

3 files changed

+84
-7
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ScrollUtils.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ static <T> Window<T> createWindow(Query query, List<T> result, Class<?> sourceTy
6161

6262
Document sortObject = query.getSortObject();
6363
KeysetScrollPosition keyset = query.getKeyset();
64-
KeysetScrollDirector director = KeysetScrollDirector.of(keyset.getDirection());
64+
Direction direction = keyset.getDirection();
65+
KeysetScrollDirector director = KeysetScrollDirector.of(direction);
6566

6667
List<T> resultsToUse = director.postPostProcessResults(result, query.getLimit());
6768

@@ -71,7 +72,7 @@ static <T> Window<T> createWindow(Query query, List<T> result, Class<?> sourceTy
7172
Entity<T> entity = operations.forEntity(last);
7273

7374
Map<String, Object> keys = entity.extractKeys(sortObject, sourceType);
74-
return ScrollPosition.forward(keys);
75+
return ScrollPosition.of(keys, direction);
7576
};
7677

7778
return Window.from(resultsToUse, positionFunction, hasMoreElements(result, query.getLimit()));

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateScrollTests.java

+25-5
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,30 @@ void shouldAllowReverseSort() {
193193
window = template.scroll(q.with(window.positionAt(0)).limit(2), Person.class);
194194

195195
assertThat(window).hasSize(2);
196-
assertThat(window).containsOnly(john20, john40_1);
197-
assertThat(window.hasNext()).isTrue();
198-
assertThat(window.isLast()).isFalse();
196+
assertThat(window).containsOnly(jane_20, jane_40);
197+
assertThat(window.hasNext()).isFalse();
198+
assertThat(window.isLast()).isTrue();
199+
}
200+
201+
@Test // GH-4413
202+
void shouldAllowInitialBackwardSort() {
203+
204+
Person jane_20 = new Person("Jane", 20);
205+
Person jane_40 = new Person("Jane", 40);
206+
Person jane_42 = new Person("Jane", 42);
207+
Person john20 = new Person("John", 20);
208+
Person john40_1 = new Person("John", 40);
209+
Person john40_2 = new Person("John", 40);
210+
211+
template.insertAll(Arrays.asList(john20, john40_1, john40_2, jane_20, jane_40, jane_42));
212+
Query q = new Query(where("firstName").regex("J.*")).with(Sort.by("firstName", "age"));
213+
q.with(ScrollPosition.keyset().backward()).limit(3);
214+
215+
Window<Person> window = template.scroll(q, Person.class);
216+
assertThat(window).containsExactly(john20, john40_1, john40_2);
217+
218+
window = template.scroll(q.with(window.positionAt(0)).limit(3), Person.class);
219+
assertThat(window).containsExactly(jane_20, jane_40, jane_42);
199220
}
200221

201222
@ParameterizedTest // GH-4308
@@ -276,8 +297,7 @@ static Stream<Arguments> positions() {
276297
return Stream.of(args(ScrollPosition.keyset(), Person.class, Function.identity()), //
277298
args(ScrollPosition.keyset(), Document.class, MongoTemplateScrollTests::toDocument), //
278299
args(ScrollPosition.offset(), Person.class, Function.identity()), //
279-
args(ScrollPosition.offset(), PersonDtoProjection.class,
280-
MongoTemplateScrollTests::toPersonDtoProjection), //
300+
args(ScrollPosition.offset(), PersonDtoProjection.class, MongoTemplateScrollTests::toPersonDtoProjection), //
281301
args(ScrollPosition.offset(), PersonInterfaceProjection.class,
282302
MongoTemplateScrollTests::toPersonInterfaceProjection, MongoTemplateScrollTests::compareProxies));
283303
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.mongodb.core;
17+
18+
import static org.assertj.core.api.AssertionsForClassTypes.*;
19+
import static org.mockito.Mockito.*;
20+
21+
import java.util.ArrayList;
22+
import java.util.List;
23+
import java.util.Map;
24+
25+
import org.junit.jupiter.api.Test;
26+
import org.springframework.data.domain.KeysetScrollPosition;
27+
import org.springframework.data.domain.ScrollPosition;
28+
import org.springframework.data.domain.Window;
29+
import org.springframework.data.mongodb.core.EntityOperations.Entity;
30+
import org.springframework.data.mongodb.core.query.Query;
31+
32+
/**
33+
* Unit tests for {@link ScrollUtils}.
34+
*
35+
* @author Mark Paluch
36+
*/
37+
class ScrollUtilsUnitTests {
38+
39+
@Test // GH-4413
40+
void positionShouldRetainScrollDirection() {
41+
42+
Query query = new Query();
43+
query.with(ScrollPosition.keyset().backward());
44+
EntityOperations entityOperationsMock = mock(EntityOperations.class);
45+
Entity entityMock = mock(Entity.class);
46+
47+
when(entityOperationsMock.forEntity(any())).thenReturn(entityMock);
48+
when(entityMock.extractKeys(any(), any())).thenReturn(Map.of("k", "v"));
49+
50+
Window<Integer> window = ScrollUtils.createWindow(query, new ArrayList<>(List.of(1, 2, 3)), Integer.class,
51+
entityOperationsMock);
52+
53+
assertThat(window.positionAt(0)).isInstanceOf(KeysetScrollPosition.class);
54+
assertThat(((KeysetScrollPosition) window.positionAt(0)).scrollsBackward()).isTrue();
55+
}
56+
}

0 commit comments

Comments
 (0)