DATAMONGO-1348 - Convert GeoJson used in NearQuery.
We now convert GeoJsonPoint used in NearQuery into its according format. This also requires to convert any given min/maxDistance as well as the distanceMultiplier into meters (metric system). Along the way we fixed an issue where the actual Query used along with NearQuery was not properly mapped to the domain types properties. Original pull request: #339.
This commit is contained in:
committed by
Mark Paluch
parent
39859b73a3
commit
f8560aac0b
@@ -972,7 +972,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
Document nearDocument = near.toDocument();
|
||||
|
||||
Document command = new Document("geoNear", collection);
|
||||
command.putAll(nearDocument);
|
||||
command.putAll(queryMapper.getMappedObject(nearDocument, Optional.empty()));
|
||||
|
||||
if (nearDocument.containsKey("query")) {
|
||||
Document query = (Document) nearDocument.get("query");
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.query;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.bson.Document;
|
||||
@@ -25,6 +27,8 @@ import org.springframework.data.geo.Metric;
|
||||
import org.springframework.data.geo.Metrics;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.data.mongodb.core.geo.GeoJson;
|
||||
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
@@ -47,6 +51,8 @@ public final class NearQuery {
|
||||
private @Nullable Long num;
|
||||
private @Nullable Long skip;
|
||||
|
||||
private static int PRECISION = 8;
|
||||
|
||||
/**
|
||||
* Creates a new {@link NearQuery}.
|
||||
*
|
||||
@@ -416,25 +422,53 @@ public final class NearQuery {
|
||||
}
|
||||
|
||||
if (maxDistance != null) {
|
||||
document.put("maxDistance", maxDistance.getNormalizedValue());
|
||||
document.put("maxDistance", getDistanceValueInRadiantsOrMeters(maxDistance));
|
||||
}
|
||||
|
||||
if (minDistance != null) {
|
||||
document.put("minDistance", minDistance.getNormalizedValue());
|
||||
document.put("minDistance", getDistanceValueInRadiantsOrMeters(minDistance));
|
||||
}
|
||||
|
||||
if (metric != null) {
|
||||
document.put("distanceMultiplier", metric.getMultiplier());
|
||||
document.put(
|
||||
"distanceMultiplier",
|
||||
usesMetricSystem() ? getMetricSystemNormalizer(metric).divide(new BigDecimal(1000), PRECISION + 3,
|
||||
RoundingMode.HALF_UP).doubleValue() : metric.getMultiplier());
|
||||
}
|
||||
|
||||
if (num != null) {
|
||||
document.put("num", num);
|
||||
}
|
||||
|
||||
document.put("near", Arrays.asList(point.getX(), point.getY()));
|
||||
if (point instanceof GeoJsonPoint) {
|
||||
document.put("near", point);
|
||||
} else {
|
||||
document.put("near", Arrays.asList(point.getX(), point.getY()));
|
||||
}
|
||||
|
||||
document.put("spherical", spherical);
|
||||
document.put("spherical", spherical ? spherical : point instanceof GeoJson);
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
private double getDistanceValueInRadiantsOrMeters(Distance distance) {
|
||||
return usesMetricSystem() ? getDistanceInMeters(distance) : distance.getNormalizedValue();
|
||||
}
|
||||
|
||||
private double getDistanceInMeters(Distance distance) {
|
||||
|
||||
return new BigDecimal(distance.getValue()).multiply(getMetricSystemNormalizer(distance.getMetric()))
|
||||
.multiply(new BigDecimal(1000)).doubleValue();
|
||||
}
|
||||
|
||||
private BigDecimal getMetricSystemNormalizer(Metric metric) {
|
||||
|
||||
return new BigDecimal(Metrics.KILOMETERS.getMultiplier()).divide(new BigDecimal(metric.getMultiplier()), PRECISION,
|
||||
RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
private boolean usesMetricSystem() {
|
||||
return point instanceof GeoJson;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
|
||||
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
|
||||
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;
|
||||
@@ -76,6 +77,7 @@ import org.springframework.data.mongodb.core.query.Criteria;
|
||||
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.DB;
|
||||
@@ -165,7 +167,10 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
when(aggregateIterable.into(any())).thenReturn(Collections.emptyList());
|
||||
|
||||
this.mappingContext = new MongoMappingContext();
|
||||
mappingContext.afterPropertiesSet();
|
||||
|
||||
this.converter = spy(new MappingMongoConverter(new DefaultDbRefResolver(factory), mappingContext));
|
||||
converter.afterPropertiesSet();
|
||||
this.template = new MongoTemplate(factory, converter);
|
||||
}
|
||||
|
||||
@@ -966,6 +971,50 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
verify(findIterable).projection(eq(new Document()));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1348
|
||||
public void geoNearShouldMapQueryCorrectly() {
|
||||
|
||||
when(db.runCommand(any(Document.class), eq(Document.class))).thenReturn(mock(Document.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<Document> capture = ArgumentCaptor.forClass(Document.class);
|
||||
verify(this.db, times(1)).runCommand(capture.capture(), any(Class.class));
|
||||
|
||||
assertThat(capture.getValue(), IsBsonObject.isBsonObject().containing("query.custom-named-field", "rand al'thor")
|
||||
.notContaining("query.customName"));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1348
|
||||
public void geoNearShouldMapGeoJsonPointCorrectly() {
|
||||
|
||||
when(db.runCommand(any(Document.class), eq(Document.class))).thenReturn(mock(Document.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<Document> capture = ArgumentCaptor.forClass(Document.class);
|
||||
verify(this.db, times(1)).runCommand(capture.capture(), any(Class.class));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2155
|
||||
public void saveVersionedEntityShouldCallUpdateCorrectly() {
|
||||
|
||||
|
||||
@@ -104,6 +104,17 @@ public class GeoJsonTests {
|
||||
assertThat(result.getAverageDistance().getMetric()).isEqualTo(Metrics.KILOMETERS);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1135
|
||||
public void geoNearWithMiles() {
|
||||
|
||||
NearQuery geoNear = NearQuery.near(new GeoJsonPoint(-73, 40), Metrics.MILES).num(10).maxDistance(93.2057);
|
||||
|
||||
GeoResults<Venue2DSphere> result = template.geoNear(geoNear, Venue2DSphere.class);
|
||||
|
||||
assertThat(result.getContent().size(), is(not(0)));
|
||||
assertThat(result.getAverageDistance().getMetric(), is((Metric) Metrics.MILES));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1135
|
||||
public void withinPolygon() {
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.springframework.data.mongodb.core.query;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.mongodb.test.util.IsBsonObject.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
@@ -26,6 +27,7 @@ import org.springframework.data.geo.Metric;
|
||||
import org.springframework.data.geo.Metrics;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.mongodb.core.DocumentTestUtils;
|
||||
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link NearQuery}.
|
||||
@@ -141,4 +143,34 @@ public class NearQueryUnitTests {
|
||||
|
||||
assertThat(DocumentTestUtils.getTypedValue(query.toDocument(), "num", Long.class), is(num));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1348
|
||||
public void shouldUseMetersForGeoJsonData() {
|
||||
|
||||
NearQuery query = NearQuery.near(new GeoJsonPoint(27.987901, 86.9165379));
|
||||
query.maxDistance(1);
|
||||
|
||||
assertThat(query.toDocument(), isBsonObject().containing("maxDistance", Metrics.KILOMETERS.getMultiplier() * 1000)
|
||||
.containing("distanceMultiplier", Metrics.KILOMETERS.getMultiplier() / 1000));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1348
|
||||
public void shouldUseMetersForGeoJsonDataWhenDistanceInKilometers() {
|
||||
|
||||
NearQuery query = NearQuery.near(new GeoJsonPoint(27.987901, 86.9165379));
|
||||
query.maxDistance(new Distance(1, Metrics.KILOMETERS));
|
||||
|
||||
assertThat(query.toDocument(),
|
||||
isBsonObject().containing("maxDistance", 1000D).containing("distanceMultiplier", 0.001D));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1348
|
||||
public void shouldUseMetersForGeoJsonDataWhenDistanceInMiles() {
|
||||
|
||||
NearQuery query = NearQuery.near(new GeoJsonPoint(27.987901, 86.9165379));
|
||||
query.maxDistance(new Distance(1, Metrics.MILES));
|
||||
|
||||
assertThat(query.toDocument(),
|
||||
isBsonObject().containing("maxDistance", 1609.34383D).containing("distanceMultiplier", 0.00160934383D));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user