DATAMONGO-1110 - Add support for $minDistance.
We now support $minDistance for NearQuery and Criteria. Please keep in mind that minDistance is only available for MongoDB 2.6 and better and can only be combined with $near or $nearSphere operator depending on the defined index type. Usage of $minDistance with NearQuery is only possible when a 2dsphere index is present. We also make sure $minDistance operator gets correctly nested when using GeoJSON types. It is now possible to use a Range<Distance> parameter within the repository queries. This allows to define near queries like: findByLocationNear(Point point, Range<Distance> distances); The lower bound of the range is treated as the minimum distance while the upper one defines the maximum distance from the given point. In case a Distance parameter is provided it will serve as maxDistance. Original pull request: #277.
This commit is contained in:
committed by
Oliver Gierke
parent
b887fa70a5
commit
7e74ec6b62
@@ -441,7 +441,8 @@ public class Criteria implements CriteriaDefinition {
|
||||
*/
|
||||
public Criteria maxDistance(double maxDistance) {
|
||||
|
||||
if (createNearCriteriaForCommand("$near", maxDistance) || createNearCriteriaForCommand("$nearSphere", maxDistance)) {
|
||||
if (createNearCriteriaForCommand("$near", "$maxDistance", maxDistance)
|
||||
|| createNearCriteriaForCommand("$nearSphere", "$maxDistance", maxDistance)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -449,6 +450,25 @@ public class Criteria implements CriteriaDefinition {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a geospatial criterion using a {@literal $minDistance} operation, for use with {@literal $near} or
|
||||
* {@literal $nearSphere}.
|
||||
*
|
||||
* @param minDistance
|
||||
* @return
|
||||
* @since 1.7
|
||||
*/
|
||||
public Criteria minDistance(double minDistance) {
|
||||
|
||||
if (createNearCriteriaForCommand("$near", "$minDistance", minDistance)
|
||||
|| createNearCriteriaForCommand("$nearSphere", "$minDistance", minDistance)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
criteria.put("$minDistance", minDistance);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the {@literal $elemMatch} operator
|
||||
*
|
||||
@@ -605,7 +625,7 @@ public class Criteria implements CriteriaDefinition {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean createNearCriteriaForCommand(String command, double maxDistance) {
|
||||
private boolean createNearCriteriaForCommand(String command, String operation, double maxDistance) {
|
||||
|
||||
if (!criteria.containsKey(command)) {
|
||||
return false;
|
||||
@@ -615,14 +635,13 @@ public class Criteria implements CriteriaDefinition {
|
||||
|
||||
if (existingNearOperationValue instanceof DBObject) {
|
||||
|
||||
((DBObject) existingNearOperationValue).put("$maxDistance", maxDistance);
|
||||
((DBObject) existingNearOperationValue).put(operation, maxDistance);
|
||||
|
||||
return true;
|
||||
|
||||
} else if (existingNearOperationValue instanceof GeoJson) {
|
||||
|
||||
BasicDBObject dbo = new BasicDBObject("$geometry", existingNearOperationValue)
|
||||
.append("$maxDistance", maxDistance);
|
||||
BasicDBObject dbo = new BasicDBObject("$geometry", existingNearOperationValue).append(operation, maxDistance);
|
||||
criteria.put(command, dbo);
|
||||
|
||||
return true;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2014 the original author or authors.
|
||||
* Copyright 2011-2015 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.
|
||||
@@ -40,6 +40,7 @@ public final class NearQuery {
|
||||
private final Point point;
|
||||
private Query query;
|
||||
private Distance maxDistance;
|
||||
private Distance minDistance;
|
||||
private Metric metric;
|
||||
private boolean spherical;
|
||||
private Integer num;
|
||||
@@ -211,6 +212,63 @@ public final class NearQuery {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum distance results shall have from the configured origin. If a {@link Metric} was set before the
|
||||
* given value will be interpreted as being a value in that metric. E.g.
|
||||
*
|
||||
* <pre>
|
||||
* NearQuery query = near(10.0, 20.0, Metrics.KILOMETERS).minDistance(150);
|
||||
* </pre>
|
||||
*
|
||||
* Will set the minimum distance to 150 kilometers.
|
||||
*
|
||||
* @param minDistance
|
||||
* @return
|
||||
* @since 1.7
|
||||
*/
|
||||
public NearQuery minDistance(double minDistance) {
|
||||
return minDistance(new Distance(minDistance, getMetric()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum distance supplied in a given metric. Will normalize the distance but not reconfigure the query's
|
||||
* result {@link Metric} if one was configured before.
|
||||
*
|
||||
* @param minDistance
|
||||
* @param metric must not be {@literal null}.
|
||||
* @return
|
||||
* @since 1.7
|
||||
*/
|
||||
public NearQuery minDistance(double minDistance, Metric metric) {
|
||||
|
||||
Assert.notNull(metric);
|
||||
return minDistance(new Distance(minDistance, metric));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum distance to the given {@link Distance}. Will set the returned {@link Metric} to be the one of the
|
||||
* given {@link Distance} if no {@link Metric} was set before.
|
||||
*
|
||||
* @param distance must not be {@literal null}.
|
||||
* @return
|
||||
* @since 1.7
|
||||
*/
|
||||
public NearQuery minDistance(Distance distance) {
|
||||
|
||||
Assert.notNull(distance);
|
||||
|
||||
if (distance.getMetric() != Metrics.NEUTRAL) {
|
||||
this.spherical(true);
|
||||
}
|
||||
|
||||
if (this.metric == null) {
|
||||
in(distance.getMetric());
|
||||
}
|
||||
|
||||
this.minDistance = distance;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum {@link Distance}.
|
||||
*
|
||||
@@ -220,6 +278,16 @@ public final class NearQuery {
|
||||
return this.maxDistance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum {@link Distance}.
|
||||
*
|
||||
* @return
|
||||
* @since 1.7
|
||||
*/
|
||||
public Distance getMinDistance() {
|
||||
return this.minDistance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a {@link CustomMetric} with the given multiplier.
|
||||
*
|
||||
@@ -352,7 +420,11 @@ public final class NearQuery {
|
||||
}
|
||||
|
||||
if (maxDistance != null) {
|
||||
dbObject.put("maxDistance", this.maxDistance.getNormalizedValue());
|
||||
dbObject.put("maxDistance", maxDistance.getNormalizedValue());
|
||||
}
|
||||
|
||||
if (minDistance != null) {
|
||||
dbObject.put("minDistance", minDistance.getNormalizedValue());
|
||||
}
|
||||
|
||||
if (metric != null) {
|
||||
|
||||
@@ -366,6 +366,11 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
|
||||
nearQuery.maxDistance(maxDistance).in(maxDistance.getMetric());
|
||||
}
|
||||
|
||||
Distance minDistance = accessor.getMinDistance();
|
||||
if (minDistance != null) {
|
||||
nearQuery.minDistance(minDistance).in(minDistance.getMetric());
|
||||
}
|
||||
|
||||
Pageable pageable = accessor.getPageable();
|
||||
if (pageable != null) {
|
||||
nearQuery.with(pageable);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2014 the original author or authors.
|
||||
* Copyright 2011-2015 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.
|
||||
@@ -104,6 +104,15 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor {
|
||||
return delegate.getMaxDistance();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.repository.query.MongoParameterAccessor#getMinDistance()
|
||||
*/
|
||||
@Override
|
||||
public Distance getMinDistance() {
|
||||
return delegate.getMinDistance();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.repository.MongoParameterAccessor#getGeoNearLocation()
|
||||
@@ -252,4 +261,5 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor {
|
||||
*/
|
||||
Object nextConverted(MongoPersistentProperty property);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2014 the original author or authors.
|
||||
* Copyright 2011-2015 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.
|
||||
@@ -50,4 +50,12 @@ 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();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2014 the original author or authors.
|
||||
* Copyright 2011-2015 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.
|
||||
@@ -36,7 +36,8 @@ import org.springframework.data.repository.query.Parameters;
|
||||
*/
|
||||
public class MongoParameters extends Parameters<MongoParameters, MongoParameter> {
|
||||
|
||||
private final Integer distanceIndex;
|
||||
private final Integer minDistanceIndex;
|
||||
private final Integer maxDistanceIndex;
|
||||
private final Integer fullTextIndex;
|
||||
|
||||
private Integer nearIndex;
|
||||
@@ -51,9 +52,13 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter>
|
||||
|
||||
super(method);
|
||||
List<Class<?>> parameterTypes = Arrays.asList(method.getParameterTypes());
|
||||
this.distanceIndex = parameterTypes.indexOf(Distance.class);
|
||||
|
||||
this.fullTextIndex = parameterTypes.indexOf(TextCriteria.class);
|
||||
|
||||
int[] distances = distances(parameterTypes);
|
||||
this.minDistanceIndex = distances[0];
|
||||
this.maxDistanceIndex = distances[1];
|
||||
|
||||
if (this.nearIndex == null && isGeoNearMethod) {
|
||||
this.nearIndex = getNearIndex(parameterTypes);
|
||||
} else if (this.nearIndex == null) {
|
||||
@@ -62,13 +67,14 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter>
|
||||
}
|
||||
|
||||
private MongoParameters(List<MongoParameter> parameters, Integer distanceIndex, Integer nearIndex,
|
||||
Integer fullTextIndex) {
|
||||
Integer fullTextIndex, Integer minDistanceIndex) {
|
||||
|
||||
super(parameters);
|
||||
|
||||
this.distanceIndex = distanceIndex;
|
||||
this.maxDistanceIndex = distanceIndex;
|
||||
this.nearIndex = nearIndex;
|
||||
this.fullTextIndex = fullTextIndex;
|
||||
this.minDistanceIndex = minDistanceIndex;
|
||||
}
|
||||
|
||||
private final int getNearIndex(List<Class<?>> parameterTypes) {
|
||||
@@ -115,9 +121,21 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter>
|
||||
* 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
|
||||
public int getDistanceIndex() {
|
||||
return distanceIndex;
|
||||
return getMaxDistanceParameterIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the {@link Distance} parameter to be used for max distance in geo queries.
|
||||
*
|
||||
* @return
|
||||
* @since 1.7
|
||||
*/
|
||||
public int getMaxDistanceParameterIndex() {
|
||||
return maxDistanceIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,13 +165,45 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter>
|
||||
return this.fullTextIndex != null && this.fullTextIndex.intValue() >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.Parameters#createFrom(java.util.List)
|
||||
*/
|
||||
@Override
|
||||
protected MongoParameters createFrom(List<MongoParameter> parameters) {
|
||||
return new MongoParameters(parameters, this.distanceIndex, this.nearIndex, this.fullTextIndex);
|
||||
return new MongoParameters(parameters, this.maxDistanceIndex, this.nearIndex, this.fullTextIndex,
|
||||
this.minDistanceIndex);
|
||||
}
|
||||
|
||||
private int[] distances(List<Class<?>> paramTypes) {
|
||||
|
||||
int maxDistance = paramTypes.lastIndexOf(Distance.class);
|
||||
if (maxDistance == -1) {
|
||||
return new int[] { -1, -1 };
|
||||
}
|
||||
|
||||
int minDistance = paramTypes.indexOf(Distance.class);
|
||||
if (minDistance == maxDistance) {
|
||||
return new int[] { -1, maxDistance };
|
||||
}
|
||||
|
||||
return new int[] { minDistance, maxDistance };
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2014 the original author or authors.
|
||||
* Copyright 2011-2015 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.
|
||||
@@ -53,6 +53,16 @@ public class MongoParametersParameterAccessor extends ParametersParameterAccesso
|
||||
return index == -1 ? null : (Distance) getValue(index);
|
||||
}
|
||||
|
||||
/*
|
||||
* (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);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.repository.MongoParameterAccessor#getGeoNearLocation()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2014 the original author or authors.
|
||||
* Copyright 2010-2015 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.
|
||||
@@ -30,6 +30,8 @@ import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.geo.Shape;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.context.PersistentPropertyPath;
|
||||
import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
|
||||
import org.springframework.data.mongodb.core.index.GeoSpatialIndexed;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
|
||||
@@ -206,18 +208,24 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
|
||||
case NEAR:
|
||||
|
||||
Distance distance = accessor.getMaxDistance();
|
||||
Distance minDistance = accessor.getMinDistance();
|
||||
Point point = accessor.getGeoNearLocation();
|
||||
point = point == null ? nextAs(parameters, Point.class) : point;
|
||||
|
||||
boolean isSpherical = isSpherical(property);
|
||||
|
||||
if (distance == null) {
|
||||
return criteria.near(point);
|
||||
return isSpherical ? criteria.nearSphere(point) : criteria.near(point);
|
||||
} else {
|
||||
if (!Metrics.NEUTRAL.equals(distance.getMetric())) {
|
||||
if (isSpherical || !Metrics.NEUTRAL.equals(distance.getMetric())) {
|
||||
criteria.nearSphere(point);
|
||||
} else {
|
||||
criteria.near(point);
|
||||
}
|
||||
criteria.maxDistance(distance.getNormalizedValue());
|
||||
if (minDistance != null) {
|
||||
criteria.minDistance(minDistance.getNormalizedValue());
|
||||
}
|
||||
}
|
||||
return criteria;
|
||||
case WITHIN:
|
||||
@@ -393,4 +401,10 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
|
||||
|
||||
return source.replaceAll("\\*", ".*");
|
||||
}
|
||||
|
||||
private boolean isSpherical(MongoPersistentProperty property) {
|
||||
|
||||
GeoSpatialIndexed index = property.findAnnotation(GeoSpatialIndexed.class);
|
||||
return index != null && index.type().equals(GeoSpatialIndexType.GEO_2DSPHERE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,6 +278,45 @@ public class GeoJsonTests {
|
||||
assertThat(result.geoJsonGeometryCollection, equalTo(obj.geoJsonGeometryCollection));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1110
|
||||
*/
|
||||
@Test
|
||||
public void nearWithMinDistance() {
|
||||
|
||||
Point point = new GeoJsonPoint(-73.99171, 40.738868);
|
||||
List<Venue2DSphere> venues = template.find(query(where("location").near(point).minDistance(0.01)),
|
||||
Venue2DSphere.class);
|
||||
|
||||
assertThat(venues.size(), is(11));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1110
|
||||
*/
|
||||
@Test
|
||||
public void nearSphereWithMinDistance() {
|
||||
|
||||
Point point = new GeoJsonPoint(-73.99171, 40.738868);
|
||||
List<Venue2DSphere> venues = template.find(query(where("location").nearSphere(point).minDistance(0.01)),
|
||||
Venue2DSphere.class);
|
||||
|
||||
assertThat(venues.size(), is(11));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1135
|
||||
*/
|
||||
@Test
|
||||
public void nearWithMinAndMaxDistance() {
|
||||
|
||||
GeoJsonPoint point = new GeoJsonPoint(-73.99171, 40.738868);
|
||||
|
||||
Query query = query(where("location").near(point).minDistance(0.01).maxDistance(100));
|
||||
List<Venue2DSphere> venues = template.find(query, Venue2DSphere.class);
|
||||
assertThat(venues.size(), is(2));
|
||||
}
|
||||
|
||||
private void addVenues() {
|
||||
|
||||
template.insert(new Venue2DSphere("Penn Station", -73.99408, 40.75057));
|
||||
|
||||
@@ -18,17 +18,24 @@ package org.springframework.data.mongodb.core.geo;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.mongodb.core.query.Criteria.*;
|
||||
import static org.springframework.data.mongodb.core.query.Query.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.data.geo.GeoResults;
|
||||
import org.springframework.data.geo.Metric;
|
||||
import org.springframework.data.geo.Metrics;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.mongodb.core.IndexOperations;
|
||||
import org.springframework.data.mongodb.core.Venue;
|
||||
import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
|
||||
import org.springframework.data.mongodb.core.index.GeospatialIndex;
|
||||
import org.springframework.data.mongodb.core.index.IndexField;
|
||||
import org.springframework.data.mongodb.core.index.IndexInfo;
|
||||
import org.springframework.data.mongodb.core.query.NearQuery;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
@@ -55,6 +62,30 @@ public class GeoSpatial2DSphereTests extends AbstractGeoSpatialTests {
|
||||
assertThat(fields, hasItem(IndexField.geo("location")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1110
|
||||
*/
|
||||
@Test
|
||||
public void geoNearWithMinDistance() {
|
||||
|
||||
NearQuery geoNear = NearQuery.near(-73, 40, Metrics.KILOMETERS).num(10).minDistance(1);
|
||||
|
||||
GeoResults<Venue> result = template.geoNear(geoNear, Venue.class);
|
||||
|
||||
assertThat(result.getContent().size(), is(not(0)));
|
||||
assertThat(result.getAverageDistance().getMetric(), is((Metric) Metrics.KILOMETERS));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1110
|
||||
*/
|
||||
@Test
|
||||
public void nearSphereWithMinDistance() {
|
||||
Point point = new Point(-73.99171, 40.738868);
|
||||
List<Venue> venues = template.find(query(where("location").nearSphere(point).minDistance(0.01)), Venue.class);
|
||||
assertThat(venues.size(), is(1));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createIndex() {
|
||||
template.indexOps(Venue.class).ensureIndex(new GeospatialIndex("location").typed(GeoSpatialIndexType.GEO_2DSPHERE));
|
||||
@@ -64,5 +95,4 @@ public class GeoSpatial2DSphereTests extends AbstractGeoSpatialTests {
|
||||
protected void dropIndex() {
|
||||
template.indexOps(Venue.class).dropIndex("location_2dsphere");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -70,6 +70,16 @@ 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));
|
||||
|
||||
@@ -211,4 +211,40 @@ public class CriteriaTests {
|
||||
|
||||
assertThat(dbo, IsBsonObject.isBsonObject().containing("foo.$nearSphere.$maxDistance", 50D));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1110
|
||||
*/
|
||||
@Test
|
||||
public void minDistanceShouldBeMappedInsideNearWhenUsedAlongWithGeoJsonType() {
|
||||
|
||||
DBObject dbo = new Criteria("foo").near(new GeoJsonPoint(100, 200)).minDistance(50D).getCriteriaObject();
|
||||
|
||||
assertThat(dbo, IsBsonObject.isBsonObject().containing("foo.$near.$minDistance", 50D));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1110
|
||||
*/
|
||||
@Test
|
||||
public void minDistanceShouldBeMappedInsideNearSphereWhenUsedAlongWithGeoJsonType() {
|
||||
|
||||
DBObject dbo = new Criteria("foo").nearSphere(new GeoJsonPoint(100, 200)).minDistance(50D).getCriteriaObject();
|
||||
|
||||
assertThat(dbo, IsBsonObject.isBsonObject().containing("foo.$nearSphere.$minDistance", 50D));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1110
|
||||
*/
|
||||
@Test
|
||||
public void minAndMaxDistanceShouldBeMappedInsideNearSphereWhenUsedAlongWithGeoJsonType() {
|
||||
|
||||
DBObject dbo = new Criteria("foo").nearSphere(new GeoJsonPoint(100, 200)).minDistance(50D).maxDistance(100D)
|
||||
.getCriteriaObject();
|
||||
|
||||
assertThat(dbo, IsBsonObject.isBsonObject().containing("foo.$nearSphere.$minDistance", 50D));
|
||||
assertThat(dbo, IsBsonObject.isBsonObject().containing("foo.$nearSphere.$maxDistance", 100D));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1165,4 +1165,19 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
|
||||
result.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1110
|
||||
*/
|
||||
@Test
|
||||
public void executesGeoNearQueryForResultsCorrectlyWhenGivenMinAndMaxDistance() {
|
||||
|
||||
Point point = new Point(-73.99171, 40.738868);
|
||||
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));
|
||||
assertThat(results.getContent().isEmpty(), is(false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2013 the original author or authors.
|
||||
* Copyright 2010-2015 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.
|
||||
@@ -21,6 +21,7 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
|
||||
import org.springframework.data.mongodb.core.index.GeoSpatialIndexed;
|
||||
import org.springframework.data.mongodb.core.index.Indexed;
|
||||
import org.springframework.data.mongodb.core.mapping.DBRef;
|
||||
@@ -32,6 +33,7 @@ import org.springframework.data.mongodb.core.mapping.Field;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@Document
|
||||
public class Person extends Contact {
|
||||
@@ -49,7 +51,7 @@ public class Person extends Contact {
|
||||
|
||||
List<String> skills;
|
||||
|
||||
@GeoSpatialIndexed private Point location;
|
||||
@GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE) private Point location;
|
||||
|
||||
private @Field("add") Address address;
|
||||
private Set<Address> shippingAddresses;
|
||||
|
||||
@@ -169,6 +169,9 @@ 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);
|
||||
|
||||
GeoPage<Person> findByLocationNear(Point point, Distance maxDistance, Pageable pageable);
|
||||
|
||||
List<Person> findByCreator(User user);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2014 the original author or authors.
|
||||
* Copyright 2011-2015 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.
|
||||
@@ -96,12 +96,33 @@ public class MongoParametersParameterAccessorUnitTests {
|
||||
equalTo("{ \"$text\" : { \"$search\" : \"data\"}}"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1110
|
||||
*/
|
||||
@Test
|
||||
public void shouldDetectMinAndMaxDistance() throws NoSuchMethodException, SecurityException {
|
||||
|
||||
Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class, Distance.class, Distance.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 });
|
||||
|
||||
assertThat(accessor.getMinDistance(), is(min));
|
||||
assertThat(accessor.getMaxDistance(), is(max));
|
||||
}
|
||||
|
||||
interface PersonRepository extends Repository<Person, Long> {
|
||||
|
||||
List<Person> findByLocationNear(Point point);
|
||||
|
||||
List<Person> findByLocationNear(Point point, Distance distance);
|
||||
|
||||
List<Person> findByLocationNear(Point point, Distance minDistance, Distance maxDistance);
|
||||
|
||||
List<Person> findByFirstname(String firstname, TextCriteria fullText);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2014 the original author or authors.
|
||||
* Copyright 2011-2015 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.
|
||||
@@ -50,7 +50,7 @@ public class MongoParametersUnitTests {
|
||||
MongoParameters parameters = new MongoParameters(method, false);
|
||||
|
||||
assertThat(parameters.getNumberOfParameters(), is(2));
|
||||
assertThat(parameters.getDistanceIndex(), is(1));
|
||||
assertThat(parameters.getMaxDistanceParameterIndex(), is(1));
|
||||
assertThat(parameters.getBindableParameters().getNumberOfParameters(), is(1));
|
||||
|
||||
Parameter parameter = parameters.getParameter(1);
|
||||
@@ -121,6 +121,33 @@ public class MongoParametersUnitTests {
|
||||
assertThat(parameters.getParameter(parameters.getFullTextParameterIndex()).isSpecialParameter(), is(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1110
|
||||
*/
|
||||
@Test
|
||||
public void shouldFindMinAndMaxDistanceParameters() throws NoSuchMethodException, SecurityException {
|
||||
|
||||
Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class, Distance.class, Distance.class);
|
||||
MongoParameters parameters = new MongoParameters(method, false);
|
||||
|
||||
assertThat(parameters.getMinDistanceParameterIndex(), is(1));
|
||||
assertThat(parameters.getMaxDistanceParameterIndex(), is(2));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1110
|
||||
*/
|
||||
@Test
|
||||
public void shouldNotHaveMinDistanceIfOnlyOneDistanceParameterPresent() throws NoSuchMethodException,
|
||||
SecurityException {
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
interface PersonRepository {
|
||||
|
||||
List<Person> findByLocationNear(Point point, Distance distance);
|
||||
@@ -136,5 +163,7 @@ public class MongoParametersUnitTests {
|
||||
GeoResults<Person> validDoubleArrays(double[] first, @Near double[] second);
|
||||
|
||||
List<Person> findByNameAndText(String name, TextCriteria text);
|
||||
|
||||
List<Person> findByLocationNear(Point point, Distance min, Distance max);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,8 @@ import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mongodb.core.Person;
|
||||
import org.springframework.data.mongodb.core.Venue;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverter;
|
||||
import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
|
||||
import org.springframework.data.mongodb.core.index.GeoSpatialIndexed;
|
||||
import org.springframework.data.mongodb.core.mapping.DBRef;
|
||||
import org.springframework.data.mongodb.core.mapping.Field;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
@@ -503,6 +505,54 @@ public class MongoQueryCreatorUnitTests {
|
||||
assertThat(query, is(query(where("address.geo").within(shape))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1110
|
||||
*/
|
||||
@Test
|
||||
public void shouldCreateNearSphereQueryForSphericalProperty() {
|
||||
|
||||
Point point = new Point(10, 20);
|
||||
|
||||
PartTree tree = new PartTree("findByAddress2dSphere_GeoNear", User.class);
|
||||
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, point), context);
|
||||
Query query = creator.createQuery();
|
||||
|
||||
assertThat(query, is(query(where("address2dSphere.geo").nearSphere(point))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1110
|
||||
*/
|
||||
@Test
|
||||
public void shouldCreateNearSphereQueryForSphericalPropertyHavingDistanceWithDefaultMetric() {
|
||||
|
||||
Point point = new Point(1.0, 1.0);
|
||||
Distance distance = new Distance(1.0);
|
||||
|
||||
PartTree tree = new PartTree("findByAddress2dSphere_GeoNear", User.class);
|
||||
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, point, distance), context);
|
||||
Query query = creator.createQuery();
|
||||
|
||||
assertThat(query, is(query(where("address2dSphere.geo").nearSphere(point).maxDistance(1.0))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1110
|
||||
*/
|
||||
@Test
|
||||
public void shouldCreateNearQueryForMinMaxDistance() {
|
||||
|
||||
Point point = new Point(10, 20);
|
||||
Distance min = new Distance(10);
|
||||
Distance max = new Distance(20);
|
||||
|
||||
PartTree tree = new PartTree("findByAddress_GeoNear", User.class);
|
||||
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, point, min, max), context);
|
||||
Query query = creator.createQuery();
|
||||
|
||||
assertThat(query, is(query(where("address.geo").near(point).minDistance(10D).maxDistance(20D))));
|
||||
}
|
||||
|
||||
interface PersonRepository extends Repository<Person, Long> {
|
||||
|
||||
List<Person> findByLocationNearAndFirstname(Point location, Distance maxDistance, String firstname);
|
||||
@@ -517,6 +567,8 @@ public class MongoQueryCreatorUnitTests {
|
||||
List<String> emailAddresses;
|
||||
|
||||
Address address;
|
||||
|
||||
Address2dSphere address2dSphere;
|
||||
}
|
||||
|
||||
static class Address {
|
||||
@@ -524,4 +576,9 @@ public class MongoQueryCreatorUnitTests {
|
||||
String street;
|
||||
Point geo;
|
||||
}
|
||||
|
||||
static class Address2dSphere {
|
||||
String street;
|
||||
@GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE) Point geo;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2014 the original author or authors.
|
||||
* Copyright 2011-2015 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.
|
||||
@@ -30,11 +30,13 @@ import org.springframework.data.repository.query.ParameterAccessor;
|
||||
* Simple {@link ParameterAccessor} that returns the given parameters unfiltered.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Christoh Strobl
|
||||
*/
|
||||
class StubParameterAccessor implements MongoParameterAccessor {
|
||||
|
||||
private final Object[] values;
|
||||
private Distance distance;
|
||||
private Distance minDistance;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ConvertingParameterAccessor} backed by a {@link StubParameterAccessor} simply returning the
|
||||
@@ -54,7 +56,12 @@ class StubParameterAccessor implements MongoParameterAccessor {
|
||||
|
||||
for (Object value : values) {
|
||||
if (value instanceof Distance) {
|
||||
this.distance = (Distance) value;
|
||||
if (this.distance == null) {
|
||||
this.distance = (Distance) value;
|
||||
} else {
|
||||
this.minDistance = this.distance;
|
||||
this.distance = (Distance) value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -99,6 +106,11 @@ class StubParameterAccessor implements MongoParameterAccessor {
|
||||
return distance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getMinDistance() {
|
||||
return minDistance;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.ParameterAccessor#iterator()
|
||||
|
||||
@@ -210,6 +210,14 @@ NOTE: Note that for version 1.0 we currently don't support referring to paramete
|
||||
| `findByLocationNear(Point point)`
|
||||
| `{"location" : {"$near" : [x,y]}}`
|
||||
|
||||
| `Near`
|
||||
| `findByLocationNear(Point point, Distance max)`
|
||||
| `{"location" : {"$near" : [x,y], "$maxDistance" : max}}`
|
||||
|
||||
| `Near`
|
||||
| `findByLocationNear(Point point, Distance min, Distance max)`
|
||||
| `{"location" : {"$near" : [x,y], "$minDistance" : min, "$maxDistance" : max}}`
|
||||
|
||||
| `Within`
|
||||
| `findByLocationWithin(Circle circle)`
|
||||
| `{"location" : {"$geoWithin" : {"$center" : [ [x, y], distance]}}}`
|
||||
@@ -283,6 +291,8 @@ Distance distance = new Distance(200, Metrics.KILOMETERS);
|
||||
|
||||
As you can see using a `Distance` equipped with a `Metric` causes `$nearSphere` clause to be added instead of a plain `$near`. Beyond that the actual distance gets calculated according to the `Metrics` used.
|
||||
|
||||
NOTE: Using `@GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE)` on the target property forces usage of `$nearSphere` operator.
|
||||
|
||||
==== Geo-near queries
|
||||
|
||||
[source,java]
|
||||
@@ -296,6 +306,11 @@ public interface PersonRepository extends MongoRepository<Person, String>
|
||||
// Metric: {'geoNear' : 'person', 'near' : [x, y], 'maxDistance' : distance,
|
||||
// 'distanceMultiplier' : metric.multiplier, 'spherical' : true }
|
||||
GeoResults<Person> findByLocationNear(Point location, Distance distance);
|
||||
|
||||
// Metric: {'geoNear' : 'person', 'near' : [x, y], 'minDistance' : min,
|
||||
// 'maxDistance' : max, 'distanceMultiplier' : metric.multiplier,
|
||||
// 'spherical' : true }
|
||||
GeoResults<Person> findByLocationNear(Point location, Distance min, Distance max);
|
||||
|
||||
// {'geoNear' : 'location', 'near' : [x, y] }
|
||||
GeoResults<Person> findByLocationNear(Point location);
|
||||
|
||||
@@ -1013,6 +1013,7 @@ There are also methods on the Criteria class for geospatial queries. Here is a l
|
||||
* `Criteria` *withinSphere* `(Circle circle)` Creates a geospatial criterion using `$geoWithin $center` operators.
|
||||
* `Criteria` *near* `(Point point)` Creates a geospatial criterion using a `$near `operation
|
||||
* `Criteria` *nearSphere* `(Point point)` Creates a geospatial criterion using `$nearSphere$center` operations. This is only available for MongoDB 1.7 and higher.
|
||||
* `Criteria` *minDistance* `(double minDistance)` Creates a geospatial criterion using the `$minDistance` operation, for use with $near.
|
||||
* `Criteria` *maxDistance* `(double maxDistance)` Creates a geospatial criterion using the `$maxDistance` operation, for use with $near.
|
||||
|
||||
The `Query` class has some additional methods used to provide options for the query.
|
||||
@@ -1111,7 +1112,7 @@ List<Venue> venues =
|
||||
template.find(new Query(Criteria.where("location").within(box)), Venue.class);
|
||||
----
|
||||
|
||||
To find venues near a `Point`, the following query can be used
|
||||
To find venues near a `Point`, the following queries can be used
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -1120,6 +1121,13 @@ List<Venue> venues =
|
||||
template.find(new Query(Criteria.where("location").near(point).maxDistance(0.01)), Venue.class);
|
||||
----
|
||||
|
||||
[source,java]
|
||||
----
|
||||
Point point = new Point(-73.99171, 40.738868);
|
||||
List<Venue> venues =
|
||||
template.find(new Query(Criteria.where("location").near(point).minDistance(0.01).maxDistance(100)), Venue.class);
|
||||
----
|
||||
|
||||
To find venues near a `Point` using spherical coordines the following query can be used
|
||||
|
||||
[source,java]
|
||||
|
||||
Reference in New Issue
Block a user