From e20f9b681768cdea2edecab11619022336ce5328 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 27 Aug 2021 09:47:48 +0200 Subject: [PATCH 1/3] Prepare issue branch. --- pom.xml | 4 ++-- spring-data-mongodb-benchmarks/pom.xml | 2 +- spring-data-mongodb-distribution/pom.xml | 2 +- spring-data-mongodb/pom.xml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 5d28c8a5c5..816131dcf6 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-mongodb-parent - 3.3.0-SNAPSHOT + 3.3.0-3757-SNAPSHOT pom Spring Data MongoDB @@ -26,7 +26,7 @@ multi spring-data-mongodb - 2.6.0-SNAPSHOT + 2.6.0-2228-SNAPSHOT 4.3.1 ${mongo} 1.19 diff --git a/spring-data-mongodb-benchmarks/pom.xml b/spring-data-mongodb-benchmarks/pom.xml index 0033bd11d5..b3e528fe1d 100644 --- a/spring-data-mongodb-benchmarks/pom.xml +++ b/spring-data-mongodb-benchmarks/pom.xml @@ -7,7 +7,7 @@ org.springframework.data spring-data-mongodb-parent - 3.3.0-SNAPSHOT + 3.3.0-3757-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml index f62c8dc7f4..45506edc05 100644 --- a/spring-data-mongodb-distribution/pom.xml +++ b/spring-data-mongodb-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-mongodb-parent - 3.3.0-SNAPSHOT + 3.3.0-3757-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index 1f157e75bc..645ffd36b5 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -11,7 +11,7 @@ org.springframework.data spring-data-mongodb-parent - 3.3.0-SNAPSHOT + 3.3.0-3757-SNAPSHOT ../pom.xml From bb712e2c5ac1d4dd072bb06f790da64f5cc6fbe6 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 27 Aug 2021 14:57:36 +0200 Subject: [PATCH 2/3] Add support for fluent Querydsl and Query by Example query definition. We now support the functional fluent query definition API for imperative and reactive usage with Querydsl and Query by Example. Page first = repository.findBy(Example.of(probe), it -> it.as(PersonProjection.class).project("firstname").page(PageRequest.of(0, 1, Sort.by("firstname")))); --- .../data/mongodb/core/query/BasicQuery.java | 4 +- .../support/FetchableFluentQuerySupport.java | 100 ++++++++++++ .../QuerydslMongoPredicateExecutor.java | 130 ++++++++++++++++ .../support/ReactiveFluentQuerySupport.java | 100 ++++++++++++ .../ReactivePageableExecutionUtils.java | 69 +++++++++ ...eactiveQuerydslMongoPredicateExecutor.java | 129 +++++++++++++++- .../ReactiveSpringDataMongodbQuery.java | 48 ++++-- .../support/SimpleMongoRepository.java | 143 ++++++++++++++++- .../SimpleReactiveMongoRepository.java | 130 +++++++++++++++- .../support/SpringDataMongodbQuery.java | 79 ++++++++-- .../SimpleReactiveMongoRepositoryTests.java | 146 ++++++++++++++++++ ...ongoPredicateExecutorIntegrationTests.java | 102 ++++++++++++ ...veQuerydslMongoPredicateExecutorTests.java | 115 +++++++++++++- .../support/SimpleMongoRepositoryTests.java | 124 +++++++++++++++ 14 files changed, 1379 insertions(+), 40 deletions(-) create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/FetchableFluentQuerySupport.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveFluentQuerySupport.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactivePageableExecutionUtils.java diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/BasicQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/BasicQuery.java index 4de789e12f..cc2ae28c23 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/BasicQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/BasicQuery.java @@ -164,9 +164,9 @@ public boolean isSorted() { * @throws IllegalArgumentException when {@code fieldsObject} is {@literal null}. * @since 1.6 */ - protected void setFieldsObject(Document fieldsObject) { + public void setFieldsObject(Document fieldsObject) { - Assert.notNull(sortObject, "Field document must not be null"); + Assert.notNull(fieldsObject, "Field document must not be null"); this.fieldsObject = fieldsObject; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/FetchableFluentQuerySupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/FetchableFluentQuerySupport.java new file mode 100644 index 0000000000..d910106bd0 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/FetchableFluentQuerySupport.java @@ -0,0 +1,100 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.repository.support; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.springframework.data.domain.Sort; +import org.springframework.data.repository.query.FluentQuery; +import org.springframework.util.Assert; + +/** + * Support class for {@link org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery} implementations. + * + * @author Mark Paluch + * @since 3.3 + */ +abstract class FetchableFluentQuerySupport implements FluentQuery.FetchableFluentQuery { + + private final P predicate; + private final Sort sort; + private final Class resultType; + private final List fieldsToInclude; + + FetchableFluentQuerySupport(P predicate, Sort sort, Class resultType, List fieldsToInclude) { + this.predicate = predicate; + this.sort = sort; + this.resultType = resultType; + this.fieldsToInclude = fieldsToInclude; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#sortBy(org.springframework.data.domain.Sort) + */ + @Override + public FluentQuery.FetchableFluentQuery sortBy(Sort sort) { + + Assert.notNull(sort, "Sort must not be null!"); + + return create(predicate, sort, resultType, fieldsToInclude); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#as(java.lang.Class) + */ + @Override + public FluentQuery.FetchableFluentQuery as(Class projection) { + + Assert.notNull(projection, "Projection target type must not be null!"); + + return create(predicate, sort, projection, fieldsToInclude); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#project(java.util.Collection) + */ + @Override + public FluentQuery.FetchableFluentQuery project(Collection properties) { + + Assert.notNull(properties, "Projection properties must not be null!"); + + return create(predicate, sort, resultType, new ArrayList<>(properties)); + } + + protected abstract FetchableFluentQuerySupport create(P predicate, Sort sort, Class resultType, + List fieldsToInclude); + + P getPredicate() { + return predicate; + } + + Sort getSort() { + return sort; + } + + Class getResultType() { + return resultType; + } + + List getFieldsToInclude() { + return fieldsToInclude; + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutor.java index 569273afb5..ad69e67398 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutor.java @@ -15,18 +15,24 @@ */ package org.springframework.data.mongodb.repository.support; +import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Stream; +import org.bson.Document; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.data.mongodb.core.query.BasicQuery; import org.springframework.data.mongodb.repository.query.MongoEntityInformation; import org.springframework.data.querydsl.EntityPathResolver; import org.springframework.data.querydsl.QuerydslPredicateExecutor; import org.springframework.data.querydsl.SimpleEntityPathResolver; +import org.springframework.data.repository.query.FluentQuery; import org.springframework.data.support.PageableExecutionUtils; import org.springframework.util.Assert; @@ -184,6 +190,21 @@ public boolean exists(Predicate predicate) { return createQueryFor(predicate).fetchCount() > 0; } + /* + * (non-Javadoc) + * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findBy(com.querydsl.core.types.Predicate, java.util.function.Function) + */ + @Override + @SuppressWarnings("unchecked") + public R findBy(Predicate predicate, + Function, R> queryFunction) { + + Assert.notNull(predicate, "Predicate must not be null!"); + Assert.notNull(queryFunction, "Query function must not be null!"); + + return queryFunction.apply(new FluentQuerydsl<>(predicate, (Class) typeInformation().getJavaType())); + } + /** * Creates a {@link SpringDataMongodbQuery} for the given {@link Predicate}. * @@ -232,4 +253,113 @@ private SpringDataMongodbQuery applySorting(SpringDataMongodbQuery query, toOrderSpecifiers(sort).forEach(query::orderBy); return query; } + + /** + * {@link org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery} using Querydsl + * {@link Predicate}. + * + * @author Mark Paluch + * @since 3.3 + */ + class FluentQuerydsl extends FetchableFluentQuerySupport { + + FluentQuerydsl(Predicate predicate, Class resultType) { + this(predicate, Sort.unsorted(), resultType, Collections.emptyList()); + } + + FluentQuerydsl(Predicate predicate, Sort sort, Class resultType, List fieldsToInclude) { + super(predicate, sort, resultType, fieldsToInclude); + } + + @Override + protected FluentQuerydsl create(Predicate predicate, Sort sort, Class resultType, + List fieldsToInclude) { + return new FluentQuerydsl<>(predicate, sort, resultType, fieldsToInclude); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#one() + */ + @Override + public T one() { + return createQuery().fetchOne(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#first() + */ + @Override + public T first() { + return createQuery().fetchFirst(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#all() + */ + @Override + public List all() { + return createQuery().fetch(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#page(org.springframework.data.domain.Pageable) + */ + @Override + public Page page(Pageable pageable) { + + Assert.notNull(pageable, "Pageable must not be null!"); + + return createQuery().fetchPage(pageable); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#stream() + */ + @Override + public Stream stream() { + return createQuery().stream(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#count() + */ + @Override + public long count() { + return createQuery().fetchCount(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#exists() + */ + @Override + public boolean exists() { + return count() > 0; + } + + private SpringDataMongodbQuery createQuery() { + return new SpringDataMongodbQuery<>(mongoOperations, typeInformation().getJavaType(), getResultType(), + mongoOperations.getCollectionName(typeInformation().getJavaType()), this::customize).where(getPredicate()); + } + + private void customize(BasicQuery query) { + + List fieldsToInclude = getFieldsToInclude(); + if (!fieldsToInclude.isEmpty()) { + Document fields = new Document(); + fieldsToInclude.forEach(field -> fields.put(field, 1)); + query.setFieldsObject(fields); + } + + if (getSort().isSorted()) { + query.with(getSort()); + } + } + } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveFluentQuerySupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveFluentQuerySupport.java new file mode 100644 index 0000000000..ee5ef32555 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveFluentQuerySupport.java @@ -0,0 +1,100 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.repository.support; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.springframework.data.domain.Sort; +import org.springframework.data.repository.query.FluentQuery; +import org.springframework.util.Assert; + +/** + * Support class for {@link org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery} implementations. + * + * @author Mark Paluch + * @since 3.3 + */ +abstract class ReactiveFluentQuerySupport implements FluentQuery.ReactiveFluentQuery { + + private final P predicate; + private final Sort sort; + private final Class resultType; + private final List fieldsToInclude; + + ReactiveFluentQuerySupport(P predicate, Sort sort, Class resultType, List fieldsToInclude) { + this.predicate = predicate; + this.sort = sort; + this.resultType = resultType; + this.fieldsToInclude = fieldsToInclude; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery#sortBy(org.springframework.data.domain.Sort) + */ + @Override + public ReactiveFluentQuery sortBy(Sort sort) { + + Assert.notNull(sort, "Sort must not be null!"); + + return create(predicate, sort, resultType, fieldsToInclude); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery#as(java.lang.Class) + */ + @Override + public ReactiveFluentQuery as(Class projection) { + + Assert.notNull(projection, "Projection target type must not be null!"); + + return create(predicate, sort, projection, fieldsToInclude); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery#project(java.util.Collection) + */ + @Override + public ReactiveFluentQuery project(Collection properties) { + + Assert.notNull(properties, "Projection properties must not be null!"); + + return create(predicate, sort, resultType, new ArrayList<>(properties)); + } + + protected abstract ReactiveFluentQuerySupport create(P predicate, Sort sort, Class resultType, + List fieldsToInclude); + + P getPredicate() { + return predicate; + } + + Sort getSort() { + return sort; + } + + Class getResultType() { + return resultType; + } + + List getFieldsToInclude() { + return fieldsToInclude; + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactivePageableExecutionUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactivePageableExecutionUtils.java new file mode 100644 index 0000000000..2bcbdba6ae --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactivePageableExecutionUtils.java @@ -0,0 +1,69 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.repository.support; + +import reactor.core.publisher.Mono; + +import java.util.List; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.util.Assert; + +/** + * Support for query execution using {@link Pageable}. Using {@link ReactivePageableExecutionUtils} assumes that data + * queries are cheaper than {@code COUNT} queries and so some cases can take advantage of optimizations. + * + * @author Mark Paluch + * @since 3.3 + */ +abstract class ReactivePageableExecutionUtils { + + private ReactivePageableExecutionUtils() {} + + /** + * Constructs a {@link Page} based on the given {@code content}, {@link Pageable} and {@link Mono} applying + * optimizations. The construction of {@link Page} omits a count query if the total can be determined based on the + * result size and {@link Pageable}. + * + * @param content must not be {@literal null}. + * @param pageable must not be {@literal null}. + * @param totalSupplier must not be {@literal null}. + * @return the {@link Page}. + */ + public static Mono> getPage(List content, Pageable pageable, Mono totalSupplier) { + + Assert.notNull(content, "Content must not be null!"); + Assert.notNull(pageable, "Pageable must not be null!"); + Assert.notNull(totalSupplier, "TotalSupplier must not be null!"); + + if (pageable.isUnpaged() || pageable.getOffset() == 0) { + + if (pageable.isUnpaged() || pageable.getPageSize() > content.size()) { + return Mono.just(new PageImpl<>(content, pageable, content.size())); + } + + return totalSupplier.map(total -> new PageImpl<>(content, pageable, total)); + } + + if (content.size() != 0 && pageable.getPageSize() > content.size()) { + return Mono.just(new PageImpl<>(content, pageable, pageable.getOffset() + content.size())); + } + + return totalSupplier.map(total -> new PageImpl<>(content, pageable, total)); + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveQuerydslMongoPredicateExecutor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveQuerydslMongoPredicateExecutor.java index 1da48bfc8e..1abf4c75d5 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveQuerydslMongoPredicateExecutor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveQuerydslMongoPredicateExecutor.java @@ -18,13 +18,23 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; + +import org.bson.Document; +import org.reactivestreams.Publisher; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.mongodb.core.ReactiveMongoOperations; +import org.springframework.data.mongodb.core.query.BasicQuery; import org.springframework.data.mongodb.repository.query.MongoEntityInformation; import org.springframework.data.querydsl.EntityPathResolver; import org.springframework.data.querydsl.QuerydslPredicateExecutor; import org.springframework.data.querydsl.ReactiveQuerydslPredicateExecutor; import org.springframework.data.querydsl.SimpleEntityPathResolver; +import org.springframework.data.repository.query.FluentQuery; import org.springframework.util.Assert; import com.querydsl.core.types.EntityPath; @@ -159,6 +169,20 @@ public Mono exists(Predicate predicate) { return createQueryFor(predicate).fetchCount().map(it -> it != 0); } + /* + * (non-Javadoc) + * @see org.springframework.data.querydsl.ReactiveQuerydslPredicateExecutor#findBy(com.querydsl.core.types.Predicate, java.util.function.Function) + */ + @Override + public > P findBy(Predicate predicate, + Function, P> queryFunction) { + + Assert.notNull(predicate, "Predicate must not be null!"); + Assert.notNull(queryFunction, "Query function must not be null!"); + + return queryFunction.apply(new ReactiveFluentQuerydsl(predicate, (Class) typeInformation().getJavaType())); + } + /** * Creates a {@link ReactiveSpringDataMongodbQuery} for the given {@link Predicate}. * @@ -177,8 +201,8 @@ private ReactiveSpringDataMongodbQuery createQueryFor(Predicate predicate) { private ReactiveSpringDataMongodbQuery createQuery() { Class javaType = typeInformation().getJavaType(); - return new ReactiveSpringDataMongodbQuery<>(mongodbSerializer(), mongoOperations, javaType, - mongoOperations.getCollectionName(javaType)); + return new ReactiveSpringDataMongodbQuery<>(mongoOperations, javaType, javaType, + mongoOperations.getCollectionName(javaType), it -> {}); } /** @@ -194,4 +218,105 @@ private ReactiveSpringDataMongodbQuery applySorting(ReactiveSpringDataMongodb return query; } + /** + * {@link org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery} using Querydsl {@link Predicate}. + * + * @since 3.3 + * @author Mark Paluch + */ + class ReactiveFluentQuerydsl extends ReactiveFluentQuerySupport { + + ReactiveFluentQuerydsl(Predicate predicate, Class resultType) { + this(predicate, Sort.unsorted(), resultType, Collections.emptyList()); + } + + ReactiveFluentQuerydsl(Predicate predicate, Sort sort, Class resultType, List fieldsToInclude) { + super(predicate, sort, resultType, fieldsToInclude); + } + + @Override + protected ReactiveFluentQuerydsl create(Predicate predicate, Sort sort, Class resultType, + List fieldsToInclude) { + return new ReactiveFluentQuerydsl<>(predicate, sort, resultType, fieldsToInclude); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery#one() + */ + @Override + public Mono one() { + return createQuery().fetchOne(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery#first() + */ + @Override + public Mono first() { + return createQuery().fetchFirst(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery#all() + */ + @Override + public Flux all() { + return createQuery().fetch(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery#page(org.springframework.data.domain.Pageable) + */ + @Override + public Mono> page(Pageable pageable) { + + Assert.notNull(pageable, "Pageable must not be null!"); + + return createQuery().fetchPage(pageable); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery#count() + */ + @Override + public Mono count() { + return createQuery().fetchCount(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery#exists() + */ + @Override + public Mono exists() { + return count().map(it -> it > 0).defaultIfEmpty(false); + } + + private ReactiveSpringDataMongodbQuery createQuery() { + + return new ReactiveSpringDataMongodbQuery<>(mongoOperations, typeInformation().getJavaType(), getResultType(), + mongoOperations.getCollectionName(typeInformation().getJavaType()), this::customize).where(getPredicate()); + } + + private void customize(BasicQuery query) { + + List fieldsToInclude = getFieldsToInclude(); + + if (!fieldsToInclude.isEmpty()) { + Document fields = new Document(); + fieldsToInclude.forEach(field -> fields.put(field, 1)); + query.setFieldsObject(fields); + } + + if (getSort().isSorted()) { + query.with(getSort()); + } + } + } + } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveSpringDataMongodbQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveSpringDataMongodbQuery.java index 8b30e585e6..d00d8873e5 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveSpringDataMongodbQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveSpringDataMongodbQuery.java @@ -21,11 +21,14 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.function.Consumer; import org.bson.Document; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.data.mongodb.core.ReactiveFindOperation.FindWithProjection; +import org.springframework.data.mongodb.core.ReactiveFindOperation; import org.springframework.data.mongodb.core.ReactiveMongoOperations; import org.springframework.data.mongodb.core.query.BasicQuery; import org.springframework.data.mongodb.core.query.Query; @@ -44,7 +47,6 @@ import com.querydsl.core.types.Path; import com.querydsl.core.types.Predicate; import com.querydsl.mongodb.MongodbOps; -import com.querydsl.mongodb.document.MongodbDocumentSerializer; /** * MongoDB query with utilizing {@link ReactiveMongoOperations} for command execution. @@ -59,21 +61,23 @@ class ReactiveSpringDataMongodbQuery extends SpringDataMongodbQuerySupport> { private final ReactiveMongoOperations mongoOperations; - private final FindWithProjection find; + private final Consumer queryCustomizer; + private final ReactiveFindOperation.FindWithQuery find; ReactiveSpringDataMongodbQuery(ReactiveMongoOperations mongoOperations, Class entityClass) { - this(new SpringDataMongodbSerializer(mongoOperations.getConverter()), mongoOperations, entityClass, null); + this(mongoOperations, entityClass, entityClass, null, it -> {}); } @SuppressWarnings("unchecked") - ReactiveSpringDataMongodbQuery(MongodbDocumentSerializer serializer, ReactiveMongoOperations mongoOperations, - Class entityClass, @Nullable String collection) { + ReactiveSpringDataMongodbQuery(ReactiveMongoOperations mongoOperations, Class domainType, + Class resultType, @Nullable String collection, Consumer queryCustomizer) { - super(serializer); + super(new SpringDataMongodbSerializer(mongoOperations.getConverter())); this.mongoOperations = mongoOperations; - this.find = StringUtils.hasText(collection) ? mongoOperations.query((Class) entityClass).inCollection(collection) - : mongoOperations.query((Class) entityClass); + this.queryCustomizer = queryCustomizer; + this.find = (StringUtils.hasText(collection) ? mongoOperations.query(domainType).inCollection(collection) + : mongoOperations.query(domainType)).as((Class) resultType); } /** @@ -86,7 +90,19 @@ Flux fetch() { } /** - * Fetch the first matching query result. + * Fetch all matching query results as page. + * + * @return {@link Mono} emitting the requested page. + */ + Mono> fetchPage(Pageable pageable) { + + Mono> content = createQuery().flatMapMany(it -> find.matching(it).all()).collectList(); + + return content.flatMap(it -> ReactivePageableExecutionUtils.getPage(it, pageable, fetchCount())); + } + + /** + * Fetch the one matching query result. * * @return {@link Mono} emitting the first query result or {@link Mono#empty()} if there are none. * @throws org.springframework.dao.IncorrectResultSizeDataAccessException if more than one match found. @@ -95,6 +111,16 @@ Mono fetchOne() { return createQuery().flatMap(it -> find.matching(it).one()); } + /** + * Fetch the first matching query result. @return {@link Mono} emitting the first query result or {@link Mono#empty()} + * if there are none. + * + * @since 3.3 + */ + Mono fetchFirst() { + return createQuery().flatMap(it -> find.matching(it).first()); + } + /** * Fetch the count of matching query results. * @@ -144,6 +170,8 @@ protected Mono createQuery(Mono filter, @Nullable Expression ids) { Assert.notNull(ids, "The given Iterable of ids must not be null!"); - mongoOperations.remove(getIdQuery(ids), entityInformation.getJavaType(), - entityInformation.getCollectionName()); + mongoOperations.remove(getIdQuery(ids), entityInformation.getJavaType(), entityInformation.getCollectionName()); } /* @@ -362,8 +365,8 @@ public Page findAll(Example example, Pageable pageable) { List list = mongoOperations.find(query, example.getProbeType(), entityInformation.getCollectionName()); - return PageableExecutionUtils.getPage(list, pageable, - () -> mongoOperations.count(Query.of(query).limit(-1).skip(-1), example.getProbeType(), entityInformation.getCollectionName())); + return PageableExecutionUtils.getPage(list, pageable, () -> mongoOperations + .count(Query.of(query).limit(-1).skip(-1), example.getProbeType(), entityInformation.getCollectionName())); } /* @@ -396,6 +399,20 @@ public boolean exists(Example example) { return mongoOperations.exists(query, example.getProbeType(), entityInformation.getCollectionName()); } + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.QueryByExampleExecutor#findBy(org.springframework.data.domain.Example, java.util.function.Function) + */ + @Override + public R findBy(Example example, + Function, R> queryFunction) { + + Assert.notNull(example, "Sample must not be null!"); + Assert.notNull(queryFunction, "Query function must not be null!"); + + return queryFunction.apply(new FluentQueryByExample<>(example, example.getProbeType())); + } + // ------------------------------------------------------------------------- // Utility methods // ------------------------------------------------------------------------- @@ -410,8 +427,7 @@ private Criteria getIdCriteria(Object id) { private Query getIdQuery(Iterable ids) { - return new Query(new Criteria(entityInformation.getIdAttribute()) - .in(toCollection(ids))); + return new Query(new Criteria(entityInformation.getIdAttribute()).in(toCollection(ids))); } private static Collection toCollection(Iterable ids) { @@ -428,4 +444,119 @@ private List findAll(@Nullable Query query) { return mongoOperations.find(query, entityInformation.getJavaType(), entityInformation.getCollectionName()); } + /** + * {@link org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery} using {@link Example}. + * + * @author Mark Paluch + * @since 3.3 + */ + class FluentQueryByExample extends FetchableFluentQuerySupport, T> { + + FluentQueryByExample(Example example, Class resultType) { + this(example, Sort.unsorted(), resultType, Collections.emptyList()); + } + + FluentQueryByExample(Example example, Sort sort, Class resultType, List fieldsToInclude) { + super(example, sort, resultType, fieldsToInclude); + } + + @Override + protected FluentQueryByExample create(Example predicate, Sort sort, Class resultType, + List fieldsToInclude) { + return new FluentQueryByExample<>(predicate, sort, resultType, fieldsToInclude); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#one() + */ + @Override + public T one() { + return createQuery().oneValue(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#first() + */ + @Override + public T first() { + return createQuery().firstValue(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#all() + */ + @Override + public List all() { + return createQuery().all(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#page(org.springframework.data.domain.Pageable) + */ + @Override + public Page page(Pageable pageable) { + + Assert.notNull(pageable, "Pageable must not be null!"); + + List list = createQuery(q -> q.with(pageable)).all(); + + return PageableExecutionUtils.getPage(list, pageable, this::count); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#stream() + */ + @Override + public Stream stream() { + return createQuery().stream(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#count() + */ + @Override + public long count() { + return createQuery().count(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#exists() + */ + @Override + public boolean exists() { + return createQuery().exists(); + } + + private ExecutableFindOperation.TerminatingFind createQuery() { + return createQuery(UnaryOperator.identity()); + } + + private ExecutableFindOperation.TerminatingFind createQuery(UnaryOperator queryCustomizer) { + + Query query = new Query(new Criteria().alike(getPredicate())) // + .collation(entityInformation.getCollation()); + + if (getSort().isSorted()) { + query.with(getSort()); + } + + if (!getFieldsToInclude().isEmpty()) { + query.fields().include(getFieldsToInclude().toArray(new String[0])); + } + + query = queryCustomizer.apply(query); + + return mongoOperations.query(getPredicate().getProbeType()).inCollection(entityInformation.getCollectionName()) + .as(getResultType()).matching(query); + } + + } + } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepository.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepository.java index 325547abee..f238f87774 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepository.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepository.java @@ -22,19 +22,26 @@ import java.io.Serializable; import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; +import java.util.function.UnaryOperator; import java.util.stream.Collectors; import org.reactivestreams.Publisher; - import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.dao.OptimisticLockingFailureException; import org.springframework.data.domain.Example; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; +import org.springframework.data.mongodb.core.ReactiveFindOperation; import org.springframework.data.mongodb.core.ReactiveMongoOperations; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.repository.ReactiveMongoRepository; import org.springframework.data.mongodb.repository.query.MongoEntityInformation; +import org.springframework.data.repository.query.FluentQuery; import org.springframework.data.util.StreamUtils; import org.springframework.data.util.Streamable; import org.springframework.util.Assert; @@ -210,7 +217,6 @@ public Mono count() { return mongoOperations.count(new Query(), entityInformation.getCollectionName()); } - /* * (non-Javadoc) * @see org.springframework.data.repository.reactive.ReactiveCrudRepository#deleteById(java.lang.Object) @@ -466,6 +472,20 @@ public Mono exists(Example example) { return mongoOperations.exists(query, example.getProbeType(), entityInformation.getCollectionName()); } + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.ReactiveQueryByExampleExecutor#findBy(org.springframework.data.domain.Example, java.util.function.Function) + */ + @Override + public > P findBy(Example example, + Function, P> queryFunction) { + + Assert.notNull(example, "Sample must not be null!"); + Assert.notNull(queryFunction, "Query function must not be null!"); + + return queryFunction.apply(new ReactiveFluentQueryByExample<>(example, example.getProbeType())); + } + private Query getIdQuery(Object id) { return new Query(getIdCriteria(id)); } @@ -486,4 +506,110 @@ private static Collection toCollection(Iterable ids) { private Flux findAll(Query query) { return mongoOperations.find(query, entityInformation.getJavaType(), entityInformation.getCollectionName()); } + + /** + * {@link org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery} using {@link Example}. + * + * @author Mark Paluch + * @since 3.3 + */ + class ReactiveFluentQueryByExample extends ReactiveFluentQuerySupport, T> { + + ReactiveFluentQueryByExample(Example example, Class resultType) { + this(example, Sort.unsorted(), resultType, Collections.emptyList()); + } + + ReactiveFluentQueryByExample(Example example, Sort sort, Class resultType, List fieldsToInclude) { + super(example, sort, resultType, fieldsToInclude); + } + + @Override + protected ReactiveFluentQueryByExample create(Example predicate, Sort sort, Class resultType, + List fieldsToInclude) { + return new ReactiveFluentQueryByExample<>(predicate, sort, resultType, fieldsToInclude); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery#one() + */ + @Override + public Mono one() { + return createQuery().one(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery#first() + */ + @Override + public Mono first() { + return createQuery().first(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery#all() + */ + @Override + public Flux all() { + return createQuery().all(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery#page(org.springframework.data.domain.Pageable) + */ + @Override + public Mono> page(Pageable pageable) { + + Assert.notNull(pageable, "Pageable must not be null!"); + + Mono> items = createQuery(q -> q.with(pageable)).all().collectList(); + + return items.flatMap(content -> ReactivePageableExecutionUtils.getPage(content, pageable, this.count())); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery#count() + */ + @Override + public Mono count() { + return createQuery().count(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery#exists() + */ + @Override + public Mono exists() { + return createQuery().exists(); + } + + private ReactiveFindOperation.TerminatingFind createQuery() { + return createQuery(UnaryOperator.identity()); + } + + private ReactiveFindOperation.TerminatingFind createQuery(UnaryOperator queryCustomizer) { + + Query query = new Query(new Criteria().alike(getPredicate())) // + .collation(entityInformation.getCollation()); + + if (getSort().isSorted()) { + query.with(getSort()); + } + + if (!getFieldsToInclude().isEmpty()) { + query.fields().include(getFieldsToInclude().toArray(new String[0])); + } + + query = queryCustomizer.apply(query); + + return mongoOperations.query(getPredicate().getProbeType()).inCollection(entityInformation.getCollectionName()) + .as(getResultType()).matching(query); + } + + } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbQuery.java index d62aa99c5e..5a914afec1 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbQuery.java @@ -16,14 +16,21 @@ package org.springframework.data.mongodb.repository.support; import java.util.Collections; +import java.util.Iterator; import java.util.List; +import java.util.function.Consumer; +import java.util.stream.Stream; import org.bson.Document; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; import org.springframework.data.mongodb.core.ExecutableFindOperation; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.query.BasicQuery; import org.springframework.data.mongodb.core.query.Query; +import org.springframework.data.support.PageableExecutionUtils; import org.springframework.lang.Nullable; import com.mysema.commons.lang.CloseableIterator; @@ -35,7 +42,6 @@ import com.querydsl.core.types.Expression; import com.querydsl.core.types.OrderSpecifier; import com.querydsl.core.types.Predicate; -import com.querydsl.mongodb.document.MongodbDocumentSerializer; /** * Spring Data specific simple {@link com.querydsl.core.Fetchable} {@link com.querydsl.core.SimpleQuery Query} @@ -48,10 +54,9 @@ public class SpringDataMongodbQuery extends SpringDataMongodbQuerySupport> implements Fetchable { - private final Class entityClass; - private final String collection; private final MongoOperations mongoOperations; - private final ExecutableFindOperation.FindWithProjection find; + private final Consumer queryCustomizer; + private final ExecutableFindOperation.FindWithQuery find; /** * Creates a new {@link SpringDataMongodbQuery}. @@ -72,18 +77,26 @@ public SpringDataMongodbQuery(MongoOperations operations, Class typ */ public SpringDataMongodbQuery(MongoOperations operations, Class type, String collectionName) { - this(new SpringDataMongodbSerializer(operations.getConverter()), operations, type, collectionName); + this(operations, type, type, collectionName, it -> {}); } - private SpringDataMongodbQuery(MongodbDocumentSerializer serializer, MongoOperations operations, - Class type, String collectionName) { - - super(serializer); + /** + * Creates a new {@link SpringDataMongodbQuery}. + * + * @param operations must not be {@literal null}. + * @param domainType must not be {@literal null}. + * @param resultType must not be {@literal null}. + * @param collectionName must not be {@literal null} or empty. + * @since 3.3 + */ + SpringDataMongodbQuery(MongoOperations operations, Class domainType, Class resultType, + String collectionName, Consumer queryCustomizer) { + super(new SpringDataMongodbSerializer(operations.getConverter())); - this.entityClass = (Class) type; - this.collection = collectionName; + Class resultType1 = (Class) resultType; this.mongoOperations = operations; - this.find = mongoOperations.query(this.entityClass).inCollection(collection); + this.queryCustomizer = queryCustomizer; + this.find = mongoOperations.query(domainType).inCollection(collectionName).as(resultType1); } /* @@ -94,19 +107,19 @@ private SpringDataMongodbQuery(MongodbDocumentSerializer serializer, MongoOperat public CloseableIterator iterate() { try { - org.springframework.data.util.CloseableIterator stream = mongoOperations.stream(createQuery(), - entityClass, collection); + Stream stream = stream(); + Iterator iterator = stream.iterator(); return new CloseableIterator() { @Override public boolean hasNext() { - return stream.hasNext(); + return iterator.hasNext(); } @Override public T next() { - return stream.next(); + return iterator.next(); } @Override @@ -124,6 +137,20 @@ public void close() { } } + /* + * (non-Javadoc) + * @see com.querydsl.core.Fetchable#iterable() + */ + @Override + public Stream stream() { + + try { + return find.matching(createQuery()).stream(); + } catch (RuntimeException e) { + return handleException(e, Stream.empty()); + } + } + /* * (non-Javadoc) * @see com.querydsl.core.Fetchable#fetch() @@ -137,6 +164,24 @@ public List fetch() { } } + /** + * Fetch a {@link Page}. + * + * @param pageable + * @return + */ + public Page fetchPage(Pageable pageable) { + + try { + + List content = find.matching(createQuery().with(pageable)).all(); + + return PageableExecutionUtils.getPage(content, pageable, this::fetchCount); + } catch (RuntimeException e) { + return handleException(e, new PageImpl<>(Collections.emptyList(), pageable, 0)); + } + } + /* * (non-Javadoc) * @see com.querydsl.core.Fetchable#fetchFirst() @@ -215,6 +260,8 @@ protected org.springframework.data.mongodb.core.query.Query createQuery(@Nullabl basicQuery.setSortObject(createSort(orderBy)); } + queryCustomizer.accept(basicQuery); + return basicQuery; } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/SimpleReactiveMongoRepositoryTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/SimpleReactiveMongoRepositoryTests.java index 0067eb3bf1..176fd175e2 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/SimpleReactiveMongoRepositoryTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/SimpleReactiveMongoRepositoryTests.java @@ -40,12 +40,14 @@ import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.data.annotation.Id; import org.springframework.data.domain.Example; +import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.domain.Sort.Order; import org.springframework.data.mongodb.core.ReactiveMongoTemplate; import org.springframework.data.mongodb.repository.support.ReactiveMongoRepositoryFactory; import org.springframework.data.mongodb.repository.support.SimpleReactiveMongoRepository; +import org.springframework.data.repository.query.FluentQuery; import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider; import org.springframework.lang.Nullable; import org.springframework.test.context.ContextConfiguration; @@ -475,6 +477,150 @@ void findOneByExampleWithoutResultShouldCompleteEmpty() { repository.findOne(example).as(StepVerifier::create).verifyComplete(); } + @Test // GH-3757 + void findByShouldReturnFirstResult() { + + ReactivePerson probe = new ReactivePerson(); + probe.setFirstname(oliver.getFirstname()); + + repository.findBy(Example.of(probe, matching().withIgnorePaths("age")), FluentQuery.ReactiveFluentQuery::first) // + .as(StepVerifier::create) // + .expectNext(oliver) // + .verifyComplete(); + } + + @Test // GH-3757 + void findByShouldReturnOneResult() { + + ReactivePerson probe = new ReactivePerson(); + probe.setFirstname(oliver.getFirstname()); + + repository.findBy(Example.of(probe, matching().withIgnorePaths("age")), FluentQuery.ReactiveFluentQuery::one) // + .as(StepVerifier::create) // + .expectNext(oliver) // + .verifyComplete(); + + probe = new ReactivePerson(); + probe.setLastname(oliver.getLastname()); + + repository.findBy(Example.of(probe, matching().withIgnorePaths("age")), FluentQuery.ReactiveFluentQuery::one) // + .as(StepVerifier::create) // + .verifyError(IncorrectResultSizeDataAccessException.class); + } + + @Test // GH-3757 + void findByShouldReturnAll() { + + ReactivePerson probe = new ReactivePerson(); + probe.setLastname(oliver.getLastname()); + + repository.findBy(Example.of(probe, matching().withIgnorePaths("age")), FluentQuery.ReactiveFluentQuery::all) // + .as(StepVerifier::create) // + .expectNextCount(2) // + .verifyComplete(); + } + + @Test // GH-3757 + void findByShouldApplySortAll() { + + ReactivePerson probe = new ReactivePerson(); + probe.setLastname(oliver.getLastname()); + + repository.findBy(Example.of(probe, matching().withIgnorePaths("age")), it -> it.sortBy(Sort.by("firstname")).all()) // + .as(StepVerifier::create) // + .expectNext(dave, oliver) // + .verifyComplete(); + + repository + .findBy(Example.of(probe, matching().withIgnorePaths("age")), + it -> it.sortBy(Sort.by(Direction.DESC, "firstname")).all()) // + .as(StepVerifier::create) // + .expectNext(oliver, dave) // + .verifyComplete(); + } + + @Test // GH-3757 + void findByShouldApplyProjection() { + + ReactivePerson probe = new ReactivePerson(); + probe.setLastname(oliver.getLastname()); + + repository.findBy(Example.of(probe, matching().withIgnorePaths("age")), it -> it.project("firstname").first()) // + .as(StepVerifier::create) // + .assertNext(it -> { + + assertThat(it.getFirstname()).isNotNull(); + assertThat(it.getLastname()).isNull(); + }).verifyComplete(); + } + + @Test // GH-3757 + void findByShouldApplyPagination() { + + ReactivePerson probe = new ReactivePerson(); + probe.setLastname(oliver.getLastname()); + + repository + .findBy(Example.of(probe, matching().withIgnorePaths("age")), + it -> it.page(PageRequest.of(0, 1, Sort.by("firstname")))) // + .as(StepVerifier::create) // + .assertNext(it -> { + + assertThat(it.getTotalElements()).isEqualTo(2); + assertThat(it.getContent()).contains(dave); + }).verifyComplete(); + + repository + .findBy(Example.of(probe, matching().withIgnorePaths("age")), + it -> it.page(PageRequest.of(1, 1, Sort.by("firstname")))) // + .as(StepVerifier::create) // + .assertNext(it -> { + + assertThat(it.getTotalElements()).isEqualTo(2); + assertThat(it.getContent()).contains(oliver); + }).verifyComplete(); + } + + @Test // GH-3757 + void findByShouldCount() { + + ReactivePerson probe = new ReactivePerson(); + probe.setLastname(oliver.getLastname()); + + repository.findBy(Example.of(probe, matching().withIgnorePaths("age")), FluentQuery.ReactiveFluentQuery::count) // + .as(StepVerifier::create) // + .expectNext(2L) // + .verifyComplete(); + + probe = new ReactivePerson(); + probe.setLastname("foo"); + + repository.findBy(Example.of(probe, matching().withIgnorePaths("age")), FluentQuery.ReactiveFluentQuery::count) // + .as(StepVerifier::create) // + .expectNext(0L) // + .verifyComplete(); + } + + @Test // GH-3757 + void findByShouldReportExists() { + + ReactivePerson probe = new ReactivePerson(); + probe.setLastname(oliver.getLastname()); + + repository.findBy(Example.of(probe, matching().withIgnorePaths("age")), FluentQuery.ReactiveFluentQuery::exists) // + .as(StepVerifier::create) // + .expectNext(true) // + .verifyComplete(); + + probe = new ReactivePerson(); + probe.setLastname("foo"); + + repository.findBy(Example.of(probe, matching().withIgnorePaths("age")), FluentQuery.ReactiveFluentQuery::exists) // + .as(StepVerifier::create) // + .expectNext(false) // + .verifyComplete(); + } + interface ReactivePersonRepository extends ReactiveMongoRepository { Flux findByLastname(String lastname); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutorIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutorIntegrationTests.java index 782e46b134..fea909bcda 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutorIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutorIntegrationTests.java @@ -24,9 +24,11 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.dao.PermissionDeniedDataAccessException; +import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; @@ -41,6 +43,7 @@ import org.springframework.data.mongodb.repository.QUser; import org.springframework.data.mongodb.repository.User; import org.springframework.data.mongodb.repository.query.MongoEntityInformation; +import org.springframework.data.repository.query.FluentQuery; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; @@ -240,4 +243,103 @@ protected MongoDatabase doGetDatabase() { repository.findOne(person.firstname.contains("batman")); } + + @Test // GH-3757 + public void findByShouldReturnFirstResult() { + + Person result = repository.findBy(person.firstname.eq(oliver.getFirstname()), + FluentQuery.FetchableFluentQuery::first); + + assertThat(result).isEqualTo(oliver); + } + + @Test // GH-3757 + public void findByShouldReturnOneResult() { + + Person result = repository.findBy(person.firstname.eq(oliver.getFirstname()), + FluentQuery.FetchableFluentQuery::one); + + assertThat(result).isEqualTo(oliver); + + assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy( + () -> repository.findBy(person.lastname.eq(oliver.getLastname()), FluentQuery.FetchableFluentQuery::one)); + } + + @Test // GH-3757 + public void findByShouldReturnAll() { + + List result = repository.findBy(person.lastname.eq(oliver.getLastname()), + FluentQuery.FetchableFluentQuery::all); + + assertThat(result).hasSize(2); + } + + @Test // GH-3757 + public void findByShouldApplySortAll() { + + Person probe = new Person(); + probe.setLastname(oliver.getLastname()); + + List result = repository.findBy(person.lastname.eq(oliver.getLastname()), + it -> it.sortBy(Sort.by("firstname")).all()); + assertThat(result).containsSequence(dave, oliver); + + result = repository.findBy(person.lastname.eq(oliver.getLastname()), + it -> it.sortBy(Sort.by(Sort.Direction.DESC, "firstname")).all()); + assertThat(result).containsSequence(oliver, dave); + } + + @Test // GH-3757 + public void findByShouldApplyProjection() { + + Person probe = new Person(); + probe.setLastname(oliver.getLastname()); + + Person result = repository.findBy(person.lastname.eq(oliver.getLastname()), it -> it.project("firstname").first()); + + assertThat(result.getFirstname()).isNotNull(); + assertThat(result.getLastname()).isNull(); + } + + @Test // GH-3757 + public void findByShouldApplyPagination() { + + Page first = repository.findBy(person.lastname.eq(oliver.getLastname()), + it -> it.page(PageRequest.of(0, 1, Sort.by("firstname")))); + assertThat(first.getTotalElements()).isEqualTo(2); + assertThat(first.getContent()).contains(dave); + + Page next = repository.findBy(person.lastname.eq(oliver.getLastname()), + it -> it.page(PageRequest.of(1, 1, Sort.by("firstname")))); + + assertThat(next.getTotalElements()).isEqualTo(2); + assertThat(next.getContent()).contains(oliver); + } + + @Test // GH-3757 + public void findByShouldCount() { + + long count = repository.findBy(person.lastname.eq(oliver.getLastname()), FluentQuery.FetchableFluentQuery::count); + assertThat(count).isEqualTo(2L); + + count = repository.findBy(person.lastname.eq("foo"), FluentQuery.FetchableFluentQuery::count); + assertThat(count).isEqualTo(0L); + } + + @Test // GH-3757 + public void findByShouldReportExists() { + + Person probe = new Person(); + probe.setLastname(oliver.getLastname()); + + boolean exists = repository.findBy(person.lastname.eq(oliver.getLastname()), + FluentQuery.FetchableFluentQuery::exists); + assertThat(exists).isTrue(); + + probe = new Person(); + probe.setLastname("foo"); + + exists = repository.findBy(person.lastname.eq("foo"), FluentQuery.FetchableFluentQuery::exists); + assertThat(exists).isFalse(); + } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/ReactiveQuerydslMongoPredicateExecutorTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/ReactiveQuerydslMongoPredicateExecutorTests.java index e16a4d7b8c..fbd5a495a8 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/ReactiveQuerydslMongoPredicateExecutorTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/ReactiveQuerydslMongoPredicateExecutorTests.java @@ -15,6 +15,8 @@ */ package org.springframework.data.mongodb.repository.support; +import static org.assertj.core.api.Assertions.*; + import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; @@ -34,6 +36,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.dao.PermissionDeniedDataAccessException; +import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory; @@ -49,6 +52,7 @@ import org.springframework.data.mongodb.repository.User; import org.springframework.data.mongodb.repository.query.MongoEntityInformation; import org.springframework.data.mongodb.test.util.MongoTestUtils; +import org.springframework.data.repository.query.FluentQuery; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; @@ -269,12 +273,11 @@ public void queryShouldTerminateWithUnsupportedOperationOnJoinWithNoResults() { .as(StepVerifier::create) // .expectNextCount(1) // .verifyComplete(); - ; + operations.save(person2) // .as(StepVerifier::create) // .expectNextCount(1) // .verifyComplete(); - ; Flux result = new ReactiveSpringDataMongodbQuery<>(operations, Person.class).where() .join(person.coworker, QUser.user).on(QUser.user.username.eq("does-not-exist")).fetch(); @@ -330,4 +333,112 @@ protected Mono doGetDatabase() { .expectError(PermissionDeniedDataAccessException.class) // .verify(); } + + @Test // GH-3757 + public void findByShouldReturnFirstResult() { + + repository.findBy(person.firstname.eq(oliver.getFirstname()), FluentQuery.ReactiveFluentQuery::first) // + .as(StepVerifier::create) // + .expectNext(oliver) // + .verifyComplete(); + } + + @Test // GH-3757 + public void findByShouldReturnOneResult() { + + repository.findBy(person.firstname.eq(oliver.getFirstname()), FluentQuery.ReactiveFluentQuery::one) // + .as(StepVerifier::create) // + .expectNext(oliver) // + .verifyComplete(); + + repository.findBy(person.lastname.eq(oliver.getLastname()), FluentQuery.ReactiveFluentQuery::one) // + .as(StepVerifier::create) // + .verifyError(IncorrectResultSizeDataAccessException.class); + } + + @Test // GH-3757 + public void findByShouldReturnAll() { + + repository.findBy(person.lastname.eq(oliver.getLastname()), FluentQuery.ReactiveFluentQuery::all) // + .as(StepVerifier::create) // + .expectNextCount(2) // + .verifyComplete(); + } + + @Test // GH-3757 + public void findByShouldApplySortAll() { + + repository.findBy(person.lastname.eq(oliver.getLastname()), it -> it.sortBy(Sort.by("firstname")).all()) // + .as(StepVerifier::create) // + .expectNext(dave, oliver) // + .verifyComplete(); + + repository + .findBy(person.lastname.eq(oliver.getLastname()), it -> it.sortBy(Sort.by(Direction.DESC, "firstname")).all()) // + .as(StepVerifier::create) // + .expectNext(oliver, dave) // + .verifyComplete(); + } + + @Test // GH-3757 + public void findByShouldApplyProjection() { + + repository.findBy(person.lastname.eq(oliver.getLastname()), it -> it.project("firstname").first()) // + .as(StepVerifier::create) // + .assertNext(it -> { + + assertThat(it.getFirstname()).isNotNull(); + assertThat(it.getLastname()).isNull(); + }).verifyComplete(); + } + + @Test // GH-3757 + public void findByShouldApplyPagination() { + + repository + .findBy(person.lastname.eq(oliver.getLastname()), it -> it.page(PageRequest.of(0, 1, Sort.by("firstname")))) // + .as(StepVerifier::create) // + .assertNext(it -> { + + assertThat(it.getTotalElements()).isEqualTo(2); + assertThat(it.getContent()).contains(dave); + }).verifyComplete(); + + repository + .findBy(person.lastname.eq(oliver.getLastname()), it -> it.page(PageRequest.of(1, 1, Sort.by("firstname")))) // + .as(StepVerifier::create) // + .assertNext(it -> { + + assertThat(it.getTotalElements()).isEqualTo(2); + assertThat(it.getContent()).contains(oliver); + }).verifyComplete(); + } + + @Test // GH-3757 + public void findByShouldCount() { + + repository.findBy(person.lastname.eq(oliver.getLastname()), FluentQuery.ReactiveFluentQuery::count) // + .as(StepVerifier::create) // + .expectNext(2L) // + .verifyComplete(); + + repository.findBy(person.lastname.eq("foo"), FluentQuery.ReactiveFluentQuery::count) // + .as(StepVerifier::create) // + .expectNext(0L) // + .verifyComplete(); + } + + @Test // GH-3757 + public void findByShouldReportExists() { + + repository.findBy(person.lastname.eq(oliver.getLastname()), FluentQuery.ReactiveFluentQuery::exists) // + .as(StepVerifier::create) // + .expectNext(true) // + .verifyComplete(); + + repository.findBy(person.lastname.eq("foo"), FluentQuery.ReactiveFluentQuery::exists) // + .as(StepVerifier::create) // + .expectNext(false) // + .verifyComplete(); + } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryTests.java index 61cd78ea93..07a7e0f0aa 100755 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryTests.java @@ -31,10 +31,13 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.data.domain.Example; +import org.springframework.data.domain.ExampleMatcher; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.data.geo.Point; import org.springframework.data.mongodb.MongoTransactionManager; import org.springframework.data.mongodb.core.geo.GeoJsonPoint; @@ -51,6 +54,7 @@ import org.springframework.data.mongodb.test.util.MongoTemplateExtension; import org.springframework.data.mongodb.test.util.MongoTestTemplate; import org.springframework.data.mongodb.test.util.Template; +import org.springframework.data.repository.query.FluentQuery; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.transaction.support.TransactionTemplate; @@ -459,6 +463,126 @@ void deleteAllByIds() { .hasSize(all.size() - 2).doesNotContain(dave, carter); } + @Test // GH-3757 + void findByShouldReturnFirstResult() { + + Person probe = new Person(); + probe.setFirstname(oliver.getFirstname()); + + Person result = repository.findBy(Example.of(probe, getMatcher()), FluentQuery.FetchableFluentQuery::first); + + assertThat(result).isEqualTo(oliver); + } + + @Test // GH-3757 + void findByShouldReturnOneResult() { + + Person probe = new Person(); + probe.setFirstname(oliver.getFirstname()); + + Person result = repository.findBy(Example.of(probe, getMatcher()), FluentQuery.FetchableFluentQuery::one); + + assertThat(result).isEqualTo(oliver); + + Person probeByLastname = new Person(); + probeByLastname.setLastname(oliver.getLastname()); + + assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy( + () -> repository.findBy(Example.of(probeByLastname, getMatcher()), FluentQuery.FetchableFluentQuery::one)); + } + + @Test // GH-3757 + void findByShouldReturnAll() { + + Person probe = new Person(); + probe.setLastname(oliver.getLastname()); + + List result = repository.findBy(Example.of(probe, getMatcher()), FluentQuery.FetchableFluentQuery::all); + + assertThat(result).hasSize(2); + } + + @Test // GH-3757 + void findByShouldApplySortAll() { + + Person probe = new Person(); + probe.setLastname(oliver.getLastname()); + + List result = repository.findBy(Example.of(probe, getMatcher()), + it -> it.sortBy(Sort.by("firstname")).all()); + assertThat(result).containsSequence(dave, oliver); + + result = repository.findBy(Example.of(probe, getMatcher()), + it -> it.sortBy(Sort.by(Sort.Direction.DESC, "firstname")).all()); + assertThat(result).containsSequence(oliver, dave); + } + + @Test // GH-3757 + void findByShouldApplyProjection() { + + Person probe = new Person(); + probe.setLastname(oliver.getLastname()); + + Person result = repository.findBy(Example.of(probe, getMatcher()), it -> it.project("firstname").first()); + + assertThat(result.getFirstname()).isNotNull(); + assertThat(result.getLastname()).isNull(); + } + + @Test // GH-3757 + void findByShouldApplyPagination() { + + Person probe = new Person(); + probe.setLastname(oliver.getLastname()); + + Page first = repository.findBy(Example.of(probe, getMatcher()), + it -> it.page(PageRequest.of(0, 1, Sort.by("firstname")))); + assertThat(first.getTotalElements()).isEqualTo(2); + assertThat(first.getContent()).contains(dave); + + Page next = repository.findBy(Example.of(probe, getMatcher()), + it -> it.page(PageRequest.of(1, 1, Sort.by("firstname")))); + + assertThat(next.getTotalElements()).isEqualTo(2); + assertThat(next.getContent()).contains(oliver); + } + + @Test // GH-3757 + void findByShouldCount() { + + Person probe = new Person(); + probe.setLastname(oliver.getLastname()); + + long count = repository.findBy(Example.of(probe, getMatcher()), FluentQuery.FetchableFluentQuery::count); + assertThat(count).isEqualTo(2L); + + probe = new Person(); + probe.setLastname("foo"); + + count = repository.findBy(Example.of(probe, getMatcher()), FluentQuery.FetchableFluentQuery::count); + assertThat(count).isEqualTo(0L); + } + + @Test // GH-3757 + void findByShouldReportExists() { + + Person probe = new Person(); + probe.setLastname(oliver.getLastname()); + + boolean exists = repository.findBy(Example.of(probe, getMatcher()), FluentQuery.FetchableFluentQuery::exists); + assertThat(exists).isTrue(); + + probe = new Person(); + probe.setLastname("foo"); + + exists = repository.findBy(Example.of(probe, getMatcher()), FluentQuery.FetchableFluentQuery::exists); + assertThat(exists).isFalse(); + } + + private ExampleMatcher getMatcher() { + return matching().withIgnorePaths("age", "createdAt", "sex", "email", "id"); + } + private void assertThatAllReferencePersonsWereStoredCorrectly(Map references, List saved) { for (Person person : saved) { From 42b5f09c3049782801b6d6ea5fdd36413d69fd9f Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 6 Sep 2021 09:57:19 +0200 Subject: [PATCH 3/3] Adapt to changes in upstream dependencies. --- .../data/mongodb/core/MongoTemplate.java | 1 - .../QuerydslMongoPredicateExecutor.java | 24 +++++++++---------- .../support/SimpleMongoRepository.java | 22 ++++++++--------- ...ongoPredicateExecutorIntegrationTests.java | 7 +++--- .../support/SimpleMongoRepositoryTests.java | 6 ++--- 5 files changed, 30 insertions(+), 30 deletions(-) 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 fb0780c5c8..11a75828a2 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 @@ -1385,7 +1385,6 @@ public T save(T objectToSave, String collectionName) { return source.isVersionedEntity() // ? doSaveVersioned(source, collectionName) // : (T) doSave(collectionName, objectToSave, this.mongoConverter); - } @SuppressWarnings("unchecked") diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutor.java index ad69e67398..5255dedaa4 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutor.java @@ -190,7 +190,7 @@ public boolean exists(Predicate predicate) { return createQueryFor(predicate).fetchCount() > 0; } - /* + /* * (non-Javadoc) * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findBy(com.querydsl.core.types.Predicate, java.util.function.Function) */ @@ -277,25 +277,25 @@ protected FluentQuerydsl create(Predicate predicate, Sort sort, Class return new FluentQuerydsl<>(predicate, sort, resultType, fieldsToInclude); } - /* + /* * (non-Javadoc) - * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#one() + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#oneValue() */ @Override - public T one() { + public T oneValue() { return createQuery().fetchOne(); } - /* + /* * (non-Javadoc) - * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#first() + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#firstValue() */ @Override - public T first() { + public T firstValue() { return createQuery().fetchFirst(); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#all() */ @@ -304,7 +304,7 @@ public List all() { return createQuery().fetch(); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#page(org.springframework.data.domain.Pageable) */ @@ -316,7 +316,7 @@ public Page page(Pageable pageable) { return createQuery().fetchPage(pageable); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#stream() */ @@ -325,7 +325,7 @@ public Stream stream() { return createQuery().stream(); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#count() */ @@ -334,7 +334,7 @@ public long count() { return createQuery().fetchCount(); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#exists() */ diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java index d57d6c73db..6b8b2fac9a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java @@ -399,7 +399,7 @@ public boolean exists(Example example) { return mongoOperations.exists(query, example.getProbeType(), entityInformation.getCollectionName()); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.QueryByExampleExecutor#findBy(org.springframework.data.domain.Example, java.util.function.Function) */ @@ -466,25 +466,25 @@ protected FluentQueryByExample create(Example predicate, Sort sort, return new FluentQueryByExample<>(predicate, sort, resultType, fieldsToInclude); } - /* + /* * (non-Javadoc) - * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#one() + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#oneValue() */ @Override - public T one() { + public T oneValue() { return createQuery().oneValue(); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#first() */ @Override - public T first() { + public T firstValue() { return createQuery().firstValue(); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#all() */ @@ -493,7 +493,7 @@ public List all() { return createQuery().all(); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#page(org.springframework.data.domain.Pageable) */ @@ -507,7 +507,7 @@ public Page page(Pageable pageable) { return PageableExecutionUtils.getPage(list, pageable, this::count); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#stream() */ @@ -516,7 +516,7 @@ public Stream stream() { return createQuery().stream(); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#count() */ @@ -525,7 +525,7 @@ public long count() { return createQuery().count(); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#exists() */ diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutorIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutorIntegrationTests.java index fea909bcda..01f5df84ba 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutorIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutorIntegrationTests.java @@ -248,7 +248,7 @@ protected MongoDatabase doGetDatabase() { public void findByShouldReturnFirstResult() { Person result = repository.findBy(person.firstname.eq(oliver.getFirstname()), - FluentQuery.FetchableFluentQuery::first); + FluentQuery.FetchableFluentQuery::oneValue); assertThat(result).isEqualTo(oliver); } @@ -257,7 +257,7 @@ public void findByShouldReturnFirstResult() { public void findByShouldReturnOneResult() { Person result = repository.findBy(person.firstname.eq(oliver.getFirstname()), - FluentQuery.FetchableFluentQuery::one); + FluentQuery.FetchableFluentQuery::oneValue); assertThat(result).isEqualTo(oliver); @@ -295,7 +295,8 @@ public void findByShouldApplyProjection() { Person probe = new Person(); probe.setLastname(oliver.getLastname()); - Person result = repository.findBy(person.lastname.eq(oliver.getLastname()), it -> it.project("firstname").first()); + Person result = repository.findBy(person.lastname.eq(oliver.getLastname()), + it -> it.project("firstname").firstValue()); assertThat(result.getFirstname()).isNotNull(); assertThat(result.getLastname()).isNull(); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryTests.java index 07a7e0f0aa..f5e14fdf70 100755 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryTests.java @@ -469,7 +469,7 @@ void findByShouldReturnFirstResult() { Person probe = new Person(); probe.setFirstname(oliver.getFirstname()); - Person result = repository.findBy(Example.of(probe, getMatcher()), FluentQuery.FetchableFluentQuery::first); + Person result = repository.findBy(Example.of(probe, getMatcher()), FluentQuery.FetchableFluentQuery::firstValue); assertThat(result).isEqualTo(oliver); } @@ -480,7 +480,7 @@ void findByShouldReturnOneResult() { Person probe = new Person(); probe.setFirstname(oliver.getFirstname()); - Person result = repository.findBy(Example.of(probe, getMatcher()), FluentQuery.FetchableFluentQuery::one); + Person result = repository.findBy(Example.of(probe, getMatcher()), FluentQuery.FetchableFluentQuery::oneValue); assertThat(result).isEqualTo(oliver); @@ -523,7 +523,7 @@ void findByShouldApplyProjection() { Person probe = new Person(); probe.setLastname(oliver.getLastname()); - Person result = repository.findBy(Example.of(probe, getMatcher()), it -> it.project("firstname").first()); + Person result = repository.findBy(Example.of(probe, getMatcher()), it -> it.project("firstname").firstValue()); assertThat(result.getFirstname()).isNotNull(); assertThat(result.getLastname()).isNull();