diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryExecution.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryExecution.java index be422616e..2f6766651 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryExecution.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryExecution.java @@ -39,6 +39,7 @@ import org.springframework.data.repository.support.PageableExecutionUtils; import org.springframework.data.util.TypeInformation; import com.mongodb.client.result.DeleteResult; +import org.springframework.util.ClassUtils; /** * Set of classes to contain query execution strategies. Depending (mostly) on the return type of a @@ -255,6 +256,10 @@ interface MongoQueryExecution { return operations.findAllAndRemove(query, type, collectionName); } + if(method.isQueryForEntity() && !ClassUtils.isPrimitiveOrWrapper(method.getReturnedObjectType())) { + return operations.findAndRemove(query, type, collectionName); + } + DeleteResult writeResult = operations.remove(query, type, collectionName); return writeResult.wasAcknowledged() ? writeResult.getDeletedCount() : 0L; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryExecution.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryExecution.java index 111005bee..863b904eb 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryExecution.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryExecution.java @@ -129,6 +129,10 @@ interface ReactiveMongoQueryExecution { return operations.findAllAndRemove(query, type, collection); } + if(method.isQueryForEntity() && !ClassUtils.isPrimitiveOrWrapper(method.getReturnedObjectType())) { + return operations.findAndRemove(query, type, collection); + } + return operations.remove(query, type, collection) .map(deleteResult -> deleteResult.wasAcknowledged() ? deleteResult.getDeletedCount() : 0L); } 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 b4f4d824a..52446d440 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 @@ -741,6 +741,13 @@ public abstract class AbstractPersonRepositoryIntegrationTests { assertThat(repository.deletePersonByLastname("Beauford")).isEqualTo(1L); } + @Test // DATAMONGO-1997 + public void deleteByShouldResultWrappedInOptionalCorrectly() { + + assertThat(repository.deleteOptionalByLastname("Beauford")).isPresent(); + assertThat(repository.deleteOptionalByLastname("dorfuaeB")).isNotPresent(); + } + @Test // DATAMONGO-566 public void deleteByShouldReturnZeroInCaseNoDocumentHasBeenRemovedAndReturnTypeIsNumber() { assertThat(repository.deletePersonByLastname("dorfuaeB")).isEqualTo(0L); 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 eb5fb0748..b65bd3d8c 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 @@ -264,6 +264,9 @@ public interface PersonRepository extends MongoRepository, Query // DATAMONGO-566 Long deletePersonByLastname(String lastname); + // DATAMONGO-1997 + Optional deleteOptionalByLastname(String lastname); + // DATAMONGO-566 @Query(value = "{ 'lastname' : ?0 }", delete = true) List removeByLastnameUsingAnnotatedQuery(String lastname); 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 e38c6782c..ae294d5eb 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 @@ -589,6 +589,28 @@ public class ReactiveMongoRepositoryTests { .verifyComplete(); } + @Test // DATAMONGO-1997 + public void deleteByShouldAllowDeletedCountAsResult() { + + repository.deleteCountByLastname(dave.getLastname()) // + .as(StepVerifier::create) // + .expectNext(2L) // + .verifyComplete(); + } + + @Test // DATAMONGO-1997 + public void deleteByShouldAllowSingleDocumentRemovalCorrectly() { + + repository.deleteSinglePersonByLastname(carter.getLastname()) // + .as(StepVerifier::create) // + .expectNext(carter) // + .verifyComplete(); + + repository.deleteSinglePersonByLastname("dorfuaeB") // + .as(StepVerifier::create) // + .verifyComplete(); + } + interface ReactivePersonRepository extends ReactiveMongoRepository, ReactiveQuerydslPredicateExecutor { @@ -661,6 +683,10 @@ public class ReactiveMongoRepositoryTests { Mono findDocumentById(String id); Mono deleteByLastname(String lastname); + + Mono deleteCountByLastname(String lastname); + + Mono deleteSinglePersonByLastname(String lastname); } interface ReactiveContactRepository extends ReactiveMongoRepository {} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryExecutionUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryExecutionUnitTests.java index 18d3f8f72..3f7ad9437 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryExecutionUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryExecutionUnitTests.java @@ -16,13 +16,13 @@ package org.springframework.data.mongodb.repository.query; import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collections; +import java.util.Optional; import org.junit.Before; import org.junit.Test; @@ -68,6 +68,7 @@ import com.mongodb.client.result.DeleteResult; * @author Mark Paluch * @author Oliver Gierke * @author Artyom Gabeev + * @author Christoph Strobl * @soundtrack U Can't Touch This - MC Hammer */ @RunWith(MockitoJUnitRunner.class) @@ -181,6 +182,9 @@ public class MongoQueryExecutionUnitTests { @Test // DATAMONGO-2351 public void acknowledgedDeleteReturnsDeletedCount() { + Method method = ReflectionUtils.findMethod(PersonRepository.class, "deleteAllByLastname", String.class); + MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, factory, context); + when(mongoOperationsMock.remove(any(Query.class), any(Class.class), anyString())) .thenReturn(DeleteResult.acknowledged(10)); @@ -190,14 +194,48 @@ public class MongoQueryExecutionUnitTests { @Test // DATAMONGO-2351 public void unacknowledgedDeleteReturnsZeroDeletedCount() { + Method method = ReflectionUtils.findMethod(PersonRepository.class, "deleteAllByLastname", String.class); + MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, factory, context); + when(mongoOperationsMock.remove(any(Query.class), any(Class.class), anyString())) .thenReturn(DeleteResult.unacknowledged()); assertThat(new DeleteExecution(mongoOperationsMock, queryMethod).execute(new Query())).isEqualTo(0L); } + @Test // DATAMONGO-1997 + public void deleteExecutionWithEntityReturnTypeTriggersFindAndRemove() { + + Method method = ReflectionUtils.findMethod(PersonRepository.class, "deleteByLastname", String.class); + MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, factory, context); + + Person person = new Person(); + + when(mongoOperationsMock.findAndRemove(any(Query.class), any(Class.class), anyString())).thenReturn(person); + + assertThat(new DeleteExecution(mongoOperationsMock, queryMethod).execute(new Query())).isEqualTo(person); + } +// @Test // DATAMONGO-1997 +// public void deleteExecutionWrapsEmptyResultInOptionalCorrectly() { +// +// Method method = ReflectionUtils.findMethod(PersonRepository.class, "deleteByLastname", String.class); +// MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, factory, context); +// +// Person person = new Person(); +// +// when(mongoOperationsMock.findAndRemove(any(Query.class), any(Class.class), anyString())).thenReturn(null); +// +// assertThat(new DeleteExecution(mongoOperationsMock, queryMethod).execute(new Query())).isEqualTo(Optional.empty()); +// } + interface PersonRepository extends Repository { GeoPage findByLocationNear(Point point, Distance distance, Pageable pageable); + + Long deleteAllByLastname(String lastname); + + Person deleteByLastname(String lastname); + + Optional deletePersonByLastname(String lastname); } } diff --git a/src/main/asciidoc/reference/mongo-repositories.adoc b/src/main/asciidoc/reference/mongo-repositories.adoc index d7a2e01a4..69f5d7156 100644 --- a/src/main/asciidoc/reference/mongo-repositories.adoc +++ b/src/main/asciidoc/reference/mongo-repositories.adoc @@ -301,15 +301,22 @@ The keywords in the preceding table can be used in conjunction with `delete…By ---- public interface PersonRepository extends MongoRepository { - List deleteByLastname(String lastname); + List deleteByLastname(String lastname); <1> - Long deletePersonByLastname(String lastname); + Long deletePersonByLastname(String lastname); <2> + + @Nullable + Person deleteSingleByLastname(String lastname); <3> + + Optional deleteByBirthdate(Date birthdate); <4> } ---- +<1> Using a return type of `List` retrieves and returns all matching documents before actually deleting them. +<2> A numeric return type directly removes the matching documents, returning the total number of documents removed. +<3> A single domain type result retrieves and removes the first matching document. +<4> Same as in 3 but wrapped in an `Optional` type. ==== -Using a return type of `List` retrieves and returns all matching documents before actually deleting them. A numeric return type directly removes the matching documents, returning the total number of documents removed. - [[mongodb.repositories.queries.geo-spatial]] === Geo-spatial Repository Queries