From 794b026f73d827d8d32c050ccd9e4d3c47871619 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 7 Jun 2018 09:57:51 +0200 Subject: [PATCH] DATAMONGO-1979 - Polishing. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename QueryUtils method to decorateSort(…) to reflect the nature of the method. Add missing generics. Convert ReactiveMongoRepositoryTests to AssertJ. Add missing verifyComplete() steps to StepVerifier. Slight tweaks to Javadoc and reference docs. Original pull request: #566. --- .../data/mongodb/repository/Query.java | 6 +-- .../repository/query/AbstractMongoQuery.java | 2 +- .../query/AbstractReactiveMongoQuery.java | 2 +- .../repository/query/MongoQueryMethod.java | 8 ++-- .../mongodb/repository/query/QueryUtils.java | 6 +-- .../query/ReactiveMongoQueryMethod.java | 2 +- .../ReactiveMongoRepositoryTests.java | 44 +++++++++---------- src/main/asciidoc/new-features.adoc | 1 + .../reference/mongo-repositories.adoc | 12 ++--- 9 files changed, 43 insertions(+), 40 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/Query.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/Query.java index 3e04d56ba..9967385c4 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/Query.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/Query.java @@ -80,14 +80,14 @@ public @interface Query { /** * Defines a default sort order for the given query.
- * NOTE The so set defaults can be altered / overwritten via an explicit + * NOTE: The so set defaults can be altered / overwritten using an explicit * {@link org.springframework.data.domain.Sort} argument of the query method. * *
 	 * 
-	 *     
+	 *
 	 * 		@Query(sort = "{ age : -1 }") // order by age descending
-	 * 		List findByFirstname(String firstname); 	 
+	 * 		List findByFirstname(String firstname);
 	 * 
 	 * 
* diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java index bbb77a9b9..e11a567db 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java @@ -151,7 +151,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { return query; } - return QueryUtils.sneakInDefaultSort(query, Document.parse(method.getAnnotatedSort())); + return QueryUtils.decorateSort(query, Document.parse(method.getAnnotatedSort())); } /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java index 53427726a..f850f3f2f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java @@ -193,7 +193,7 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery { return query; } - return QueryUtils.sneakInDefaultSort(query, Document.parse(method.getAnnotatedSort())); + return QueryUtils.decorateSort(query, Document.parse(method.getAnnotatedSort())); } /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java index 2aa8e8edf..eb61f3c85 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java @@ -80,7 +80,7 @@ public class MongoQueryMethod extends QueryMethod { this.method = method; this.mappingContext = mappingContext; - this.annotationCache = new ConcurrentReferenceHashMap(); + this.annotationCache = new ConcurrentReferenceHashMap<>(); } /* @@ -124,6 +124,7 @@ public class MongoQueryMethod extends QueryMethod { * * @return */ + @Nullable String getFieldSpecification() { return lookupQueryAnnotation() // @@ -159,7 +160,7 @@ public class MongoQueryMethod extends QueryMethod { MongoPersistentEntity collectionEntity = domainClass.isAssignableFrom(returnedObjectType) ? returnedEntity : managedEntity; - this.metadata = new SimpleMongoEntityMetadata((Class) returnedEntity.getType(), + this.metadata = new SimpleMongoEntityMetadata<>((Class) returnedEntity.getType(), collectionEntity); } } @@ -315,9 +316,10 @@ public class MongoQueryMethod extends QueryMethod { "Expected to find @Query annotation but did not. Make sure to check hasAnnotatedSort() before.")); } + @SuppressWarnings("unchecked") private Optional doFindAnnotation(Class annotationType) { - return (Optional) this.annotationCache.computeIfAbsent(annotationType, + return (Optional) this.annotationCache.computeIfAbsent(annotationType, it -> Optional.ofNullable(AnnotatedElementUtils.findMergedAnnotation(method, it))); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java index 68d953ca7..fb938740a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java @@ -31,14 +31,14 @@ import org.springframework.data.mongodb.core.query.Query; class QueryUtils { /** - * Add a default sort expression to the given Query. Attributes of the given {@code sort} may be overwritten by the - * sort explicitly defined by the {@link Query} itself. + * Decorate {@link Query} and add a default sort expression to the given {@link Query}. Attributes of the given + * {@code sort} may be overwritten by the sort explicitly defined by the {@link Query} itself. * * @param query the {@link Query} to decorate. * @param defaultSort the default sort expression to apply to the query. * @return the query having the given {@code sort} applied. */ - static Query sneakInDefaultSort(Query query, Document defaultSort) { + static Query decorateSort(Query query, Document defaultSort) { if (defaultSort.isEmpty()) { return query; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryMethod.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryMethod.java index 0ad0d91d5..91af6b342 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryMethod.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryMethod.java @@ -79,7 +79,7 @@ public class ReactiveMongoQueryMethod extends MongoQueryMethod { ClassUtils.getShortName(method.getDeclaringClass()), method.getName())); } - if (!multiWrapper && !singleWrapperWithWrappedPageableResult) { + if (!multiWrapper) { throw new IllegalStateException(String.format( "Method has to use a either multi-item reactive wrapper return type or a wrapped Page/Slice type. Offending method: %s", method.toString())); 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 ade137d1b..1651bbb3e 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 @@ -15,13 +15,11 @@ */ package org.springframework.data.mongodb.repository; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; +import static org.assertj.core.api.Assertions.offset; import static org.springframework.data.domain.Sort.Direction.*; +import static org.springframework.data.mongodb.test.util.Assertions.assertThat; import lombok.NoArgsConstructor; -import org.hamcrest.collection.IsIterableContainingInOrder; -import org.springframework.data.domain.Sort.Direction; import reactor.core.Disposable; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -45,6 +43,7 @@ import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Sort.Direction; import org.springframework.data.geo.Circle; import org.springframework.data.geo.Distance; import org.springframework.data.geo.GeoResult; @@ -130,12 +129,12 @@ public class ReactiveMongoRepositoryTests implements BeanClassLoaderAware, BeanF @Test // DATAMONGO-1444 public void shouldFindOneByLastName() { - StepVerifier.create(repository.findOneByLastname(carter.getLastname())).expectNext(carter); + StepVerifier.create(repository.findOneByLastname(carter.getLastname())).expectNext(carter).verifyComplete(); } @Test // DATAMONGO-1444 public void shouldFindOneByPublisherOfLastName() { - StepVerifier.create(repository.findByLastname(Mono.just(carter.getLastname()))).expectNext(carter); + StepVerifier.create(repository.findByLastname(Mono.just(carter.getLastname()))).expectNext(carter).verifyComplete(); } @Test // DATAMONGO-1444 @@ -164,6 +163,7 @@ public class ReactiveMongoRepositoryTests implements BeanClassLoaderAware, BeanF @Test // DATAMONGO-1444 public void shouldFindByLastNameAndSort() { + StepVerifier.create(repository.findByLastname("Matthews", Sort.by(ASC, "age"))) // .expectNext(oliver, dave) // .verifyComplete(); @@ -188,11 +188,11 @@ public class ReactiveMongoRepositoryTests implements BeanClassLoaderAware, BeanF Disposable disposable = cappedRepository.findByKey("value").doOnNext(documents::add).subscribe(); - assertThat(documents.poll(5, TimeUnit.SECONDS), is(notNullValue())); + assertThat(documents.poll(5, TimeUnit.SECONDS)).isNotNull(); StepVerifier.create(template.insert(new Capped("value", Math.random()))).expectNextCount(1).verifyComplete(); - assertThat(documents.poll(5, TimeUnit.SECONDS), is(notNullValue())); - assertThat(documents.isEmpty(), is(true)); + assertThat(documents.poll(5, TimeUnit.SECONDS)).isNotNull(); + assertThat(documents).isEmpty(); disposable.dispose(); } @@ -213,16 +213,16 @@ public class ReactiveMongoRepositoryTests implements BeanClassLoaderAware, BeanF Disposable disposable = cappedRepository.findProjectionByKey("value").doOnNext(documents::add).subscribe(); CappedProjection projection1 = documents.poll(5, TimeUnit.SECONDS); - assertThat(projection1, is(notNullValue())); - assertThat(projection1.getRandom(), is(not(0))); + assertThat(projection1).isNotNull(); + assertThat(projection1.getRandom()).isNotEqualTo(0); StepVerifier.create(template.insert(new Capped("value", Math.random()))).expectNextCount(1).verifyComplete(); CappedProjection projection2 = documents.poll(5, TimeUnit.SECONDS); - assertThat(projection2, is(notNullValue())); - assertThat(projection2.getRandom(), is(not(0))); + assertThat(projection2).isNotNull(); + assertThat(projection2.getRandom()).isNotEqualTo(0); - assertThat(documents.isEmpty(), is(true)); + assertThat(documents).isEmpty(); disposable.dispose(); } @@ -263,8 +263,8 @@ public class ReactiveMongoRepositoryTests implements BeanClassLoaderAware, BeanF new Distance(2000, Metrics.KILOMETERS)) // ).consumeNextWith(actual -> { - assertThat(actual.getDistance().getValue(), is(closeTo(1, 1))); - assertThat(actual.getContent(), is(equalTo(dave))); + assertThat(actual.getDistance().getValue()).isCloseTo(1, offset(1d)); + assertThat(actual.getContent()).isEqualTo(dave); }).verifyComplete(); } @@ -280,8 +280,8 @@ public class ReactiveMongoRepositoryTests implements BeanClassLoaderAware, BeanF PageRequest.of(0, 10))) // .consumeNextWith(actual -> { - assertThat(actual.getDistance().getValue(), is(closeTo(1, 1))); - assertThat(actual.getContent(), is(equalTo(dave))); + assertThat(actual.getDistance().getValue()).isCloseTo(1, offset(1d)); + assertThat(actual.getContent()).isEqualTo(dave); }).verifyComplete(); } @@ -313,8 +313,8 @@ public class ReactiveMongoRepositoryTests implements BeanClassLoaderAware, BeanF public void findAppliesAnnotatedSort() { repository.findByAgeGreaterThan(40).collectList().as(StepVerifier::create).consumeNextWith(result -> { - assertThat(result, IsIterableContainingInOrder.contains(carter, boyd, dave, leroi)); - }); + assertThat(result).containsSequence(carter, boyd, dave, leroi); + }).verifyComplete(); } @Test // DATAMONGO-1979 @@ -322,8 +322,8 @@ public class ReactiveMongoRepositoryTests implements BeanClassLoaderAware, BeanF repository.findByAgeGreaterThan(40, Sort.by(Direction.ASC, "age")).collectList().as(StepVerifier::create) .consumeNextWith(result -> { - assertThat(result, IsIterableContainingInOrder.contains(leroi, dave, boyd, carter)); - }); + assertThat(result).containsSequence(leroi, dave, boyd, carter); + }).verifyComplete(); } interface ReactivePersonRepository extends ReactiveMongoRepository { diff --git a/src/main/asciidoc/new-features.adoc b/src/main/asciidoc/new-features.adoc index c6dbbecd1..d3ed101f3 100644 --- a/src/main/asciidoc/new-features.adoc +++ b/src/main/asciidoc/new-features.adoc @@ -13,6 +13,7 @@ * Tailable cursors for imperative driver. * <> support for the imperative and reactive Template APIs. * <> support and a MongoDB-specific transaction manager implementation. +* <> using `@Query(sort=…)`. [[new-features.2-0-0]] == What's New in Spring Data MongoDB 2.0 diff --git a/src/main/asciidoc/reference/mongo-repositories.adoc b/src/main/asciidoc/reference/mongo-repositories.adoc index 93c00db0c..5087354a8 100644 --- a/src/main/asciidoc/reference/mongo-repositories.adoc +++ b/src/main/asciidoc/reference/mongo-repositories.adoc @@ -398,9 +398,9 @@ The query in the preceding example returns only the `firstname`, `lastname` and [[mongodb.repositories.queries.sort]] === Sorting Query Method results -When it comes to sorting MongoDB query results via the repository interface there are several options as listed below. +MongoDB repositories allow various approaches to define sorting order. Let's take a look at the following example: -.Sorting query results +.Sorting Query Results ==== [source,java] ---- @@ -417,10 +417,10 @@ public interface PersonRepository extends MongoRepository { List findByLastname(String lastname, Sort sort); <4> } ---- -<1> Fixed sorting derived from method name. `SortByAgeDesc` results in `{ age : -1 }` sort parameter. -<2> Dynamic sorting via method argument. `Sort.by(DESC, "age")` creates a `{ age : -1 }` sort parameter. -<3> Fixed sorting via `Query` annotation. Sort parameter applied as stated in the `sort` attribute. -<4> Default sorting via `Query` annotation combined with dynamic one via method argument. `Sort.unsorted()` +<1> Static sorting derived from method name. `SortByAgeDesc` results in `{ age : -1 }` for the sort parameter. +<2> Dynamic sorting using a method argument. `Sort.by(DESC, "age")` creates `{ age : -1 }` for the sort parameter. +<3> Static sorting via `Query` annotation. Sort parameter applied as stated in the `sort` attribute. +<4> Default sorting via `Query` annotation combined with dynamic one via a method argument. `Sort.unsorted()` results in `{ age : -1 }`. Using `Sort.by(ASC, "age")` overrides the defaults and creates `{ age : 1 }`. `Sort.by (ASC, "firstname")` alters the default and results in `{ age : -1, firstname : 1 }`. ====