Support QueryResultConverter for reactive delete, replace and update operations.
Original Pull Request: #4949
This commit is contained in:
committed by
Mark Paluch
parent
51d64c3768
commit
50aca14d1f
@@ -55,6 +55,10 @@ public interface ExecutableRemoveOperation {
|
||||
*/
|
||||
<T> ExecutableRemove<T> remove(Class<T> domainType);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 5.0
|
||||
*/
|
||||
interface TerminatingResults<T> {
|
||||
|
||||
/**
|
||||
|
||||
@@ -109,6 +109,7 @@ class ExecutableRemoveOperationSupport implements ExecutableRemoveOperation {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public <R> TerminatingResults<R> map(QueryResultConverter<? super T, ? extends R> converter) {
|
||||
return new ExecutableRemoveSupport<>(template, (Class) domainType, query, collection, converter);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,6 @@ import org.bson.types.ObjectId;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.reactivestreams.Subscriber;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
@@ -114,6 +113,7 @@ import org.springframework.data.mongodb.core.mapping.event.*;
|
||||
import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;
|
||||
import org.springframework.data.mongodb.core.query.BasicQuery;
|
||||
import org.springframework.data.mongodb.core.query.Collation;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.Meta;
|
||||
import org.springframework.data.mongodb.core.query.NearQuery;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
@@ -1137,9 +1137,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
return findAndModify(query, update, options, entityClass, getCollectionName(entityClass));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Mono<T> findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options,
|
||||
Class<T> entityClass, String collectionName) {
|
||||
public <S, T> Mono<T> findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options,
|
||||
Class<S> entityClass, String collectionName, QueryResultConverter<? super S, ? extends T> resultConverter) {
|
||||
|
||||
Assert.notNull(options, "Options must not be null ");
|
||||
Assert.notNull(entityClass, "Entity class must not be null");
|
||||
@@ -1156,13 +1155,27 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
}
|
||||
|
||||
return doFindAndModify(collectionName, ReactiveCollectionPreparerDelegate.of(query), query.getQueryObject(),
|
||||
query.getFieldsObject(), getMappedSortObject(query, entityClass), entityClass, update, optionsToUse);
|
||||
query.getFieldsObject(), getMappedSortObject(query, entityClass), entityClass, update, optionsToUse,
|
||||
resultConverter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Mono<T> findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options,
|
||||
Class<T> entityClass, String collectionName) {
|
||||
return findAndModify(query, update, options, entityClass, collectionName, QueryResultConverter.entity());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("NullAway")
|
||||
public <S, T> Mono<T> findAndReplace(Query query, S replacement, FindAndReplaceOptions options, Class<S> entityType,
|
||||
String collectionName, Class<T> resultType) {
|
||||
return findAndReplace(query, replacement, options, entityType, collectionName, resultType,
|
||||
QueryResultConverter.entity());
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullAway")
|
||||
public <S, T, R> Mono<R> findAndReplace(Query query, S replacement, FindAndReplaceOptions options,
|
||||
Class<S> entityType, String collectionName, Class<T> resultType,
|
||||
QueryResultConverter<? super T, ? extends R> resultConverter) {
|
||||
|
||||
Assert.notNull(query, "Query must not be null");
|
||||
Assert.notNull(replacement, "Replacement must not be null");
|
||||
@@ -1199,9 +1212,9 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
mapped.getCollection()));
|
||||
}).flatMap(it -> {
|
||||
|
||||
Mono<T> afterFindAndReplace = doFindAndReplace(it.getCollection(), collectionPreparer, mappedQuery,
|
||||
Mono<R> afterFindAndReplace = doFindAndReplace(it.getCollection(), collectionPreparer, mappedQuery,
|
||||
mappedFields, mappedSort, queryContext.getCollation(entityType).orElse(null), entityType, it.getTarget(),
|
||||
options, projection);
|
||||
options, projection, resultConverter);
|
||||
return afterFindAndReplace.flatMap(saved -> {
|
||||
maybeEmitEvent(new AfterSaveEvent<>(saved, it.getTarget(), it.getCollection()));
|
||||
return maybeCallAfterSave(saved, it.getTarget(), it.getCollection());
|
||||
@@ -2280,6 +2293,43 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
.flatMapSequential(deleteResult -> Flux.fromIterable(list)));
|
||||
}
|
||||
|
||||
<S, T> Flux<T> doFindAndDelete(String collectionName, Query query, Class<S> entityClass,
|
||||
QueryResultConverter<? super S, ? extends T> resultConverter) {
|
||||
|
||||
List<Object> ids = new ArrayList<>();
|
||||
ProjectingReadCallback readCallback = new ProjectingReadCallback(getConverter(),
|
||||
EntityProjection.nonProjecting(entityClass), collectionName);
|
||||
|
||||
QueryResultConverterCallback<S, T> callback = new QueryResultConverterCallback<>(resultConverter, readCallback) {
|
||||
|
||||
@Override
|
||||
public Mono<T> doWith(Document object) {
|
||||
ids.add(object.get("_id"));
|
||||
return super.doWith(object);
|
||||
}
|
||||
};
|
||||
|
||||
Flux<T> flux = doFind(collectionName, ReactiveCollectionPreparerDelegate.of(query), query.getQueryObject(),
|
||||
query.getFieldsObject(), entityClass,
|
||||
new QueryFindPublisherPreparer(query, query.getSortObject(), query.getLimit(), query.getSkip(), entityClass),
|
||||
callback);
|
||||
|
||||
return Flux.from(flux).collectList().filter(it -> !it.isEmpty()).flatMapMany(list -> {
|
||||
|
||||
Criteria[] criterias = ids.stream() //
|
||||
.map(it -> Criteria.where("_id").is(it)) //
|
||||
.toArray(Criteria[]::new);
|
||||
|
||||
Query removeQuery = new Query(criterias.length == 1 ? criterias[0] : new Criteria().orOperator(criterias));
|
||||
if (query.hasReadPreference()) {
|
||||
removeQuery.withReadPreference(query.getReadPreference());
|
||||
}
|
||||
|
||||
return Flux.from(remove(removeQuery, entityClass, collectionName))
|
||||
.flatMapSequential(deleteResult -> Flux.fromIterable(list));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the specified collection using the provided options
|
||||
*
|
||||
@@ -2487,9 +2537,10 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
new ReadDocumentCallback<>(this.mongoConverter, entityClass, collectionName), collectionName);
|
||||
}
|
||||
|
||||
protected <T> Mono<T> doFindAndModify(String collectionName,
|
||||
<S, T> Mono<T> doFindAndModify(String collectionName,
|
||||
CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, Document fields,
|
||||
@Nullable Document sort, Class<T> entityClass, UpdateDefinition update, FindAndModifyOptions options) {
|
||||
@Nullable Document sort, Class<S> entityClass, UpdateDefinition update, FindAndModifyOptions options,
|
||||
QueryResultConverter<? super S, ? extends T> resultConverter) {
|
||||
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
|
||||
UpdateContext updateContext = queryOperations.updateSingleContext(update, query, false);
|
||||
@@ -2508,10 +2559,13 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
serializeToJsonSafely(mappedUpdate), collectionName));
|
||||
}
|
||||
|
||||
EntityProjection<S, ?> projection = EntityProjection.nonProjecting(entityClass);
|
||||
DocumentCallback<T> callback = getResultReader(projection, collectionName, resultConverter);
|
||||
|
||||
return executeFindOneInternal(
|
||||
new FindAndModifyCallback(collectionPreparer, mappedQuery, fields, sort, mappedUpdate,
|
||||
update.getArrayFilters().stream().map(ArrayFilter::asDocument).collect(Collectors.toList()), options),
|
||||
new ReadDocumentCallback<>(this.mongoConverter, entityClass, collectionName), collectionName);
|
||||
callback, collectionName);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2540,7 +2594,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
EntityProjection<T, ?> projection = operations.introspectProjection(resultType, entityType);
|
||||
|
||||
return doFindAndReplace(collectionName, collectionPreparer, mappedQuery, mappedFields, mappedSort, collation,
|
||||
entityType, replacement, options, projection);
|
||||
entityType, replacement, options, projection, QueryResultConverter.entity());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2560,10 +2614,11 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
* {@literal false} and {@link FindAndReplaceOptions#isUpsert() upsert} is {@literal false}.
|
||||
* @since 3.4
|
||||
*/
|
||||
private <T> Mono<T> doFindAndReplace(String collectionName,
|
||||
private <S, T> Mono<T> doFindAndReplace(String collectionName,
|
||||
CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document mappedQuery, Document mappedFields,
|
||||
Document mappedSort, com.mongodb.client.model.Collation collation, Class<?> entityType, Document replacement,
|
||||
FindAndReplaceOptions options, EntityProjection<T, ?> projection) {
|
||||
FindAndReplaceOptions options, EntityProjection<S, ?> projection,
|
||||
QueryResultConverter<? super S, ? extends T> resultConverter) {
|
||||
|
||||
return Mono.defer(() -> {
|
||||
|
||||
@@ -2575,9 +2630,10 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
serializeToJsonSafely(replacement), collectionName));
|
||||
}
|
||||
|
||||
DocumentCallback<T> resultReader = getResultReader(projection, collectionName, resultConverter);
|
||||
|
||||
return executeFindOneInternal(new FindAndReplaceCallback(collectionPreparer, mappedQuery, mappedFields,
|
||||
mappedSort, replacement, collation, options),
|
||||
new ProjectingReadCallback<>(this.mongoConverter, projection, collectionName), collectionName);
|
||||
mappedSort, replacement, collation, options), resultReader, collectionName);
|
||||
|
||||
});
|
||||
}
|
||||
@@ -3124,7 +3180,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
FindPublisher<T> doInCollection(MongoCollection<Document> collection) throws MongoException, DataAccessException;
|
||||
}
|
||||
|
||||
static final class QueryResultConverterCallback<T, R> implements DocumentCallback<R> {
|
||||
static class QueryResultConverterCallback<T, R> implements DocumentCallback<R> {
|
||||
|
||||
private final QueryResultConverter<? super T, ? extends R> converter;
|
||||
private final DocumentCallback<T> delegate;
|
||||
|
||||
@@ -20,6 +20,7 @@ import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.lang.Contract;
|
||||
|
||||
import com.mongodb.client.result.DeleteResult;
|
||||
|
||||
@@ -56,16 +57,22 @@ public interface ReactiveRemoveOperation {
|
||||
<T> ReactiveRemove<T> remove(Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Compose remove execution by calling one of the terminating methods.
|
||||
* @author Christoph Strobl
|
||||
* @since 5.0
|
||||
*/
|
||||
interface TerminatingRemove<T> {
|
||||
interface TerminatingResults<T> {
|
||||
|
||||
/**
|
||||
* Remove all documents matching.
|
||||
* Map the query result to a different type using {@link QueryResultConverter}.
|
||||
*
|
||||
* @return {@link Mono} emitting the {@link DeleteResult}. Never {@literal null}.
|
||||
* @param <R> {@link Class type} of the result.
|
||||
* @param converter the converter, must not be {@literal null}.
|
||||
* @return new instance of {@link ExecutableFindOperation.TerminatingResults}.
|
||||
* @throws IllegalArgumentException if {@link QueryResultConverter converter} is {@literal null}.
|
||||
* @since 5.0
|
||||
*/
|
||||
Mono<DeleteResult> all();
|
||||
@Contract("_ -> new")
|
||||
<R> TerminatingResults<R> map(QueryResultConverter<? super T, ? extends R> converter);
|
||||
|
||||
/**
|
||||
* Remove and return all matching documents. <br/>
|
||||
@@ -78,6 +85,20 @@ public interface ReactiveRemoveOperation {
|
||||
Flux<T> findAndRemove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose remove execution by calling one of the terminating methods.
|
||||
*/
|
||||
interface TerminatingRemove<T> extends TerminatingResults<T> {
|
||||
|
||||
/**
|
||||
* Remove all documents matching.
|
||||
*
|
||||
* @return {@link Mono} emitting the {@link DeleteResult}. Never {@literal null}.
|
||||
*/
|
||||
Mono<DeleteResult> all();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Collection override (optional).
|
||||
*/
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -47,22 +47,25 @@ class ReactiveRemoveOperationSupport implements ReactiveRemoveOperation {
|
||||
|
||||
Assert.notNull(domainType, "DomainType must not be null");
|
||||
|
||||
return new ReactiveRemoveSupport<>(template, domainType, ALL_QUERY, null);
|
||||
return new ReactiveRemoveSupport<>(template, domainType, ALL_QUERY, null, QueryResultConverter.entity());
|
||||
}
|
||||
|
||||
static class ReactiveRemoveSupport<T> implements ReactiveRemove<T>, RemoveWithCollection<T> {
|
||||
static class ReactiveRemoveSupport<S, T> implements ReactiveRemove<T>, RemoveWithCollection<T> {
|
||||
|
||||
private final ReactiveMongoTemplate template;
|
||||
private final Class<T> domainType;
|
||||
private final Class<S> domainType;
|
||||
private final Query query;
|
||||
private final @Nullable String collection;
|
||||
private final QueryResultConverter<? super S, ? extends T> resultConverter;
|
||||
|
||||
ReactiveRemoveSupport(ReactiveMongoTemplate template, Class<T> domainType, Query query, @Nullable String collection) {
|
||||
ReactiveRemoveSupport(ReactiveMongoTemplate template, Class<S> domainType, Query query, @Nullable String collection,
|
||||
QueryResultConverter<? super S, ? extends T> resultConverter) {
|
||||
|
||||
this.template = template;
|
||||
this.domainType = domainType;
|
||||
this.query = query;
|
||||
this.collection = collection;
|
||||
this.resultConverter = resultConverter;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -70,7 +73,7 @@ class ReactiveRemoveOperationSupport implements ReactiveRemoveOperation {
|
||||
|
||||
Assert.hasText(collection, "Collection must not be null nor empty");
|
||||
|
||||
return new ReactiveRemoveSupport<>(template, domainType, query, collection);
|
||||
return new ReactiveRemoveSupport<>(template, domainType, query, collection, resultConverter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -78,7 +81,7 @@ class ReactiveRemoveOperationSupport implements ReactiveRemoveOperation {
|
||||
|
||||
Assert.notNull(query, "Query must not be null");
|
||||
|
||||
return new ReactiveRemoveSupport<>(template, domainType, query, collection);
|
||||
return new ReactiveRemoveSupport<>(template, domainType, query, collection, resultConverter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -94,7 +97,13 @@ class ReactiveRemoveOperationSupport implements ReactiveRemoveOperation {
|
||||
|
||||
String collectionName = getCollectionName();
|
||||
|
||||
return template.doFindAndDelete(collectionName, query, domainType);
|
||||
return template.doFindAndDelete(collectionName, query, domainType, resultConverter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public <R> TerminatingResults<R> map(QueryResultConverter<? super T, ? extends R> converter) {
|
||||
return new ReactiveRemoveSupport<>(template, (Class) domainType, query, collection, converter);
|
||||
}
|
||||
|
||||
private String getCollectionName() {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationUpdate;
|
||||
@@ -64,6 +65,18 @@ public interface ReactiveUpdateOperation {
|
||||
*/
|
||||
interface TerminatingFindAndModify<T> {
|
||||
|
||||
/**
|
||||
* Map the query result to a different type using {@link QueryResultConverter}.
|
||||
*
|
||||
* @param <R> {@link Class type} of the result.
|
||||
* @param converter the converter, must not be {@literal null}.
|
||||
* @return new instance of {@link TerminatingFindAndModify}.
|
||||
* @throws IllegalArgumentException if {@link QueryResultConverter converter} is {@literal null}.
|
||||
* @since 5.0
|
||||
*/
|
||||
@Contract("_ -> new")
|
||||
<R> TerminatingFindAndModify<R> map(QueryResultConverter<? super T, ? extends R> converter);
|
||||
|
||||
/**
|
||||
* Find, modify and return the first matching document.
|
||||
*
|
||||
@@ -97,6 +110,18 @@ public interface ReactiveUpdateOperation {
|
||||
*/
|
||||
interface TerminatingFindAndReplace<T> extends TerminatingReplace {
|
||||
|
||||
/**
|
||||
* Map the query result to a different type using {@link QueryResultConverter}.
|
||||
*
|
||||
* @param <R> {@link Class type} of the result.
|
||||
* @param converter the converter, must not be {@literal null}.
|
||||
* @return new instance of {@link TerminatingFindAndModify}.
|
||||
* @throws IllegalArgumentException if {@link QueryResultConverter converter} is {@literal null}.
|
||||
* @since 5.0
|
||||
*/
|
||||
@Contract("_ -> new")
|
||||
<R> TerminatingFindAndReplace<R> map(QueryResultConverter<? super T, ? extends R> converter);
|
||||
|
||||
/**
|
||||
* Find, replace and return the first matching document.
|
||||
*
|
||||
|
||||
@@ -47,10 +47,10 @@ class ReactiveUpdateOperationSupport implements ReactiveUpdateOperation {
|
||||
|
||||
Assert.notNull(domainType, "DomainType must not be null");
|
||||
|
||||
return new ReactiveUpdateSupport<>(template, domainType, ALL_QUERY, null, null, null, null, null, domainType);
|
||||
return new ReactiveUpdateSupport<>(template, domainType, ALL_QUERY, null, null, null, null, null, domainType, QueryResultConverter.entity());
|
||||
}
|
||||
|
||||
static class ReactiveUpdateSupport<T>
|
||||
static class ReactiveUpdateSupport<S, T>
|
||||
implements ReactiveUpdate<T>, UpdateWithCollection<T>, UpdateWithQuery<T>, TerminatingUpdate<T>,
|
||||
FindAndReplaceWithOptions<T>, FindAndReplaceWithProjection<T>, TerminatingFindAndReplace<T> {
|
||||
|
||||
@@ -62,11 +62,12 @@ class ReactiveUpdateOperationSupport implements ReactiveUpdateOperation {
|
||||
private final @Nullable FindAndModifyOptions findAndModifyOptions;
|
||||
private final @Nullable FindAndReplaceOptions findAndReplaceOptions;
|
||||
private final @Nullable Object replacement;
|
||||
private final Class<T> targetType;
|
||||
private final Class<S> targetType;
|
||||
private final QueryResultConverter<? super S, ? extends T> resultConverter;
|
||||
|
||||
ReactiveUpdateSupport(ReactiveMongoTemplate template, Class<?> domainType, Query query, @Nullable UpdateDefinition update,
|
||||
@Nullable String collection, @Nullable FindAndModifyOptions findAndModifyOptions, @Nullable FindAndReplaceOptions findAndReplaceOptions,
|
||||
@Nullable Object replacement, Class<T> targetType) {
|
||||
@Nullable Object replacement, Class<S> targetType, QueryResultConverter<? super S, ? extends T> resultConverter) {
|
||||
|
||||
this.template = template;
|
||||
this.domainType = domainType;
|
||||
@@ -77,6 +78,7 @@ class ReactiveUpdateOperationSupport implements ReactiveUpdateOperation {
|
||||
this.findAndReplaceOptions = findAndReplaceOptions;
|
||||
this.replacement = replacement;
|
||||
this.targetType = targetType;
|
||||
this.resultConverter = resultConverter;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -85,7 +87,7 @@ class ReactiveUpdateOperationSupport implements ReactiveUpdateOperation {
|
||||
Assert.notNull(update, "Update must not be null");
|
||||
|
||||
return new ReactiveUpdateSupport<>(template, domainType, query, update, collection, findAndModifyOptions,
|
||||
findAndReplaceOptions, replacement, targetType);
|
||||
findAndReplaceOptions, replacement, targetType, resultConverter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -94,7 +96,7 @@ class ReactiveUpdateOperationSupport implements ReactiveUpdateOperation {
|
||||
Assert.hasText(collection, "Collection must not be null nor empty");
|
||||
|
||||
return new ReactiveUpdateSupport<>(template, domainType, query, update, collection, findAndModifyOptions,
|
||||
findAndReplaceOptions, replacement, targetType);
|
||||
findAndReplaceOptions, replacement, targetType, resultConverter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -108,14 +110,14 @@ class ReactiveUpdateOperationSupport implements ReactiveUpdateOperation {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("NullAway")
|
||||
@SuppressWarnings({"unchecked", "rawtypes", "NullAway"})
|
||||
public Mono<T> findAndModify() {
|
||||
|
||||
String collectionName = getCollectionName();
|
||||
|
||||
return template.findAndModify(query, update,
|
||||
findAndModifyOptions != null ? findAndModifyOptions : FindAndModifyOptions.none(), targetType,
|
||||
collectionName);
|
||||
findAndModifyOptions != null ? findAndModifyOptions : FindAndModifyOptions.none(), (Class) targetType,
|
||||
collectionName, resultConverter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -126,7 +128,7 @@ class ReactiveUpdateOperationSupport implements ReactiveUpdateOperation {
|
||||
|
||||
return template.findAndReplace(query, replacement,
|
||||
findAndReplaceOptions != null ? findAndReplaceOptions : FindAndReplaceOptions.none(), (Class) domainType,
|
||||
getCollectionName(), targetType);
|
||||
getCollectionName(), targetType, resultConverter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -135,7 +137,7 @@ class ReactiveUpdateOperationSupport implements ReactiveUpdateOperation {
|
||||
Assert.notNull(query, "Query must not be null");
|
||||
|
||||
return new ReactiveUpdateSupport<>(template, domainType, query, update, collection, findAndModifyOptions,
|
||||
findAndReplaceOptions, replacement, targetType);
|
||||
findAndReplaceOptions, replacement, targetType, resultConverter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -149,7 +151,7 @@ class ReactiveUpdateOperationSupport implements ReactiveUpdateOperation {
|
||||
Assert.notNull(options, "Options must not be null");
|
||||
|
||||
return new ReactiveUpdateSupport<>(template, domainType, query, update, collection, options,
|
||||
findAndReplaceOptions, replacement, targetType);
|
||||
findAndReplaceOptions, replacement, targetType, resultConverter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -158,7 +160,7 @@ class ReactiveUpdateOperationSupport implements ReactiveUpdateOperation {
|
||||
Assert.notNull(replacement, "Replacement must not be null");
|
||||
|
||||
return new ReactiveUpdateSupport<>(template, domainType, query, update, collection, findAndModifyOptions,
|
||||
findAndReplaceOptions, replacement, targetType);
|
||||
findAndReplaceOptions, replacement, targetType, resultConverter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -167,7 +169,7 @@ class ReactiveUpdateOperationSupport implements ReactiveUpdateOperation {
|
||||
Assert.notNull(options, "Options must not be null");
|
||||
|
||||
return new ReactiveUpdateSupport<>(template, domainType, query, update, collection, findAndModifyOptions, options,
|
||||
replacement, targetType);
|
||||
replacement, targetType, resultConverter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -178,7 +180,7 @@ class ReactiveUpdateOperationSupport implements ReactiveUpdateOperation {
|
||||
target.upsert();
|
||||
}
|
||||
return new ReactiveUpdateSupport<>(template, domainType, query, update, collection, findAndModifyOptions,
|
||||
target, replacement, targetType);
|
||||
target, replacement, targetType, resultConverter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -187,7 +189,13 @@ class ReactiveUpdateOperationSupport implements ReactiveUpdateOperation {
|
||||
Assert.notNull(resultType, "ResultType must not be null");
|
||||
|
||||
return new ReactiveUpdateSupport<>(template, domainType, query, update, collection, findAndModifyOptions,
|
||||
findAndReplaceOptions, replacement, resultType);
|
||||
findAndReplaceOptions, replacement, resultType, QueryResultConverter.entity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R> ReactiveUpdateSupport<S, R> map(QueryResultConverter<? super T, ? extends R> converter) {
|
||||
return new ReactiveUpdateSupport<>(template, domainType, query, update, collection, findAndModifyOptions,
|
||||
findAndReplaceOptions, replacement, targetType, this.resultConverter.andThen(converter));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,13 +15,14 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.springframework.data.mongodb.core.query.Criteria.*;
|
||||
import static org.springframework.data.mongodb.core.query.Query.*;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.data.mongodb.core.query.Criteria.where;
|
||||
import static org.springframework.data.mongodb.core.query.Query.query;
|
||||
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -108,6 +109,15 @@ class ReactiveRemoveOperationSupportTests {
|
||||
.expectNext(han).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // GH-4949
|
||||
void removeConvertAndReturnAllMatching() {
|
||||
|
||||
template.remove(Person.class).matching(query(where("firstname").is("han"))).map((raw, it) -> Optional.of(it.get()))
|
||||
.findAndRemove().as(StepVerifier::create).expectNext(Optional.of(han)).verifyComplete();
|
||||
|
||||
template.findById(han.id, Person.class).as(StepVerifier::create).verifyComplete();
|
||||
}
|
||||
|
||||
@org.springframework.data.mongodb.core.mapping.Document(collection = STAR_WARS)
|
||||
static class Person {
|
||||
|
||||
|
||||
@@ -15,13 +15,15 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.springframework.data.mongodb.core.query.Criteria.*;
|
||||
import static org.springframework.data.mongodb.core.query.Query.*;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.springframework.data.mongodb.core.query.Criteria.where;
|
||||
import static org.springframework.data.mongodb.core.query.Query.query;
|
||||
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.bson.BsonString;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -175,6 +177,18 @@ class ReactiveUpdateOperationSupportTests {
|
||||
"Han");
|
||||
}
|
||||
|
||||
@Test // GH-4949
|
||||
void findAndModifyWithWithResultConversion() {
|
||||
|
||||
template.update(Jedi.class).inCollection(STAR_WARS).matching(query(where("_id").is(han.getId())))
|
||||
.apply(new Update().set("name", "Han")).map((raw, it) -> Optional.of(it.get())).findAndModify()
|
||||
.as(StepVerifier::create).consumeNextWith(actual -> assertThat(actual.get().getName()).isEqualTo("han"))
|
||||
.verifyComplete();
|
||||
|
||||
assertThat(blocking.findOne(queryHan(), Person.class)).isNotEqualTo(han).hasFieldOrPropertyWithValue("firstname",
|
||||
"Han");
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1719
|
||||
void findAndModifyWithOptions() {
|
||||
|
||||
@@ -225,6 +239,18 @@ class ReactiveUpdateOperationSupportTests {
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // GH-4949
|
||||
void findAndReplaceWithResultConversion() {
|
||||
|
||||
Person luke = new Person();
|
||||
luke.firstname = "Luke";
|
||||
|
||||
template.update(Person.class).matching(queryHan()).replaceWith(luke).map((raw, it) -> Optional.of(it.get())).findAndReplace() //
|
||||
.as(StepVerifier::create).consumeNextWith(it -> {
|
||||
assertThat(it.get().getFirstname()).isEqualTo(han.firstname);
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1827
|
||||
void findAndReplaceWithCollection() {
|
||||
|
||||
|
||||
@@ -116,6 +116,19 @@ WARNING: Projections must not be applied to xref:mongodb/mapping/document-refere
|
||||
|
||||
You can switch between retrieving a single entity and retrieving multiple entities as a `List` or a `Stream` through the terminating methods: `first()`, `one()`, `all()`, or `stream()`.
|
||||
|
||||
Results can be contextually post-processed by using a `QueryResultConverter` that has access to both the raw result `Document` and the already mapped object by calling `map(...)` as outlined below.
|
||||
|
||||
[source,java]
|
||||
====
|
||||
----
|
||||
List<Optional<Jedi>> result = template.query(Person.class)
|
||||
.as(Jedi.class)
|
||||
.matching(query(where("firstname").is("luke")))
|
||||
.map((document, reader) -> Optional.of(reader.get()))
|
||||
.all();
|
||||
----
|
||||
====
|
||||
|
||||
When writing a geo-spatial query with `near(NearQuery)`, the number of terminating methods is altered to include only the methods that are valid for running a `geoNear` command in MongoDB (fetching entities as a `GeoResult` within `GeoResults`), as the following example shows:
|
||||
|
||||
[tabs]
|
||||
|
||||
Reference in New Issue
Block a user