Skip to content

Commit 27de368

Browse files
committed
Fix reverse keyset scrolling.
We now use gt/lt instead of gte/lte to avoid duplicates during reverse keyset scrolling. Closes #4343
1 parent 244b949 commit 27de368

File tree

2 files changed

+54
-18
lines changed

2 files changed

+54
-18
lines changed

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

+46-17
Original file line numberDiff line numberDiff line change
@@ -63,18 +63,18 @@ static <T> Window<T> createWindow(Query query, List<T> result, Class<?> sourceTy
6363
KeysetScrollPosition keyset = query.getKeyset();
6464
KeysetScrollDirector director = KeysetScrollDirector.of(keyset.getDirection());
6565

66-
director.postPostProcessResults(result);
66+
List<T> resultsToUse = director.postPostProcessResults(result, query.getLimit());
6767

6868
IntFunction<KeysetScrollPosition> positionFunction = value -> {
6969

70-
T last = result.get(value);
70+
T last = resultsToUse.get(value);
7171
Entity<T> entity = operations.forEntity(last);
7272

7373
Map<String, Object> keys = entity.extractKeys(sortObject, sourceType);
7474
return KeysetScrollPosition.of(keys);
7575
};
7676

77-
return createWindow(result, query.getLimit(), positionFunction);
77+
return Window.from(resultsToUse, positionFunction, hasMoreElements(result, query.getLimit()));
7878
}
7979

8080
static <T> Window<T> createWindow(List<T> result, int limit, IntFunction<? extends ScrollPosition> positionFunction) {
@@ -187,13 +187,14 @@ public Document createQuery(KeysetScrollPosition keyset, Document queryObject, D
187187
return queryObject;
188188
}
189189

190-
public <T> void postPostProcessResults(List<T> result) {
191-
192-
}
193-
194190
protected String getComparator(int sortOrder) {
195191
return sortOrder == 1 ? "$gt" : "$lt";
196192
}
193+
194+
protected <T> List<T> postPostProcessResults(List<T> list, int limit) {
195+
return getFirst(limit, list);
196+
}
197+
197198
}
198199

199200
/**
@@ -219,20 +220,48 @@ public Document getSortObject(String idPropertyName, Query query) {
219220
}
220221

221222
@Override
222-
protected String getComparator(int sortOrder) {
223+
public <T> List<T> postPostProcessResults(List<T> list, int limit) {
223224

224-
// use gte/lte to include the object at the cursor/keyset so that
225-
// we can include it in the result to check whether there is a next object.
226-
// It needs to be filtered out later on.
227-
return sortOrder == 1 ? "$gte" : "$lte";
228-
}
229-
230-
@Override
231-
public <T> void postPostProcessResults(List<T> result) {
232225
// flip direction of the result list as we need to accomodate for the flipped sort order for proper offset
233226
// querying.
234-
Collections.reverse(result);
227+
Collections.reverse(list);
228+
229+
return getLast(limit, list);
235230
}
231+
236232
}
237233

234+
/**
235+
* Return the first {@code count} items from the list.
236+
*
237+
* @param count
238+
* @param list
239+
* @return
240+
* @param <T>
241+
*/
242+
static <T> List<T> getFirst(int count, List<T> list) {
243+
244+
if (count > 0 && list.size() > count) {
245+
return list.subList(0, count);
246+
}
247+
248+
return list;
249+
}
250+
251+
/**
252+
* Return the last {@code count} items from the list.
253+
*
254+
* @param count
255+
* @param list
256+
* @return
257+
* @param <T>
258+
*/
259+
static <T> List<T> getLast(int count, List<T> list) {
260+
261+
if (count > 0 && list.size() > count) {
262+
return list.subList(list.size() - (count), list.size());
263+
}
264+
265+
return list;
266+
}
238267
}

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

+8-1
Original file line numberDiff line numberDiff line change
@@ -184,11 +184,18 @@ void shouldAllowReverseSort() {
184184
assertThat(window.isLast()).isTrue();
185185
assertThat(window).hasSize(6);
186186

187-
KeysetScrollPosition scrollPosition = (KeysetScrollPosition) window.positionAt(window.size() - 1);
187+
KeysetScrollPosition scrollPosition = (KeysetScrollPosition) window.positionAt(window.size() - 2);
188188
KeysetScrollPosition reversePosition = KeysetScrollPosition.of(scrollPosition.getKeys(), Direction.Backward);
189189

190190
window = template.scroll(q.with(reversePosition).limit(2), Person.class);
191191

192+
assertThat(window).hasSize(2);
193+
assertThat(window).containsOnly(jane_42, john20);
194+
assertThat(window.hasNext()).isTrue();
195+
assertThat(window.isLast()).isFalse();
196+
197+
window = template.scroll(q.with(window.positionAt(0)).limit(2), Person.class);
198+
192199
assertThat(window).hasSize(2);
193200
assertThat(window).containsOnly(john20, john40_1);
194201
assertThat(window.hasNext()).isTrue();

0 commit comments

Comments
 (0)