Browse Source

DATADOC-68 - Added support for geoNear command.

Introduced GeoResult value object as well as NearQuery. NearQuery allows definition of an origin and distances. Introduced a Metric interface and Metrics enum to carry commonly used metrics like kilometers and miles to ease the handling in NearQueries. Introduced Distance value object to capture distances in Metrics.
pull/1/head
Oliver Gierke 15 years ago
parent
commit
fedcbdae4f
  1. 23
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java
  2. 63
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java
  3. 145
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/Distance.java
  4. 102
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoResult.java
  5. 143
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoResults.java
  6. 16
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/Metric.java
  7. 27
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/Metrics.java
  8. 266
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/NearQuery.java
  9. 50
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/DistanceUnitTests.java
  10. 55
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoResultUnitTests.java
  11. 24
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoResultsUnitTests.java
  12. 2
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatialAppConfig.java
  13. 16
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatialTests.java
  14. 2
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/GeoIndexedAppConfig.java
  15. 44
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/NearQueryUnitTests.java

23
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java

@ -25,7 +25,10 @@ import com.mongodb.DBObject; @@ -25,7 +25,10 @@ import com.mongodb.DBObject;
import com.mongodb.WriteResult;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.geo.GeoResult;
import org.springframework.data.mongodb.core.geo.GeoResults;
import org.springframework.data.mongodb.core.index.IndexDefinition;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
@ -255,6 +258,26 @@ public interface MongoOperations { @@ -255,6 +258,26 @@ public interface MongoOperations {
* @return the converted collection
*/
<T> List<T> findAll(Class<T> entityClass, String collectionName);
/**
* Returns {@link GeoResult} for all entities matching the given {@link NearQuery}. Will consider entity mapping
* information to determine the collection the query is ran against.
*
* @param near must not be {@literal null}.
* @param entityClass must not be {@literal null}.
* @return
*/
<T> GeoResults<T> geoNear(NearQuery near, Class<T> entityClass);
/**
* Returns {@link GeoResult} for all entities matching the given {@link NearQuery}.
*
* @param near must not be {@literal null}.
* @param entityClass must not be {@literal null}.
* @param collectionName the collection to trigger the query against. If no collection name is given the entity class will be inspected.
* @return
*/
<T> GeoResults<T> geoNear(NearQuery near, Class<T> entityClass, String collectionName);
/**
* Ensure that an index for the provided {@link IndexDefinition} exists for the collection indicated by the entity class.

63
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java

@ -27,6 +27,7 @@ import java.util.List; @@ -27,6 +27,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.CommandResult;
import com.mongodb.DB;
@ -60,6 +61,10 @@ import org.springframework.data.mongodb.core.convert.MappingMongoConverter; @@ -60,6 +61,10 @@ import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.convert.MongoReader;
import org.springframework.data.mongodb.core.convert.MongoWriter;
import org.springframework.data.mongodb.core.geo.Distance;
import org.springframework.data.mongodb.core.geo.GeoResult;
import org.springframework.data.mongodb.core.geo.GeoResults;
import org.springframework.data.mongodb.core.geo.Metric;
import org.springframework.data.mongodb.core.index.IndexDefinition;
import org.springframework.data.mongodb.core.index.MongoMappingEventPublisher;
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator;
@ -72,10 +77,12 @@ import org.springframework.data.mongodb.core.mapping.event.AfterSaveEvent; @@ -72,10 +77,12 @@ import org.springframework.data.mongodb.core.mapping.event.AfterSaveEvent;
import org.springframework.data.mongodb.core.mapping.event.BeforeConvertEvent;
import org.springframework.data.mongodb.core.mapping.event.BeforeSaveEvent;
import org.springframework.data.mongodb.core.mapping.event.MongoMappingEvent;
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.jca.cci.core.ConnectionCallback;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Primary implementation of {@link MongoOperations}.
@ -430,6 +437,30 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -430,6 +437,30 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
return doFindOne(collectionName, new BasicDBObject(idKey, id), null, entityClass);
}
public <T> GeoResults<T> geoNear(NearQuery near, Class<T> entityClass) {
return geoNear(near, entityClass, determineCollectionName(entityClass));
}
public <T> GeoResults<T> geoNear(NearQuery near, Class<T> entityClass, String collectionName) {
String collection = StringUtils.hasText(collectionName) ? collectionName : determineCollectionName(entityClass);
BasicDBObject command = new BasicDBObject("geoNear", collection);
command.putAll(near.toDBObject());
CommandResult commandResult = executeCommand(command);
BasicDBList results = (BasicDBList) commandResult.get("results");
DbObjectCallback<GeoResult<T>> callback = new GeoNearResultDbObjectCallback<T>(new ReadDbObjectCallback<T>(mongoConverter, entityClass), near.getMetric());
List<GeoResult<T>> result = new ArrayList<GeoResult<T>>(results.size());
for (Object element : results) {
result.add(callback.doWith((DBObject) element));
}
double averageDistance = (Double) ((DBObject) commandResult.get("stats")).get("avgDistance");
return new GeoResults<T>(result, new Distance(averageDistance, near.getMetric()));
}
// Find methods that take a Query to express the query and that return a single object that is also removed from the
// collection in the database.
@ -843,7 +874,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -843,7 +874,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
entityClass));
}
protected <T> List<T> doFind(String collectionName, DBObject query, DBObject fields, Class<T> entityClass,
protected <S, T> List<T> doFind(String collectionName, DBObject query, DBObject fields, Class<S> entityClass,
CursorPreparer preparer, DbObjectCallback<T> objectCallback) {
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
if (LOGGER.isDebugEnabled()) {
@ -1258,7 +1289,37 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -1258,7 +1289,37 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
}
/**
* {@link DbObjectCallback} that assumes a {@link GeoResult} to be created, delegates actual content unmarshalling to
* a delegate and creates a {@link GeoResult} from the result.
*
* @author Oliver Gierke
*/
static class GeoNearResultDbObjectCallback<T> implements DbObjectCallback<GeoResult<T>> {
private final DbObjectCallback<T> delegate;
private final Metric metric;
/**
* Creates a new {@link GeoNearResultDbObjectCallback} using the given {@link DbObjectCallback} delegate for
* {@link GeoResult} content unmarshalling.
*
* @param delegate
*/
public GeoNearResultDbObjectCallback(DbObjectCallback<T> delegate, Metric metric) {
Assert.notNull(delegate);
this.delegate = delegate;
this.metric = metric;
}
public GeoResult<T> doWith(DBObject object) {
double distance = ((Double) object.get("dis")).doubleValue();
DBObject content = (DBObject) object.get("obj");
T doWith = delegate.doWith(content);
return new GeoResult<T>(doWith, new Distance(distance, metric));
}
}
}

145
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/Distance.java

@ -0,0 +1,145 @@ @@ -0,0 +1,145 @@
/*
* Copyright 2010-2011 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.geo;
import org.springframework.util.ObjectUtils;
/**
* Value object to represent distances in a given metric.
*
* @author Oliver Gierke
*/
public class Distance {
private final double value;
private final Metric metric;
/**
* Creates a new {@link Distance}.
*
* @param value
*/
public Distance(double value) {
this(value, Metrics.NEUTRAL);
}
/**
* Creates a new {@link Distance} with the given {@link Metric}.
*
* @param value
* @param metric
*/
public Distance(double value, Metric metric) {
this.value = value;
this.metric = metric == null ? Metrics.NEUTRAL : metric;
}
/**
* @return the value
*/
public double getValue() {
return value;
}
/**
* Returns the normalized value regarding the underlying {@link Metric}.
*
* @return
*/
public double getNormalizedValue() {
return value / metric.getMultiplier();
}
/**
* @return the metric
*/
public Metric getMetric() {
return metric;
}
/**
* Adds the given distance to the current one. The resulting {@link Distance} will be in the same metric as the
* current one.
*
* @param other
* @return
*/
public Distance add(Distance other) {
double newNormalizedValue = getNormalizedValue() + other.getNormalizedValue();
return new Distance(newNormalizedValue * metric.getMultiplier(), metric);
}
/**
* Adds the given {@link Distance} to the current one and forces the result to be in a given {@link Metric}.
*
* @param other
* @param metric
* @return
*/
public Distance add(Distance other, Metric metric) {
double newLeft = getNormalizedValue() * metric.getMultiplier();
double newRight = other.getNormalizedValue() * metric.getMultiplier();
return new Distance(newLeft + newRight, metric);
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || !getClass().equals(obj.getClass())) {
return false;
}
Distance that = (Distance) obj;
return this.value == that.value && ObjectUtils.nullSafeEquals(this.metric, that.metric);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = 17;
result += 31 * Double.doubleToLongBits(value);
result += 31 * ObjectUtils.nullSafeHashCode(metric);
return result;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(value);
if (metric != null) {
builder.append(" ").append(metric.toString());
}
return builder.toString();
}
}

102
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoResult.java

@ -0,0 +1,102 @@ @@ -0,0 +1,102 @@
/*
* Copyright 2011 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.geo;
import org.springframework.util.Assert;
/**
* Calue object capturing some arbitrary object plus a distance.
*
* @author Oliver Gierke
*/
public class GeoResult<T> {
private final T content;
private final Distance distance;
/**
* Creates a new {@link GeoResult} for the given content and distance.
*
* @param content must not be {@literal null}.
* @param distance must not be {@literal null}.
*/
public GeoResult(T content, Distance distance) {
Assert.notNull(content);
Assert.notNull(distance);
this.content = content;
this.distance = distance;
}
/**
* Returns the actual content object.
*
* @return the content
*/
public T getContent() {
return content;
}
/**
* Returns the distance the actual content object has from the origin.
*
* @return the distance
*/
public Distance getDistance() {
return distance;
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || !getClass().equals(obj.getClass())) {
return false;
}
GeoResult<?> that = (GeoResult<?>) obj;
return this.content.equals(that.content) && this.distance.equals(that.distance);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = 17;
result += 31 * distance.hashCode();
result += 31 * content.hashCode();
return result;
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String.format("GeoResult [content: %s, distance: %s, ]", content.toString(), distance.toString());
}
}

143
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoResults.java

@ -0,0 +1,143 @@ @@ -0,0 +1,143 @@
/*
* Copyright 2011 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.geo;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Value object to capture {@link GeoResult}s as well as the average distance they have.
*
* @author Oliver Gierke
*/
public class GeoResults<T> implements Iterable<GeoResult<T>> {
private final List<GeoResult<T>> results;
private final Distance averageDistance;
/**
* Creates a new {@link GeoResults} instance manually calculating the average distance from the distance values of the
* given {@link GeoResult}s.
*
* @param results must not be {@literal null}.
*/
public GeoResults(List<GeoResult<T>> results) {
this(results, (Metric) null);
}
public GeoResults(List<GeoResult<T>> results, Metric metric) {
this(results, calculateAverageDistance(results, metric));
}
/**
* Creates a new {@link GeoResults} instance from the given {@link GeoResult}s and average distance.
*
* @param results must not be {@literal null}.
* @param averageDistance
*/
@PersistenceConstructor
public GeoResults(List<GeoResult<T>> results, Distance averageDistance) {
Assert.notNull(results);
this.results = results;
this.averageDistance = averageDistance;
}
/**
* @return the averageDistance
*/
public Distance getAverageDistance() {
return averageDistance;
}
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
public Iterator<GeoResult<T>> iterator() {
return results.iterator();
}
/**
* Returns the actual
*
* @return
*/
public List<GeoResult<T>> getContent() {
return Collections.unmodifiableList(results);
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || !getClass().equals(obj.getClass())) {
return false;
}
GeoResults<?> that = (GeoResults<?>) obj;
return this.results.equals(that.results) && this.averageDistance == that.averageDistance;
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = 17;
result += 31 * results.hashCode();
result += 31 * averageDistance.hashCode();
return result;
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String.format("GeoResults: [averageDistance: %s, results: %s]", averageDistance.toString(),
StringUtils.collectionToCommaDelimitedString(results));
}
private static Distance calculateAverageDistance(List<? extends GeoResult<?>> results, Metric metric) {
if (results.isEmpty()) {
return new Distance(0, null);
}
double averageDistance = 0;
for (GeoResult<?> result : results) {
averageDistance += result.getDistance().getValue();
}
return new Distance(averageDistance / results.size(), metric);
}
}

16
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/Metric.java

@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
package org.springframework.data.mongodb.core.geo;
/**
* Interface for {@link Metric}s that can be applied to a base scale.
*
* @author Oliver Gierke
*/
public interface Metric {
/**
* Returns the multiplier to calculate metrics values from a base scale.
*
* @return
*/
double getMultiplier();
}

27
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/Metrics.java

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
package org.springframework.data.mongodb.core.geo;
import org.springframework.data.mongodb.core.query.NearQuery;
/**
* Commonly used {@link Metrics} for {@link NearQuery}s.
*
* @author Oliver Gierke
*/
public enum Metrics implements Metric {
KILOMETERS(6378.137), MILES(3963.191), NEUTRAL(1);
private final double multiplier;
private Metrics(double multiplier) {
this.multiplier = multiplier;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.geo.Metric#getMultiplier()
*/
public double getMultiplier() {
return multiplier;
}
}

266
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/NearQuery.java

@ -0,0 +1,266 @@ @@ -0,0 +1,266 @@
/*
* Copyright 2011 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 org.springframework.data.mongodb.core.geo.Distance;
import org.springframework.data.mongodb.core.geo.Metric;
import org.springframework.data.mongodb.core.geo.Metrics;
import org.springframework.data.mongodb.core.geo.Point;
import org.springframework.util.Assert;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
* Builder class to build near-queries.
*
* @author Oliver Gierke
*/
public class NearQuery {
private final DBObject criteria;
private Query query;
private Double maxDistance;
private Metric metric;
/**
* Creates a new {@link NearQuery}.
*
* @param point
*/
private NearQuery(Point point, Metric metric) {
Assert.notNull(point);
this.criteria = new BasicDBObject();
this.criteria.put("near", point.asArray());
this.metric = metric;
if (metric != null) {
spherical(true);
distanceMultiplier(metric);
}
}
/**
* Creates a new {@link NearQuery} starting near the given coordinates.
*
* @param i
* @param j
* @return
*/
public static NearQuery near(double x, double y) {
return near(x, y, null);
}
/**
* 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
* @return
*/
public static NearQuery near(double x, double y, Metric metric) {
return near(new Point(x, y), metric);
}
/**
* Creates a new {@link NearQuery} starting at the given {@link Point}.
*
* @param point must not be {@literal null}.
* @return
*/
public static NearQuery near(Point point) {
return near(point, null);
}
/**
* 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
*/
public static NearQuery near(Point point, Metric metric) {
Assert.notNull(point);
return new NearQuery(point, metric);
}
/**
* Returns the {@link Metric} underlying the actual query.
*
* @return
*/
public Metric getMetric() {
return metric;
}
/**
* Configures the number of results to return.
*
* @param num
* @return
*/
public NearQuery num(int num) {
this.criteria.put("num", num);
return this;
}
/**
* Sets the max distance results shall have from the configured origin. Will normalize the given value using a
* potentially already configured {@link Metric}.
*
* @param maxDistance
* @return
*/
public NearQuery maxDistance(double maxDistance) {
this.maxDistance = getNormalizedDistance(maxDistance, this.metric);
return this;
}
/**
* Sets the maximum distance supplied in a given metric. Will normalize the distance but not reconfigure the query's
* {@link Metric}.
*
* @param maxDistance
* @param metric must not be {@literal null}.
* @return
*/
public NearQuery maxDistance(double maxDistance, Metric metric) {
Assert.notNull(metric);
this.spherical(true);
return maxDistance(getNormalizedDistance(maxDistance, metric));
}
/**
* Sets the maximum distance to the given {@link Distance}.
*
* @param distance
* @return
*/
public NearQuery maxDistance(Distance distance) {
Assert.notNull(distance);
return maxDistance(distance.getValue(), distance.getMetric());
}
/**
* Configures a distance multiplier the resulting distances get applied.
*
* @param distanceMultiplier
* @return
*/
public NearQuery distanceMultiplier(double distanceMultiplier) {
this.criteria.put("distanceMultiplier", distanceMultiplier);
return this;
}
/**
* Configures the distance multiplier to the multiplier of the given {@link Metric}. Does <em>not</em> recalculate the
* {@link #maxDistance(double)}.
*
* @param metric must not be {@literal null}.
* @return
*/
public NearQuery distanceMultiplier(Metric metric) {
Assert.notNull(metric);
return distanceMultiplier(metric.getMultiplier());
}
/**
* Configures whether to return spherical values for the actual distance.
*
* @param spherical
* @return
*/
public NearQuery spherical(boolean spherical) {
this.criteria.put("spherical", spherical);
return this;
}
/**
* Will cause the results' distances being returned in kilometers. Sets {@link #distanceMultiplier(double)} and
* {@link #spherical(boolean)} accordingly.
*
* @return
*/
public NearQuery inKilometers() {
return adaptMetric(Metrics.KILOMETERS);
}
/**
* Will cause the results' distances being returned in miles. Sets {@link #distanceMultiplier(double)} and
* {@link #spherical(boolean)} accordingly.
*
* @return
*/
public NearQuery inMiles() {
return adaptMetric(Metrics.MILES);
}
/**
* 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) {
if (this.metric == null && maxDistance != null) {
maxDistance(this.maxDistance, metric);
}
spherical(true);
return distanceMultiplier(metric);
}
/**
* Adds an actual query to the {@link NearQuery} to restrict the objects considered for the actual near operation.
*
* @param query
* @return
*/
public NearQuery query(Query query) {
this.query = query;
return this;
}
/**
* Returns the {@link DBObject} built by the {@link NearQuery}.
*
* @return
*/
public DBObject toDBObject() {
BasicDBObject dbObject = new BasicDBObject(criteria.toMap());
if (query != null) {
dbObject.put("query", query.getQueryObject());
}
if (maxDistance != null) {
dbObject.put("maxDistance", maxDistance);
}
return dbObject;
}
private double getNormalizedDistance(double distance, Metric metric) {
return metric == null ? distance : distance / metric.getMultiplier();
}
}

50
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/DistanceUnitTests.java

@ -0,0 +1,50 @@ @@ -0,0 +1,50 @@
/*
* Copyright 2010-2011 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.geo;
import static org.springframework.data.mongodb.core.geo.Metrics.*;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
/**
* Unit tests for {@link Distance}.
*
* @author Oliver Gierke
*/
public class DistanceUnitTests {
@Test
public void defaultsMetricToNeutralOne() {
assertThat(new Distance(2.5).getMetric(), is((Metric) Metrics.NEUTRAL));
assertThat(new Distance(2.5, null).getMetric(), is((Metric) Metrics.NEUTRAL));
}
@Test
public void addsDistancesWithoutExplicitMetric() {
Distance left = new Distance(2.5, KILOMETERS);
Distance right = new Distance(2.5, KILOMETERS);
assertThat(left.add(right), is(new Distance(5.0, KILOMETERS)));
}
@Test
public void addsDistancesWithExplicitMetric() {
Distance left = new Distance(2.5, KILOMETERS);
Distance right = new Distance(2.5, KILOMETERS);
assertThat(left.add(right, MILES), is(new Distance(3.106856281073925, MILES)));
}
}

55
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoResultUnitTests.java

@ -0,0 +1,55 @@ @@ -0,0 +1,55 @@
/*
* Copyright 2011 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.geo;
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
import org.junit.Test;
/**
* Unit tests for {@link GeoResult}.
*
* @author Oliver Gierke
*/
public class GeoResultUnitTests {
GeoResult<String> first = new GeoResult<String>("Foo", new Distance(2.5));
GeoResult<String> second = new GeoResult<String>("Foo", new Distance(2.5));
GeoResult<String> third = new GeoResult<String>("Bar", new Distance(2.5));
GeoResult<String> fourth = new GeoResult<String>("Foo", new Distance(5.2));
@Test
public void considersSameInstanceEqual() {
assertThat(first.equals(first), is(true));
}
@Test
public void considersSameValuesAsEqual() {
assertThat(first.equals(second), is(true));
assertThat(second.equals(first), is(true));
assertThat(first.equals(third), is(false));
assertThat(third.equals(first), is(false));
assertThat(first.equals(fourth), is(false));
assertThat(fourth.equals(first), is(false));
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Test(expected = IllegalArgumentException.class)
public void rejectsNullContent() {
new GeoResult(null, new Distance(2.5));
}
}

24
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoResultsUnitTests.java

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
/*
* Copyright 2011 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.geo;
/**
*
* @author Oliver Gierke
*/
public class GeoResultsUnitTests {
}

2
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatialAppConfig.java

@ -33,7 +33,7 @@ public class GeoSpatialAppConfig extends AbstractMongoConfiguration { @@ -33,7 +33,7 @@ public class GeoSpatialAppConfig extends AbstractMongoConfiguration {
@Override
@Bean
public Mongo mongo() throws Exception {
return new Mongo("localhost");
return new Mongo("127.0.0.1");
}
@Bean

16
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatialTests.java

@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
package org.springframework.data.mongodb.core.geo;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.*;
import java.net.UnknownHostException;
import java.util.Collection;
@ -34,11 +34,9 @@ import org.springframework.dao.DataAccessException; @@ -34,11 +34,9 @@ import org.springframework.dao.DataAccessException;
import org.springframework.data.mongodb.core.CollectionCallback;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.Venue;
import org.springframework.data.mongodb.core.geo.Box;
import org.springframework.data.mongodb.core.geo.Circle;
import org.springframework.data.mongodb.core.geo.Point;
import org.springframework.data.mongodb.core.index.GeospatialIndex;
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.monitor.ServerInfo;
import org.springframework.expression.ExpressionParser;
@ -111,11 +109,13 @@ public class GeoSpatialTests { @@ -111,11 +109,13 @@ public class GeoSpatialTests {
template.insert(new Venue("Maplewood, NJ", -74.2713, 40.73137));
}
/*
@Test
public void geoNear() {
GeoNearResult<Venue> geoNearResult = template.geoNear(new Query(Criteria.where("type").is("Office")), Venue.class,
GeoNearCriteria.near(2,3).num(10).maxDistance(10).distanceMultiplier(10).spherical(true));
}*/
NearQuery geoNear = NearQuery.near(-73,40, Metrics.KILOMETERS).num(10).maxDistance(150);
GeoResults<Venue> geoNearResult = template.geoNear(geoNear, Venue.class);
assertThat(geoNearResult.getContent().size(), is(not(0)));
}
@Test
public void withinCenter() {

2
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/GeoIndexedAppConfig.java

@ -19,7 +19,7 @@ public class GeoIndexedAppConfig extends AbstractMongoConfiguration { @@ -19,7 +19,7 @@ public class GeoIndexedAppConfig extends AbstractMongoConfiguration {
@Override
@Bean
public Mongo mongo() throws Exception {
return new Mongo("localhost");
return new Mongo("127.0.0.1");
}
@Override

44
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/NearQueryUnitTests.java

@ -0,0 +1,44 @@ @@ -0,0 +1,44 @@
package org.springframework.data.mongodb.core.query;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.data.mongodb.core.geo.Metrics;
/**
*
* @author Oliver Gierke
*/
public class NearQueryUnitTests {
@Test(expected = IllegalArgumentException.class)
public void rejectsNullPoint() {
NearQuery.near(null);
}
@Test
public void settingUpNearWithMetricRecalculatesDistance() {
NearQuery query = NearQuery.near(2.5, 2.5, Metrics.KILOMETERS).maxDistance(150);
assertThat((Double) query.toDBObject().get("maxDistance"), is(0.02351783914331097));
assertThat((Boolean) query.toDBObject().get("spherical"), is(true));
assertThat((Double) query.toDBObject().get("distanceMultiplier"), is(Metrics.KILOMETERS.getMultiplier()));
}
@Test
public void settingMetricRecalculatesMaxDistance() {
NearQuery query = NearQuery.near(2.5, 2.5, Metrics.KILOMETERS).maxDistance(150);
assertThat((Double) query.toDBObject().get("maxDistance"), is(0.02351783914331097));
assertThat((Double) query.toDBObject().get("distanceMultiplier"), is(Metrics.KILOMETERS.getMultiplier()));
query.inMiles();
assertThat((Double) query.toDBObject().get("distanceMultiplier"), is(Metrics.MILES.getMultiplier()));
NearQuery.near(2.5, 2.5).maxDistance(150).inKilometers();
assertThat((Double) query.toDBObject().get("maxDistance"), is(0.02351783914331097));
}
}
Loading…
Cancel
Save