From 5b47648f491e9055dbf4d3a8c46e009ff052a1b8 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 19 Feb 2019 07:49:42 +0100 Subject: [PATCH] DATAMONGO-2072 - Support Range in repository between queries. We now support o.s.d.domain.Range as method parameter for between queries. This allows more fine grained control over the inclusion/exclusion of the upper/lower bounds. Up till now between required 2 parameters which had been strictly bound to excluding bounds using $gt and $lt. Original pull request: #645. --- .../repository/query/MongoQueryCreator.java | 48 ++++++++++++++++++- .../query/MongoQueryCreatorUnitTests.java | 11 +++++ .../reference/mongo-repositories.adoc | 6 ++- 3 files changed, 61 insertions(+), 4 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java index 309660540..d8689d3a1 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java @@ -26,6 +26,7 @@ import java.util.regex.Pattern; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.domain.Range; +import org.springframework.data.domain.Range.Bound; import org.springframework.data.domain.Sort; import org.springframework.data.geo.Distance; import org.springframework.data.geo.Metrics; @@ -42,7 +43,6 @@ import org.springframework.data.mongodb.core.query.CriteriaDefinition; import org.springframework.data.mongodb.core.query.MongoRegexCreator; import org.springframework.data.mongodb.core.query.MongoRegexCreator.MatchMode; import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.repository.query.ConvertingParameterAccessor.PotentiallyConvertingIterator; import org.springframework.data.repository.query.parser.AbstractQueryCreator; import org.springframework.data.repository.query.parser.Part; import org.springframework.data.repository.query.parser.Part.IgnoreCaseType; @@ -187,7 +187,7 @@ class MongoQueryCreator extends AbstractQueryCreator { case LESS_THAN_EQUAL: return criteria.lte(parameters.next()); case BETWEEN: - return criteria.gt(parameters.next()).lt(parameters.next()); + return computeBetweenPart(criteria, parameters.next(), parameters); case IS_NOT_NULL: return criteria.ne(null); case IS_NULL: @@ -418,6 +418,50 @@ class MongoQueryCreator extends AbstractQueryCreator { return false; } + /** + * Compute a {@link Type#BETWEEN} typed {@link Part} using {@link Criteria#gt(Object) $gt}, + * {@link Criteria#gte(Object) $gte}, {@link Criteria#lt(Object) $lt} and {@link Criteria#lte(Object) $lte}.
+ * In case the given {@literal value} is actually a {@link Range} the lower and upper bounds of the {@link Range} are + * used according to their {@link Bound#isInclusive() inclusion} definition. Otherwise the {@literal value} is used + * for {@literal $gt} and {@link Iterator#next() parameters.next()} as {@literal $lt}. + * + * @param criteria must not be {@literal null}. + * @param value current value. Must not be {@literal null}. + * @param parameters must not be {@literal null}. + * @return + * @since 2.2 + */ + private static Criteria computeBetweenPart(Criteria criteria, Object value, Iterator parameters) { + + if (!(value instanceof Range)) { + return criteria.gt(value).lt(parameters.next()); + } + + Range range = (Range) value; + Optional min = range.getLowerBound().getValue(); + Optional max = range.getUpperBound().getValue(); + + min.ifPresent(it -> { + + if (range.getLowerBound().isInclusive()) { + criteria.gte(it); + } else { + criteria.gt(it); + } + }); + + max.ifPresent(it -> { + + if (range.getUpperBound().isInclusive()) { + criteria.lte(it); + } else { + criteria.lt(it); + } + }); + + return criteria; + } + private static MatchMode toMatchMode(Type type) { switch (type) { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java index 88d2c68fb..0c207b2b8 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java @@ -32,6 +32,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.springframework.data.domain.Range; +import org.springframework.data.domain.Range.Bound; import org.springframework.data.geo.Distance; import org.springframework.data.geo.Metrics; import org.springframework.data.geo.Point; @@ -648,6 +649,16 @@ public class MongoQueryCreatorUnitTests { assertThat(creator.createQuery()).isEqualTo(query(where("firstName").regex(".*", "iu"))); } + @Test // DATAMONGO-2071 + public void betweenShouldAllowSingleRageParameter() { + + PartTree tree = new PartTree("findByAgeBetween", Person.class); + MongoQueryCreator creator = new MongoQueryCreator(tree, + getAccessor(converter, Range.of(Bound.exclusive(10), Bound.exclusive(11))), context); + + assertThat(creator.createQuery()).isEqualTo(query(where("age").gt(10).lt(11))); + } + interface PersonRepository extends Repository { List findByLocationNearAndFirstname(Point location, Distance maxDistance, String firstname); diff --git a/src/main/asciidoc/reference/mongo-repositories.adoc b/src/main/asciidoc/reference/mongo-repositories.adoc index dc518dc29..20a87179b 100644 --- a/src/main/asciidoc/reference/mongo-repositories.adoc +++ b/src/main/asciidoc/reference/mongo-repositories.adoc @@ -188,8 +188,10 @@ The following table shows the keywords that are supported for query methods: | `{"age" : {"$lte" : age}}` | `Between` -| `findByAgeBetween(int from, int to)` -| `{"age" : {"$gt" : from, "$lt" : to}}` +| `findByAgeBetween(int from, int to)` + +`findByAgeBetween(Range range)` +| `{"age" : {"$gt" : from, "$lt" : to}}` + +lower / upper bounds (`$gt` / `$gte` & `$lt` / `$lte`) according to `Range` | `In` | `findByAgeIn(Collection ages)`