Skip to content

Commit 9d0afc9

Browse files
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
1 parent eaa6393 commit 9d0afc9

21 files changed

+159
-96
lines changed

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

+22-5
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.Map;
2222
import java.util.Optional;
2323

24+
import org.bson.BsonNull;
2425
import org.bson.Document;
2526
import org.springframework.core.convert.ConversionService;
2627
import org.springframework.dao.InvalidDataAccessApiUsageException;
@@ -471,6 +472,7 @@ default boolean isVersionedEntity() {
471472
* @param sortObject
472473
* @return
473474
* @since 3.1
475+
* @throws IllegalStateException if a sort key yields {@literal null}.
474476
*/
475477
Map<String, Object> extractKeys(Document sortObject);
476478

@@ -600,7 +602,14 @@ public Map<String, Object> extractKeys(Document sortObject) {
600602
keyset.put(ID_FIELD, getId());
601603

602604
for (String key : sortObject.keySet()) {
603-
keyset.put(key, BsonUtils.resolveValue(map, key));
605+
Object value = BsonUtils.resolveValue(map, key);
606+
607+
if (value == null) {
608+
throw new IllegalStateException(
609+
String.format("Cannot extract value for key %s because its value is null", key));
610+
}
611+
612+
keyset.put(key, value);
604613
}
605614

606615
return keyset;
@@ -756,14 +765,22 @@ public Map<String, Object> extractKeys(Document sortObject) {
756765

757766
for (String key : sortObject.keySet()) {
758767

768+
Object value;
759769
if (key.indexOf('.') != -1) {
760770

761771
// follow the path across nested levels.
762772
// TODO: We should have a MongoDB-specific property path abstraction to allow diving into Document.
763-
keyset.put(key, getNestedPropertyValue(key));
773+
value = getNestedPropertyValue(key);
764774
} else {
765-
keyset.put(key, getPropertyValue(key));
775+
value = getPropertyValue(key);
766776
}
777+
778+
if (value == null) {
779+
throw new IllegalStateException(
780+
String.format("Cannot extract value for key %s because its value is null", key));
781+
}
782+
783+
keyset.put(key, value);
767784
}
768785

769786
return keyset;
@@ -774,7 +791,7 @@ private Object getNestedPropertyValue(String key) {
774791

775792
String[] segments = key.split("\\.");
776793
Entity<?> currentEntity = this;
777-
Object currentValue = null;
794+
Object currentValue = BsonNull.VALUE;
778795

779796
for (int i = 0; i < segments.length; i++) {
780797

@@ -786,7 +803,7 @@ private Object getNestedPropertyValue(String key) {
786803
}
787804
}
788805

789-
return currentValue;
806+
return currentValue != null ? currentValue : BsonNull.VALUE;
790807
}
791808
}
792809

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

+5-4
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import java.util.stream.Stream;
2121

2222
import org.springframework.dao.DataAccessException;
23-
import org.springframework.data.domain.Scroll;
23+
import org.springframework.data.domain.Window;
2424
import org.springframework.data.domain.ScrollPosition;
2525
import org.springframework.data.geo.GeoResults;
2626
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
@@ -126,16 +126,17 @@ default Optional<T> first() {
126126
Stream<T> stream();
127127

128128
/**
129-
* Return a scroll of elements either starting or resuming at
129+
* Return a window of elements either starting or resuming at
130130
* {@link org.springframework.data.domain.ScrollPosition}.
131131
*
132132
* @param scrollPosition the scroll position.
133-
* @return a scroll of the resulting elements.
133+
* @return a window of the resulting elements.
134+
* @throws IllegalStateException if a potential {@literal KeysetScrollPosition} contains an invalid position.
134135
* @since 4.1
135136
* @see org.springframework.data.domain.OffsetScrollPosition
136137
* @see org.springframework.data.domain.KeysetScrollPosition
137138
*/
138-
Scroll<T> scroll(ScrollPosition scrollPosition);
139+
Window<T> scroll(ScrollPosition scrollPosition);
139140

140141
/**
141142
* Get the number of matching elements. <br />

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
import org.bson.Document;
2323
import org.springframework.dao.IncorrectResultSizeDataAccessException;
24-
import org.springframework.data.domain.Scroll;
24+
import org.springframework.data.domain.Window;
2525
import org.springframework.data.domain.ScrollPosition;
2626
import org.springframework.data.mongodb.core.query.NearQuery;
2727
import org.springframework.data.mongodb.core.query.Query;
@@ -141,7 +141,7 @@ public Stream<T> stream() {
141141
}
142142

143143
@Override
144-
public Scroll<T> scroll(ScrollPosition scrollPosition) {
144+
public Window<T> scroll(ScrollPosition scrollPosition) {
145145
return template.doScroll(query.with(scrollPosition), domainType, returnType, getCollectionName());
146146
}
147147

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

+11-9
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
import org.bson.Document;
2626
import org.springframework.data.domain.KeysetScrollPosition;
27-
import org.springframework.data.domain.Scroll;
27+
import org.springframework.data.domain.Window;
2828
import org.springframework.data.geo.GeoResults;
2929
import org.springframework.data.mongodb.core.BulkOperations.BulkMode;
3030
import org.springframework.data.mongodb.core.aggregation.Aggregation;
@@ -807,7 +807,7 @@ <T> MapReduceResults<T> mapReduce(Query query, String inputCollectionName, Strin
807807
<T> List<T> find(Query query, Class<T> entityClass, String collectionName);
808808

809809
/**
810-
* Query for a scroll window of objects of type T from the specified collection. <br />
810+
* Query for a window window of objects of type T from the specified collection. <br />
811811
* Make sure to either set {@link Query#skip(long)} or {@link Query#with(KeysetScrollPosition)} along with
812812
* {@link Query#limit(int)} to limit large query results for efficient scrolling. <br />
813813
* Result objects are converted from the MongoDB native representation using an instance of {@see MongoConverter}.
@@ -817,16 +817,17 @@ <T> MapReduceResults<T> mapReduce(Query query, String inputCollectionName, Strin
817817
*
818818
* @param query the query class that specifies the criteria used to find a record and also an optional fields
819819
* specification. Must not be {@literal null}.
820-
* @param entityType the parametrized type of the returned list.
821-
* @return the converted scroll.
820+
* @param entityType the parametrized type of the returned window.
821+
* @return the converted window.
822+
* @throws IllegalStateException if a potential {@link Query#getKeyset() KeysetScrollPosition} contains an invalid position.
822823
* @since 4.1
823824
* @see Query#with(org.springframework.data.domain.OffsetScrollPosition)
824825
* @see Query#with(org.springframework.data.domain.KeysetScrollPosition)
825826
*/
826-
<T> Scroll<T> scroll(Query query, Class<T> entityType);
827+
<T> Window<T> scroll(Query query, Class<T> entityType);
827828

828829
/**
829-
* Query for a scroll of objects of type T from the specified collection. <br />
830+
* Query for a window of objects of type T from the specified collection. <br />
830831
* Make sure to either set {@link Query#skip(long)} or {@link Query#with(KeysetScrollPosition)} along with
831832
* {@link Query#limit(int)} to limit large query results for efficient scrolling. <br />
832833
* Result objects are converted from the MongoDB native representation using an instance of {@see MongoConverter}.
@@ -836,14 +837,15 @@ <T> MapReduceResults<T> mapReduce(Query query, String inputCollectionName, Strin
836837
*
837838
* @param query the query class that specifies the criteria used to find a record and also an optional fields
838839
* specification. Must not be {@literal null}.
839-
* @param entityType the parametrized type of the returned list.
840+
* @param entityType the parametrized type of the returned window.
840841
* @param collectionName name of the collection to retrieve the objects from.
841-
* @return the converted scroll.
842+
* @return the converted window.
843+
* @throws IllegalStateException if a potential {@link Query#getKeyset() KeysetScrollPosition} contains an invalid position.
842844
* @since 4.1
843845
* @see Query#with(org.springframework.data.domain.OffsetScrollPosition)
844846
* @see Query#with(org.springframework.data.domain.KeysetScrollPosition)
845847
*/
846-
<T> Scroll<T> scroll(Query query, Class<T> entityType, String collectionName);
848+
<T> Window<T> scroll(Query query, Class<T> entityType, String collectionName);
847849

848850
/**
849851
* Returns a document with the given id mapped onto the given class. The collection the query is ran against will be

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

+6-6
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
import org.springframework.dao.support.PersistenceExceptionTranslator;
4646
import org.springframework.data.convert.EntityReader;
4747
import org.springframework.data.domain.OffsetScrollPosition;
48-
import org.springframework.data.domain.Scroll;
48+
import org.springframework.data.domain.Window;
4949
import org.springframework.data.geo.Distance;
5050
import org.springframework.data.geo.GeoResult;
5151
import org.springframework.data.geo.GeoResults;
@@ -66,7 +66,7 @@
6666
import org.springframework.data.mongodb.core.QueryOperations.DistinctQueryContext;
6767
import org.springframework.data.mongodb.core.QueryOperations.QueryContext;
6868
import org.springframework.data.mongodb.core.QueryOperations.UpdateContext;
69-
import org.springframework.data.mongodb.core.ScrollUtils.KeySetCursorQuery;
69+
import org.springframework.data.mongodb.core.ScrollUtils.KeySetScrollQuery;
7070
import org.springframework.data.mongodb.core.aggregation.Aggregation;
7171
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
7272
import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
@@ -851,19 +851,19 @@ public <T> List<T> find(Query query, Class<T> entityClass, String collectionName
851851
}
852852

853853
@Override
854-
public <T> Scroll<T> scroll(Query query, Class<T> entityType) {
854+
public <T> Window<T> scroll(Query query, Class<T> entityType) {
855855

856856
Assert.notNull(entityType, "Entity type must not be null");
857857

858858
return scroll(query, entityType, getCollectionName(entityType));
859859
}
860860

861861
@Override
862-
public <T> Scroll<T> scroll(Query query, Class<T> entityType, String collectionName) {
862+
public <T> Window<T> scroll(Query query, Class<T> entityType, String collectionName) {
863863
return doScroll(query, entityType, entityType, collectionName);
864864
}
865865

866-
<T> Scroll<T> doScroll(Query query, Class<?> sourceClass, Class<T> targetClass, String collectionName) {
866+
<T> Window<T> doScroll(Query query, Class<?> sourceClass, Class<T> targetClass, String collectionName) {
867867

868868
Assert.notNull(query, "Query must not be null");
869869
Assert.notNull(collectionName, "CollectionName must not be null");
@@ -875,7 +875,7 @@ <T> Scroll<T> doScroll(Query query, Class<?> sourceClass, Class<T> targetClass,
875875

876876
if (query.hasKeyset()) {
877877

878-
KeySetCursorQuery keysetPaginationQuery = ScrollUtils.createKeysetPaginationQuery(query,
878+
KeySetScrollQuery keysetPaginationQuery = ScrollUtils.createKeysetPaginationQuery(query,
879879
operations.getIdPropertyName(sourceClass));
880880

881881
List<T> result = doFind(collectionName, createDelegate(query), keysetPaginationQuery.query(),

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
import reactor.core.publisher.Flux;
1919
import reactor.core.publisher.Mono;
2020

21-
import org.springframework.data.domain.Scroll;
21+
import org.springframework.data.domain.Window;
2222
import org.springframework.data.domain.ScrollPosition;
2323
import org.springframework.data.geo.GeoResult;
2424
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
@@ -98,7 +98,7 @@ interface TerminatingFind<T> {
9898
* @see org.springframework.data.domain.OffsetScrollPosition
9999
* @see org.springframework.data.domain.KeysetScrollPosition
100100
*/
101-
Mono<Scroll<T>> scroll(ScrollPosition scrollPosition);
101+
Mono<Window<T>> scroll(ScrollPosition scrollPosition);
102102

103103
/**
104104
* Get all matching elements using a {@link com.mongodb.CursorType#TailableAwait tailable cursor}. The stream will

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
import org.bson.Document;
2222
import org.springframework.dao.IncorrectResultSizeDataAccessException;
23-
import org.springframework.data.domain.Scroll;
23+
import org.springframework.data.domain.Window;
2424
import org.springframework.data.domain.ScrollPosition;
2525
import org.springframework.data.mongodb.core.CollectionPreparerSupport.ReactiveCollectionPreparerDelegate;
2626
import org.springframework.data.mongodb.core.query.NearQuery;
@@ -140,7 +140,7 @@ public Flux<T> all() {
140140
}
141141

142142
@Override
143-
public Mono<Scroll<T>> scroll(ScrollPosition scrollPosition) {
143+
public Mono<Window<T>> scroll(ScrollPosition scrollPosition) {
144144
return template.doScroll(query.with(scrollPosition), domainType, returnType, getCollectionName());
145145
}
146146

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

+8-6
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
import org.reactivestreams.Publisher;
2727
import org.reactivestreams.Subscription;
2828
import org.springframework.data.domain.KeysetScrollPosition;
29-
import org.springframework.data.domain.Scroll;
29+
import org.springframework.data.domain.Window;
3030
import org.springframework.data.geo.GeoResult;
3131
import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory;
3232
import org.springframework.data.mongodb.core.aggregation.Aggregation;
@@ -477,15 +477,16 @@ Mono<MongoCollection<Document>> createView(String name, String source, Aggregati
477477
* @param query the query class that specifies the criteria used to find a record and also an optional fields
478478
* specification. Must not be {@literal null}.
479479
* @param entityType the parametrized type of the returned list.
480-
* @return {@link Mono} emitting the converted scroll.
480+
* @return {@link Mono} emitting the converted window.
481+
* @throws IllegalStateException if a potential {@link Query#getKeyset() KeysetScrollPosition} contains an invalid position.
481482
* @since 4.1
482483
* @see Query#with(org.springframework.data.domain.OffsetScrollPosition)
483484
* @see Query#with(org.springframework.data.domain.KeysetScrollPosition)
484485
*/
485-
<T> Mono<Scroll<T>> scroll(Query query, Class<T> entityType);
486+
<T> Mono<Window<T>> scroll(Query query, Class<T> entityType);
486487

487488
/**
488-
* Query for a scroll of objects of type T from the specified collection. <br />
489+
* Query for a window of objects of type T from the specified collection. <br />
489490
* Make sure to either set {@link Query#skip(long)} or {@link Query#with(KeysetScrollPosition)} along with
490491
* {@link Query#limit(int)} to limit large query results for efficient scrolling. <br />
491492
* Result objects are converted from the MongoDB native representation using an instance of {@see MongoConverter}.
@@ -497,12 +498,13 @@ Mono<MongoCollection<Document>> createView(String name, String source, Aggregati
497498
* specification. Must not be {@literal null}.
498499
* @param entityType the parametrized type of the returned list.
499500
* @param collectionName name of the collection to retrieve the objects from.
500-
* @return {@link Mono} emitting the converted scroll window.
501+
* @return {@link Mono} emitting the converted window.
502+
* @throws IllegalStateException if a potential {@link Query#getKeyset() KeysetScrollPosition} contains an invalid position.
501503
* @since 4.1
502504
* @see Query#with(org.springframework.data.domain.OffsetScrollPosition)
503505
* @see Query#with(org.springframework.data.domain.KeysetScrollPosition)
504506
*/
505-
<T> Mono<Scroll<T>> scroll(Query query, Class<T> entityType, String collectionName);
507+
<T> Mono<Window<T>> scroll(Query query, Class<T> entityType, String collectionName);
506508

507509
/**
508510
* Returns a document with the given id mapped onto the given class. The collection the query is ran against will be

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

+6-6
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
import org.springframework.dao.support.PersistenceExceptionTranslator;
6060
import org.springframework.data.convert.EntityReader;
6161
import org.springframework.data.domain.OffsetScrollPosition;
62-
import org.springframework.data.domain.Scroll;
62+
import org.springframework.data.domain.Window;
6363
import org.springframework.data.geo.Distance;
6464
import org.springframework.data.geo.GeoResult;
6565
import org.springframework.data.geo.Metric;
@@ -80,7 +80,7 @@
8080
import org.springframework.data.mongodb.core.QueryOperations.DistinctQueryContext;
8181
import org.springframework.data.mongodb.core.QueryOperations.QueryContext;
8282
import org.springframework.data.mongodb.core.QueryOperations.UpdateContext;
83-
import org.springframework.data.mongodb.core.ScrollUtils.KeySetCursorQuery;
83+
import org.springframework.data.mongodb.core.ScrollUtils.KeySetScrollQuery;
8484
import org.springframework.data.mongodb.core.aggregation.Aggregation;
8585
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
8686
import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
@@ -830,19 +830,19 @@ public <T> Flux<T> find(@Nullable Query query, Class<T> entityClass, String coll
830830
}
831831

832832
@Override
833-
public <T> Mono<Scroll<T>> scroll(Query query, Class<T> entityType) {
833+
public <T> Mono<Window<T>> scroll(Query query, Class<T> entityType) {
834834

835835
Assert.notNull(entityType, "Entity type must not be null");
836836

837837
return scroll(query, entityType, getCollectionName(entityType));
838838
}
839839

840840
@Override
841-
public <T> Mono<Scroll<T>> scroll(Query query, Class<T> entityType, String collectionName) {
841+
public <T> Mono<Window<T>> scroll(Query query, Class<T> entityType, String collectionName) {
842842
return doScroll(query, entityType, entityType, collectionName);
843843
}
844844

845-
<T> Mono<Scroll<T>> doScroll(Query query, Class<?> sourceClass, Class<T> targetClass, String collectionName) {
845+
<T> Mono<Window<T>> doScroll(Query query, Class<?> sourceClass, Class<T> targetClass, String collectionName) {
846846

847847
Assert.notNull(query, "Query must not be null");
848848
Assert.notNull(collectionName, "CollectionName must not be null");
@@ -853,7 +853,7 @@ <T> Mono<Scroll<T>> doScroll(Query query, Class<?> sourceClass, Class<T> targetC
853853

854854
if (query.hasKeyset()) {
855855

856-
KeySetCursorQuery keysetPaginationQuery = ScrollUtils.createKeysetPaginationQuery(query,
856+
KeySetScrollQuery keysetPaginationQuery = ScrollUtils.createKeysetPaginationQuery(query,
857857
operations.getIdPropertyName(sourceClass));
858858

859859
Mono<List<T>> result = doFind(collectionName, ReactiveCollectionPreparerDelegate.of(query),

0 commit comments

Comments
 (0)