DATAMONGO-1890 - Polishing.
Remove mapReduce default methods in favor of adding variants through a fluent API at a later stage. Assert mapReduce arguments and remove subsequent null guards. Adapt tests. Original pull request: #548.
This commit is contained in:
@@ -1227,107 +1227,6 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
|
||||
<T> Flux<ChangeStreamEvent<T>> changeStream(List<Document> filter, Class<T> resultType, ChangeStreamOptions options,
|
||||
String collectionName);
|
||||
|
||||
/**
|
||||
* Execute an inline map-reduce operation returning the operation result without storing it in an collection.
|
||||
*
|
||||
* @param resultType the mapping target of the operations result documents. Also used to determine the input
|
||||
* collection mame. Must not be {@literal null}.
|
||||
* @param mapFunction the JavaScript map function. Must not be {@literal null}.
|
||||
* @param reduceFunction the JavaScript reduce function. Must not be {@literal null}.
|
||||
* @return a {@link Flux} emitting the result document sequence. Never {@literal null}.
|
||||
* @since 2.1
|
||||
*/
|
||||
default <T> Flux<T> mapReduce(Class<T> resultType, String mapFunction, String reduceFunction) {
|
||||
return mapReduce(resultType, resultType, mapFunction, reduceFunction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute an inline map-reduce operation returning the operation result without storing it in an collection.
|
||||
*
|
||||
* @param domainType source type used to determine the input collection name. Must not be {@literal null}.
|
||||
* @param resultType the mapping target of the operations result documents. Must not be {@literal null}.
|
||||
* @param mapFunction the JavaScript map function. Must not be {@literal null}.
|
||||
* @param reduceFunction the JavaScript reduce function. Must not be {@literal null}.
|
||||
* @return a {@link Flux} emitting the result document sequence. Never {@literal null}.
|
||||
* @since 2.1
|
||||
*/
|
||||
default <T> Flux<T> mapReduce(Class<?> domainType, Class<T> resultType, String mapFunction, String reduceFunction) {
|
||||
return mapReduce(new Query(), domainType, resultType, mapFunction, reduceFunction, MapReduceOptions.options());
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute an inline map-reduce operation returning the operation result without storing it in an collection.
|
||||
*
|
||||
* @param filterQuery the selection criteria for the documents going input to the map function. Must not be
|
||||
* {@literal null}.
|
||||
* @param resultType the mapping target of the operations result documents. Also used to determine the input
|
||||
* collection mame. Must not be {@literal null}.
|
||||
* @param mapFunction the JavaScript map function. Must not be {@literal null}.
|
||||
* @param reduceFunction the JavaScript reduce function. Must not be {@literal null}.
|
||||
* @return a {@link Flux} emitting the result document sequence. Never {@literal null}.
|
||||
* @since 2.1
|
||||
*/
|
||||
default <T> Flux<T> mapReduce(Query filterQuery, Class<T> resultType, String mapFunction, String reduceFunction) {
|
||||
return mapReduce(filterQuery, resultType, resultType, mapFunction, reduceFunction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute an inline map-reduce operation returning the operation result without storing it in an collection.
|
||||
*
|
||||
* @param filterQuery the selection criteria for the documents going input to the map function. Must not be
|
||||
* {@literal null}.
|
||||
* @param domainType source type used to determine the input collection name and map the filter {@link Query} against.
|
||||
* Must not be {@literal null}.
|
||||
* @param resultType the mapping target of the operations result documents. Must not be {@literal null}.
|
||||
* @param mapFunction the JavaScript map function. Must not be {@literal null}.
|
||||
* @param reduceFunction the JavaScript reduce function. Must not be {@literal null}.
|
||||
* @return a {@link Flux} emitting the result document sequence. Never {@literal null}.
|
||||
* @since 2.1
|
||||
*/
|
||||
default <T> Flux<T> mapReduce(Query filterQuery, Class<?> domainType, Class<T> resultType, String mapFunction,
|
||||
String reduceFunction) {
|
||||
|
||||
return mapReduce(filterQuery, domainType, resultType, mapFunction, reduceFunction, MapReduceOptions.options());
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a map-reduce operation. Use {@link MapReduceOptions} to optionally specify an output collection and other
|
||||
* args.
|
||||
*
|
||||
* @param resultType he mapping target of the operations result documents. Also used to determine the input collection
|
||||
* mame. Must not be {@literal null}.
|
||||
* @param mapFunction the JavaScript map function. Must not be {@literal null}.
|
||||
* @param reduceFunction the JavaScript reduce function. Must not be {@literal null}.
|
||||
* @param options additional options like output collection. Must not be {@literal null}.
|
||||
* @return a {@link Flux} emitting the result document sequence. Never {@literal null}.
|
||||
* @since 2.1
|
||||
*/
|
||||
default <T> Flux<T> mapReduce(Class<T> resultType, String mapFunction, String reduceFunction,
|
||||
MapReduceOptions options) {
|
||||
|
||||
return mapReduce(new Query(), resultType, mapFunction, reduceFunction, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a map-reduce operation. Use {@link MapReduceOptions} to optionally specify an output collection and other
|
||||
* args.
|
||||
*
|
||||
* @param filterQuery the selection criteria for the documents going input to the map function. Must not be
|
||||
* {@literal null}.
|
||||
* @param resultType he mapping target of the operations result documents. Also used to determine the input collection
|
||||
* mame. Must not be {@literal null}.
|
||||
* @param mapFunction the JavaScript map function. Must not be {@literal null}.
|
||||
* @param reduceFunction the JavaScript reduce function. Must not be {@literal null}.
|
||||
* @param options additional options like output collection. Must not be {@literal null}.
|
||||
* @return a {@link Flux} emitting the result document sequence. Never {@literal null}.
|
||||
* @since 2.1
|
||||
*/
|
||||
default <T> Flux<T> mapReduce(Query filterQuery, Class<T> resultType, String mapFunction, String reduceFunction,
|
||||
MapReduceOptions options) {
|
||||
|
||||
return mapReduce(filterQuery, resultType, resultType, mapFunction, reduceFunction, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a map-reduce operation. Use {@link MapReduceOptions} to optionally specify an output collection and other
|
||||
* args.
|
||||
@@ -1346,76 +1245,6 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
|
||||
<T> Flux<T> mapReduce(Query filterQuery, Class<?> domainType, Class<T> resultType, String mapFunction,
|
||||
String reduceFunction, MapReduceOptions options);
|
||||
|
||||
/**
|
||||
* Execute an inline map-reduce operation returning the operation result without storing it in an collection.
|
||||
*
|
||||
* @param resultType the mapping target of the operations result documents. Must not be {@literal null}.
|
||||
* @param collectionName the input collection.
|
||||
* @param mapFunction the JavaScript map function. Must not be {@literal null}.
|
||||
* @param reduceFunction the JavaScript reduce function. Must not be {@literal null}.
|
||||
* @return a {@link Flux} emitting the result document sequence. Never {@literal null}.
|
||||
* @since 2.1
|
||||
*/
|
||||
default <T> Flux<T> mapReduce(Class<T> resultType, String collectionName, String mapFunction, String reduceFunction) {
|
||||
return mapReduce(new Query(), resultType, collectionName, mapFunction, reduceFunction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute an inline map-reduce operation returning the operation result without storing it in an collection.
|
||||
*
|
||||
* @param filterQuery the selection criteria for the documents going input to the map function. Must not be
|
||||
* {@literal null}.
|
||||
* @param resultType the mapping target of the operations result documents. Must not be {@literal null}.
|
||||
* @param collectionName the input collection.
|
||||
* @param mapFunction the JavaScript map function. Must not be {@literal null}.
|
||||
* @param reduceFunction the JavaScript reduce function. Must not be {@literal null}.
|
||||
* @return a {@link Flux} emitting the result document sequence. Never {@literal null}.
|
||||
* @since 2.1
|
||||
*/
|
||||
default <T> Flux<T> mapReduce(Query filterQuery, Class<T> resultType, String collectionName, String mapFunction,
|
||||
String reduceFunction) {
|
||||
|
||||
return mapReduce(filterQuery, resultType, resultType, collectionName, mapFunction, reduceFunction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute an inline map-reduce operation returning the operation result without storing it in an collection.
|
||||
*
|
||||
* @param filterQuery the selection criteria for the documents going input to the map function. Must not be
|
||||
* {@literal null}.
|
||||
* @param domainType source type used to map the filter {@link Query} against. Must not be {@literal null}.
|
||||
* @param resultType the mapping target of the operations result documents. Must not be {@literal null}.
|
||||
* @param collectionName the input collection.
|
||||
* @param mapFunction the JavaScript map function. Must not be {@literal null}.
|
||||
* @param reduceFunction the JavaScript reduce function. Must not be {@literal null}.
|
||||
* @return a {@link Flux} emitting the result document sequence. Never {@literal null}.
|
||||
* @since 2.1
|
||||
*/
|
||||
default <T> Flux<T> mapReduce(Query filterQuery, Class<?> domainType, Class<T> resultType, String collectionName,
|
||||
String mapFunction, String reduceFunction) {
|
||||
|
||||
return mapReduce(filterQuery, domainType, collectionName, resultType, mapFunction, reduceFunction,
|
||||
MapReduceOptions.options());
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a map-reduce operation. Use {@link MapReduceOptions} to optionally specify an output collection and other
|
||||
* args.
|
||||
*
|
||||
* @param resultType the mapping target of the operations result documents. Must not be {@literal null}.
|
||||
* @param collectionName the input collection.
|
||||
* @param mapFunction the JavaScript map function. Must not be {@literal null}.
|
||||
* @param reduceFunction the JavaScript reduce function. Must not be {@literal null}.
|
||||
* @param options additional options like output collection. Must not be {@literal null}.
|
||||
* @return a {@link Flux} emitting the result document sequence. Never {@literal null}.
|
||||
* @since 2.1
|
||||
*/
|
||||
default <T> Flux<T> mapReduce(Class<T> resultType, String collectionName, String mapFunction, String reduceFunction,
|
||||
MapReduceOptions options) {
|
||||
|
||||
return mapReduce(new Query(), resultType, collectionName, resultType, mapFunction, reduceFunction, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a map-reduce operation. Use {@link MapReduceOptions} to optionally specify an output collection and other
|
||||
* args.
|
||||
|
||||
@@ -1936,74 +1936,73 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
public <T> Flux<T> mapReduce(Query filterQuery, Class<?> domainType, String inputCollectionName, Class<T> resultType,
|
||||
String mapFunction, String reduceFunction, MapReduceOptions options) {
|
||||
|
||||
verifyFunctions(mapFunction, reduceFunction);
|
||||
|
||||
Class<?> mappingTarget = domainType != null ? domainType : resultType;
|
||||
Assert.notNull(filterQuery, "Filter query must not be null!");
|
||||
Assert.notNull(domainType, "Domain type must not be null!");
|
||||
Assert.hasText(inputCollectionName, "Input collection name must not be null or empty!");
|
||||
Assert.notNull(resultType, "Result type must not be null!");
|
||||
Assert.notNull(options, "MapReduceOptions must not be null!");
|
||||
Assert.notNull(mapFunction, "Map function must not be null!");
|
||||
Assert.notNull(reduceFunction, "Reduce function must not be null!");
|
||||
assertLocalFunctionNames(mapFunction, reduceFunction);
|
||||
|
||||
return createFlux(inputCollectionName, collection -> {
|
||||
|
||||
Document mappedQuery = queryMapper.getMappedObject(filterQuery.getQueryObject(),
|
||||
mappingContext.getPersistentEntity(mappingTarget));
|
||||
mappingContext.getPersistentEntity(domainType));
|
||||
|
||||
MapReducePublisher<Document> publisher = collection.mapReduce(mapFunction, reduceFunction, Document.class);
|
||||
|
||||
if (options != null) {
|
||||
if (StringUtils.hasText(options.getOutputCollection())) {
|
||||
publisher = publisher.collectionName(options.getOutputCollection());
|
||||
}
|
||||
if (StringUtils.hasText(options.getOutputCollection())) {
|
||||
publisher = publisher.collectionName(options.getOutputCollection());
|
||||
}
|
||||
|
||||
publisher.filter(mappedQuery);
|
||||
publisher.sort(getMappedSortObject(filterQuery, mappingTarget));
|
||||
publisher.sort(getMappedSortObject(filterQuery, domainType));
|
||||
|
||||
if (filterQuery.getMeta() != null && filterQuery.getMeta().getMaxTimeMsec() != null) {
|
||||
if (filterQuery.getMeta().getMaxTimeMsec() != null) {
|
||||
publisher.maxTime(filterQuery.getMeta().getMaxTimeMsec(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
if (filterQuery.getLimit() > 0 || (options != null && options.getLimit() != null)) {
|
||||
if (filterQuery.getLimit() > 0 || (options.getLimit() != null)) {
|
||||
|
||||
if (filterQuery.getLimit() > 0 && (options != null && options.getLimit() != null)) {
|
||||
throw new IllegalArgumentException("which one do ya want?");
|
||||
if (filterQuery.getLimit() > 0 && (options.getLimit() != null)) {
|
||||
throw new IllegalArgumentException("Both Query and MapReduceOptions define a limit. Please provide the limit only via one of the two.");
|
||||
}
|
||||
|
||||
if (filterQuery.getLimit() > 0) {
|
||||
publisher.limit(filterQuery.getLimit());
|
||||
}
|
||||
|
||||
if ((options != null && options.getLimit() != null)) {
|
||||
if (options.getLimit() != null) {
|
||||
publisher.limit(options.getLimit());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Optional<Collation> collation = filterQuery.getCollation();
|
||||
|
||||
if (options != null) {
|
||||
Optionals.ifAllPresent(filterQuery.getCollation(), options.getCollation(), (l, r) -> {
|
||||
throw new IllegalArgumentException(
|
||||
"Both Query and MapReduceOptions define a collation. Please provide the collation only via one of the two.");
|
||||
});
|
||||
|
||||
Optionals.ifAllPresent(filterQuery.getCollation(), options.getCollation(), (l, r) -> {
|
||||
throw new IllegalArgumentException(
|
||||
"Both Query and MapReduceOptions define a collation. Please provide the collation only via one of the two.");
|
||||
});
|
||||
if (options.getCollation().isPresent()) {
|
||||
collation = options.getCollation();
|
||||
}
|
||||
|
||||
if (options.getCollation().isPresent()) {
|
||||
collation = options.getCollation();
|
||||
}
|
||||
|
||||
if (!CollectionUtils.isEmpty(options.getScopeVariables())) {
|
||||
publisher = publisher.scope(new Document(options.getScopeVariables()));
|
||||
}
|
||||
if (options.getLimit() != null && options.getLimit().intValue() > 0) {
|
||||
publisher = publisher.limit(options.getLimit());
|
||||
}
|
||||
if (options.getFinalizeFunction().filter(StringUtils::hasText).isPresent()) {
|
||||
publisher = publisher.finalizeFunction(options.getFinalizeFunction().get());
|
||||
}
|
||||
if (options.getJavaScriptMode() != null) {
|
||||
publisher = publisher.jsMode(options.getJavaScriptMode());
|
||||
}
|
||||
if (options.getOutputSharded().isPresent()) {
|
||||
publisher = publisher.sharded(options.getOutputSharded().get());
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(options.getScopeVariables())) {
|
||||
publisher = publisher.scope(new Document(options.getScopeVariables()));
|
||||
}
|
||||
if (options.getLimit() != null && options.getLimit() > 0) {
|
||||
publisher = publisher.limit(options.getLimit());
|
||||
}
|
||||
if (options.getFinalizeFunction().filter(StringUtils::hasText).isPresent()) {
|
||||
publisher = publisher.finalizeFunction(options.getFinalizeFunction().get());
|
||||
}
|
||||
if (options.getJavaScriptMode() != null) {
|
||||
publisher = publisher.jsMode(options.getJavaScriptMode());
|
||||
}
|
||||
if (options.getOutputSharded().isPresent()) {
|
||||
publisher = publisher.sharded(options.getOutputSharded().get());
|
||||
}
|
||||
|
||||
publisher = collation.map(Collation::toMongoCollation).map(publisher::collation).orElse(publisher);
|
||||
@@ -2013,7 +2012,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
});
|
||||
}
|
||||
|
||||
private void verifyFunctions(String... functions) {
|
||||
private static void assertLocalFunctionNames(String... functions) {
|
||||
|
||||
for (String function : functions) {
|
||||
|
||||
|
||||
@@ -37,8 +37,8 @@ public class MapReduceOptions {
|
||||
|
||||
private Optional<String> outputDatabase = Optional.empty();
|
||||
private MapReduceCommand.OutputType outputType = MapReduceCommand.OutputType.REPLACE;
|
||||
private Map<String, Object> scopeVariables = new HashMap<String, Object>();
|
||||
private Map<String, Object> extraOptions = new HashMap<String, Object>();
|
||||
private Map<String, Object> scopeVariables = new HashMap<>();
|
||||
private Map<String, Object> extraOptions = new HashMap<>();
|
||||
private @Nullable Boolean jsMode;
|
||||
private Boolean verbose = Boolean.TRUE;
|
||||
private @Nullable Integer limit;
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.mongodb.core.Person;
|
||||
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
|
||||
import org.springframework.data.mongodb.core.SimpleReactiveMongoDatabaseFactory;
|
||||
import org.springframework.data.mongodb.core.mapping.Field;
|
||||
@@ -69,7 +70,7 @@ public class ReactiveMapReduceTests {
|
||||
createMapReduceData();
|
||||
|
||||
StepVerifier
|
||||
.create(template.mapReduce(new Query(), null, "jmr1", ValueObject.class, mapFunction, reduceFunction,
|
||||
.create(template.mapReduce(new Query(), Person.class, "jmr1", ValueObject.class, mapFunction, reduceFunction,
|
||||
MapReduceOptions.options()).buffer(4)) //
|
||||
.consumeNextWith(result -> {
|
||||
assertThat(result).containsExactlyInAnyOrder(new ValueObject("a", 1), new ValueObject("b", 2),
|
||||
@@ -145,8 +146,8 @@ public class ReactiveMapReduceTests {
|
||||
@Test // DATAMONGO-1890
|
||||
public void throwsExceptionWhenTryingToLoadFunctionsFromDisk() {
|
||||
|
||||
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> template.mapReduce(new Query(), null,
|
||||
"foo", ValueObject.class, "classpath:map.js", "classpath:reduce.js", null))
|
||||
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> template.mapReduce(new Query(), Person.class,
|
||||
"foo", ValueObject.class, "classpath:map.js", "classpath:reduce.js", MapReduceOptions.options()))
|
||||
.withMessageContaining("classpath:map.js");
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
== What's new in Spring Data MongoDB 2.1
|
||||
* Cursor-based aggregation execution.
|
||||
* <<mongo-template.query.distinct,Distinct queries>> for imperative and reactive Template API.
|
||||
* Support for Map/Reduce through reactive Template API.
|
||||
* <<mongo.mongo-3.validation,`validator` support for collections>>.
|
||||
* <<mongo.jsonSchema,`$jsonSchema` support>> for queries and collection creation.
|
||||
* <<change-streams, Change Stream support>> for imperative and reactive drivers.
|
||||
|
||||
Reference in New Issue
Block a user