From c28ace6d405dd37e8bc042b3baac063573b8e445 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 18 Oct 2019 10:53:34 +0200 Subject: [PATCH] DATAMONGO-2394 - Fix distance conversion for derived finder using near along with GeoJSON. GeoJson requries the distance to be in meters instead of radians, so we now make sure to convert it correctly Original pull request: #798. --- .../data/mongodb/core/query/MetricConversion.java | 6 +++--- .../mongodb/repository/query/MongoQueryCreator.java | 12 ++++++++++-- .../mongodb/core/convert/QueryMapperUnitTests.java | 11 +++++++++++ .../repository/query/MongoQueryCreatorUnitTests.java | 11 +++++++++++ 4 files changed, 35 insertions(+), 5 deletions(-) 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 index 72e76b997..f78bb850d 100644 --- 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 @@ -29,7 +29,7 @@ import org.springframework.data.geo.Metrics; * @author Mark Paluch * @since 2.2 */ -class MetricConversion { +public class MetricConversion { private static final BigDecimal METERS_MULTIPLIER = new BigDecimal(Metrics.KILOMETERS.getMultiplier()) .multiply(new BigDecimal(1000)); @@ -43,7 +43,7 @@ class MetricConversion { * @param metric * @return */ - protected static double getMetersToMetricMultiplier(Metric metric) { + public static double getMetersToMetricMultiplier(Metric metric) { ConversionMultiplier conversionMultiplier = ConversionMultiplier.builder().from(METERS_MULTIPLIER).to(metric) .build(); @@ -56,7 +56,7 @@ class MetricConversion { * @param distance * @return */ - protected static double getDistanceInMeters(Distance distance) { + public static double getDistanceInMeters(Distance distance) { return new BigDecimal(distance.getValue()).multiply(getMetricToMetersMultiplier(distance.getMetric())) .doubleValue(); } 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 ae4499919..d9595ffd3 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 @@ -36,11 +36,13 @@ import org.springframework.data.geo.Shape; import org.springframework.data.mapping.PersistentPropertyPath; import org.springframework.data.mapping.PropertyPath; import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.mongodb.core.geo.GeoJson; 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; +import org.springframework.data.mongodb.core.query.MetricConversion; import org.springframework.data.mongodb.core.query.MongoRegexCreator; import org.springframework.data.mongodb.core.query.MongoRegexCreator.MatchMode; import org.springframework.data.mongodb.core.query.Query; @@ -235,8 +237,14 @@ class MongoQueryCreator extends AbstractQueryCreator { criteria.near(pointToUse); } - criteria.maxDistance(it.getNormalizedValue()); - minDistance.ifPresent(min -> criteria.minDistance(min.getNormalizedValue())); + if(pointToUse instanceof GeoJson) { // using GeoJson distance is in meters. + + criteria.maxDistance(MetricConversion.getDistanceInMeters(it)); + minDistance.map(MetricConversion::getDistanceInMeters).ifPresent(min -> criteria.minDistance(min)); + } else { + criteria.maxDistance(it.getNormalizedValue()); + minDistance.ifPresent(min -> criteria.minDistance(min.getNormalizedValue())); + } return criteria; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java index d1fd28622..db14d6bc1 100755 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java @@ -890,6 +890,17 @@ public class QueryMapperUnitTests { assertThat(target).isEqualTo(new org.bson.Document("_id", "id-1")); } + @Test // DATAMONGO-2394 + public void leavesDistanceUntouchedWhenUsingGeoJson() { + + Query query = query(where("geoJsonPoint").near(new GeoJsonPoint(27.987901, 86.9165379)).maxDistance(1000)); + + org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), + context.getPersistentEntity(ClassWithGeoTypes.class)); + assertThat(document).containsEntry("geoJsonPoint.$near.$geometry.type", "Point"); + assertThat(document).containsEntry("geoJsonPoint.$near.$maxDistance", 1000.0D); + } + @Document public class Foo { @Id private ObjectId id; 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 327da33a1..3b83348b2 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 @@ -652,6 +652,17 @@ public class MongoQueryCreatorUnitTests { assertThat(creator.createQuery()).isEqualTo(query(where("age").gt(10).lt(11))); } + @Test // DATAMONGO-2394 + public void nearShouldUseMetricDistanceForGeoJsonTypes() { + + GeoJsonPoint point = new GeoJsonPoint(27.987901, 86.9165379); + PartTree tree = new PartTree("findByLocationNear", User.class); + MongoQueryCreator creator = new MongoQueryCreator(tree, + getAccessor(converter, point, new Distance(1, Metrics.KILOMETERS)), context); + + assertThat(creator.createQuery()).isEqualTo(query(where("location").nearSphere(point).maxDistance(1000.0D))); + } + interface PersonRepository extends Repository { List findByLocationNearAndFirstname(Point location, Distance maxDistance, String firstname);