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 38725ff4f..8b9ddad87 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
@@ -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;
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 3a96914bc..b847d192e 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
@@ -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.
+ *
+ *
+ * NearQuery query = near(10.0, 20.0, Metrics.KILOMETERS).minDistance(150);
+ *
+ *
+ * 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) {
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java
index 425363f1a..69c78b584 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java
@@ -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);
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessor.java
index c59b74be8..b52f97f4e 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessor.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessor.java
@@ -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);
}
+
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameterAccessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameterAccessor.java
index 51b0f4d1c..9b107857e 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameterAccessor.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameterAccessor.java
@@ -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();
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameters.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameters.java
index debaab3da..bbd52d278 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameters.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameters.java
@@ -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 {
- 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
super(method);
List> 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
}
private MongoParameters(List 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> parameterTypes) {
@@ -115,9 +121,21 @@ public class MongoParameters extends Parameters
* 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
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 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> 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 };
}
/**
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParametersParameterAccessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParametersParameterAccessor.java
index df74089fd..a38664ec3 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParametersParameterAccessor.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParametersParameterAccessor.java
@@ -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()
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 83dd74324..aa4fb9375 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
@@ -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 {
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 {
return source.replaceAll("\\*", ".*");
}
+
+ private boolean isSpherical(MongoPersistentProperty property) {
+
+ GeoSpatialIndexed index = property.findAnnotation(GeoSpatialIndexed.class);
+ return index != null && index.type().equals(GeoSpatialIndexType.GEO_2DSPHERE);
+ }
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonTests.java
index 0d97e73d5..01d961167 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonTests.java
@@ -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 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 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 venues = template.find(query, Venue2DSphere.class);
+ assertThat(venues.size(), is(2));
+ }
+
private void addVenues() {
template.insert(new Venue2DSphere("Penn Station", -73.99408, 40.75057));
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatial2DSphereTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatial2DSphereTests.java
index 086397067..a51bd46b9 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatial2DSphereTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatial2DSphereTests.java
@@ -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 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 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");
}
-
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatial2DTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatial2DTests.java
index e9248f63b..f72ab83cc 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatial2DTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatial2DTests.java
@@ -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 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));
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaTests.java
index e42cd5365..4244af9d2 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaTests.java
@@ -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));
+ }
+
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java
index 7cdf22bcf..576f8eee3 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java
@@ -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 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));
+ }
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Person.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Person.java
index 1b47b2b68..4c165f739 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Person.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Person.java
@@ -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 skills;
- @GeoSpatialIndexed private Point location;
+ @GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE) private Point location;
private @Field("add") Address address;
private Set shippingAddresses;
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java
index 9d2ee73ee..8004ca949 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java
@@ -169,6 +169,9 @@ public interface PersonRepository extends MongoRepository, Query
GeoResults findByLocationNear(Point point, Distance maxDistance);
+ /** @see DATAMONGO-1110 */
+ GeoResults findPersonByLocationNear(Point point, Distance maxDistance, Distance minDistance);
+
GeoPage findByLocationNear(Point point, Distance maxDistance, Pageable pageable);
List findByCreator(User user);
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoParametersParameterAccessorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoParametersParameterAccessorUnitTests.java
index 7b089b044..1a9aca5b0 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoParametersParameterAccessorUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoParametersParameterAccessorUnitTests.java
@@ -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 {
List findByLocationNear(Point point);
List findByLocationNear(Point point, Distance distance);
+ List findByLocationNear(Point point, Distance minDistance, Distance maxDistance);
+
List findByFirstname(String firstname, TextCriteria fullText);
}
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoParametersUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoParametersUnitTests.java
index 2213bc684..6b9fb6eca 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoParametersUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoParametersUnitTests.java
@@ -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 findByLocationNear(Point point, Distance distance);
@@ -136,5 +163,7 @@ public class MongoParametersUnitTests {
GeoResults validDoubleArrays(double[] first, @Near double[] second);
List findByNameAndText(String name, TextCriteria text);
+
+ List findByLocationNear(Point point, Distance min, Distance max);
}
}
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 769dfd3d4..6b8d6a3f1 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
@@ -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 {
List findByLocationNearAndFirstname(Point location, Distance maxDistance, String firstname);
@@ -517,6 +567,8 @@ public class MongoQueryCreatorUnitTests {
List 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;
+ }
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StubParameterAccessor.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StubParameterAccessor.java
index 72986a0f3..a5909af80 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StubParameterAccessor.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StubParameterAccessor.java
@@ -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()
diff --git a/src/main/asciidoc/reference/mongo-repositories.adoc b/src/main/asciidoc/reference/mongo-repositories.adoc
index 45c5508d2..6f334624b 100644
--- a/src/main/asciidoc/reference/mongo-repositories.adoc
+++ b/src/main/asciidoc/reference/mongo-repositories.adoc
@@ -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
// Metric: {'geoNear' : 'person', 'near' : [x, y], 'maxDistance' : distance,
// 'distanceMultiplier' : metric.multiplier, 'spherical' : true }
GeoResults findByLocationNear(Point location, Distance distance);
+
+ // Metric: {'geoNear' : 'person', 'near' : [x, y], 'minDistance' : min,
+ // 'maxDistance' : max, 'distanceMultiplier' : metric.multiplier,
+ // 'spherical' : true }
+ GeoResults findByLocationNear(Point location, Distance min, Distance max);
// {'geoNear' : 'location', 'near' : [x, y] }
GeoResults findByLocationNear(Point location);
diff --git a/src/main/asciidoc/reference/mongodb.adoc b/src/main/asciidoc/reference/mongodb.adoc
index 4376fd0d8..aee74a780 100644
--- a/src/main/asciidoc/reference/mongodb.adoc
+++ b/src/main/asciidoc/reference/mongodb.adoc
@@ -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 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 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 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]