DATAMONGO-1110 - Polishing.

Moved to newly introduced Range type in Spring Data Commons to more safely bind minimum and maximum distances. Changed internal APIs to always use a Range<Distance> which gets populated based on the method signature's characteristics: if only one Distance parameter is found it's interpreted as a range with upper bound only.

Removed invalid testcase for minDistance on 2D index.

Original pull request: #277.
This commit is contained in:
Oliver Gierke
2015-03-04 17:59:49 +01:00
parent 7e74ec6b62
commit 6687cdc101
13 changed files with 112 additions and 123 deletions

View File

@@ -20,6 +20,7 @@ import java.util.List;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Range;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.SliceImpl;
import org.springframework.data.geo.Distance;
@@ -361,12 +362,15 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
nearQuery.query(query);
}
Distance maxDistance = accessor.getMaxDistance();
Range<Distance> distances = accessor.getDistanceRange();
Distance maxDistance = distances.getUpperBound();
if (maxDistance != null) {
nearQuery.maxDistance(maxDistance).in(maxDistance.getMetric());
}
Distance minDistance = accessor.getMinDistance();
Distance minDistance = distances.getLowerBound();
if (minDistance != null) {
nearQuery.minDistance(minDistance).in(minDistance.getMetric());
}

View File

@@ -22,6 +22,7 @@ import java.util.Iterator;
import java.util.List;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Range;
import org.springframework.data.domain.Sort;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
@@ -96,21 +97,13 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor {
return getConvertedValue(delegate.getBindableValue(index), null);
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.MongoParameterAccessor#getMaxDistance()
*/
public Distance getMaxDistance() {
return delegate.getMaxDistance();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.MongoParameterAccessor#getMinDistance()
* @see org.springframework.data.mongodb.repository.query.MongoParameterAccessor#getDistanceRange()
*/
@Override
public Distance getMinDistance() {
return delegate.getMinDistance();
public Range<Distance> getDistanceRange() {
return delegate.getDistanceRange();
}
/*

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.data.mongodb.repository.query;
import org.springframework.data.domain.Range;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.query.TextCriteria;
@@ -34,7 +35,7 @@ public interface MongoParameterAccessor extends ParameterAccessor {
* @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();
Range<Distance> getDistanceRange();
/**
* Returns the {@link Point} to use for a geo-near query.
@@ -50,12 +51,4 @@ public interface MongoParameterAccessor extends ParameterAccessor {
* @since 1.6
*/
TextCriteria getFullText();
/**
* Returns a {@link Distance} to be applied to {@literal $minDistance} for MongoDB geo queries.
*
* @return
* @since 1.7
*/
Distance getMinDistance();
}

View File

@@ -20,6 +20,7 @@ import java.util.Arrays;
import java.util.List;
import org.springframework.core.MethodParameter;
import org.springframework.data.domain.Range;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.query.TextCriteria;
@@ -27,6 +28,8 @@ import org.springframework.data.mongodb.repository.Near;
import org.springframework.data.mongodb.repository.query.MongoParameters.MongoParameter;
import org.springframework.data.repository.query.Parameter;
import org.springframework.data.repository.query.Parameters;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
/**
* Custom extension of {@link Parameters} discovering additional
@@ -36,8 +39,8 @@ import org.springframework.data.repository.query.Parameters;
*/
public class MongoParameters extends Parameters<MongoParameters, MongoParameter> {
private final Integer minDistanceIndex;
private final Integer maxDistanceIndex;
private final int rangeIndex;
private final int maxDistanceIndex;
private final Integer fullTextIndex;
private Integer nearIndex;
@@ -55,9 +58,11 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter>
this.fullTextIndex = parameterTypes.indexOf(TextCriteria.class);
int[] distances = distances(parameterTypes);
this.minDistanceIndex = distances[0];
this.maxDistanceIndex = distances[1];
ClassTypeInformation<?> declaringClassInfo = ClassTypeInformation.from(method.getDeclaringClass());
List<TypeInformation<?>> parameterTypeInfo = declaringClassInfo.getParameterTypes(method);
this.rangeIndex = getTypeIndex(parameterTypeInfo, Range.class, Distance.class);
this.maxDistanceIndex = this.rangeIndex == -1 ? getTypeIndex(parameterTypeInfo, Distance.class, null) : -1;
if (this.nearIndex == null && isGeoNearMethod) {
this.nearIndex = getNearIndex(parameterTypes);
@@ -66,15 +71,15 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter>
}
}
private MongoParameters(List<MongoParameter> parameters, Integer distanceIndex, Integer nearIndex,
Integer fullTextIndex, Integer minDistanceIndex) {
private MongoParameters(List<MongoParameter> parameters, int maxDistanceIndex, Integer nearIndex,
Integer fullTextIndex, int rangeIndex) {
super(parameters);
this.maxDistanceIndex = distanceIndex;
this.nearIndex = nearIndex;
this.fullTextIndex = fullTextIndex;
this.minDistanceIndex = minDistanceIndex;
this.maxDistanceIndex = maxDistanceIndex;
this.rangeIndex = rangeIndex;
}
private final int getNearIndex(List<Class<?>> parameterTypes) {
@@ -117,15 +122,19 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter>
return mongoParameter;
}
public int getDistanceRangeIndex() {
return -1;
}
/**
* Returns the index of a {@link Distance} parameter to be used for geo queries.
*
* @return
* @deprecated since 1.7. Please use {@link #getMaxDistanceParameterIndex()} instead.
* @deprecated since 1.7. Please use {@link #getMaxDistanceIndex()} instead.
*/
@Deprecated
public int getDistanceIndex() {
return getMaxDistanceParameterIndex();
return getMaxDistanceIndex();
}
/**
@@ -134,7 +143,7 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter>
* @return
* @since 1.7
*/
public int getMaxDistanceParameterIndex() {
public int getMaxDistanceIndex() {
return maxDistanceIndex;
}
@@ -169,16 +178,8 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter>
* @return
* @since 1.7
*/
public boolean hasMinDistanceParameter() {
return minDistanceIndex != null && minDistanceIndex.intValue() >= 0;
}
/**
* @return
* @since 1.7
*/
public int getMinDistanceParameterIndex() {
return minDistanceIndex != null ? minDistanceIndex.intValue() : -1;
public int getRangeIndex() {
return rangeIndex;
}
/*
@@ -187,23 +188,26 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter>
*/
@Override
protected MongoParameters createFrom(List<MongoParameter> parameters) {
return new MongoParameters(parameters, this.maxDistanceIndex, this.nearIndex, this.fullTextIndex,
this.minDistanceIndex);
return new MongoParameters(parameters, this.maxDistanceIndex, this.nearIndex, this.fullTextIndex, this.rangeIndex);
}
private int[] distances(List<Class<?>> paramTypes) {
private int getTypeIndex(List<TypeInformation<?>> parameterTypes, Class<?> type, Class<?> componentType) {
int maxDistance = paramTypes.lastIndexOf(Distance.class);
if (maxDistance == -1) {
return new int[] { -1, -1 };
for (int i = 0; i < parameterTypes.size(); i++) {
TypeInformation<?> candidate = parameterTypes.get(i);
if (candidate.getType().equals(type)) {
if (componentType == null) {
return i;
} else if (componentType.equals(candidate.getComponentType().getType())) {
return i;
}
}
}
int minDistance = paramTypes.indexOf(Distance.class);
if (minDistance == maxDistance) {
return new int[] { -1, maxDistance };
}
return new int[] { minDistance, maxDistance };
return -1;
}
/**

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.data.mongodb.repository.query;
import org.springframework.data.domain.Range;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.query.Term;
@@ -44,23 +45,20 @@ public class MongoParametersParameterAccessor extends ParametersParameterAccesso
this.method = method;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.MongoParameterAccessor#getMaxDistance()
*/
public Distance getMaxDistance() {
int index = method.getParameters().getDistanceIndex();
return index == -1 ? null : (Distance) getValue(index);
}
public Range<Distance> getDistanceRange() {
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.MongoParameterAccessor#getMinDistance()
*/
@Override
public Distance getMinDistance() {
int index = method.getParameters().getMinDistanceParameterIndex();
return index == -1 ? null : (Distance) getValue(index);
MongoParameters mongoParameters = method.getParameters();
int rangeIndex = mongoParameters.getRangeIndex();
if (rangeIndex != -1) {
return getValue(rangeIndex);
}
int maxDistanceIndex = mongoParameters.getMaxDistanceIndex();
Distance maxDistance = maxDistanceIndex == -1 ? null : (Distance) getValue(maxDistanceIndex);
return new Range<Distance>(null, maxDistance);
}
/*

View File

@@ -23,6 +23,7 @@ import java.util.Iterator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Range;
import org.springframework.data.domain.Sort;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Metrics;
@@ -207,8 +208,10 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
return criteria.is(false);
case NEAR:
Distance distance = accessor.getMaxDistance();
Distance minDistance = accessor.getMinDistance();
Range<Distance> range = accessor.getDistanceRange();
Distance distance = range.getUpperBound();
Distance minDistance = range.getLowerBound();
Point point = accessor.getGeoNearLocation();
point = point == null ? nextAs(parameters, Point.class) : point;

View File

@@ -70,16 +70,6 @@ public class GeoSpatial2DTests extends AbstractGeoSpatialTests {
assertThat(fields, hasItem(IndexField.geo("location")));
}
/**
* @see DATAMONGO-1110
*/
@Test
public void nearPointWithMinDistance() {
Point point = new Point(-73.99171, 40.738868);
List<Venue> venues = template.find(query(where("location").near(point).minDistance(0.01)), Venue.class);
assertThat(venues.size(), is(5));
}
@Override
protected void createIndex() {
template.indexOps(Venue.class).ensureIndex(new GeospatialIndex("location").typed(GeoSpatialIndexType.GEO_2D));

View File

@@ -18,6 +18,7 @@ package org.springframework.data.mongodb.repository;
import static java.util.Arrays.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.springframework.data.geo.Metrics.*;
import java.util.ArrayList;
import java.util.Arrays;
@@ -35,6 +36,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Range;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
@@ -1176,8 +1178,9 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
dave.setLocation(point);
repository.save(dave);
GeoResults<Person> results = repository.findPersonByLocationNear(new Point(-73.99, 40.73), new Distance(0.01,
Metrics.KILOMETERS), new Distance(2000, Metrics.KILOMETERS));
Range<Distance> range = Distance.between(new Distance(0.01, KILOMETERS), new Distance(2000, KILOMETERS));
GeoResults<Person> results = repository.findPersonByLocationNear(new Point(-73.99, 40.73), range);
assertThat(results.getContent().isEmpty(), is(false));
}
}

View File

@@ -22,6 +22,7 @@ import java.util.stream.Stream;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Range;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.Sort;
import org.springframework.data.geo.Box;
@@ -169,8 +170,10 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
GeoResults<Person> findByLocationNear(Point point, Distance maxDistance);
/** @see DATAMONGO-1110 */
GeoResults<Person> findPersonByLocationNear(Point point, Distance maxDistance, Distance minDistance);
/**
* @see DATAMONGO-1110
*/
GeoResults<Person> findPersonByLocationNear(Point point, Range<Distance> distance);
GeoPage<Person> findByLocationNear(Point point, Distance maxDistance, Pageable pageable);

View File

@@ -23,6 +23,7 @@ import java.util.List;
import org.hamcrest.core.IsNull;
import org.junit.Test;
import org.springframework.data.domain.Range;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
@@ -53,7 +54,7 @@ public class MongoParametersParameterAccessorUnitTests {
MongoParameterAccessor accessor = new MongoParametersParameterAccessor(queryMethod,
new Object[] { new Point(10, 20) });
assertThat(accessor.getMaxDistance(), is(nullValue()));
assertThat(accessor.getDistanceRange().getUpperBound(), is(nullValue()));
}
@Test
@@ -64,7 +65,7 @@ public class MongoParametersParameterAccessorUnitTests {
MongoParameterAccessor accessor = new MongoParametersParameterAccessor(queryMethod, new Object[] {
new Point(10, 20), DISTANCE });
assertThat(accessor.getMaxDistance(), is(DISTANCE));
assertThat(accessor.getDistanceRange().getUpperBound(), is(DISTANCE));
}
/**
@@ -102,17 +103,19 @@ public class MongoParametersParameterAccessorUnitTests {
@Test
public void shouldDetectMinAndMaxDistance() throws NoSuchMethodException, SecurityException {
Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class, Distance.class, Distance.class);
Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class, Range.class);
MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, context);
Distance min = new Distance(10, Metrics.KILOMETERS);
Distance max = new Distance(20, Metrics.KILOMETERS);
MongoParameterAccessor accessor = new MongoParametersParameterAccessor(queryMethod, new Object[] {
new Point(10, 20), min, max });
new Point(10, 20), Distance.between(min, max) });
assertThat(accessor.getMinDistance(), is(min));
assertThat(accessor.getMaxDistance(), is(max));
Range<Distance> range = accessor.getDistanceRange();
assertThat(range.getLowerBound(), is(min));
assertThat(range.getUpperBound(), is(max));
}
interface PersonRepository extends Repository<Person, Long> {
@@ -121,7 +124,7 @@ public class MongoParametersParameterAccessorUnitTests {
List<Person> findByLocationNear(Point point, Distance distance);
List<Person> findByLocationNear(Point point, Distance minDistance, Distance maxDistance);
List<Person> findByLocationNear(Point point, Range<Distance> distances);
List<Person> findByFirstname(String firstname, TextCriteria fullText);
}

View File

@@ -25,6 +25,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.domain.Range;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Point;
@@ -50,7 +51,7 @@ public class MongoParametersUnitTests {
MongoParameters parameters = new MongoParameters(method, false);
assertThat(parameters.getNumberOfParameters(), is(2));
assertThat(parameters.getMaxDistanceParameterIndex(), is(1));
assertThat(parameters.getMaxDistanceIndex(), is(1));
assertThat(parameters.getBindableParameters().getNumberOfParameters(), is(1));
Parameter parameter = parameters.getParameter(1);
@@ -127,11 +128,11 @@ public class MongoParametersUnitTests {
@Test
public void shouldFindMinAndMaxDistanceParameters() throws NoSuchMethodException, SecurityException {
Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class, Distance.class, Distance.class);
Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class, Range.class);
MongoParameters parameters = new MongoParameters(method, false);
assertThat(parameters.getMinDistanceParameterIndex(), is(1));
assertThat(parameters.getMaxDistanceParameterIndex(), is(2));
assertThat(parameters.getRangeIndex(), is(1));
assertThat(parameters.getMaxDistanceIndex(), is(-1));
}
/**
@@ -144,8 +145,8 @@ public class MongoParametersUnitTests {
Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class, Distance.class);
MongoParameters parameters = new MongoParameters(method, false);
assertThat(parameters.getMinDistanceParameterIndex(), is(-1));
assertThat(parameters.getMaxDistanceParameterIndex(), is(1));
assertThat(parameters.getRangeIndex(), is(-1));
assertThat(parameters.getMaxDistanceIndex(), is(1));
}
interface PersonRepository {
@@ -164,6 +165,6 @@ public class MongoParametersUnitTests {
List<Person> findByNameAndText(String name, TextCriteria text);
List<Person> findByLocationNear(Point point, Distance min, Distance max);
List<Person> findByLocationNear(Point point, Range<Distance> range);
}
}

View File

@@ -36,6 +36,7 @@ import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.runners.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
import org.springframework.data.domain.Range;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
@@ -543,11 +544,10 @@ public class MongoQueryCreatorUnitTests {
public void shouldCreateNearQueryForMinMaxDistance() {
Point point = new Point(10, 20);
Distance min = new Distance(10);
Distance max = new Distance(20);
Range<Distance> range = Distance.between(new Distance(10), new Distance(20));
PartTree tree = new PartTree("findByAddress_GeoNear", User.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, point, min, max), context);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, point, range), context);
Query query = creator.createQuery();
assertThat(query, is(query(where("address.geo").near(point).minDistance(10D).maxDistance(20D))));

View File

@@ -19,6 +19,7 @@ import java.util.Arrays;
import java.util.Iterator;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Range;
import org.springframework.data.domain.Sort;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
@@ -35,8 +36,7 @@ import org.springframework.data.repository.query.ParameterAccessor;
class StubParameterAccessor implements MongoParameterAccessor {
private final Object[] values;
private Distance distance;
private Distance minDistance;
private Range<Distance> range = new Range<Distance>(null, null);
/**
* Creates a new {@link ConvertingParameterAccessor} backed by a {@link StubParameterAccessor} simply returning the
@@ -50,18 +50,16 @@ class StubParameterAccessor implements MongoParameterAccessor {
return new ConvertingParameterAccessor(converter, new StubParameterAccessor(parameters));
}
@SuppressWarnings("unchecked")
public StubParameterAccessor(Object... values) {
this.values = values;
for (Object value : values) {
if (value instanceof Distance) {
if (this.distance == null) {
this.distance = (Distance) value;
} else {
this.minDistance = this.distance;
this.distance = (Distance) value;
}
if (value instanceof Range) {
this.range = (Range<Distance>) value;
} else if (value instanceof Distance) {
this.range = new Range<Distance>(null, (Distance) value);
}
}
}
@@ -98,17 +96,13 @@ class StubParameterAccessor implements MongoParameterAccessor {
return null;
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.MongoParameterAccessor#getMaxDistance()
* @see org.springframework.data.mongodb.repository.query.MongoParameterAccessor#getDistanceRange()
*/
public Distance getMaxDistance() {
return distance;
}
@Override
public Distance getMinDistance() {
return minDistance;
public Range<Distance> getDistanceRange() {
return range;
}
/*