From 9d0afc975a4af7f73abef1d9e4c2c3bbc9518ac5 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Wed, 15 Mar 2023 16:56:41 +0100 Subject: [PATCH] Prevent key extraction if a keyset value is null. Follow the changes in data commons that renamed scroll to window. Also error when a certain scroll position does not allow creating a query out of it because of null values. See: #4308 Original Pull Request: #4317 --- .../data/mongodb/core/EntityOperations.java | 27 ++++++++-- .../mongodb/core/ExecutableFindOperation.java | 9 ++-- .../core/ExecutableFindOperationSupport.java | 4 +- .../data/mongodb/core/MongoOperations.java | 20 +++---- .../data/mongodb/core/MongoTemplate.java | 12 ++--- .../mongodb/core/ReactiveFindOperation.java | 4 +- .../core/ReactiveFindOperationSupport.java | 4 +- .../mongodb/core/ReactiveMongoOperations.java | 14 ++--- .../mongodb/core/ReactiveMongoTemplate.java | 12 ++--- .../data/mongodb/core/ScrollUtils.java | 21 +++++--- .../QuerydslMongoPredicateExecutor.java | 4 +- ...eactiveQuerydslMongoPredicateExecutor.java | 4 +- .../ReactiveSpringDataMongodbQuery.java | 4 +- .../support/SimpleMongoRepository.java | 4 +- .../SimpleReactiveMongoRepository.java | 4 +- .../support/SpringDataMongodbQuery.java | 6 +-- .../core/MongoTemplateScrollTests.java | 52 ++++++++++++++++--- .../ReactiveMongoTemplateScrollTests.java | 10 ++-- ...tractPersonRepositoryIntegrationTests.java | 12 ++--- .../mongodb/repository/PersonRepository.java | 10 ++-- .../ReactiveMongoRepositoryTests.java | 18 +++---- 21 files changed, 159 insertions(+), 96 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityOperations.java index a8e0d8e17..cbf252f1d 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityOperations.java @@ -21,6 +21,7 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.Optional; +import org.bson.BsonNull; import org.bson.Document; import org.springframework.core.convert.ConversionService; import org.springframework.dao.InvalidDataAccessApiUsageException; @@ -471,6 +472,7 @@ class EntityOperations { * @param sortObject * @return * @since 3.1 + * @throws IllegalStateException if a sort key yields {@literal null}. */ Map extractKeys(Document sortObject); @@ -600,7 +602,14 @@ class EntityOperations { keyset.put(ID_FIELD, getId()); for (String key : sortObject.keySet()) { - keyset.put(key, BsonUtils.resolveValue(map, key)); + Object value = BsonUtils.resolveValue(map, key); + + if (value == null) { + throw new IllegalStateException( + String.format("Cannot extract value for key %s because its value is null", key)); + } + + keyset.put(key, value); } return keyset; @@ -756,14 +765,22 @@ class EntityOperations { for (String key : sortObject.keySet()) { + Object value; if (key.indexOf('.') != -1) { // follow the path across nested levels. // TODO: We should have a MongoDB-specific property path abstraction to allow diving into Document. - keyset.put(key, getNestedPropertyValue(key)); + value = getNestedPropertyValue(key); } else { - keyset.put(key, getPropertyValue(key)); + value = getPropertyValue(key); } + + if (value == null) { + throw new IllegalStateException( + String.format("Cannot extract value for key %s because its value is null", key)); + } + + keyset.put(key, value); } return keyset; @@ -774,7 +791,7 @@ class EntityOperations { String[] segments = key.split("\\."); Entity currentEntity = this; - Object currentValue = null; + Object currentValue = BsonNull.VALUE; for (int i = 0; i < segments.length; i++) { @@ -786,7 +803,7 @@ class EntityOperations { } } - return currentValue; + return currentValue != null ? currentValue : BsonNull.VALUE; } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperation.java index e8b38c8e8..5ae201093 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperation.java @@ -20,7 +20,7 @@ import java.util.Optional; import java.util.stream.Stream; import org.springframework.dao.DataAccessException; -import org.springframework.data.domain.Scroll; +import org.springframework.data.domain.Window; import org.springframework.data.domain.ScrollPosition; import org.springframework.data.geo.GeoResults; import org.springframework.data.mongodb.core.query.CriteriaDefinition; @@ -126,16 +126,17 @@ public interface ExecutableFindOperation { Stream stream(); /** - * Return a scroll of elements either starting or resuming at + * Return a window of elements either starting or resuming at * {@link org.springframework.data.domain.ScrollPosition}. * * @param scrollPosition the scroll position. - * @return a scroll of the resulting elements. + * @return a window of the resulting elements. + * @throws IllegalStateException if a potential {@literal KeysetScrollPosition} contains an invalid position. * @since 4.1 * @see org.springframework.data.domain.OffsetScrollPosition * @see org.springframework.data.domain.KeysetScrollPosition */ - Scroll scroll(ScrollPosition scrollPosition); + Window scroll(ScrollPosition scrollPosition); /** * Get the number of matching elements.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupport.java index 81d7557e7..d99cffbe3 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupport.java @@ -21,7 +21,7 @@ import java.util.stream.Stream; import org.bson.Document; import org.springframework.dao.IncorrectResultSizeDataAccessException; -import org.springframework.data.domain.Scroll; +import org.springframework.data.domain.Window; import org.springframework.data.domain.ScrollPosition; import org.springframework.data.mongodb.core.query.NearQuery; import org.springframework.data.mongodb.core.query.Query; @@ -141,7 +141,7 @@ class ExecutableFindOperationSupport implements ExecutableFindOperation { } @Override - public Scroll scroll(ScrollPosition scrollPosition) { + public Window scroll(ScrollPosition scrollPosition) { return template.doScroll(query.with(scrollPosition), domainType, returnType, getCollectionName()); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java index cd019aae6..2d51a56c1 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java @@ -24,7 +24,7 @@ import java.util.stream.Stream; import org.bson.Document; import org.springframework.data.domain.KeysetScrollPosition; -import org.springframework.data.domain.Scroll; +import org.springframework.data.domain.Window; import org.springframework.data.geo.GeoResults; import org.springframework.data.mongodb.core.BulkOperations.BulkMode; import org.springframework.data.mongodb.core.aggregation.Aggregation; @@ -807,7 +807,7 @@ public interface MongoOperations extends FluentMongoOperations { List find(Query query, Class entityClass, String collectionName); /** - * Query for a scroll window of objects of type T from the specified collection.
+ * Query for a window window of objects of type T from the specified collection.
* Make sure to either set {@link Query#skip(long)} or {@link Query#with(KeysetScrollPosition)} along with * {@link Query#limit(int)} to limit large query results for efficient scrolling.
* Result objects are converted from the MongoDB native representation using an instance of {@see MongoConverter}. @@ -817,16 +817,17 @@ public interface MongoOperations extends FluentMongoOperations { * * @param query the query class that specifies the criteria used to find a record and also an optional fields * specification. Must not be {@literal null}. - * @param entityType the parametrized type of the returned list. - * @return the converted scroll. + * @param entityType the parametrized type of the returned window. + * @return the converted window. + * @throws IllegalStateException if a potential {@link Query#getKeyset() KeysetScrollPosition} contains an invalid position. * @since 4.1 * @see Query#with(org.springframework.data.domain.OffsetScrollPosition) * @see Query#with(org.springframework.data.domain.KeysetScrollPosition) */ - Scroll scroll(Query query, Class entityType); + Window scroll(Query query, Class entityType); /** - * Query for a scroll of objects of type T from the specified collection.
+ * Query for a window of objects of type T from the specified collection.
* Make sure to either set {@link Query#skip(long)} or {@link Query#with(KeysetScrollPosition)} along with * {@link Query#limit(int)} to limit large query results for efficient scrolling.
* Result objects are converted from the MongoDB native representation using an instance of {@see MongoConverter}. @@ -836,14 +837,15 @@ public interface MongoOperations extends FluentMongoOperations { * * @param query the query class that specifies the criteria used to find a record and also an optional fields * specification. Must not be {@literal null}. - * @param entityType the parametrized type of the returned list. + * @param entityType the parametrized type of the returned window. * @param collectionName name of the collection to retrieve the objects from. - * @return the converted scroll. + * @return the converted window. + * @throws IllegalStateException if a potential {@link Query#getKeyset() KeysetScrollPosition} contains an invalid position. * @since 4.1 * @see Query#with(org.springframework.data.domain.OffsetScrollPosition) * @see Query#with(org.springframework.data.domain.KeysetScrollPosition) */ - Scroll scroll(Query query, Class entityType, String collectionName); + Window scroll(Query query, Class entityType, String collectionName); /** * Returns a document with the given id mapped onto the given class. The collection the query is ran against will be diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java index 007c20f10..ae2b26def 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java @@ -45,7 +45,7 @@ import org.springframework.dao.OptimisticLockingFailureException; import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.data.convert.EntityReader; import org.springframework.data.domain.OffsetScrollPosition; -import org.springframework.data.domain.Scroll; +import org.springframework.data.domain.Window; import org.springframework.data.geo.Distance; import org.springframework.data.geo.GeoResult; import org.springframework.data.geo.GeoResults; @@ -66,7 +66,7 @@ import org.springframework.data.mongodb.core.QueryOperations.DeleteContext; import org.springframework.data.mongodb.core.QueryOperations.DistinctQueryContext; import org.springframework.data.mongodb.core.QueryOperations.QueryContext; import org.springframework.data.mongodb.core.QueryOperations.UpdateContext; -import org.springframework.data.mongodb.core.ScrollUtils.KeySetCursorQuery; +import org.springframework.data.mongodb.core.ScrollUtils.KeySetScrollQuery; import org.springframework.data.mongodb.core.aggregation.Aggregation; import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext; import org.springframework.data.mongodb.core.aggregation.AggregationOptions; @@ -851,7 +851,7 @@ public class MongoTemplate } @Override - public Scroll scroll(Query query, Class entityType) { + public Window scroll(Query query, Class entityType) { Assert.notNull(entityType, "Entity type must not be null"); @@ -859,11 +859,11 @@ public class MongoTemplate } @Override - public Scroll scroll(Query query, Class entityType, String collectionName) { + public Window scroll(Query query, Class entityType, String collectionName) { return doScroll(query, entityType, entityType, collectionName); } - Scroll doScroll(Query query, Class sourceClass, Class targetClass, String collectionName) { + Window doScroll(Query query, Class sourceClass, Class targetClass, String collectionName) { Assert.notNull(query, "Query must not be null"); Assert.notNull(collectionName, "CollectionName must not be null"); @@ -875,7 +875,7 @@ public class MongoTemplate if (query.hasKeyset()) { - KeySetCursorQuery keysetPaginationQuery = ScrollUtils.createKeysetPaginationQuery(query, + KeySetScrollQuery keysetPaginationQuery = ScrollUtils.createKeysetPaginationQuery(query, operations.getIdPropertyName(sourceClass)); List result = doFind(collectionName, createDelegate(query), keysetPaginationQuery.query(), diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveFindOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveFindOperation.java index 1a81f92a9..3786cdac0 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveFindOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveFindOperation.java @@ -18,7 +18,7 @@ package org.springframework.data.mongodb.core; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import org.springframework.data.domain.Scroll; +import org.springframework.data.domain.Window; import org.springframework.data.domain.ScrollPosition; import org.springframework.data.geo.GeoResult; import org.springframework.data.mongodb.core.query.CriteriaDefinition; @@ -98,7 +98,7 @@ public interface ReactiveFindOperation { * @see org.springframework.data.domain.OffsetScrollPosition * @see org.springframework.data.domain.KeysetScrollPosition */ - Mono> scroll(ScrollPosition scrollPosition); + Mono> scroll(ScrollPosition scrollPosition); /** * Get all matching elements using a {@link com.mongodb.CursorType#TailableAwait tailable cursor}. The stream will diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveFindOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveFindOperationSupport.java index 13894c896..30b8ab092 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveFindOperationSupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveFindOperationSupport.java @@ -20,7 +20,7 @@ import reactor.core.publisher.Mono; import org.bson.Document; import org.springframework.dao.IncorrectResultSizeDataAccessException; -import org.springframework.data.domain.Scroll; +import org.springframework.data.domain.Window; import org.springframework.data.domain.ScrollPosition; import org.springframework.data.mongodb.core.CollectionPreparerSupport.ReactiveCollectionPreparerDelegate; import org.springframework.data.mongodb.core.query.NearQuery; @@ -140,7 +140,7 @@ class ReactiveFindOperationSupport implements ReactiveFindOperation { } @Override - public Mono> scroll(ScrollPosition scrollPosition) { + public Mono> scroll(ScrollPosition scrollPosition) { return template.doScroll(query.with(scrollPosition), domainType, returnType, getCollectionName()); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoOperations.java index d252cb4a4..8030fb8a9 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoOperations.java @@ -26,7 +26,7 @@ import org.bson.Document; import org.reactivestreams.Publisher; import org.reactivestreams.Subscription; import org.springframework.data.domain.KeysetScrollPosition; -import org.springframework.data.domain.Scroll; +import org.springframework.data.domain.Window; import org.springframework.data.geo.GeoResult; import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory; import org.springframework.data.mongodb.core.aggregation.Aggregation; @@ -477,15 +477,16 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations { * @param query the query class that specifies the criteria used to find a record and also an optional fields * specification. Must not be {@literal null}. * @param entityType the parametrized type of the returned list. - * @return {@link Mono} emitting the converted scroll. + * @return {@link Mono} emitting the converted window. + * @throws IllegalStateException if a potential {@link Query#getKeyset() KeysetScrollPosition} contains an invalid position. * @since 4.1 * @see Query#with(org.springframework.data.domain.OffsetScrollPosition) * @see Query#with(org.springframework.data.domain.KeysetScrollPosition) */ - Mono> scroll(Query query, Class entityType); + Mono> scroll(Query query, Class entityType); /** - * Query for a scroll of objects of type T from the specified collection.
+ * Query for a window of objects of type T from the specified collection.
* Make sure to either set {@link Query#skip(long)} or {@link Query#with(KeysetScrollPosition)} along with * {@link Query#limit(int)} to limit large query results for efficient scrolling.
* Result objects are converted from the MongoDB native representation using an instance of {@see MongoConverter}. @@ -497,12 +498,13 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations { * specification. Must not be {@literal null}. * @param entityType the parametrized type of the returned list. * @param collectionName name of the collection to retrieve the objects from. - * @return {@link Mono} emitting the converted scroll window. + * @return {@link Mono} emitting the converted window. + * @throws IllegalStateException if a potential {@link Query#getKeyset() KeysetScrollPosition} contains an invalid position. * @since 4.1 * @see Query#with(org.springframework.data.domain.OffsetScrollPosition) * @see Query#with(org.springframework.data.domain.KeysetScrollPosition) */ - Mono> scroll(Query query, Class entityType, String collectionName); + Mono> scroll(Query query, Class entityType, String collectionName); /** * Returns a document with the given id mapped onto the given class. The collection the query is ran against will be diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java index 6b4266a57..22747d382 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java @@ -59,7 +59,7 @@ import org.springframework.dao.OptimisticLockingFailureException; import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.data.convert.EntityReader; import org.springframework.data.domain.OffsetScrollPosition; -import org.springframework.data.domain.Scroll; +import org.springframework.data.domain.Window; import org.springframework.data.geo.Distance; import org.springframework.data.geo.GeoResult; import org.springframework.data.geo.Metric; @@ -80,7 +80,7 @@ import org.springframework.data.mongodb.core.QueryOperations.DeleteContext; import org.springframework.data.mongodb.core.QueryOperations.DistinctQueryContext; import org.springframework.data.mongodb.core.QueryOperations.QueryContext; import org.springframework.data.mongodb.core.QueryOperations.UpdateContext; -import org.springframework.data.mongodb.core.ScrollUtils.KeySetCursorQuery; +import org.springframework.data.mongodb.core.ScrollUtils.KeySetScrollQuery; import org.springframework.data.mongodb.core.aggregation.Aggregation; import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext; import org.springframework.data.mongodb.core.aggregation.AggregationOptions; @@ -830,7 +830,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati } @Override - public Mono> scroll(Query query, Class entityType) { + public Mono> scroll(Query query, Class entityType) { Assert.notNull(entityType, "Entity type must not be null"); @@ -838,11 +838,11 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati } @Override - public Mono> scroll(Query query, Class entityType, String collectionName) { + public Mono> scroll(Query query, Class entityType, String collectionName) { return doScroll(query, entityType, entityType, collectionName); } - Mono> doScroll(Query query, Class sourceClass, Class targetClass, String collectionName) { + Mono> doScroll(Query query, Class sourceClass, Class targetClass, String collectionName) { Assert.notNull(query, "Query must not be null"); Assert.notNull(collectionName, "CollectionName must not be null"); @@ -853,7 +853,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati if (query.hasKeyset()) { - KeySetCursorQuery keysetPaginationQuery = ScrollUtils.createKeysetPaginationQuery(query, + KeySetScrollQuery keysetPaginationQuery = ScrollUtils.createKeysetPaginationQuery(query, operations.getIdPropertyName(sourceClass)); Mono> result = doFind(collectionName, ReactiveCollectionPreparerDelegate.of(query), 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 6458fa9d2..320bafa4d 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 @@ -20,17 +20,19 @@ import java.util.List; import java.util.Map; import java.util.function.IntFunction; +import org.bson.BsonNull; import org.bson.Document; import org.springframework.data.domain.KeysetScrollPosition; -import org.springframework.data.domain.Scroll; 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; /** - * Utilities to run scroll queries and create {@link Scroll} results. + * Utilities to run scroll queries and create {@link Window} results. * * @author Mark Paluch + * @author Christoph Strobl * @since 4.1 */ class ScrollUtils { @@ -42,7 +44,7 @@ class ScrollUtils { * @param idPropertyName * @return */ - static KeySetCursorQuery createKeysetPaginationQuery(Query query, String idPropertyName) { + static KeySetScrollQuery createKeysetPaginationQuery(Query query, String idPropertyName) { Document sortObject = query.isSorted() ? query.getSortObject() : new Document(); sortObject.put(idPropertyName, 1); @@ -84,6 +86,9 @@ class ScrollUtils { Object o = keysetValues.get(sortSegment); if (j >= i) { // tail segment + if(o instanceof BsonNull) { + throw new IllegalStateException("Cannot resume from KeysetScrollPosition. Offending key: '%s' is 'null'".formatted(sortSegment)); + } sortConstraint.put(sortSegment, new Document(sortOrder == 1 ? "$gt" : "$lt", o)); break; } @@ -104,10 +109,10 @@ class ScrollUtils { queryObject.put("$or", or); } - return new KeySetCursorQuery(queryObject, fieldsObject, sortObject); + return new KeySetScrollQuery(queryObject, fieldsObject, sortObject); } - static Scroll createWindow(Document sortObject, int limit, List result, EntityOperations operations) { + static Window createWindow(Document sortObject, int limit, List result, EntityOperations operations) { IntFunction positionFunction = value -> { @@ -121,8 +126,8 @@ class ScrollUtils { return createWindow(result, limit, positionFunction); } - static Scroll createWindow(List result, int limit, IntFunction positionFunction) { - return Scroll.from(getSubList(result, limit), positionFunction, hasMoreElements(result, limit)); + static Window createWindow(List result, int limit, IntFunction positionFunction) { + return Window.from(getSubList(result, limit), positionFunction, hasMoreElements(result, limit)); } static boolean hasMoreElements(List result, int limit) { @@ -138,7 +143,7 @@ class ScrollUtils { return result; } - record KeySetCursorQuery(Document query, Document fields, Document sort) { + record KeySetScrollQuery(Document query, Document fields, Document sort) { } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutor.java index 4cf3ab3fe..f95092adb 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutor.java @@ -25,7 +25,7 @@ import org.bson.Document; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Scroll; +import org.springframework.data.domain.Window; import org.springframework.data.domain.ScrollPosition; import org.springframework.data.domain.Sort; import org.springframework.data.mongodb.core.MongoOperations; @@ -259,7 +259,7 @@ public class QuerydslMongoPredicateExecutor extends QuerydslPredicateExecutor } @Override - public Scroll scroll(ScrollPosition scrollPosition) { + public Window scroll(ScrollPosition scrollPosition) { return createQuery().scroll(scrollPosition); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveQuerydslMongoPredicateExecutor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveQuerydslMongoPredicateExecutor.java index ff2b44293..d2269fe69 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveQuerydslMongoPredicateExecutor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveQuerydslMongoPredicateExecutor.java @@ -26,7 +26,7 @@ import org.bson.Document; import org.reactivestreams.Publisher; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Scroll; +import org.springframework.data.domain.Window; import org.springframework.data.domain.ScrollPosition; import org.springframework.data.domain.Sort; import org.springframework.data.mongodb.core.ReactiveMongoOperations; @@ -227,7 +227,7 @@ public class ReactiveQuerydslMongoPredicateExecutor extends QuerydslPredicate } @Override - public Mono> scroll(ScrollPosition scrollPosition) { + public Mono> scroll(ScrollPosition scrollPosition) { return createQuery().scroll(scrollPosition); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveSpringDataMongodbQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveSpringDataMongodbQuery.java index cc27f81d6..e29341425 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveSpringDataMongodbQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveSpringDataMongodbQuery.java @@ -26,7 +26,7 @@ import java.util.function.Consumer; import org.bson.Document; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Scroll; +import org.springframework.data.domain.Window; import org.springframework.data.domain.ScrollPosition; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.ReactiveFindOperation; @@ -91,7 +91,7 @@ class ReactiveSpringDataMongodbQuery extends SpringDataMongodbQuerySupport find.matching(it).all()); } - Mono> scroll(ScrollPosition scrollPosition) { + Mono> scroll(ScrollPosition scrollPosition) { return createQuery().flatMap(it -> find.matching(it).scroll(scrollPosition)); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java index 750be26ae..6577a23e4 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java @@ -32,7 +32,7 @@ import org.springframework.data.domain.Example; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Scroll; +import org.springframework.data.domain.Window; import org.springframework.data.domain.ScrollPosition; import org.springframework.data.domain.Sort; import org.springframework.data.mongodb.core.ExecutableFindOperation; @@ -392,7 +392,7 @@ public class SimpleMongoRepository implements MongoRepository { } @Override - public Scroll scroll(ScrollPosition scrollPosition) { + public Window scroll(ScrollPosition scrollPosition) { return createQuery().scroll(scrollPosition); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepository.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepository.java index 9114f5313..bca6b9b79 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepository.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepository.java @@ -34,7 +34,7 @@ import org.springframework.dao.OptimisticLockingFailureException; import org.springframework.data.domain.Example; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Scroll; +import org.springframework.data.domain.Window; import org.springframework.data.domain.ScrollPosition; import org.springframework.data.domain.Sort; import org.springframework.data.mongodb.core.ReactiveFindOperation; @@ -436,7 +436,7 @@ public class SimpleReactiveMongoRepository implement } @Override - public Mono> scroll(ScrollPosition scrollPosition) { + public Mono> scroll(ScrollPosition scrollPosition) { return createQuery().scroll(scrollPosition); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbQuery.java index 2c073986d..0ecff3958 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbQuery.java @@ -25,7 +25,7 @@ import org.bson.Document; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Scroll; +import org.springframework.data.domain.Window; import org.springframework.data.domain.ScrollPosition; import org.springframework.data.mongodb.core.ExecutableFindOperation; import org.springframework.data.mongodb.core.MongoOperations; @@ -133,12 +133,12 @@ public class SpringDataMongodbQuery extends SpringDataMongodbQuerySupport scroll(ScrollPosition scrollPosition) { + public Window scroll(ScrollPosition scrollPosition) { try { return find.matching(createQuery()).scroll(scrollPosition); } catch (RuntimeException e) { - return handleException(e, Scroll.from(Collections.emptyList(), value -> { + return handleException(e, Window.from(Collections.emptyList(), value -> { throw new UnsupportedOperationException(); })); } 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 ef13509b2..35c9b9d0c 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 @@ -38,9 +38,9 @@ import org.springframework.data.annotation.PersistenceCreator; import org.springframework.data.auditing.IsNewAwareAuditingHandler; import org.springframework.data.domain.KeysetScrollPosition; import org.springframework.data.domain.OffsetScrollPosition; -import org.springframework.data.domain.Scroll; import org.springframework.data.domain.ScrollPosition; import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Window; import org.springframework.data.mapping.context.PersistentEntities; import org.springframework.data.mongodb.core.MongoTemplateTests.PersonWithIdPropertyOfTypeUUIDListener; import org.springframework.data.mongodb.core.query.Query; @@ -51,9 +51,10 @@ import org.springframework.data.mongodb.test.util.MongoTestTemplate; import com.mongodb.client.MongoClient; /** - * Integration tests for {@link Scroll} queries. + * Integration tests for {@link org.springframework.data.domain.Window} queries. * * @author Mark Paluch + * @author Christoph Strobl */ @ExtendWith(MongoClientExtension.class) class MongoTemplateScrollTests { @@ -94,7 +95,7 @@ class MongoTemplateScrollTests { template.remove(WithNestedDocument.class).all(); } - @Test + @Test // GH-4308 void shouldUseKeysetScrollingWithNestedSort() { WithNestedDocument john20 = new WithNestedDocument(null, "John", 120, new WithNestedDocument("John", 20), @@ -110,20 +111,55 @@ class MongoTemplateScrollTests { .limit(2); q.with(KeysetScrollPosition.initial()); - Scroll scroll = template.scroll(q, WithNestedDocument.class); + Window scroll = template.scroll(q, WithNestedDocument.class); assertThat(scroll.hasNext()).isTrue(); assertThat(scroll.isLast()).isFalse(); assertThat(scroll).hasSize(2); assertThat(scroll).containsOnly(john20, john40); - scroll = template.scroll(q.with(scroll.lastPosition()), WithNestedDocument.class); + scroll = template.scroll(q.with(scroll.positionAt(scroll.size()-1)), WithNestedDocument.class); assertThat(scroll.hasNext()).isFalse(); assertThat(scroll.isLast()).isTrue(); assertThat(scroll).hasSize(1); assertThat(scroll).containsOnly(john41); + } + + @Test // GH-4308 + void shouldErrorOnNullValueForQuery() { + + WithNestedDocument john20 = new WithNestedDocument(null, "John", 120, new WithNestedDocument("John", 20), + new Document("name", "bar")); + WithNestedDocument john40 = new WithNestedDocument(null, "John", 140, new WithNestedDocument("John", 41), + new Document()); + WithNestedDocument john41 = new WithNestedDocument(null, "John", 140, new WithNestedDocument("John", 41), + new Document()); + WithNestedDocument john42 = new WithNestedDocument(null, "John", 140, new WithNestedDocument("John", 41), + new Document()); + WithNestedDocument john43 = new WithNestedDocument(null, "John", 140, new WithNestedDocument("John", 41), + new Document()); + WithNestedDocument john44 = new WithNestedDocument(null, "John", 141, new WithNestedDocument("John", 41), + new Document("name", "foo")); + + template.insertAll(Arrays.asList(john20, john40, john41, john42, john43, john44)); + + Query q = new Query(where("name").regex("J.*")).with(Sort.by("nested.name", "nested.age", "document.name")) + .limit(2); + q.with(KeysetScrollPosition.initial()); + + Window scroll = template.scroll(q, WithNestedDocument.class); + + assertThat(scroll.hasNext()).isTrue(); + assertThat(scroll.isLast()).isFalse(); + assertThat(scroll).hasSize(2); + assertThat(scroll).containsOnly(john20, john40); + + ScrollPosition startAfter = scroll.positionAt(scroll.size()-1); + assertThatExceptionOfType(IllegalStateException.class) + .isThrownBy(() -> template.scroll(q.with(startAfter), WithNestedDocument.class)) + .withMessageContaining("document.name"); } @ParameterizedTest // GH-4308 @@ -142,14 +178,14 @@ class MongoTemplateScrollTests { Query q = new Query(where("firstName").regex("J.*")).with(Sort.by("firstName", "age")).limit(2); q.with(scrollPosition); - Scroll scroll = template.scroll(q, resultType, "person"); + Window scroll = template.scroll(q, resultType, "person"); assertThat(scroll.hasNext()).isTrue(); assertThat(scroll.isLast()).isFalse(); assertThat(scroll).hasSize(2); assertThat(scroll).containsOnly(assertionConverter.apply(jane_20), assertionConverter.apply(jane_40)); - scroll = template.scroll(q.with(scroll.lastPosition()).limit(3), resultType, "person"); + scroll = template.scroll(q.with(scroll.positionAt(scroll.size()-1)).limit(3), resultType, "person"); assertThat(scroll.hasNext()).isTrue(); assertThat(scroll.isLast()).isFalse(); @@ -157,7 +193,7 @@ class MongoTemplateScrollTests { assertThat(scroll).contains(assertionConverter.apply(jane_42), assertionConverter.apply(john20)); assertThat(scroll).containsAnyOf(assertionConverter.apply(john40_1), assertionConverter.apply(john40_2)); - scroll = template.scroll(q.with(scroll.lastPosition()).limit(1), resultType, "person"); + scroll = template.scroll(q.with(scroll.positionAt(scroll.size()-1)).limit(1), resultType, "person"); assertThat(scroll.hasNext()).isFalse(); assertThat(scroll.isLast()).isTrue(); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateScrollTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateScrollTests.java index ede69c396..d42d8d99f 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateScrollTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateScrollTests.java @@ -35,7 +35,7 @@ import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.GenericApplicationContext; import org.springframework.data.domain.KeysetScrollPosition; import org.springframework.data.domain.OffsetScrollPosition; -import org.springframework.data.domain.Scroll; +import org.springframework.data.domain.Window; import org.springframework.data.domain.ScrollPosition; import org.springframework.data.domain.Sort; import org.springframework.data.mongodb.core.query.Query; @@ -46,7 +46,7 @@ import org.springframework.data.mongodb.test.util.ReactiveMongoTestTemplate; import com.mongodb.reactivestreams.client.MongoClient; /** - * Integration tests for {@link Scroll} queries. + * Integration tests for {@link Window} queries. * * @author Mark Paluch */ @@ -100,14 +100,14 @@ class ReactiveMongoTemplateScrollTests { Query q = new Query(where("firstName").regex("J.*")).with(Sort.by("firstName", "age")).limit(2); q.with(scrollPosition); - Scroll scroll = template.scroll(q, resultType, "person").block(Duration.ofSeconds(10)); + Window scroll = template.scroll(q, resultType, "person").block(Duration.ofSeconds(10)); assertThat(scroll.hasNext()).isTrue(); assertThat(scroll.isLast()).isFalse(); assertThat(scroll).hasSize(2); assertThat(scroll).containsOnly(assertionConverter.apply(jane_20), assertionConverter.apply(jane_40)); - scroll = template.scroll(q.limit(3).with(scroll.lastPosition()), resultType, "person") + scroll = template.scroll(q.limit(3).with(scroll.positionAt(scroll.size() - 1)), resultType, "person") .block(Duration.ofSeconds(10)); assertThat(scroll.hasNext()).isTrue(); @@ -116,7 +116,7 @@ class ReactiveMongoTemplateScrollTests { assertThat(scroll).contains(assertionConverter.apply(jane_42), assertionConverter.apply(john20)); assertThat(scroll).containsAnyOf(assertionConverter.apply(john40_1), assertionConverter.apply(john40_2)); - scroll = template.scroll(q.limit(1).with(scroll.lastPosition()), resultType, "person") + scroll = template.scroll(q.limit(1).with(scroll.positionAt(scroll.size() - 1)), resultType, "person") .block(Duration.ofSeconds(10)); assertThat(scroll.hasNext()).isFalse(); 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 697bf33b9..6a1e35d0b 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 @@ -205,7 +205,7 @@ public abstract class AbstractPersonRepositoryIntegrationTests implements Dirtie @Test // GH-4308 void appliesScrollPositionCorrectly() { - Scroll page = repository.findTop2ByLastnameLikeOrderByLastnameAscFirstnameAsc("*a*", + Window page = repository.findTop2ByLastnameLikeOrderByLastnameAscFirstnameAsc("*a*", KeysetScrollPosition.initial()); assertThat(page.isLast()).isFalse(); @@ -216,7 +216,7 @@ public abstract class AbstractPersonRepositoryIntegrationTests implements Dirtie @Test // GH-4308 void appliesScrollPositionWithProjectionCorrectly() { - Scroll page = repository.findCursorProjectionByLastnameLike("*a*", + Window page = repository.findCursorProjectionByLastnameLike("*a*", PageRequest.of(0, 2, Sort.by(Direction.ASC, "lastname", "firstname"))); assertThat(page.isLast()).isFalse(); @@ -956,12 +956,12 @@ public abstract class AbstractPersonRepositoryIntegrationTests implements Dirtie @Test // DATAMONGO-969 void shouldScrollPersonsWhenUsingQueryDslPerdicatedOnIdProperty() { - Scroll scroll = repository.findBy(person.id.in(asList(dave.id, carter.id, boyd.id)), // + Window scroll = repository.findBy(person.id.in(asList(dave.id, carter.id, boyd.id)), // q -> q.limit(2).sortBy(Sort.by("firstname")).scroll(KeysetScrollPosition.initial())); assertThat(scroll).containsExactly(boyd, carter); - ScrollPosition resumeFrom = scroll.lastPosition(); + ScrollPosition resumeFrom = scroll.positionAt(scroll.size() - 1); scroll = repository.findBy(person.id.in(asList(dave.id, carter.id, boyd.id)), // q -> q.limit(2).sortBy(Sort.by("firstname")).scroll(resumeFrom)); @@ -1185,14 +1185,14 @@ public abstract class AbstractPersonRepositoryIntegrationTests implements Dirtie ReflectionTestUtils.setField(sample, "createdAt", null); ReflectionTestUtils.setField(sample, "email", null); - Scroll result = repository.findBy( + Window result = repository.findBy( Example.of(sample, ExampleMatcher.matching().withMatcher("lastname", GenericPropertyMatcher::startsWith)), q -> q.limit(2).sortBy(Sort.by("firstname")).scroll(KeysetScrollPosition.initial())); assertThat(result).containsOnly(dave, leroi); assertThat(result.hasNext()).isTrue(); - ScrollPosition position = result.lastPosition(); + ScrollPosition position = result.positionAt(result.size() - 1); result = repository.findBy( Example.of(sample, ExampleMatcher.matching().withMatcher("lastname", GenericPropertyMatcher::startsWith)), q -> q.limit(2).sortBy(Sort.by("firstname")).scroll(position)); 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 b34b8cecd..1e6a37b0a 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 @@ -23,11 +23,11 @@ import java.util.UUID; import java.util.regex.Pattern; import java.util.stream.Stream; -import org.springframework.data.domain.KeysetScrollPosition; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Range; -import org.springframework.data.domain.Scroll; +import org.springframework.data.domain.Window; +import org.springframework.data.domain.ScrollPosition; import org.springframework.data.domain.Slice; import org.springframework.data.domain.Sort; import org.springframework.data.geo.Box; @@ -123,8 +123,8 @@ public interface PersonRepository extends MongoRepository, Query * @param scrollPosition * @return */ - Scroll findTop2ByLastnameLikeOrderByLastnameAscFirstnameAsc(String lastname, - KeysetScrollPosition scrollPosition); + Window findTop2ByLastnameLikeOrderByLastnameAscFirstnameAsc(String lastname, + ScrollPosition scrollPosition); /** * Returns a scroll of {@link Person}s applying projections with a lastname matching the given one (*-wildcards @@ -134,7 +134,7 @@ public interface PersonRepository extends MongoRepository, Query * @param pageable * @return */ - Scroll findCursorProjectionByLastnameLike(String lastname, Pageable pageable); + Window findCursorProjectionByLastnameLike(String lastname, Pageable pageable); /** * Returns a page of {@link Person}s with a lastname matching the given one (*-wildcards supported). diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactiveMongoRepositoryTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactiveMongoRepositoryTests.java index a4b83c33b..5d54eda62 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactiveMongoRepositoryTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactiveMongoRepositoryTests.java @@ -50,10 +50,10 @@ import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.data.domain.KeysetScrollPosition; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Scroll; import org.springframework.data.domain.ScrollPosition; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; +import org.springframework.data.domain.Window; import org.springframework.data.geo.Circle; import org.springframework.data.geo.Distance; import org.springframework.data.geo.GeoResult; @@ -293,15 +293,15 @@ class ReactiveMongoRepositoryTests implements DirtiesStateExtension.StateFunctio @Test // GH-4308 void appliesScrollingCorrectly() { - Scroll scroll = repository + Window scroll = repository .findTop2ByLastnameLikeOrderByFirstnameAscLastnameAsc("*", KeysetScrollPosition.initial()).block(); assertThat(scroll).hasSize(2); assertThat(scroll).containsSequence(alicia, boyd); assertThat(scroll.isLast()).isFalse(); - Scroll nextScroll = repository - .findTop2ByLastnameLikeOrderByFirstnameAscLastnameAsc("*", scroll.lastPosition()).block(); + Window nextScroll = repository + .findTop2ByLastnameLikeOrderByFirstnameAscLastnameAsc("*", scroll.positionAt(scroll.size() - 1)).block(); assertThat(nextScroll).hasSize(2); assertThat(nextScroll).containsSequence(carter, dave); @@ -474,7 +474,7 @@ class ReactiveMongoRepositoryTests implements DirtiesStateExtension.StateFunctio @Test // GH-4308 void shouldScrollWithId() { - List> capture = new ArrayList<>(); + List> capture = new ArrayList<>(); repository.findBy(person.id.in(Arrays.asList(dave.id, carter.id, boyd.id)), // q -> q.limit(2).sortBy(Sort.by("firstname")).scroll(KeysetScrollPosition.initial())) // .as(StepVerifier::create) // @@ -482,10 +482,10 @@ class ReactiveMongoRepositoryTests implements DirtiesStateExtension.StateFunctio assertThat(actual).hasSize(2).containsExactly(boyd, carter); }).verifyComplete(); - Scroll scroll = capture.get(0); + Window scroll = capture.get(0); repository.findBy(person.id.in(Arrays.asList(dave.id, carter.id, boyd.id)), // - q -> q.limit(2).sortBy(Sort.by("firstname")).scroll(scroll.lastPosition())) // + q -> q.limit(2).sortBy(Sort.by("firstname")).scroll(scroll.positionAt(scroll.size() - 1))) // .as(StepVerifier::create) // .recordWith(() -> capture).assertNext(actual -> { assertThat(actual).containsOnly(dave); @@ -768,10 +768,10 @@ class ReactiveMongoRepositoryTests implements DirtiesStateExtension.StateFunctio @Query("{ lastname: { $in: ?0 }, age: { $gt : ?1 } }") Flux findStringQuery(Flux lastname, Mono age); - Mono> findTop2ByLastnameLikeOrderByFirstnameAscLastnameAsc(String lastname, + Mono> findTop2ByLastnameLikeOrderByFirstnameAscLastnameAsc(String lastname, ScrollPosition scrollPosition); - Mono> findCursorProjectionByLastnameLike(String lastname, Pageable pageable); + Mono> findCursorProjectionByLastnameLike(String lastname, Pageable pageable); Flux findByLocationWithin(Circle circle);