entityClass, String co
}
String collection = StringUtils.hasText(collectionName) ? collectionName : determineCollectionName(entityClass);
- DBObject nearDbObject = near.toDBObject();
+ DBObject dbo = near.toDBObject();
+ DBObject query = dbo.containsField("query") ? (DBObject) dbo.removeField("query") : new BasicDBObject();
BasicDBObject command = new BasicDBObject("geoNear", collection);
- command.putAll(nearDbObject);
+ command.putAll(queryMapper.getMappedObject(dbo, null));
- if (nearDbObject.containsField("query")) {
- DBObject query = (DBObject) nearDbObject.get("query");
+ if(!query.keySet().isEmpty()) {
command.put("query", queryMapper.getMappedObject(query, getPersistentEntity(entityClass)));
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/MetricConversion.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/MetricConversion.java
new file mode 100644
index 0000000000..1c5bc86b3b
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/MetricConversion.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.data.mongodb.core.query;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+
+import org.springframework.data.geo.Distance;
+import org.springframework.data.geo.Metric;
+import org.springframework.data.geo.Metrics;
+
+/**
+ * {@link Metric} and {@link Distance} conversions using the metric system.
+ *
+ * @author Mark Paluch
+ * @since 1.10
+ */
+class MetricConversion {
+
+ private static final BigDecimal METERS_MULTIPLIER = new BigDecimal(Metrics.KILOMETERS.getMultiplier())
+ .multiply(new BigDecimal(1000));
+
+ // to achieve a calculation that is accurate to 0.3 meters
+ private static final int PRECISION = 8;
+
+ /**
+ * Return meters to {@code metric} multiplier.
+ *
+ * @param metric
+ * @return
+ */
+ protected static double getMetersToMetricMultiplier(Metric metric) {
+
+ ConversionMultiplier conversionMultiplier = ConversionMultiplier.builder().from(METERS_MULTIPLIER).to(metric)
+ .build();
+ return conversionMultiplier.multiplier().doubleValue();
+ }
+
+ /**
+ * Return {@code distance} in meters.
+ *
+ * @param distance
+ * @return
+ */
+ protected static double getDistanceInMeters(Distance distance) {
+ return new BigDecimal(distance.getValue()).multiply(getMetricToMetersMultiplier(distance.getMetric()))
+ .doubleValue();
+ }
+
+ /**
+ * Return {@code metric} to meters multiplier.
+ *
+ * @param metric
+ * @return
+ */
+ private static BigDecimal getMetricToMetersMultiplier(Metric metric) {
+
+ ConversionMultiplier conversionMultiplier = ConversionMultiplier.builder().from(metric).to(METERS_MULTIPLIER)
+ .build();
+ return conversionMultiplier.multiplier();
+ }
+
+ /**
+ * Provides a multiplier to convert between various metrics. Metrics must share the same base scale and provide a
+ * multiplier to convert between the base scale and its own metric.
+ *
+ * @author Mark Paluch
+ */
+ private static class ConversionMultiplier {
+
+ private final BigDecimal source;
+ private final BigDecimal target;
+
+ ConversionMultiplier(Number source, Number target) {
+
+ if (source instanceof BigDecimal) {
+ this.source = (BigDecimal) source;
+ } else {
+ this.source = new BigDecimal(source.doubleValue());
+ }
+
+ if (target instanceof BigDecimal) {
+ this.target = (BigDecimal) target;
+ } else {
+ this.target = new BigDecimal(target.doubleValue());
+ }
+ }
+
+ /**
+ * Returns the multiplier to convert a number from the {@code source} metric to the {@code target} metric.
+ *
+ * @return
+ */
+ BigDecimal multiplier() {
+ return target.divide(source, PRECISION, RoundingMode.HALF_UP);
+ }
+
+ /**
+ * Creates a new {@link ConversionMultiplierBuilder}.
+ *
+ * @return
+ */
+ static ConversionMultiplierBuilder builder() {
+ return new ConversionMultiplierBuilder();
+ }
+
+ }
+
+ /**
+ * Builder for {@link ConversionMultiplier}.
+ *
+ * @author Mark Paluch
+ */
+ private static class ConversionMultiplierBuilder {
+
+ private Number from;
+ private Number to;
+
+ ConversionMultiplierBuilder() {}
+
+ ConversionMultiplierBuilder from(Number from) {
+ this.from = from;
+ return this;
+ }
+
+ ConversionMultiplierBuilder from(Metric from) {
+ this.from = from.getMultiplier();
+ return this;
+ }
+
+ ConversionMultiplierBuilder to(Number to) {
+ this.to = to;
+ return this;
+ }
+
+ ConversionMultiplierBuilder to(Metric to) {
+ this.to = to.getMultiplier();
+ return this;
+ }
+
+ ConversionMultiplier build() {
+ return new ConversionMultiplier(this.from, this.to);
+ }
+ }
+}
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 b847d192e6..5bb77d3989 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-2015 the original author or authors.
+ * Copyright 2011-2016 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.
@@ -23,6 +23,8 @@
import org.springframework.data.geo.Metric;
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
+import org.springframework.data.mongodb.core.geo.GeoJson;
+import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.util.Assert;
import com.mongodb.BasicDBObject;
@@ -30,10 +32,11 @@
/**
* Builder class to build near-queries.
- *
+ *
* @author Oliver Gierke
* @author Thomas Darimont
* @author Christoph Strobl
+ * @author Mark Paluch
*/
public final class NearQuery {
@@ -48,7 +51,7 @@ public final class NearQuery {
/**
* Creates a new {@link NearQuery}.
- *
+ *
* @param point
*/
private NearQuery(Point point, Metric metric) {
@@ -65,9 +68,9 @@ private NearQuery(Point point, Metric metric) {
/**
* Creates a new {@link NearQuery} starting near the given coordinates.
- *
- * @param i
- * @param j
+ *
+ * @param x
+ * @param y
* @return
*/
public static NearQuery near(double x, double y) {
@@ -78,7 +81,7 @@ public static NearQuery near(double x, double y) {
* Creates a new {@link NearQuery} starting at the given coordinates using the given {@link Metric} to adapt given
* values to further configuration. E.g. setting a {@link #maxDistance(double)} will be interpreted as a value of the
* initially set {@link Metric}.
- *
+ *
* @param x
* @param y
* @param metric
@@ -90,7 +93,7 @@ public static NearQuery near(double x, double y, Metric metric) {
/**
* Creates a new {@link NearQuery} starting at the given {@link Point}.
- *
+ *
* @param point must not be {@literal null}.
* @return
*/
@@ -102,7 +105,7 @@ public static NearQuery near(Point point) {
* Creates a {@link NearQuery} starting near the given {@link Point} using the given {@link Metric} to adapt given
* values to further configuration. E.g. setting a {@link #maxDistance(double)} will be interpreted as a value of the
* initially set {@link Metric}.
- *
+ *
* @param point must not be {@literal null}.
* @param metric
* @return
@@ -115,7 +118,7 @@ public static NearQuery near(Point point, Metric metric) {
/**
* Returns the {@link Metric} underlying the actual query. If no metric was set explicitly {@link Metrics#NEUTRAL}
* will be returned.
- *
+ *
* @return will never be {@literal null}.
*/
public Metric getMetric() {
@@ -124,7 +127,7 @@ public Metric getMetric() {
/**
* Configures the maximum number of results to return.
- *
+ *
* @param num
* @return
*/
@@ -135,7 +138,7 @@ public NearQuery num(int num) {
/**
* Configures the number of results to skip.
- *
+ *
* @param skip
* @return
*/
@@ -146,7 +149,7 @@ public NearQuery skip(int skip) {
/**
* Configures the {@link Pageable} to use.
- *
+ *
* @param pageable must not be {@literal null}
* @return
*/
@@ -161,13 +164,13 @@ public NearQuery with(Pageable pageable) {
/**
* Sets the max 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).maxDistance(150);
*
- *
+ *
* Will set the maximum distance to 150 kilometers.
- *
+ *
* @param maxDistance
* @return
*/
@@ -178,7 +181,7 @@ public NearQuery maxDistance(double maxDistance) {
/**
* Sets the maximum 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 maxDistance
* @param metric must not be {@literal null}.
* @return
@@ -192,7 +195,7 @@ public NearQuery maxDistance(double maxDistance, Metric metric) {
/**
* Sets the maximum 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
*/
@@ -215,13 +218,13 @@ public NearQuery maxDistance(Distance distance) {
/**
* 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
@@ -233,7 +236,7 @@ public NearQuery minDistance(double minDistance) {
/**
* 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
@@ -248,7 +251,7 @@ public NearQuery minDistance(double minDistance, Metric 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
@@ -271,7 +274,7 @@ public NearQuery minDistance(Distance distance) {
/**
* Returns the maximum {@link Distance}.
- *
+ *
* @return
*/
public Distance getMaxDistance() {
@@ -280,7 +283,7 @@ public Distance getMaxDistance() {
/**
* Returns the maximum {@link Distance}.
- *
+ *
* @return
* @since 1.7
*/
@@ -290,7 +293,7 @@ public Distance getMinDistance() {
/**
* Configures a {@link CustomMetric} with the given multiplier.
- *
+ *
* @param distanceMultiplier
* @return
*/
@@ -302,7 +305,7 @@ public NearQuery distanceMultiplier(double distanceMultiplier) {
/**
* Configures the distance multiplier to the multiplier of the given {@link Metric}.
- *
+ *
* @deprecated use {@link #in(Metric)} instead.
* @param metric must not be {@literal null}.
* @return
@@ -315,7 +318,7 @@ public NearQuery distanceMultiplier(Metric metric) {
/**
* Configures whether to return spherical values for the actual distance.
- *
+ *
* @param spherical
* @return
*/
@@ -326,7 +329,7 @@ public NearQuery spherical(boolean spherical) {
/**
* Returns whether spharical values will be returned.
- *
+ *
* @return
*/
public boolean isSpherical() {
@@ -336,7 +339,7 @@ public boolean isSpherical() {
/**
* Will cause the results' distances being returned in kilometers. Sets {@link #distanceMultiplier(double)} and
* {@link #spherical(boolean)} accordingly.
- *
+ *
* @return
*/
public NearQuery inKilometers() {
@@ -346,7 +349,7 @@ public NearQuery inKilometers() {
/**
* Will cause the results' distances being returned in miles. Sets {@link #distanceMultiplier(double)} and
* {@link #spherical(boolean)} accordingly.
- *
+ *
* @return
*/
public NearQuery inMiles() {
@@ -356,7 +359,7 @@ public NearQuery inMiles() {
/**
* Will cause the results' distances being returned in the given metric. Sets {@link #distanceMultiplier(double)}
* accordingly as well as {@link #spherical(boolean)} if the given {@link Metric} is not {@link Metrics#NEUTRAL}.
- *
+ *
* @param metric the metric the results shall be returned in. Uses {@link Metrics#NEUTRAL} if {@literal null} is
* passed.
* @return
@@ -368,7 +371,7 @@ public NearQuery in(Metric metric) {
/**
* Configures the given {@link Metric} to be used as base on for this query and recalculate the maximum distance if no
* metric was set before.
- *
+ *
* @param metric
*/
private NearQuery adaptMetric(Metric metric) {
@@ -383,7 +386,7 @@ private NearQuery adaptMetric(Metric metric) {
/**
* Adds an actual query to the {@link NearQuery} to restrict the objects considered for the actual near operation.
- *
+ *
* @param query must not be {@literal null}.
* @return
*/
@@ -408,7 +411,7 @@ public Integer getSkip() {
/**
* Returns the {@link DBObject} built by the {@link NearQuery}.
- *
+ *
* @return
*/
public DBObject toDBObject() {
@@ -420,25 +423,46 @@ public DBObject toDBObject() {
}
if (maxDistance != null) {
- dbObject.put("maxDistance", maxDistance.getNormalizedValue());
+ dbObject.put("maxDistance", getDistanceValueInRadiantsOrMeters(maxDistance));
}
if (minDistance != null) {
- dbObject.put("minDistance", minDistance.getNormalizedValue());
+ dbObject.put("minDistance", getDistanceValueInRadiantsOrMeters(minDistance));
}
if (metric != null) {
- dbObject.put("distanceMultiplier", metric.getMultiplier());
+ dbObject.put("distanceMultiplier", getDistanceMultiplier());
}
if (num != null) {
dbObject.put("num", num);
}
- dbObject.put("near", Arrays.asList(point.getX(), point.getY()));
+ if (usesGeoJson()) {
+ dbObject.put("near", point);
+ } else {
+ dbObject.put("near", Arrays.asList(point.getX(), point.getY()));
+ }
- dbObject.put("spherical", spherical);
+ dbObject.put("spherical", spherical ? spherical : usesGeoJson());
return dbObject;
}
+
+ private double getDistanceMultiplier() {
+ return usesMetricSystem() ? MetricConversion.getMetersToMetricMultiplier(metric) : metric.getMultiplier();
+ }
+
+ private double getDistanceValueInRadiantsOrMeters(Distance distance) {
+ return usesMetricSystem() ? MetricConversion.getDistanceInMeters(distance) : distance.getNormalizedValue();
+ }
+
+ private boolean usesMetricSystem() {
+ return usesGeoJson();
+ }
+
+ private boolean usesGeoJson() {
+ return point instanceof GeoJsonPoint;
+ }
+
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java
index 40af85d3e1..9a54895ece 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2010-2015 the original author or authors.
+ * Copyright 2010-2016 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,9 @@
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.QueryMapper;
+import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator;
+import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;
import org.springframework.data.mongodb.core.query.BasicQuery;
@@ -58,6 +60,7 @@
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
+import org.springframework.data.mongodb.test.util.IsBsonObject;
import org.springframework.test.util.ReflectionTestUtils;
import com.mongodb.BasicDBObject;
@@ -78,6 +81,7 @@
*
* @author Oliver Gierke
* @author Christoph Strobl
+ * @author Mark Paluch
*/
@RunWith(MockitoJUnitRunner.class)
public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
@@ -107,7 +111,11 @@ public void setUp() {
when(cursor.hint(anyString())).thenReturn(cursor);
this.mappingContext = new MongoMappingContext();
+ mappingContext.afterPropertiesSet();
+
this.converter = new MappingMongoConverter(new DefaultDbRefResolver(factory), mappingContext);
+ converter.afterPropertiesSet();
+
this.template = new MongoTemplate(factory, converter);
}
@@ -531,6 +539,54 @@ public void mapReduceShouldPickUpLimitFromOptionsEvenWhenQueryDefinesItDifferent
assertThat(captor.getValue().getLimit(), is(1000));
}
+ /**
+ * @see DATAMONGO-1348
+ */
+ @Test
+ public void geoNearShouldMapQueryCorrectly() {
+
+ when(db.command(Mockito.any(DBObject.class))).thenReturn(mock(CommandResult.class));
+
+ NearQuery query = NearQuery.near(new Point(1, 1));
+ query.query(Query.query(Criteria.where("customName").is("rand al'thor")));
+
+ template.geoNear(query, WithNamedFields.class);
+
+ ArgumentCaptor capture = ArgumentCaptor.forClass(DBObject.class);
+ verify(this.db, times(1)).command(capture.capture());
+
+ assertThat(capture.getValue(), IsBsonObject.isBsonObject().containing("query.custom-named-field", "rand al'thor")
+ .notContaining("query.customName"));
+ }
+
+ /**
+ * @see DATAMONGO-1348
+ */
+ @Test
+ public void geoNearShouldMapGeoJsonPointCorrectly() {
+
+ when(db.command(Mockito.any(DBObject.class))).thenReturn(mock(CommandResult.class));
+
+ NearQuery query = NearQuery.near(new GeoJsonPoint(1, 2));
+ query.query(Query.query(Criteria.where("customName").is("rand al'thor")));
+
+ template.geoNear(query, WithNamedFields.class);
+
+ ArgumentCaptor capture = ArgumentCaptor.forClass(DBObject.class);
+ verify(this.db, times(1)).command(capture.capture());
+
+ assertThat(capture.getValue(), IsBsonObject.isBsonObject().containing("near.type", "Point")
+ .containing("near.coordinates.[0]", 1D).containing("near.coordinates.[1]", 2D));
+ }
+
+ static class WithNamedFields {
+
+ @Id String id;
+
+ String name;
+ @Field("custom-named-field") String customName;
+ }
+
class AutogenerateableId {
@Id BigInteger id;
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationTests.java
index 7a23b03782..8f3dff7ce8 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationTests.java
@@ -54,6 +54,8 @@
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.Venue;
import org.springframework.data.mongodb.core.aggregation.AggregationTests.CarDescriptor.Entry;
+import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
+import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
import org.springframework.data.mongodb.core.index.GeospatialIndex;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
@@ -71,7 +73,7 @@
/**
* Tests for {@link MongoTemplate#aggregate(String, AggregationPipeline, Class)}.
- *
+ *
* @see DATAMONGO-586
* @author Tobias Trelle
* @author Thomas Darimont
@@ -134,7 +136,7 @@ private void cleanDb() {
/**
* Imports the sample dataset (zips.json) if necessary (e.g. if it doen't exist yet). The dataset can originally be
* found on the mongodb aggregation framework example website:
- *
+ *
* @see http://docs.mongodb.org/manual/tutorial/aggregation-examples/.
*/
private void initSampleDataIfNecessary() {
@@ -270,7 +272,7 @@ public void shouldDetectResultMismatch() {
public void complexAggregationFrameworkUsageLargestAndSmallestCitiesByState() {
/*
//complex mongodb aggregation framework example from http://docs.mongodb.org/manual/tutorial/aggregation-examples/#largest-and-smallest-cities-by-state
- db.zipInfo.aggregate(
+ db.zipInfo.aggregate(
{
$group: {
_id: {
@@ -377,18 +379,18 @@ public void complexAggregationFrameworkUsageLargestAndSmallestCitiesByState() {
@Test
public void findStatesWithPopulationOver10MillionAggregationExample() {
/*
- //complex mongodb aggregation framework example from
+ //complex mongodb aggregation framework example from
http://docs.mongodb.org/manual/tutorial/aggregation-examples/#largest-and-smallest-cities-by-state
-
- db.zipcodes.aggregate(
+
+ db.zipcodes.aggregate(
{
$group: {
_id:"$state",
totalPop:{ $sum:"$pop"}
}
},
- {
- $sort: { _id: 1, "totalPop": 1 }
+ {
+ $sort: { _id: 1, "totalPop": 1 }
},
{
$match: {
@@ -602,7 +604,7 @@ public void shouldThrowExceptionIfUnknownFieldIsReferencedInArithmenticExpressio
/**
* @see DATAMONGO-753
- * @see http
+ * @see http
* ://stackoverflow.com/questions/18653574/spring-data-mongodb-aggregation-framework-invalid-reference-in-group
* -operati
*/
@@ -632,7 +634,7 @@ public void allowsNestedFieldReferencesAsGroupIdsInGroupExpressions() {
/**
* @see DATAMONGO-753
- * @see http
+ * @see http
* ://stackoverflow.com/questions/18653574/spring-data-mongodb-aggregation-framework-invalid-reference-in-group
* -operati
*/
@@ -1048,6 +1050,57 @@ public void shouldSupportGeoNearQueriesForAggregationWithDistanceField() {
assertThat((Double) firstResult.get("distance"), closeTo(117.620092203928, 0.00001));
}
+ /**
+ * @see DATAMONGO-1348
+ */
+ @Test
+ public void shouldSupportGeoJsonInGeoNearQueriesForAggregationWithDistanceField() {
+
+ mongoTemplate.insert(new Venue("Penn Station", -73.99408, 40.75057));
+ mongoTemplate.insert(new Venue("10gen Office", -73.99171, 40.738868));
+ mongoTemplate.insert(new Venue("Flatiron Building", -73.988135, 40.741404));
+
+ mongoTemplate.indexOps(Venue.class)
+ .ensureIndex(new GeospatialIndex("location").typed(GeoSpatialIndexType.GEO_2DSPHERE));
+
+ NearQuery geoNear = NearQuery.near(new GeoJsonPoint(-73, 40), Metrics.KILOMETERS).num(10).maxDistance(150);
+
+ Aggregation agg = newAggregation(Aggregation.geoNear(geoNear, "distance"));
+ AggregationResults result = mongoTemplate.aggregate(agg, Venue.class, DBObject.class);
+
+ assertThat(result.getMappedResults(), hasSize(3));
+
+ DBObject firstResult = result.getMappedResults().get(0);
+ assertThat(firstResult.containsField("distance"), is(true));
+ assertThat((Double) firstResult.get("distance"), closeTo(117.61940988193759, 0.00001));
+ }
+
+ /**
+ * @see DATAMONGO-1348
+ */
+ @Test
+ public void shouldSupportGeoJsonInGeoNearQueriesForAggregationWithDistanceFieldInMiles() {
+
+ mongoTemplate.insert(new Venue("Penn Station", -73.99408, 40.75057));
+ mongoTemplate.insert(new Venue("10gen Office", -73.99171, 40.738868));
+ mongoTemplate.insert(new Venue("Flatiron Building", -73.988135, 40.741404));
+
+ mongoTemplate.indexOps(Venue.class)
+ .ensureIndex(new GeospatialIndex("location").typed(GeoSpatialIndexType.GEO_2DSPHERE));
+
+ NearQuery geoNear = NearQuery.near(new GeoJsonPoint(-73, 40), Metrics.KILOMETERS).num(10).maxDistance(150)
+ .inMiles();
+
+ Aggregation agg = newAggregation(Aggregation.geoNear(geoNear, "distance"));
+ AggregationResults result = mongoTemplate.aggregate(agg, Venue.class, DBObject.class);
+
+ assertThat(result.getMappedResults(), hasSize(3));
+
+ DBObject firstResult = result.getMappedResults().get(0);
+ assertThat(firstResult.containsField("distance"), is(true));
+ assertThat((Double) firstResult.get("distance"), closeTo(73.08517, 0.00001));
+ }
+
/**
* @see DATAMONGO-1133
*/
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 01d9611675..e8a2400cc6 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
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 the original author or authors.
+ * Copyright 2015-2016 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.
@@ -90,7 +90,7 @@ public void tearDown() {
}
/**
- * @see DATAMONGO-1135
+ * @see DATAMONGO-1348
*/
@Test
public void geoNear() {
@@ -103,6 +103,20 @@ public void geoNear() {
assertThat(result.getAverageDistance().getMetric(), is((Metric) Metrics.KILOMETERS));
}
+ /**
+ * @see DATAMONGO-1348
+ */
+ @Test
+ public void geoNearWithMiles() {
+
+ NearQuery geoNear = NearQuery.near(new GeoJsonPoint(-73, 40), Metrics.MILES).num(10).maxDistance(93.2057);
+
+ GeoResults result = template.geoNear(geoNear, Venue2DSphere.class);
+
+ assertThat(result.getContent().size(), is(not(0)));
+ assertThat(result.getAverageDistance().getMetric(), is((Metric) Metrics.MILES));
+ }
+
/**
* @see DATAMONGO-1135
*/
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/MetricConversionUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/MetricConversionUnitTests.java
new file mode 100644
index 0000000000..303d15451c
--- /dev/null
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/MetricConversionUnitTests.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.data.mongodb.core.query;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+import org.springframework.data.geo.Distance;
+import org.springframework.data.geo.Metrics;
+
+/**
+ * Unit tests for {@link MetricConversion}.
+ *
+ * @author Mark Paluch
+ */
+public class MetricConversionUnitTests {
+
+ /**
+ * @see DATAMONGO-1348
+ */
+ @Test
+ public void shouldConvertMilesToMeters() {
+
+ Distance distance = new Distance(1, Metrics.MILES);
+ double distanceInMeters = MetricConversion.getDistanceInMeters(distance);
+
+ assertThat(distanceInMeters, is(closeTo(1609.3438343d, 0.000000001)));
+ }
+
+ /**
+ * @see DATAMONGO-1348
+ */
+ @Test
+ public void shouldConvertKilometersToMeters() {
+
+ Distance distance = new Distance(1, Metrics.KILOMETERS);
+ double distanceInMeters = MetricConversion.getDistanceInMeters(distance);
+
+ assertThat(distanceInMeters, is(closeTo(1000, 0.000000001)));
+ }
+
+ /**
+ * @see DATAMONGO-1348
+ */
+ @Test
+ public void shouldCalculateMetersToKilometersMultiplier() {
+
+ double multiplier = MetricConversion.getMetersToMetricMultiplier(Metrics.KILOMETERS);
+
+ assertThat(multiplier, is(closeTo(0.001, 0.000000001)));
+ }
+
+ /**
+ * @see DATAMONGO-1348
+ */
+ @Test
+ public void shouldCalculateMetersToMilesMultiplier() {
+
+ double multiplier = MetricConversion.getMetersToMetricMultiplier(Metrics.MILES);
+
+ assertThat(multiplier, is(closeTo(0.00062137, 0.000000001)));
+ }
+
+}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/NearQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/NearQueryUnitTests.java
index 10232f56f7..bc678cf69c 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/NearQueryUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/NearQueryUnitTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2011-2014 the original author or authors.
+ * Copyright 2011-2016 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.
@@ -17,6 +17,10 @@
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
+import static org.springframework.data.mongodb.test.util.IsBsonObject.*;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
import org.junit.Test;
import org.springframework.data.domain.PageRequest;
@@ -26,10 +30,11 @@
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.DBObjectTestUtils;
+import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
/**
* Unit tests for {@link NearQuery}.
- *
+ *
* @author Oliver Gierke
* @author Thomas Darimont
* @author Christoph Strobl
@@ -158,4 +163,119 @@ public void numShouldNotBeAlteredByQueryWithoutPageable() {
assertThat(DBObjectTestUtils.getTypedValue(query.toDBObject(), "num", Integer.class), is(num));
}
+
+ /**
+ * @see DATAMONGO-1348
+ */
+ @Test
+ public void shouldNotUseSphericalForLegacyPoint() {
+
+ NearQuery query = NearQuery.near(new Point(27.987901, 86.9165379));
+
+ assertThat(query.toDBObject(), isBsonObject().containing("spherical", false));
+ }
+
+ /**
+ * @see DATAMONGO-1348
+ */
+ @Test
+ public void shouldUseSphericalForLegacyPointIfSet() {
+
+ NearQuery query = NearQuery.near(new Point(27.987901, 86.9165379));
+ query.spherical(true);
+
+ assertThat(query.toDBObject(), isBsonObject().containing("spherical", true));
+ }
+
+ /**
+ * @see DATAMONGO-1348
+ */
+ @Test
+ public void shouldUseSphericalForGeoJsonData() {
+
+ NearQuery query = NearQuery.near(new GeoJsonPoint(27.987901, 86.9165379));
+
+ assertThat(query.toDBObject(), isBsonObject().containing("spherical", true));
+ }
+
+ /**
+ * @see DATAMONGO-1348
+ */
+ @Test
+ public void shouldUseSphericalForGeoJsonDataIfSphericalIsFalse() {
+
+ NearQuery query = NearQuery.near(new GeoJsonPoint(27.987901, 86.9165379));
+ query.spherical(false);
+
+ assertThat(query.toDBObject(), isBsonObject().containing("spherical", true));
+ }
+
+ /**
+ * @see DATAMONGO-1348
+ */
+ @Test
+ public void shouldUseMetersForGeoJsonData() {
+
+ NearQuery query = NearQuery.near(new GeoJsonPoint(27.987901, 86.9165379));
+ query.maxDistance(1);
+
+ double meterToRadianMultiplier = BigDecimal.valueOf(1 / Metrics.KILOMETERS.getMultiplier() / 1000).//
+ setScale(8, RoundingMode.HALF_UP).//
+ doubleValue();
+ assertThat(query.toDBObject(), isBsonObject().containing("maxDistance", Metrics.KILOMETERS.getMultiplier() * 1000)
+ .containing("distanceMultiplier", meterToRadianMultiplier));
+ }
+
+ /**
+ * @see DATAMONGO-1348
+ */
+ @Test
+ public void shouldUseMetersForGeoJsonDataWhenDistanceInKilometers() {
+
+ NearQuery query = NearQuery.near(new GeoJsonPoint(27.987901, 86.9165379));
+ query.maxDistance(new Distance(1, Metrics.KILOMETERS));
+
+ assertThat(query.toDBObject(),
+ isBsonObject().containing("maxDistance", 1000D).containing("distanceMultiplier", 0.001D));
+ }
+
+ /**
+ * @see DATAMONGO-1348
+ */
+ @Test
+ public void shouldUseMetersForGeoJsonDataWhenDistanceInMiles() {
+
+ NearQuery query = NearQuery.near(new GeoJsonPoint(27.987901, 86.9165379));
+ query.maxDistance(new Distance(1, Metrics.MILES));
+
+ assertThat(query.toDBObject(),
+ isBsonObject().containing("maxDistance", 1609.3438343D).containing("distanceMultiplier", 0.00062137D));
+ }
+
+ /**
+ * @see DATAMONGO-1348
+ */
+ @Test
+ public void shouldUseKilometersForDistanceWhenMaxDistanceInMiles() {
+
+ NearQuery query = NearQuery.near(new GeoJsonPoint(27.987901, 86.9165379));
+ query.maxDistance(new Distance(1, Metrics.MILES)).in(Metrics.KILOMETERS);
+
+ assertThat(query.toDBObject(),
+ isBsonObject().containing("maxDistance", 1609.3438343D).containing("distanceMultiplier", 0.001D));
+ }
+
+ /**
+ * @see DATAMONGO-1348
+ */
+ @Test
+ public void shouldUseMilesForDistanceWhenMaxDistanceInKilometers() {
+
+ NearQuery query = NearQuery.near(new GeoJsonPoint(27.987901, 86.9165379));
+ query.maxDistance(new Distance(1, Metrics.KILOMETERS)).in(Metrics.MILES);
+
+ assertThat(query.toDBObject(),
+ isBsonObject().containing("maxDistance", 1000D).containing("distanceMultiplier", 0.00062137D));
+ }
+
}