From e130fb5a2d83205b9ed5125142f134e987f5eb05 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Sun, 21 Aug 2011 15:18:36 +0200 Subject: [PATCH] DATACMNS-61 - Adapted changes in Spring Data Commons. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use QueryMethod accessor methods instead of dropped Type enum to determine query execution. Added custom MongoParameters and MongoParameter to allow discovering a Distance parameter for repository queries which will transparently add a 'maxDistance' clause for *Near criterias in query methods. If the given Distance is equipped with a Metric we will rather use $nearSphere over $near. Added asList() to Point class to circumvent bug in BasicBSONObject.equals(…) which breaks equals(…) comparisons of DBObjects in case they use arrays as values. See [0] for details. Adapted usage of Point objects to use asList() over asArray(). [0] https://jira.mongodb.org/browse/JAVA-416 --- .../data/mongodb/core/geo/Point.java | 7 ++ .../data/mongodb/core/query/Criteria.java | 19 +++--- .../data/mongodb/core/query/NearQuery.java | 2 +- .../repository/AbstractMongoQuery.java | 17 +++-- .../ConvertingParameterAccessor.java | 17 +++-- .../repository/MongoParameterAccessor.java | 35 ++++++++++ .../mongodb/repository/MongoParameters.java | 63 ++++++++++++++++++ .../MongoParametersParameterAccessor.java | 47 +++++++++++++ .../mongodb/repository/MongoQueryCreator.java | 24 +++++-- .../mongodb/repository/MongoQueryMethod.java | 26 +++++++- ...oParametersParameterAccessorUnitTests.java | 66 +++++++++++++++++++ .../repository/MongoParametersUnitTests.java | 57 ++++++++++++++++ .../MongoQueryCreatorUnitTests.java | 47 ++++++++++++- .../repository/StubParameterAccessor.java | 11 +++- 14 files changed, 405 insertions(+), 33 deletions(-) create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/MongoParameterAccessor.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/MongoParameters.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/MongoParametersParameterAccessor.java create mode 100644 spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MongoParametersParameterAccessorUnitTests.java create mode 100644 spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MongoParametersUnitTests.java diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/Point.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/Point.java index 1f1117991..ccc990462 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/Point.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/Point.java @@ -15,6 +15,9 @@ */ package org.springframework.data.mongodb.core.geo; +import java.util.Arrays; +import java.util.List; + import org.springframework.data.annotation.PersistenceConstructor; import org.springframework.data.mongodb.core.mapping.Field; import org.springframework.util.Assert; @@ -55,6 +58,10 @@ public class Point { public double[] asArray() { return new double[] { x, y }; } + + public List asList() { + return Arrays.asList(x, y); + } @Override public int hashCode() { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java index 10ba354c4..22f9c96fc 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java @@ -18,7 +18,6 @@ package org.springframework.data.mongodb.core.query; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; -import java.util.LinkedList; import java.util.List; import com.mongodb.BasicDBObject; @@ -295,8 +294,8 @@ public class Criteria implements CriteriaDefinition { */ public Criteria withinCenter(Circle circle) { Assert.notNull(circle); - LinkedList list = new LinkedList(); - list.addLast(circle.getCenter().asArray()); + List list = new ArrayList(); + list.add(circle.getCenter().asList()); list.add(circle.getRadius()); criteria.put("$within", new BasicDBObject("$center", list)); return this; @@ -311,8 +310,8 @@ public class Criteria implements CriteriaDefinition { */ public Criteria withinCenterSphere(Circle circle) { Assert.notNull(circle); - LinkedList list = new LinkedList(); - list.addLast(circle.getCenter().asArray()); + List list = new ArrayList(); + list.add(circle.getCenter().asList()); list.add(circle.getRadius()); criteria.put("$within", new BasicDBObject("$centerSphere", list)); return this; @@ -326,9 +325,9 @@ public class Criteria implements CriteriaDefinition { */ public Criteria withinBox(Box box) { Assert.notNull(box); - LinkedList list = new LinkedList(); - list.addLast(box.getLowerLeft().asArray()); - list.addLast(box.getUpperRight().asArray()); + List> list = new ArrayList>(); + list.add(box.getLowerLeft().asList()); + list.add(box.getUpperRight().asList()); criteria.put("$within", new BasicDBObject("$box", list)); return this; } @@ -342,7 +341,7 @@ public class Criteria implements CriteriaDefinition { */ public Criteria near(Point point) { Assert.notNull(point); - criteria.put("$near", point.asArray()); + criteria.put("$near", point.asList()); return this; } @@ -355,7 +354,7 @@ public class Criteria implements CriteriaDefinition { */ public Criteria nearSphere(Point point) { Assert.notNull(point); - criteria.put("$nearSphere", point.asArray()); + criteria.put("$nearSphere", point.asList()); return this; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/NearQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/NearQuery.java index e5b6add78..afebdbe33 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/NearQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/NearQuery.java @@ -46,7 +46,7 @@ public class NearQuery { Assert.notNull(point); this.criteria = new BasicDBObject(); - this.criteria.put("near", point.asArray()); + this.criteria.put("near", point.asList()); this.metric = metric; if (metric != null) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/AbstractMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/AbstractMongoQuery.java index db3297617..5561e7322 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/AbstractMongoQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/AbstractMongoQuery.java @@ -19,19 +19,19 @@ import static org.springframework.data.mongodb.repository.QueryUtils.*; import java.util.List; -import com.mongodb.DBCollection; -import com.mongodb.DBCursor; -import com.mongodb.DBObject; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.data.mongodb.core.CollectionCallback; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.repository.query.ParameterAccessor; -import org.springframework.data.repository.query.ParametersParameterAccessor; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.util.Assert; +import com.mongodb.DBCollection; +import com.mongodb.DBCursor; +import com.mongodb.DBObject; + /** * Base class for {@link RepositoryQuery} implementations for Mongo. * @@ -72,15 +72,14 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { */ public Object execute(Object[] parameters) { - ParameterAccessor accessor = new ParametersParameterAccessor(method.getParameters(), parameters); + MongoParameterAccessor accessor = new MongoParametersParameterAccessor(method.getParameters(), parameters); Query query = createQuery(new ConvertingParameterAccessor(template.getConverter(), accessor)); - switch (method.getType()) { - case COLLECTION: + if (method.isCollectionQuery()) { return new CollectionExecution().execute(query); - case PAGING: + } else if (method.isPageQuery()) { return new PagedExecution(accessor.getPageable()).execute(query); - default: + } else { return new SingleEntityExecution().execute(query); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/ConvertingParameterAccessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/ConvertingParameterAccessor.java index 2f9a8d751..0a7b5a009 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/ConvertingParameterAccessor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/ConvertingParameterAccessor.java @@ -21,6 +21,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.mongodb.core.convert.MappingMongoConverter; import org.springframework.data.mongodb.core.convert.MongoWriter; +import org.springframework.data.mongodb.core.geo.Distance; import org.springframework.data.repository.query.ParameterAccessor; import com.mongodb.BasicDBList; @@ -31,17 +32,17 @@ import com.mongodb.DBObject; * * @author Oliver Gierke */ -public class ConvertingParameterAccessor implements ParameterAccessor { +public class ConvertingParameterAccessor implements MongoParameterAccessor { private final MongoWriter writer; - private final ParameterAccessor delegate; + private final MongoParameterAccessor delegate; /** * Creates a new {@link ConvertingParameterAccessor} with the given {@link MongoWriter} and delegate. * * @param writer */ - public ConvertingParameterAccessor(MongoWriter writer, ParameterAccessor delegate) { + public ConvertingParameterAccessor(MongoWriter writer, MongoParameterAccessor delegate) { this.writer = writer; this.delegate = delegate; } @@ -51,7 +52,7 @@ public class ConvertingParameterAccessor implements ParameterAccessor { * * @see java.lang.Iterable#iterator() */ - public Iterator iterator() { + public PotentiallyConvertingIterator iterator() { return new ConvertingIterator(delegate.iterator()); } @@ -80,6 +81,14 @@ public class ConvertingParameterAccessor implements ParameterAccessor { return getConvertedValue(delegate.getBindableValue(index)); } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.repository.MongoParameterAccessor#getMaxDistance() + */ + public Distance getMaxDistance() { + return delegate.getMaxDistance(); + } /** * Converts the given value with the underlying {@link MongoWriter}. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/MongoParameterAccessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/MongoParameterAccessor.java new file mode 100644 index 000000000..9ab252b83 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/MongoParameterAccessor.java @@ -0,0 +1,35 @@ +/* + * Copyright 2011 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 + * + * http://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; + +import org.springframework.data.mongodb.core.geo.Distance; +import org.springframework.data.repository.query.ParameterAccessor; + +/** + * Mongo-specific {@link ParameterAccessor} exposing a maximum distance parameter. + * + * @author Oliver Gierke + */ +public interface MongoParameterAccessor extends ParameterAccessor { + + /** + * Returns a {@link Distance} to be applied to Mongo geo queries. + * + * @return the maximum distance to apply to the geo query or {@literal null} if there's no {@link Distance} parameter + * at all or the given value for it was {@literal null}. + */ + Distance getMaxDistance(); +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/MongoParameters.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/MongoParameters.java new file mode 100644 index 000000000..a033d10ce --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/MongoParameters.java @@ -0,0 +1,63 @@ +package org.springframework.data.mongodb.repository; + +import java.lang.reflect.Method; +import java.util.Arrays; + +import org.springframework.core.MethodParameter; +import org.springframework.data.mongodb.core.geo.Distance; +import org.springframework.data.repository.query.Parameter; +import org.springframework.data.repository.query.Parameters; + +/** + * Custom extension of {@link Parameters} discovering additional + * + * @author Oliver Gierke + */ +public class MongoParameters extends Parameters { + + private int distanceIndex = -1; + + public MongoParameters(Method method) { + + super(method); + this.distanceIndex = Arrays.asList(method.getParameterTypes()).indexOf(Distance.class); + } + + /* (non-Javadoc) + * @see org.springframework.data.repository.query.Parameters#createParameter(org.springframework.core.MethodParameter) + */ + @Override + protected Parameter createParameter(MethodParameter parameter) { + return new MongoParameter(parameter, this); + } + + public int getDistanceIndex() { + return distanceIndex; + } + + /** + * Custom {@link Parameter} implementation adding parameters of type {@link Distance} to the special ones. + * + * @author Oliver Gierke + */ + static class MongoParameter extends Parameter { + + /** + * + * @param parameter + * @param parameters + */ + MongoParameter(MethodParameter parameter, Parameters parameters) { + super(parameter, parameters); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.Parameter#isSpecialParameter() + */ + @Override + public boolean isSpecialParameter() { + return super.isSpecialParameter() || getType().equals(Distance.class); + } + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/MongoParametersParameterAccessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/MongoParametersParameterAccessor.java new file mode 100644 index 000000000..f35e03546 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/MongoParametersParameterAccessor.java @@ -0,0 +1,47 @@ +/* + * Copyright 2011 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 + * + * http://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; + +import org.springframework.data.mongodb.core.geo.Distance; +import org.springframework.data.repository.query.ParametersParameterAccessor; + +/** + * Mongo-specific {@link ParametersParameterAccessor} to allow access to the {@link Distance} parameter. + * + * @author Oliver Gierke + */ +public class MongoParametersParameterAccessor extends ParametersParameterAccessor implements MongoParameterAccessor { + + private final MongoParameters parameters; + + /** + * @param parameters + * @param values + */ + public MongoParametersParameterAccessor(MongoParameters parameters, Object[] values) { + super(parameters, values); + this.parameters = parameters; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.repository.MongoParameterAccessor#getMaxDistance() + */ + public Distance getMaxDistance() { + int index = parameters.getDistanceIndex(); + return index == -1 ? null : (Distance) getValue(index); + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/MongoQueryCreator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/MongoQueryCreator.java index 18cf6076d..bd3ecb30f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/MongoQueryCreator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/MongoQueryCreator.java @@ -26,12 +26,12 @@ import org.apache.commons.logging.LogFactory; import org.springframework.data.domain.Sort; import org.springframework.data.mongodb.core.geo.Box; import org.springframework.data.mongodb.core.geo.Circle; +import org.springframework.data.mongodb.core.geo.Distance; import org.springframework.data.mongodb.core.geo.Point; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.CriteriaDefinition; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.repository.ConvertingParameterAccessor.PotentiallyConvertingIterator; -import org.springframework.data.repository.query.ParameterAccessor; import org.springframework.data.repository.query.ParametersParameterAccessor; import org.springframework.data.repository.query.parser.AbstractQueryCreator; import org.springframework.data.repository.query.parser.Part; @@ -46,6 +46,7 @@ import org.springframework.data.repository.query.parser.PartTree; class MongoQueryCreator extends AbstractQueryCreator { private static final Log LOG = LogFactory.getLog(MongoQueryCreator.class); + private final MongoParameterAccessor accessor; /** * Creates a new {@link MongoQueryCreator} from the given {@link PartTree} and {@link ParametersParameterAccessor}. @@ -53,9 +54,10 @@ class MongoQueryCreator extends AbstractQueryCreator { * @param tree * @param accessor */ - public MongoQueryCreator(PartTree tree, ParameterAccessor accessor) { + public MongoQueryCreator(PartTree tree, ConvertingParameterAccessor accessor) { super(tree, accessor); + this.accessor = accessor; } /* @@ -144,9 +146,23 @@ class MongoQueryCreator extends AbstractQueryCreator { String value = parameters.next().toString(); return criteria.is(toLikeRegex(value)); case NEAR: - return criteria.near(nextAs(parameters, Point.class)); + + Distance distance = accessor.getMaxDistance(); + Point point = nextAs(parameters, Point.class); + + if (distance == null) { + return criteria.near(point); + } else { + if (distance.getMetric() != null) { + criteria.nearSphere(point); + } else { + criteria.near(point); + } + criteria.maxDistance(distance.getNormalizedValue()); + } + return criteria; + case WITHIN: - Object parameter = parameters.next(); if (parameter instanceof Box) { return criteria.withinBox((Box) parameter); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/MongoQueryMethod.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/MongoQueryMethod.java index 5236e7b38..b345a9a82 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/MongoQueryMethod.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/MongoQueryMethod.java @@ -20,6 +20,7 @@ import java.lang.reflect.Method; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.data.mongodb.repository.MongoRepositoryFactoryBean.EntityInformationCreator; import org.springframework.data.repository.core.RepositoryMetadata; +import org.springframework.data.repository.query.Parameters; import org.springframework.data.repository.query.QueryMethod; import org.springframework.data.repository.util.ClassUtils; import org.springframework.util.StringUtils; @@ -45,6 +46,15 @@ class MongoQueryMethod extends QueryMethod { this.method = method; this.entityInformation = entityInformationCreator.getEntityInformation(ClassUtils.getReturnedDomainClass(method)); } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.QueryMethod#getParameters(java.lang.reflect.Method) + */ + @Override + protected Parameters createParameters(Method method) { + return new MongoParameters(method); + } /** * Returns whether the method has an annotated query. @@ -78,14 +88,24 @@ class MongoQueryMethod extends QueryMethod { return StringUtils.hasText(value) ? value : null; } - /* (non-Javadoc) - * @see org.springframework.data.repository.query.QueryMethod#getEntityMetadata() - */ + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.QueryMethod#getEntityInformation() + */ @Override public MongoEntityInformation getEntityInformation() { return entityInformation; } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.QueryMethod#getParameters() + */ + @Override + public MongoParameters getParameters() { + return (MongoParameters) super.getParameters(); + } /** * Returns the {@link Query} annotation that is applied to the method or {@code null} if none available. diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MongoParametersParameterAccessorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MongoParametersParameterAccessorUnitTests.java new file mode 100644 index 000000000..5fd300953 --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MongoParametersParameterAccessorUnitTests.java @@ -0,0 +1,66 @@ +/* + * Copyright 2011 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 + * + * http://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; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import java.lang.reflect.Method; +import java.util.List; + +import org.junit.Test; +import org.springframework.data.mongodb.core.geo.Distance; +import org.springframework.data.mongodb.core.geo.Metrics; +import org.springframework.data.mongodb.core.geo.Point; + +/** + * Unit tests for {@link MongoParametersParameterAccessor}. + * + * @author Oliver Gierke + */ +public class MongoParametersParameterAccessorUnitTests { + + private static final Distance DISTANCE = new Distance(2.5, Metrics.KILOMETERS); + + @Test + public void returnsNullForDistanceIfNoneAvailable() throws NoSuchMethodException, SecurityException { + + Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class); + MongoParameters parameters = new MongoParameters(method); + + MongoParameterAccessor accessor = new MongoParametersParameterAccessor(parameters, + new Object[] { new Point(10, 20) }); + assertThat(accessor.getMaxDistance(), is(nullValue())); + } + + @Test + public void returnsDistanceIfAvailable() throws NoSuchMethodException, SecurityException { + + Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class, Distance.class); + MongoParameters parameters = new MongoParameters(method); + + MongoParameterAccessor accessor = new MongoParametersParameterAccessor(parameters, new Object[] { + new Point(10, 20), DISTANCE }); + assertThat(accessor.getMaxDistance(), is(DISTANCE)); + } + + interface PersonRepository { + + List findByLocationNear(Point point); + + List findByLocationNear(Point point, Distance distance); + } +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MongoParametersUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MongoParametersUnitTests.java new file mode 100644 index 000000000..f85b22250 --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MongoParametersUnitTests.java @@ -0,0 +1,57 @@ +/* + * Copyright 2011 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 + * + * http://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; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import java.lang.reflect.Method; +import java.util.List; + +import org.junit.Test; +import org.springframework.core.MethodParameter; +import org.springframework.data.mongodb.core.geo.Distance; +import org.springframework.data.mongodb.core.geo.Point; +import org.springframework.data.mongodb.repository.MongoParameters.MongoParameter; + +/** + * Unit tests for {@link MongoParameters}. + * + * @author Oliver Gierke + */ +public class MongoParametersUnitTests { + + @Test + public void discoversDistanceParameter() throws NoSuchMethodException, SecurityException { + Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class, Distance.class); + MongoParameters parameters = new MongoParameters(method); + + assertThat(parameters.getNumberOfParameters(), is(2)); + assertThat(parameters.getDistanceIndex(), is(1)); + assertThat(parameters.getBindableParameters().getNumberOfParameters(), is(1)); + + MongoParameter parameter = new MongoParameters.MongoParameter(new MethodParameter(method, + parameters.getDistanceIndex()), parameters); + + assertThat(parameter.isSpecialParameter(), is(true)); + assertThat(parameter.isBindable(), is(false)); + } + + interface PersonRepository { + + List findByLocationNear(Point point, Distance distance); + } +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MongoQueryCreatorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MongoQueryCreatorUnitTests.java index 4dcb8e457..a9e480241 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MongoQueryCreatorUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MongoQueryCreatorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2011 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. @@ -19,8 +19,12 @@ import static org.junit.Assert.*; import static org.mockito.Mockito.*; import static org.hamcrest.CoreMatchers.*; import static org.springframework.data.mongodb.repository.StubParameterAccessor.*; +import static org.springframework.data.mongodb.core.query.Query.*; +import static org.springframework.data.mongodb.core.query.Criteria.*; import java.lang.reflect.Method; +import java.util.List; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -31,6 +35,9 @@ import org.mockito.runners.MockitoJUnitRunner; import org.mockito.stubbing.Answer; import org.springframework.data.mongodb.core.Person; import org.springframework.data.mongodb.core.convert.MongoConverter; +import org.springframework.data.mongodb.core.geo.Distance; +import org.springframework.data.mongodb.core.geo.Metrics; +import org.springframework.data.mongodb.core.geo.Point; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.repository.MongoQueryCreator; @@ -96,4 +103,42 @@ public class MongoQueryCreatorUnitTests { assertThat(query.getQueryObject(), is(new Query(Criteria.where("firstName").is(null)).getQueryObject())); } + + @Test + public void bindsMetricDistanceParameterToNearSphereCorrectly() throws Exception { + + Point point = new Point(10, 20); + Distance distance = new Distance(2.5, Metrics.KILOMETERS); + + Query query = query(where("location").nearSphere(point).maxDistance(distance.getNormalizedValue()).and("firstname").is("Dave")); + assertBindsDistanceToQuery(point, distance, query); + } + + @Test + public void bindsDistanceParameterToNearCorrectly() throws Exception { + + Point point = new Point(10, 20); + Distance distance = new Distance(2.5); + + Query query = query(where("location").near(point).maxDistance(distance.getNormalizedValue()).and("firstname").is("Dave")); + assertBindsDistanceToQuery(point, distance, query); + } + + private void assertBindsDistanceToQuery(Point point, Distance distance, Query reference) throws Exception { + + when(converter.convertToMongoType("Dave")).thenReturn("Dave"); + + PartTree tree = new PartTree("findByLocationNearAndFirstname", org.springframework.data.mongodb.repository.Person.class); + MongoParameters parameters = new MongoParameters(PersonRepository.class.getMethod("findByLocationNearAndFirstname", Point.class, Distance.class, String.class)); + MongoParameterAccessor accessor = new MongoParametersParameterAccessor(parameters, new Object[] { point, distance, "Dave" }); + + Query query = new MongoQueryCreator(tree, new ConvertingParameterAccessor(converter, accessor)).createQuery(); + assertThat(query.getQueryObject(), is(query.getQueryObject())); + + } + + interface PersonRepository { + + List findByLocationNearAndFirstname(Point location, Distance maxDistance, String firstname); + } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/StubParameterAccessor.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/StubParameterAccessor.java index 7657f1d40..95bbfa30d 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/StubParameterAccessor.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/StubParameterAccessor.java @@ -21,6 +21,7 @@ import java.util.Iterator; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.mongodb.core.convert.MongoWriter; +import org.springframework.data.mongodb.core.geo.Distance; import org.springframework.data.mongodb.repository.ConvertingParameterAccessor; import org.springframework.data.repository.query.ParameterAccessor; @@ -29,7 +30,7 @@ import org.springframework.data.repository.query.ParameterAccessor; * * @author Oliver Gierke */ -class StubParameterAccessor implements ParameterAccessor { +class StubParameterAccessor implements MongoParameterAccessor { private final Object[] values; @@ -74,6 +75,14 @@ class StubParameterAccessor implements ParameterAccessor { public Sort getSort() { return null; } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.repository.MongoParameterAccessor#getMaxDistance() + */ + public Distance getMaxDistance() { + return null; + } /* * (non-Javadoc)