DATAMONGO-1979 - Polishing.
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.
This commit is contained in:
@@ -80,14 +80,14 @@ public @interface Query {
|
||||
|
||||
/**
|
||||
* Defines a default sort order for the given query.<br />
|
||||
* <strong>NOTE</strong> The so set defaults can be altered / overwritten via an explicit
|
||||
* <strong>NOTE:</strong> The so set defaults can be altered / overwritten using an explicit
|
||||
* {@link org.springframework.data.domain.Sort} argument of the query method.
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
*
|
||||
*
|
||||
* @Query(sort = "{ age : -1 }") // order by age descending
|
||||
* List<Person> findByFirstname(String firstname);
|
||||
* List<Person> findByFirstname(String firstname);
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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<Object>((Class<Object>) returnedEntity.getType(),
|
||||
this.metadata = new SimpleMongoEntityMetadata<>((Class<Object>) 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 <A extends Annotation> Optional<A> doFindAnnotation(Class<A> annotationType) {
|
||||
|
||||
return (Optional) this.annotationCache.computeIfAbsent(annotationType,
|
||||
return (Optional<A>) this.annotationCache.computeIfAbsent(annotationType,
|
||||
it -> Optional.ofNullable(AnnotatedElementUtils.findMergedAnnotation(method, it)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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<Person, String> {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* Tailable cursors for imperative driver.
|
||||
* <<mongo.sessions, MongoDB 3.6 Session>> support for the imperative and reactive Template APIs.
|
||||
* <<mongo.transactions, MongoDB 4.0 Transaction>> support and a MongoDB-specific transaction manager implementation.
|
||||
* <<mongodb.repositories.queries.sort,Default sort specifications for repository query methods>> using `@Query(sort=…)`.
|
||||
|
||||
[[new-features.2-0-0]]
|
||||
== What's New in Spring Data MongoDB 2.0
|
||||
|
||||
@@ -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<Person, String> {
|
||||
List<Person> 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 }`.
|
||||
====
|
||||
|
||||
Reference in New Issue
Block a user