From bc997aae1dc68f0dd32046def9a9a19925908d7f Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 10 Feb 2026 12:09:18 +0100 Subject: [PATCH] ScrollUtils must not modify source query when creating scroll filter. This commit makes sure to create a new filter query object if needed instead of modifying the source query. In doing so we retain the original source and prevent errors when attempting to alter immutable sources. Closes #5159 Original pull request: #5160 --- .../data/mongodb/core/ScrollUtils.java | 11 +++++++---- .../AbstractPersonRepositoryIntegrationTests.java | 13 +++++++++++++ .../data/mongodb/repository/PersonRepository.java | 9 +++++++++ 3 files changed, 29 insertions(+), 4 deletions(-) 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 4a4552caf..65ac54d54 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 @@ -29,6 +29,7 @@ import org.springframework.data.domain.ScrollPosition.Direction; import org.springframework.data.domain.Window; import org.springframework.data.mongodb.core.EntityOperations.Entity; import org.springframework.data.mongodb.core.query.Query; +import org.springframework.lang.CheckReturnValue; import org.springframework.util.Assert; /** @@ -49,7 +50,6 @@ class ScrollUtils { */ static KeysetScrollQuery createKeysetPaginationQuery(Query query, String idPropertyName) { - KeysetScrollPosition keyset = query.getKeyset(); Assert.notNull(keyset, "Query.keyset must not be null"); @@ -145,6 +145,7 @@ class ScrollUtils { return fieldsObject; } + @CheckReturnValue public Document createQuery(KeysetScrollPosition keyset, Document queryObject, Document sortObject) { Map keysetValues = keyset.getKeys(); @@ -189,11 +190,13 @@ class ScrollUtils { } } - if (!or.isEmpty()) { - queryObject.put("$or", or); + if (or.isEmpty()) { + return queryObject; } - return queryObject; + Document filterQuery = new Document(queryObject); + filterQuery.put("$or", or); + return filterQuery; } protected String getComparator(int sortOrder) { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java index b6cac676b..4d3f6a792 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java @@ -232,6 +232,19 @@ public abstract class AbstractPersonRepositoryIntegrationTests implements Dirtie assertThat(page).contains(carter); } + @Test // GH-5159 + void allowsToScrollThroughEntriesWithoutAnyCriteria() { + + Window page = repository.findAllBy(Limit.of(2), ScrollPosition.keyset()); + assertThat(page.isLast()).isFalse(); + + while (!page.isLast()) { + + assertThat(page.size()).isEqualTo(2); + page = repository.findAllBy(Limit.of(2), page.positionAt(1)); + } + } + @Test // GH-4397 void appliesLimitToScrollingCorrectly() { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java index f46c9153d..e0ec8b9ec 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java @@ -126,6 +126,15 @@ public interface PersonRepository extends MongoRepository, Query @Query("{'age' : { '$lt' : ?0 } }") List findByAgeLessThan(int age, Sort sort); + /** + * Returns a scroll of all {@link Person}s in natural order. + * + * @param limit window size + * @param scrollPosition scroll position start from + * @return + */ + Window findAllBy(Limit limit, ScrollPosition scrollPosition); + /** * Returns a scroll of {@link Person}s with a lastname matching the given one (*-wildcards supported). *