diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableUpdateOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableUpdateOperationSupport.java index eeb7dbb558..0e2c0f9e35 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableUpdateOperationSupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableUpdateOperationSupport.java @@ -188,6 +188,7 @@ public UpdateResult upsert() { @Override public UpdateResult replaceFirst() { + if (replacement != null) { return template.replace(query, domainType, replacement, findAndReplaceOptions != null ? findAndReplaceOptions : ReplaceOptions.none(), 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 9bc8410b38..51d93db0ec 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 @@ -1783,8 +1783,6 @@ default UpdateResult replace(Query query, T replacement) { * @param replacement the replacement document. Must not be {@literal null}. * @param collectionName the collection to query. Must not be {@literal null}. * @return the {@link UpdateResult} which lets you access the results of the previous replacement. - * @throws org.springframework.data.mapping.MappingException if the collection name cannot be - * {@link #getCollectionName(Class) derived} from the given replacement value. * @since 4.2 */ default UpdateResult replace(Query query, T replacement, String collectionName) { @@ -1819,52 +1817,9 @@ default UpdateResult replace(Query query, T replacement, ReplaceOptions opti * @param replacement the replacement document. Must not be {@literal null}. * @param options the {@link ReplaceOptions} holding additional information. Must not be {@literal null}. * @return the {@link UpdateResult} which lets you access the results of the previous replacement. - * @throws org.springframework.data.mapping.MappingException if the collection name cannot be - * {@link #getCollectionName(Class) derived} from the given replacement value. * @since 4.2 */ - default UpdateResult replace(Query query, T replacement, ReplaceOptions options, String collectionName) { - - Assert.notNull(replacement, "Replacement must not be null"); - return replace(query, (Class) ClassUtils.getUserClass(replacement), replacement, options, collectionName); - } - - /** - * Replace a single document matching the {@link Criteria} of given {@link Query} with the {@code replacement} - * document taking {@link ReplaceOptions} into account. - * - * @param query the {@link Query} class that specifies the {@link Criteria} used to find a document. The query may - * contain an index {@link Query#withHint(String) hint} or the {@link Query#collation(Collation) collation} - * to use. Must not be {@literal null}. - * @param entityType the type used for mapping the {@link Query} to domain type fields and deriving the collection - * @param replacement the replacement document. Must not be {@literal null}. - * @param options the {@link ReplaceOptions} holding additional information. Must not be {@literal null}. - * from. Must not be {@literal null}. - * @return the {@link UpdateResult} which lets you access the results of the previous replacement. - * @throws org.springframework.data.mapping.MappingException if the collection name cannot be - * {@link #getCollectionName(Class) derived} from the given replacement value. - * @since 4.2 - */ - default UpdateResult replace(Query query, Class entityType, T replacement, ReplaceOptions options) { - return replace(query, entityType, replacement, options, getCollectionName(ClassUtils.getUserClass(entityType))); - } - - /** - * Replace a single document matching the {@link Criteria} of given {@link Query} with the {@code replacement} - * document taking {@link ReplaceOptions} into account. - * - * @param query the {@link Query} class that specifies the {@link Criteria} used to find a document. The query may - * contain an index {@link Query#withHint(String) hint} or the {@link Query#collation(Collation) collation} - * to use. Must not be {@literal null}. - * @param entityType the type used for mapping the {@link Query} to domain type fields. Must not be {@literal null}. - * @param replacement the replacement document. Must not be {@literal null}. - * @param options the {@link ReplaceOptions} holding additional information. Must not be {@literal null}. - * @param collectionName the collection to query. Must not be {@literal null}. - * @return the {@link UpdateResult} which lets you access the results of the previous replacement. - * @since 4.2 - */ - UpdateResult replace(Query query, Class entityType, T replacement, ReplaceOptions options, - String collectionName); + UpdateResult replace(Query query, T replacement, ReplaceOptions options, String collectionName); /** * Returns the underlying {@link MongoConverter}. 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 fc39b4cc5b..ed85da9eb5 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 @@ -2069,7 +2069,13 @@ public List findAllAndRemove(Query query, Class entityClass, String co } @Override - public UpdateResult replace(Query query, Class entityType, T replacement, ReplaceOptions options, + public UpdateResult replace(Query query, T replacement, ReplaceOptions options, String collectionName){ + + Assert.notNull(replacement, "Replacement must not be null"); + return replace(query, (Class) ClassUtils.getUserClass(replacement), replacement, options, collectionName); + } + + protected UpdateResult replace(Query query, Class entityType, T replacement, ReplaceOptions options, String collectionName) { Assert.notNull(query, "Query must not be null"); 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 27adc58fa4..84d617e177 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 @@ -1668,8 +1668,6 @@ default Mono replace(Query query, T replacement) { * @param replacement the replacement document. Must not be {@literal null}. * @param collectionName the collection to query. Must not be {@literal null}. * @return the {@link UpdateResult} which lets you access the results of the previous replacement. - * @throws org.springframework.data.mapping.MappingException if the collection name cannot be - * {@link #getCollectionName(Class) derived} from the given replacement value. * @since 4.2 */ default Mono replace(Query query, T replacement, String collectionName) { @@ -1708,48 +1706,7 @@ default Mono replace(Query query, T replacement, ReplaceOption * {@link #getCollectionName(Class) derived} from the given replacement value. * @since 4.2 */ - default Mono replace(Query query, T replacement, ReplaceOptions options, String collectionName) { - - Assert.notNull(replacement, "Replacement must not be null"); - return replace(query, (Class) ClassUtils.getUserClass(replacement), replacement, options, collectionName); - } - - /** - * Replace a single document matching the {@link Criteria} of given {@link Query} with the {@code replacement} - * document taking {@link ReplaceOptions} into account. - * - * @param query the {@link Query} class that specifies the {@link Criteria} used to find a document. The query may - * contain an index {@link Query#withHint(String) hint} or the {@link Query#collation(Collation) collation} - * to use. Must not be {@literal null}. - * @param entityType the type used for mapping the {@link Query} to domain type fields and deriving the collection - * @param replacement the replacement document. Must not be {@literal null}. - * @param options the {@link ReplaceOptions} holding additional information. Must not be {@literal null}. - * from. Must not be {@literal null}. - * @return the {@link UpdateResult} which lets you access the results of the previous replacement. - * @throws org.springframework.data.mapping.MappingException if the collection name cannot be - * {@link #getCollectionName(Class) derived} from the given replacement value. - * @since 4.2 - */ - default Mono replace(Query query, Class entityType, T replacement, ReplaceOptions options) { - return replace(query, entityType, replacement, options, getCollectionName(ClassUtils.getUserClass(entityType))); - } - - /** - * Replace a single document matching the {@link Criteria} of given {@link Query} with the {@code replacement} - * document taking {@link ReplaceOptions} into account. - * - * @param query the {@link Query} class that specifies the {@link Criteria} used to find a document. The query may - * contain an index {@link Query#withHint(String) hint} or the {@link Query#collation(Collation) collation} - * to use. Must not be {@literal null}. - * @param entityType the type used for mapping the {@link Query} to domain type fields. Must not be {@literal null}. - * @param replacement the replacement document. Must not be {@literal null}. - * @param options the {@link ReplaceOptions} holding additional information. Must not be {@literal null}. - * @param collectionName the collection to query. Must not be {@literal null}. - * @return the {@link UpdateResult} which lets you access the results of the previous replacement. - * @since 4.2 - */ - Mono replace(Query query, Class entityType, T replacement, ReplaceOptions options, - String collectionName); + Mono replace(Query query, T replacement, ReplaceOptions options, String collectionName); /** * Map the results of an ad-hoc query on the collection for the entity class to a stream of objects of the specified 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 95cf4f767f..46e240474d 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 @@ -1961,7 +1961,13 @@ public Flux findAllAndRemove(Query query, Class entityClass, String co } @Override - public Mono replace(Query query, Class entityType, T replacement, ReplaceOptions options, + public Mono replace(Query query, T replacement, ReplaceOptions options, String collectionName) { + + Assert.notNull(replacement, "Replacement must not be null"); + return replace(query, (Class) ClassUtils.getUserClass(replacement), replacement, options, collectionName); + } + + protected Mono replace(Query query, Class entityType, T replacement, ReplaceOptions options, String collectionName) { MongoPersistentEntity entity = mappingContext.getPersistentEntity(entityType); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveUpdateOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveUpdateOperation.java index 13b651d22a..06fcff9d3f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveUpdateOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveUpdateOperation.java @@ -72,13 +72,30 @@ interface TerminatingFindAndModify { Mono findAndModify(); } + /** + * Trigger replaceOne + * execution by calling one of the terminating methods. + * + * @author Christoph Strobl + * @since 4.2 + */ + interface TerminatingReplace { + + /** + * Find first and replace/upsert. + * + * @return never {@literal null}. + */ + Mono replaceFirst(); + } + /** * Compose findAndReplace execution by calling one of the terminating methods. * * @author Mark Paluch * @since 2.1 */ - interface TerminatingFindAndReplace { + interface TerminatingFindAndReplace extends TerminatingReplace { /** * Find, replace and return the first matching document. @@ -202,6 +219,22 @@ interface FindAndModifyWithOptions { TerminatingFindAndModify withOptions(FindAndModifyOptions options); } + /** + * @author Christoph Strobl + * @since 4.2 + */ + interface ReplaceWithOptions extends TerminatingReplace { + + /** + * Explicitly define {@link ReplaceOptions}. + * + * @param options must not be {@literal null}. + * @return new instance of {@link FindAndReplaceOptions}. + * @throws IllegalArgumentException if options is {@literal null}. + */ + TerminatingReplace withOptions(ReplaceOptions options); + } + /** * Define {@link FindAndReplaceOptions}. * @@ -209,7 +242,7 @@ interface FindAndModifyWithOptions { * @author Christoph Strobl * @since 2.1 */ - interface FindAndReplaceWithOptions extends TerminatingFindAndReplace { + interface FindAndReplaceWithOptions extends TerminatingFindAndReplace, ReplaceWithOptions { /** * Explicitly define {@link FindAndReplaceOptions} for the {@link Update}. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveUpdateOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveUpdateOperationSupport.java index 50a4d57bbd..6f7f8242c9 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveUpdateOperationSupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveUpdateOperationSupport.java @@ -165,6 +165,17 @@ public FindAndReplaceWithProjection withOptions(FindAndReplaceOptions options replacement, targetType); } + @Override + public TerminatingReplace withOptions(ReplaceOptions options) { + + FindAndReplaceOptions target = new FindAndReplaceOptions(); + if (options.isUpsert()) { + target.upsert(); + } + return new ReactiveUpdateSupport<>(template, domainType, query, update, collection, findAndModifyOptions, + target, replacement, targetType); + } + @Override public FindAndReplaceWithOptions as(Class resultType) { @@ -174,6 +185,18 @@ public FindAndReplaceWithOptions as(Class resultType) { findAndReplaceOptions, replacement, resultType); } + @Override + public Mono replaceFirst() { + + if (replacement != null) { + return template.replace(query, domainType, replacement, + findAndReplaceOptions != null ? findAndReplaceOptions : ReplaceOptions.none(), getCollectionName()); + } + + return template.replace(query, domainType, update, + findAndReplaceOptions != null ? findAndReplaceOptions : ReplaceOptions.none(), getCollectionName()); + } + private Mono doUpdate(boolean multi, boolean upsert) { return template.doUpdate(getCollectionName(), query, update, domainType, upsert, multi); } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateReplaceTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateReplaceTests.java index 91cfd97039..f32a3b7de5 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateReplaceTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateReplaceTests.java @@ -114,7 +114,7 @@ void replacesExistingDocumentWithRawDoc() { void replacesExistingDocumentWithRawDocMappingQueryAgainstDomainType() { UpdateResult result = template.replace(query(where("name").is("Central Perk Cafe")), Restaurant.class, - Document.parse("{ 'r-name' : 'Central Pork Cafe', 'Borough' : 'Manhattan' }"), ReplaceOptions.none()); + Document.parse("{ 'r-name' : 'Central Pork Cafe', 'Borough' : 'Manhattan' }"), ReplaceOptions.none(), template.getCollectionName(Restaurant.class)); assertThat(result.getMatchedCount()).isEqualTo(1); assertThat(result.getModifiedCount()).isEqualTo(1); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateReplaceTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateReplaceTests.java index 3f4fab96b0..1470be5520 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateReplaceTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateReplaceTests.java @@ -128,7 +128,7 @@ void replacesExistingDocumentWithRawDoc() { void replacesExistingDocumentWithRawDocMappingQueryAgainstDomainType() { Mono result = template.replace(query(where("name").is("Central Perk Cafe")), Restaurant.class, - Document.parse("{ 'r-name' : 'Central Pork Cafe', 'Borough' : 'Manhattan' }"), ReplaceOptions.none()); + Document.parse("{ 'r-name' : 'Central Pork Cafe', 'Borough' : 'Manhattan' }"), ReplaceOptions.none(), template.getCollectionName(Restaurant.class)); result.as(StepVerifier::create).consumeNextWith(it -> { assertThat(it.getMatchedCount()).isEqualTo(1);