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 }`.
====