Merge branch 'geo-repo'
* geo-repo: DATADOC-68 - Support for geo-near queries at repositories.
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
/**
|
||||
* Custom {@link Page} to carry the average distance retrieved from the {@link GeoResults} the {@link GeoPage} is set up
|
||||
* from.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class GeoPage<T> extends PageImpl<GeoResult<T>> {
|
||||
|
||||
private static final long serialVersionUID = 23421312312412L;
|
||||
private final Distance averageDistance;
|
||||
|
||||
/**
|
||||
* Creates a new {@link GeoPage} from the given {@link GeoResults}.
|
||||
*
|
||||
* @param content must not be {@literal null}.
|
||||
*/
|
||||
public GeoPage(GeoResults<T> results) {
|
||||
super(results.getContent());
|
||||
this.averageDistance = results.getAverageDistance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link GeoPage} from the given {@link GeoResults}, {@link Pageable} and total.
|
||||
*
|
||||
* @param results must not be {@literal null}.
|
||||
* @param pageable must not be {@literal null}.
|
||||
* @param total
|
||||
*/
|
||||
public GeoPage(GeoResults<T> results, Pageable pageable, long total) {
|
||||
super(results.getContent(), pageable, total);
|
||||
this.averageDistance = results.getAverageDistance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the average distance of the underlying results.
|
||||
*
|
||||
* @return the averageDistance
|
||||
*/
|
||||
public Distance getAverageDistance() {
|
||||
return averageDistance;
|
||||
}
|
||||
}
|
||||
@@ -23,9 +23,15 @@ import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.mongodb.core.CollectionCallback;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
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.Point;
|
||||
import org.springframework.data.mongodb.core.query.NearQuery;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.repository.query.ParameterAccessor;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.DBCollection;
|
||||
@@ -72,10 +78,12 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
|
||||
*/
|
||||
public Object execute(Object[] parameters) {
|
||||
|
||||
MongoParameterAccessor accessor = new MongoParametersParameterAccessor(method.getParameters(), parameters);
|
||||
MongoParameterAccessor accessor = new MongoParametersParameterAccessor(method, parameters);
|
||||
Query query = createQuery(new ConvertingParameterAccessor(template.getConverter(), accessor));
|
||||
|
||||
if (method.isCollectionQuery()) {
|
||||
if (method.isGeoNearQuery()) {
|
||||
return new GeoNearExecution(accessor).execute(query);
|
||||
} else if (method.isCollectionQuery()) {
|
||||
return new CollectionExecution().execute(query);
|
||||
} else if (method.isPageQuery()) {
|
||||
return new PagedExecution(accessor.getPageable()).execute(query);
|
||||
@@ -146,10 +154,9 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.mongodb.repository.MongoQuery.Execution #execute(com.mongodb.DBObject)
|
||||
*/
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.repository.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.query.Query)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
Object execute(Query query) {
|
||||
@@ -193,4 +200,49 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
|
||||
return template.findOne(query, entityInformation.getJavaType());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Execution} to execute geo-near queries.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
class GeoNearExecution extends Execution {
|
||||
|
||||
private final MongoParameterAccessor accessor;
|
||||
|
||||
public GeoNearExecution(MongoParameterAccessor accessor) {
|
||||
this.accessor = accessor;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.repository.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.query.Query)
|
||||
*/
|
||||
@Override
|
||||
Object execute(Query query) {
|
||||
|
||||
Point nearLocation = accessor.getGeoNearLocation();
|
||||
NearQuery nearQuery = NearQuery.near(nearLocation);
|
||||
|
||||
if (query != null) {
|
||||
nearQuery.query(query);
|
||||
}
|
||||
|
||||
Distance maxDistance = accessor.getMaxDistance();
|
||||
if (maxDistance != null) {
|
||||
nearQuery.maxDistance(maxDistance);
|
||||
}
|
||||
|
||||
MongoEntityInformation<?,?> entityInformation = method.getEntityInformation();
|
||||
GeoResults<?> results = template.geoNear(nearQuery, entityInformation.getJavaType(), entityInformation.getCollectionName());
|
||||
|
||||
return isListOfGeoResult() ? results.getContent() : results;
|
||||
}
|
||||
|
||||
private boolean isListOfGeoResult() {
|
||||
|
||||
TypeInformation<?> returnType = method.getReturnType();
|
||||
return returnType.getType().equals(List.class) && GeoResult.class.equals(returnType.getComponentType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.springframework.data.mongodb.core.convert.MongoWriter;
|
||||
import org.springframework.data.mongodb.core.convert.TypeMapper;
|
||||
import org.springframework.data.mongodb.core.convert.TypeMapperProvider;
|
||||
import org.springframework.data.mongodb.core.geo.Distance;
|
||||
import org.springframework.data.mongodb.core.geo.Point;
|
||||
import org.springframework.data.repository.query.ParameterAccessor;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
@@ -90,6 +91,13 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor {
|
||||
public Distance getMaxDistance() {
|
||||
return delegate.getMaxDistance();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.repository.MongoParameterAccessor#getGeoNearLocation()
|
||||
*/
|
||||
public Point getGeoNearLocation() {
|
||||
return delegate.getGeoNearLocation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given value with the underlying {@link MongoWriter}.
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.data.mongodb.repository;
|
||||
|
||||
import org.springframework.data.mongodb.core.geo.Distance;
|
||||
import org.springframework.data.mongodb.core.geo.Point;
|
||||
import org.springframework.data.repository.query.ParameterAccessor;
|
||||
|
||||
/**
|
||||
@@ -32,4 +33,11 @@ public interface MongoParameterAccessor extends ParameterAccessor {
|
||||
* at all or the given value for it was {@literal null}.
|
||||
*/
|
||||
Distance getMaxDistance();
|
||||
|
||||
/**
|
||||
* Returns the {@link Point} to use for a geo-near query.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Point getGeoNearLocation();
|
||||
}
|
||||
|
||||
@@ -17,9 +17,11 @@ package org.springframework.data.mongodb.repository;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.data.mongodb.core.geo.Distance;
|
||||
import org.springframework.data.mongodb.core.geo.Point;
|
||||
import org.springframework.data.repository.query.Parameter;
|
||||
import org.springframework.data.repository.query.Parameters;
|
||||
|
||||
@@ -30,40 +32,107 @@ import org.springframework.data.repository.query.Parameters;
|
||||
*/
|
||||
public class MongoParameters extends Parameters {
|
||||
|
||||
private int distanceIndex = -1;
|
||||
|
||||
public MongoParameters(Method method) {
|
||||
|
||||
private final Integer distanceIndex;
|
||||
private Integer nearIndex;
|
||||
|
||||
/**
|
||||
* Creates a new {@link MongoParameters} instance from the given {@link Method} and {@link MongoQueryMethod}.
|
||||
*
|
||||
* @param method must not be {@literal null}.
|
||||
* @param queryMethod must not be {@literal null}.
|
||||
*/
|
||||
public MongoParameters(Method method, boolean isGeoNearMethod) {
|
||||
|
||||
super(method);
|
||||
this.distanceIndex = Arrays.asList(method.getParameterTypes()).indexOf(Distance.class);
|
||||
List<Class<?>> parameterTypes = Arrays.asList(method.getParameterTypes());
|
||||
this.distanceIndex = parameterTypes.indexOf(Distance.class);
|
||||
|
||||
if (this.nearIndex == null && isGeoNearMethod) {
|
||||
this.nearIndex = getNearIndex(parameterTypes);
|
||||
} else if (this.nearIndex == null) {
|
||||
this.nearIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private final int getNearIndex(List<Class<?>> parameterTypes) {
|
||||
|
||||
for (Class<?> reference : Arrays.asList(Point.class, double[].class)) {
|
||||
|
||||
int nearIndex = parameterTypes.indexOf(reference);
|
||||
|
||||
if (nearIndex == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nearIndex == parameterTypes.lastIndexOf(reference)) {
|
||||
return nearIndex;
|
||||
} else {
|
||||
throw new IllegalStateException("Multiple Point parameters found but none annotated with @Near!");
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.Parameters#createParameter(org.springframework.core.MethodParameter)
|
||||
*/
|
||||
@Override
|
||||
protected Parameter createParameter(MethodParameter parameter) {
|
||||
return new MongoParameter(parameter, this);
|
||||
}
|
||||
|
||||
public int getDistanceIndex() {
|
||||
return distanceIndex;
|
||||
|
||||
MongoParameter mongoParameter = new MongoParameter(parameter);
|
||||
|
||||
// Detect manually annotated @Near Point and reject multiple annotated ones
|
||||
if (this.nearIndex == null && mongoParameter.isManuallyAnnotatedNearParameter()) {
|
||||
this.nearIndex = mongoParameter.getIndex();
|
||||
} else if (mongoParameter.isManuallyAnnotatedNearParameter()) {
|
||||
throw new IllegalStateException(String.format("Found multiple @Near annotations ond method %s! Only one allowed!", parameter.getMethod().toString()));
|
||||
}
|
||||
|
||||
return mongoParameter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom {@link Parameter} implementation adding parameters of type {@link Distance} to the special ones.
|
||||
* Returns the index of a {@link Distance} parameter to be used for geo queries.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getDistanceIndex() {
|
||||
return distanceIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the parameter to be used to start a geo-near query from.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getNearIndex() {
|
||||
return nearIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom {@link Parameter} implementation adding parameters of type {@link Distance} to the special ones.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
static class MongoParameter extends Parameter {
|
||||
|
||||
class MongoParameter extends Parameter {
|
||||
|
||||
private final MethodParameter parameter;
|
||||
|
||||
/**
|
||||
* Creates a new {@link MongoParameter}.
|
||||
*
|
||||
* @param parameter
|
||||
* @param parameters
|
||||
* @param parameter must not be {@literal null}.
|
||||
*/
|
||||
MongoParameter(MethodParameter parameter, Parameters parameters) {
|
||||
super(parameter, parameters);
|
||||
MongoParameter(MethodParameter parameter) {
|
||||
super(parameter);
|
||||
this.parameter = parameter;
|
||||
|
||||
if (!isPoint() && hasNearAnnotation()) {
|
||||
throw new IllegalArgumentException("Near annotation is only allowed at Point parameter!");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -72,7 +141,25 @@ public class MongoParameters extends Parameters {
|
||||
*/
|
||||
@Override
|
||||
public boolean isSpecialParameter() {
|
||||
return super.isSpecialParameter() || getType().equals(Distance.class);
|
||||
return super.isSpecialParameter() || getType().equals(Distance.class)
|
||||
|| isNearParameter();
|
||||
}
|
||||
|
||||
private boolean isNearParameter() {
|
||||
Integer nearIndex = MongoParameters.this.nearIndex;
|
||||
return nearIndex != null && nearIndex.equals(getIndex());
|
||||
}
|
||||
|
||||
private boolean isManuallyAnnotatedNearParameter() {
|
||||
return isPoint() && hasNearAnnotation();
|
||||
}
|
||||
|
||||
private boolean isPoint() {
|
||||
return getType().equals(Point.class) || getType().equals(double[].class);
|
||||
}
|
||||
|
||||
private boolean hasNearAnnotation() {
|
||||
return parameter.getParameterAnnotation(Near.class) != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,24 +16,27 @@
|
||||
package org.springframework.data.mongodb.repository;
|
||||
|
||||
import org.springframework.data.mongodb.core.geo.Distance;
|
||||
import org.springframework.data.mongodb.core.geo.Point;
|
||||
import org.springframework.data.repository.query.ParametersParameterAccessor;
|
||||
|
||||
/**
|
||||
* Mongo-specific {@link ParametersParameterAccessor} to allow access to the {@link Distance} parameter.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class MongoParametersParameterAccessor extends ParametersParameterAccessor implements MongoParameterAccessor {
|
||||
|
||||
private final MongoParameters parameters;
|
||||
|
||||
private final MongoQueryMethod method;
|
||||
|
||||
/**
|
||||
* @param parameters
|
||||
* @param values
|
||||
* Creates a new {@link MongoParametersParameterAccessor}.
|
||||
*
|
||||
* @param method must not be {@literal null}.
|
||||
* @param values must not be {@@iteral null}.
|
||||
*/
|
||||
public MongoParametersParameterAccessor(MongoParameters parameters, Object[] values) {
|
||||
super(parameters, values);
|
||||
this.parameters = parameters;
|
||||
public MongoParametersParameterAccessor(MongoQueryMethod method, Object[] values) {
|
||||
super(method.getParameters(), values);
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -41,7 +44,37 @@ public class MongoParametersParameterAccessor extends ParametersParameterAccesso
|
||||
* @see org.springframework.data.mongodb.repository.MongoParameterAccessor#getMaxDistance()
|
||||
*/
|
||||
public Distance getMaxDistance() {
|
||||
int index = parameters.getDistanceIndex();
|
||||
int index = method.getParameters().getDistanceIndex();
|
||||
return index == -1 ? null : (Distance) getValue(index);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.repository.MongoParameterAccessor#getGeoNearLocation()
|
||||
*/
|
||||
public Point getGeoNearLocation() {
|
||||
|
||||
int nearIndex = method.getParameters().getNearIndex();
|
||||
|
||||
if (nearIndex == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object value = getValue(nearIndex);
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (value instanceof double[]) {
|
||||
double[] typedValue = (double[]) value;
|
||||
if (typedValue.length != 2) {
|
||||
throw new IllegalArgumentException("The given double[] must have exactly 2 elements!");
|
||||
} else {
|
||||
return new Point(typedValue[0], typedValue[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return (Point) value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,18 +47,26 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Query> {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(MongoQueryCreator.class);
|
||||
private final MongoParameterAccessor accessor;
|
||||
private final boolean isGeoNearQuery;
|
||||
|
||||
public MongoQueryCreator(PartTree tree, ConvertingParameterAccessor accessor) {
|
||||
this(tree, accessor, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link MongoQueryCreator} from the given {@link PartTree} and {@link ParametersParameterAccessor}.
|
||||
*
|
||||
* @param tree
|
||||
* @param accessor
|
||||
* @param isGeoNearQuery
|
||||
*/
|
||||
public MongoQueryCreator(PartTree tree, ConvertingParameterAccessor accessor) {
|
||||
public MongoQueryCreator(PartTree tree, ConvertingParameterAccessor accessor, boolean isGeoNearQuery) {
|
||||
|
||||
super(tree, accessor);
|
||||
this.accessor = accessor;
|
||||
this.isGeoNearQuery = isGeoNearQuery;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
@@ -66,6 +74,10 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Query> {
|
||||
*/
|
||||
@Override
|
||||
protected Query create(Part part, Iterator<Object> iterator) {
|
||||
|
||||
if (isGeoNearQuery && part.getType().equals(Type.NEAR)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Criteria criteria = from(part.getType(), where(part.getProperty().toDotPath()),
|
||||
(PotentiallyConvertingIterator) iterator);
|
||||
@@ -79,6 +91,10 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Query> {
|
||||
*/
|
||||
@Override
|
||||
protected Query and(Part part, Query base, Iterator<Object> iterator) {
|
||||
|
||||
if (base == null) {
|
||||
return create(part, iterator);
|
||||
}
|
||||
|
||||
Criteria criteria = from(part.getType(), where(part.getProperty().toDotPath()),
|
||||
(PotentiallyConvertingIterator) iterator);
|
||||
|
||||
@@ -16,13 +16,21 @@
|
||||
package org.springframework.data.mongodb.repository;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.data.mongodb.core.geo.GeoPage;
|
||||
import org.springframework.data.mongodb.core.geo.GeoResult;
|
||||
import org.springframework.data.mongodb.core.geo.GeoResults;
|
||||
import org.springframework.data.mongodb.repository.MongoRepositoryFactoryBean.EntityInformationCreator;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.query.Parameters;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
import org.springframework.data.repository.util.ClassUtils;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -33,6 +41,10 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
class MongoQueryMethod extends QueryMethod {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static final List<Class<?>> GEO_NEAR_RESULTS = Arrays
|
||||
.asList(GeoResult.class, GeoResults.class, GeoPage.class);
|
||||
|
||||
private final Method method;
|
||||
private final MongoEntityInformation<?, ?> entityInformation;
|
||||
|
||||
@@ -43,6 +55,7 @@ class MongoQueryMethod extends QueryMethod {
|
||||
*/
|
||||
public MongoQueryMethod(Method method, RepositoryMetadata metadata, EntityInformationCreator entityInformationCreator) {
|
||||
super(method, metadata);
|
||||
Assert.notNull(entityInformationCreator, "EntityInformationCreator must not be null!");
|
||||
this.method = method;
|
||||
this.entityInformation = entityInformationCreator.getEntityInformation(ClassUtils.getReturnedDomainClass(method),
|
||||
getDomainClass());
|
||||
@@ -54,7 +67,7 @@ class MongoQueryMethod extends QueryMethod {
|
||||
*/
|
||||
@Override
|
||||
protected Parameters createParameters(Method method) {
|
||||
return new MongoParameters(method);
|
||||
return new MongoParameters(method, isGeoNearQuery(method));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,13 +121,40 @@ class MongoQueryMethod extends QueryMethod {
|
||||
return (MongoParameters) super.getParameters();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether te query is a geoNear query.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isGeoNearQuery() {
|
||||
|
||||
return isGeoNearQuery(this.method);
|
||||
}
|
||||
|
||||
private boolean isGeoNearQuery(Method method) {
|
||||
|
||||
if (GEO_NEAR_RESULTS.contains(method.getReturnType())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Iterable.class.isAssignableFrom(method.getReturnType())) {
|
||||
TypeInformation<?> from = ClassTypeInformation.fromReturnTypeOf(method);
|
||||
return GeoResult.class.equals(from.getComponentType().getType());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Query} annotation that is applied to the method or {@code null} if none available.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private Query getQueryAnnotation() {
|
||||
|
||||
return method.getAnnotation(Query.class);
|
||||
}
|
||||
|
||||
TypeInformation<?> getReturnType() {
|
||||
return ClassTypeInformation.fromReturnTypeOf(method);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.springframework.data.mongodb.repository;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
|
||||
import org.springframework.data.mongodb.core.geo.Distance;
|
||||
|
||||
/**
|
||||
* Annotation to be used for disambiguing method parameters that shall be used to trigger geo near queries. By default
|
||||
* those parameters are found without the need for additional annotation if they are the only parameters of the
|
||||
* according type (e.g. {@link Point}, {@code double[]}, {@link Distance}).
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.PARAMETER)
|
||||
public @interface Near {
|
||||
|
||||
}
|
||||
@@ -29,6 +29,7 @@ import org.springframework.data.repository.query.parser.PartTree;
|
||||
public class PartTreeMongoQuery extends AbstractMongoQuery {
|
||||
|
||||
private final PartTree tree;
|
||||
private final boolean isGeoNearQuery;
|
||||
|
||||
/**
|
||||
* Creates a new {@link PartTreeMongoQuery} from the given {@link QueryMethod} and {@link MongoTemplate}.
|
||||
@@ -40,6 +41,7 @@ public class PartTreeMongoQuery extends AbstractMongoQuery {
|
||||
|
||||
super(method, template);
|
||||
this.tree = new PartTree(method.getName(), method.getEntityInformation().getJavaType());
|
||||
this.isGeoNearQuery = method.isGeoNearQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,7 +61,7 @@ public class PartTreeMongoQuery extends AbstractMongoQuery {
|
||||
@Override
|
||||
protected Query createQuery(ConvertingParameterAccessor accessor) {
|
||||
|
||||
MongoQueryCreator creator = new MongoQueryCreator(tree, accessor);
|
||||
MongoQueryCreator creator = new MongoQueryCreator(tree, accessor, isGeoNearQuery);
|
||||
return creator.createQuery();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2011 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -21,6 +21,9 @@ import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.data.mongodb.core.geo.Box;
|
||||
import org.springframework.data.mongodb.core.geo.Circle;
|
||||
import org.springframework.data.mongodb.core.geo.Distance;
|
||||
import org.springframework.data.mongodb.core.geo.GeoResults;
|
||||
import org.springframework.data.mongodb.core.geo.Metrics;
|
||||
import org.springframework.data.mongodb.core.geo.Point;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
@@ -317,4 +320,15 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
|
||||
assertThat(result.get(4), is(leroi));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void executesGeoNearQueryForResultsCorrectly() {
|
||||
|
||||
Point point = new Point(-73.99171, 40.738868);
|
||||
dave.setLocation(point);
|
||||
repository.save(dave);
|
||||
|
||||
GeoResults<Person> results = repository.findByLocationNear(new Point(-73.99, 40.73), new Distance(2000, Metrics.KILOMETERS));
|
||||
assertThat(results.getContent().isEmpty(), is(false));
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,11 @@ import org.junit.Test;
|
||||
import org.springframework.data.mongodb.core.geo.Distance;
|
||||
import org.springframework.data.mongodb.core.geo.Metrics;
|
||||
import org.springframework.data.mongodb.core.geo.Point;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.repository.MongoRepositoryFactoryBean.EntityInformationCreator;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link MongoParametersParameterAccessor}.
|
||||
@@ -34,14 +39,17 @@ import org.springframework.data.mongodb.core.geo.Point;
|
||||
public class MongoParametersParameterAccessorUnitTests {
|
||||
|
||||
private static final Distance DISTANCE = new Distance(2.5, Metrics.KILOMETERS);
|
||||
private static final RepositoryMetadata metadata = new DefaultRepositoryMetadata(PersonRepository.class);
|
||||
private static final MongoMappingContext context = new MongoMappingContext();
|
||||
private static final EntityInformationCreator creator = new EntityInformationCreator(context);
|
||||
|
||||
@Test
|
||||
public void returnsNullForDistanceIfNoneAvailable() throws NoSuchMethodException, SecurityException {
|
||||
|
||||
Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class);
|
||||
MongoParameters parameters = new MongoParameters(method);
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, creator);
|
||||
|
||||
MongoParameterAccessor accessor = new MongoParametersParameterAccessor(parameters,
|
||||
MongoParameterAccessor accessor = new MongoParametersParameterAccessor(queryMethod,
|
||||
new Object[] { new Point(10, 20) });
|
||||
assertThat(accessor.getMaxDistance(), is(nullValue()));
|
||||
}
|
||||
@@ -50,14 +58,14 @@ public class MongoParametersParameterAccessorUnitTests {
|
||||
public void returnsDistanceIfAvailable() throws NoSuchMethodException, SecurityException {
|
||||
|
||||
Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class, Distance.class);
|
||||
MongoParameters parameters = new MongoParameters(method);
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, creator);
|
||||
|
||||
MongoParameterAccessor accessor = new MongoParametersParameterAccessor(parameters, new Object[] {
|
||||
MongoParameterAccessor accessor = new MongoParametersParameterAccessor(queryMethod, new Object[] {
|
||||
new Point(10, 20), DISTANCE });
|
||||
assertThat(accessor.getMaxDistance(), is(DISTANCE));
|
||||
}
|
||||
|
||||
interface PersonRepository {
|
||||
interface PersonRepository extends Repository<Person, Long> {
|
||||
|
||||
List<Person> findByLocationNear(Point point);
|
||||
|
||||
|
||||
@@ -22,36 +22,92 @@ import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.mongodb.core.geo.Distance;
|
||||
import org.springframework.data.mongodb.core.geo.GeoResults;
|
||||
import org.springframework.data.mongodb.core.geo.Point;
|
||||
import org.springframework.data.mongodb.repository.MongoParameters.MongoParameter;
|
||||
import org.springframework.data.repository.query.Parameter;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link MongoParameters}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class MongoParametersUnitTests {
|
||||
|
||||
@Mock
|
||||
MongoQueryMethod queryMethod;
|
||||
|
||||
@Test
|
||||
public void discoversDistanceParameter() throws NoSuchMethodException, SecurityException {
|
||||
Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class, Distance.class);
|
||||
MongoParameters parameters = new MongoParameters(method);
|
||||
|
||||
MongoParameters parameters = new MongoParameters(method, false);
|
||||
|
||||
assertThat(parameters.getNumberOfParameters(), is(2));
|
||||
assertThat(parameters.getDistanceIndex(), is(1));
|
||||
assertThat(parameters.getBindableParameters().getNumberOfParameters(), is(1));
|
||||
|
||||
MongoParameter parameter = new MongoParameters.MongoParameter(new MethodParameter(method,
|
||||
parameters.getDistanceIndex()), parameters);
|
||||
Parameter parameter = parameters.getParameter(1);
|
||||
|
||||
assertThat(parameter.isSpecialParameter(), is(true));
|
||||
assertThat(parameter.isBindable(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotConsiderPointAsNearForSimpleQuery() throws Exception {
|
||||
Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class, Distance.class);
|
||||
MongoParameters parameters = new MongoParameters(method, false);
|
||||
|
||||
assertThat(parameters.getNearIndex(), is(-1));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void rejectsMultiplePointsForGeoNearMethod() throws Exception {
|
||||
Method method = PersonRepository.class.getMethod("findByLocationNearAndOtherLocation", Point.class, Point.class);
|
||||
new MongoParameters(method, true);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void rejectsMultipleDoubleArraysForGeoNearMethod() throws Exception {
|
||||
Method method = PersonRepository.class.getMethod("invalidDoubleArrays", double[].class, double[].class);
|
||||
new MongoParameters(method, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotRejectMultiplePointsForSimpleQueryMethod() throws Exception {
|
||||
Method method = PersonRepository.class.getMethod("someOtherMethod", Point.class, Point.class);
|
||||
new MongoParameters(method, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findsAnnotatedPointForGeoNearQuery() throws Exception {
|
||||
Method method = PersonRepository.class.getMethod("findByOtherLocationAndLocationNear", Point.class, Point.class);
|
||||
MongoParameters parameters = new MongoParameters(method, true);
|
||||
assertThat(parameters.getNearIndex(), is(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findsAnnotatedDoubleArrayForGeoNearQuery() throws Exception {
|
||||
Method method = PersonRepository.class.getMethod("validDoubleArrays", double[].class, double[].class);
|
||||
MongoParameters parameters = new MongoParameters(method, true);
|
||||
assertThat(parameters.getNearIndex(), is(1));
|
||||
}
|
||||
|
||||
interface PersonRepository {
|
||||
|
||||
List<Person> findByLocationNear(Point point, Distance distance);
|
||||
|
||||
GeoResults<Person> findByLocationNearAndOtherLocation(Point point, Point anotherLocation);
|
||||
|
||||
GeoResults<Person> invalidDoubleArrays(double[] first, double[] second);
|
||||
|
||||
List<Person> someOtherMethod(Point first, Point second);
|
||||
|
||||
GeoResults<Person> findByOtherLocationAndLocationNear(Point point, @Near Point anotherLocation);
|
||||
|
||||
GeoResults<Person> validDoubleArrays(double[] first, @Near double[] second);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,12 +15,13 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.repository;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.springframework.data.mongodb.repository.StubParameterAccessor.*;
|
||||
import static org.springframework.data.mongodb.core.query.Query.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.data.mongodb.core.query.Criteria.*;
|
||||
import static org.springframework.data.mongodb.core.query.Query.*;
|
||||
import static org.springframework.data.mongodb.repository.StubParameterAccessor.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
@@ -38,9 +39,12 @@ import org.springframework.data.mongodb.core.convert.MongoConverter;
|
||||
import org.springframework.data.mongodb.core.geo.Distance;
|
||||
import org.springframework.data.mongodb.core.geo.Metrics;
|
||||
import org.springframework.data.mongodb.core.geo.Point;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.repository.MongoQueryCreator;
|
||||
import org.springframework.data.mongodb.repository.MongoRepositoryFactoryBean.EntityInformationCreator;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
@@ -103,42 +107,49 @@ public class MongoQueryCreatorUnitTests {
|
||||
|
||||
assertThat(query.getQueryObject(), is(new Query(Criteria.where("firstName").is(null)).getQueryObject()));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void bindsMetricDistanceParameterToNearSphereCorrectly() throws Exception {
|
||||
|
||||
Point point = new Point(10, 20);
|
||||
Distance distance = new Distance(2.5, Metrics.KILOMETERS);
|
||||
|
||||
Query query = query(where("location").nearSphere(point).maxDistance(distance.getNormalizedValue()).and("firstname").is("Dave"));
|
||||
|
||||
Query query = query(where("location").nearSphere(point).maxDistance(distance.getNormalizedValue()).and("firstname")
|
||||
.is("Dave"));
|
||||
assertBindsDistanceToQuery(point, distance, query);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void bindsDistanceParameterToNearCorrectly() throws Exception {
|
||||
|
||||
Point point = new Point(10, 20);
|
||||
Distance distance = new Distance(2.5);
|
||||
|
||||
Query query = query(where("location").near(point).maxDistance(distance.getNormalizedValue()).and("firstname").is("Dave"));
|
||||
|
||||
Query query = query(where("location").near(point).maxDistance(distance.getNormalizedValue()).and("firstname")
|
||||
.is("Dave"));
|
||||
assertBindsDistanceToQuery(point, distance, query);
|
||||
}
|
||||
|
||||
private void assertBindsDistanceToQuery(Point point, Distance distance, Query reference) throws Exception {
|
||||
|
||||
when(converter.convertToMongoType("Dave")).thenReturn("Dave");
|
||||
|
||||
PartTree tree = new PartTree("findByLocationNearAndFirstname", org.springframework.data.mongodb.repository.Person.class);
|
||||
MongoParameters parameters = new MongoParameters(PersonRepository.class.getMethod("findByLocationNearAndFirstname", Point.class, Distance.class, String.class));
|
||||
MongoParameterAccessor accessor = new MongoParametersParameterAccessor(parameters, new Object[] { point, distance, "Dave" });
|
||||
|
||||
Query query = new MongoQueryCreator(tree, new ConvertingParameterAccessor(converter, accessor)).createQuery();
|
||||
assertThat(query.getQueryObject(), is(query.getQueryObject()));
|
||||
|
||||
private void assertBindsDistanceToQuery(Point point, Distance distance, Query reference) throws Exception {
|
||||
|
||||
when(converter.convertToMongoType("Dave")).thenReturn("Dave");
|
||||
|
||||
PartTree tree = new PartTree("findByLocationNearAndFirstname",
|
||||
org.springframework.data.mongodb.repository.Person.class);
|
||||
Method method = PersonRepository.class.getMethod("findByLocationNearAndFirstname", Point.class, Distance.class,
|
||||
String.class);
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, new DefaultRepositoryMetadata(PersonRepository.class),
|
||||
new EntityInformationCreator(new MongoMappingContext()));
|
||||
MongoParameterAccessor accessor = new MongoParametersParameterAccessor(queryMethod, new Object[] { point, distance,
|
||||
"Dave" });
|
||||
|
||||
Query query = new MongoQueryCreator(tree, new ConvertingParameterAccessor(converter, accessor)).createQuery();
|
||||
assertThat(query.getQueryObject(), is(query.getQueryObject()));
|
||||
|
||||
}
|
||||
|
||||
interface PersonRepository {
|
||||
|
||||
|
||||
interface PersonRepository extends Repository<Person, Long> {
|
||||
|
||||
List<Person> findByLocationNearAndFirstname(Point location, Distance maxDistance, String firstname);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,15 +15,22 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.repository;
|
||||
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.mongodb.core.User;
|
||||
import org.springframework.data.mongodb.core.geo.Distance;
|
||||
import org.springframework.data.mongodb.core.geo.GeoPage;
|
||||
import org.springframework.data.mongodb.core.geo.GeoResult;
|
||||
import org.springframework.data.mongodb.core.geo.GeoResults;
|
||||
import org.springframework.data.mongodb.core.geo.Point;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.repository.MongoRepositoryFactoryBean.EntityInformationCreator;
|
||||
import org.springframework.data.repository.Repository;
|
||||
@@ -31,13 +38,13 @@ import org.springframework.data.repository.core.support.DefaultRepositoryMetadat
|
||||
|
||||
/**
|
||||
* Unit test for {@link MongoQueryMethod}.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class MongoQueryMethodUnitTests {
|
||||
|
||||
|
||||
EntityInformationCreator creator;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MongoMappingContext context = new MongoMappingContext();
|
||||
@@ -46,36 +53,91 @@ public class MongoQueryMethodUnitTests {
|
||||
|
||||
@Test
|
||||
public void detectsCollectionFromRepoTypeIfReturnTypeNotAssignable() throws Exception {
|
||||
|
||||
|
||||
Method method = SampleRepository.class.getMethod("method");
|
||||
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, new DefaultRepositoryMetadata(SampleRepository.class), creator);
|
||||
MongoEntityInformation<?,?> entityInformation = queryMethod.getEntityInformation();
|
||||
|
||||
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, new DefaultRepositoryMetadata(SampleRepository.class),
|
||||
creator);
|
||||
MongoEntityInformation<?, ?> entityInformation = queryMethod.getEntityInformation();
|
||||
|
||||
assertThat(entityInformation.getJavaType(), is(typeCompatibleWith(Address.class)));
|
||||
assertThat(entityInformation.getCollectionName(), is("contact"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void detectsCollectionFromReturnTypeIfReturnTypeAssignable() throws Exception {
|
||||
|
||||
|
||||
Method method = SampleRepository2.class.getMethod("method");
|
||||
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, new DefaultRepositoryMetadata(SampleRepository.class), creator);
|
||||
MongoEntityInformation<?,?> entityInformation = queryMethod.getEntityInformation();
|
||||
|
||||
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, new DefaultRepositoryMetadata(SampleRepository.class),
|
||||
creator);
|
||||
MongoEntityInformation<?, ?> entityInformation = queryMethod.getEntityInformation();
|
||||
|
||||
assertThat(entityInformation.getJavaType(), is(typeCompatibleWith(Person.class)));
|
||||
assertThat(entityInformation.getCollectionName(), is("person"));
|
||||
}
|
||||
|
||||
|
||||
interface SampleRepository extends Repository<Contact, Long> {
|
||||
|
||||
@Test
|
||||
public void discoversUserAsDomainTypeForGeoPageQueryMethod() throws Exception {
|
||||
|
||||
MongoQueryMethod queryMethod = queryMethod("findByLocationNear", Point.class, Distance.class, Pageable.class);
|
||||
assertThat(queryMethod.isGeoNearQuery(), is(true));
|
||||
assertThat(queryMethod.isPageQuery(), is(true));
|
||||
|
||||
List<Address> method();
|
||||
queryMethod = queryMethod("findByFirstname", String.class, Point.class);
|
||||
assertThat(queryMethod.isGeoNearQuery(), is(true));
|
||||
assertThat(queryMethod.isPageQuery(), is(false));
|
||||
assertThat(queryMethod.getEntityInformation().getJavaType(), is(typeCompatibleWith(User.class)));
|
||||
|
||||
assertThat(queryMethod("findByEmailAddress", String.class, Point.class).isGeoNearQuery(), is(true));
|
||||
assertThat(queryMethod("findByFirstname", String.class, Point.class).isGeoNearQuery(), is(true));
|
||||
assertThat(queryMethod("findByLastname", String.class, Point.class).isGeoNearQuery(), is(true));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsGeoPageQueryWithoutPageable() throws Exception {
|
||||
queryMethod("findByLocationNear", Point.class, Distance.class);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsNullEntityCreator() throws Exception {
|
||||
Method method = PersonRepository.class.getMethod("findByFirstname", String.class, Point.class);
|
||||
new MongoQueryMethod(method, new DefaultRepositoryMetadata(PersonRepository.class), null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void considersMethodReturningGeoPageAsPagingMethod() throws Exception {
|
||||
MongoQueryMethod method = queryMethod("findByLocationNear", Point.class, Distance.class, Pageable.class);
|
||||
assertThat(method.isPageQuery(), is(true));
|
||||
assertThat(method.isCollectionQuery(), is(false));
|
||||
}
|
||||
|
||||
private MongoQueryMethod queryMethod(String name, Class<?>... parameters) throws Exception {
|
||||
Method method = PersonRepository.class.getMethod(name, parameters);
|
||||
return new MongoQueryMethod(method, new DefaultRepositoryMetadata(PersonRepository.class), creator);
|
||||
}
|
||||
|
||||
interface PersonRepository extends Repository<User, Long> {
|
||||
|
||||
// Misses Pageable
|
||||
GeoPage<User> findByLocationNear(Point point, Distance distance);
|
||||
|
||||
GeoPage<User> findByLocationNear(Point point, Distance distance, Pageable pageable);
|
||||
|
||||
GeoResult<User> findByEmailAddress(String lastname, Point location);
|
||||
|
||||
GeoResults<User> findByFirstname(String firstname, Point location);
|
||||
|
||||
Collection<GeoResult<User>> findByLastname(String lastname, Point location);
|
||||
}
|
||||
|
||||
interface SampleRepository extends Repository<Contact, Long> {
|
||||
|
||||
List<Address> method();
|
||||
}
|
||||
|
||||
interface SampleRepository2 extends Repository<Contact, Long> {
|
||||
|
||||
|
||||
List<Person> method();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.mongodb.core.geo.Box;
|
||||
import org.springframework.data.mongodb.core.geo.Circle;
|
||||
import org.springframework.data.mongodb.core.geo.Distance;
|
||||
import org.springframework.data.mongodb.core.geo.GeoResults;
|
||||
import org.springframework.data.mongodb.core.geo.Point;
|
||||
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||
import org.springframework.data.mongodb.repository.Query;
|
||||
@@ -140,4 +142,6 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
|
||||
List<Person> findBySex(Sex sex);
|
||||
|
||||
List<Person> findByNamedQuery(String firstname);
|
||||
|
||||
GeoResults<Person> findByLocationNear(Point point, Distance maxDistance);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.mongodb.core.convert.MongoWriter;
|
||||
import org.springframework.data.mongodb.core.geo.Distance;
|
||||
import org.springframework.data.mongodb.core.geo.Point;
|
||||
import org.springframework.data.mongodb.repository.ConvertingParameterAccessor;
|
||||
import org.springframework.data.repository.query.ParameterAccessor;
|
||||
|
||||
@@ -92,4 +93,11 @@ class StubParameterAccessor implements MongoParameterAccessor {
|
||||
public Iterator<Object> iterator() {
|
||||
return Arrays.asList(values).iterator();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.repository.MongoParameterAccessor#getGeoNearLocation()
|
||||
*/
|
||||
public Point getGeoNearLocation() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -285,6 +285,66 @@ public class PersonRepositoryTests {
|
||||
</tgroup>
|
||||
</table></para>
|
||||
|
||||
<section>
|
||||
<title>Geo-spatial repository queries</title>
|
||||
|
||||
<para>As you've just seen there are a few keywords triggering
|
||||
geo-spatial operations within a MongoDB query. The <code>Near</code>
|
||||
keyword allows some further modification. Let's have look at some
|
||||
examples:</para>
|
||||
|
||||
<example>
|
||||
<title>Advanced <code>Near</code> queries</title>
|
||||
|
||||
<programlisting language="java">public interface PersonRepository extends MongoRepository<Person, String>
|
||||
|
||||
// { 'location' : { '$near' : [point.x, point.y], '$maxDistance' : distance}}
|
||||
List<Person> findByLocationNear(Point location, Distance distance);
|
||||
}</programlisting>
|
||||
</example>
|
||||
|
||||
<para>Adding a <classname>Distance</classname> parameter to the query
|
||||
method allows restricting results to those within the given distance. If
|
||||
the <classname>Distance</classname> was set up containing a
|
||||
<interfacename>Metric</interfacename> we will transparently use
|
||||
<code>$nearSphere</code> instead of $code.</para>
|
||||
|
||||
<example>
|
||||
<title>Using <code>Distance</code> with <code>Metrics</code></title>
|
||||
|
||||
<programlisting language="java">Point point = new Point(43.7, 48.8);
|
||||
Distance distance = new Distance(200, Metrics.KILOMETERS);
|
||||
… = repository.findByLocationNear(point, distance);
|
||||
// {'location' : {'$nearSphere' : [43.7, 48.8], '$maxDistance' : 0.03135711885774796}}</programlisting>
|
||||
</example>
|
||||
|
||||
<para>As you can see using a <classname>Distance</classname> equipped
|
||||
with a <interfacename>Metric</interfacename> causes
|
||||
<code>$nearSphere</code> clause to be added instead of a plain
|
||||
<code>$near</code>. Beyond that the actual distance gets calculated
|
||||
according to the <classname>Metrics</classname> used.</para>
|
||||
|
||||
<simplesect>
|
||||
<title>Geo-near queries</title>
|
||||
|
||||
<para></para>
|
||||
|
||||
<programlisting language="java">public interface PersonRepository extends MongoRepository<Person, String>
|
||||
|
||||
// {'geoNear' : 'location', 'near' : [x, y] }
|
||||
GeoResults<Person> findByLocationNear(Point location);
|
||||
|
||||
// No metric: {'geoNear' : 'person', 'near' : [x, y], maxDistance : distance }
|
||||
// Metric: {'geoNear' : 'person', 'near' : [x, y], 'maxDistance' : distance,
|
||||
// 'distanceMultiplier' : metric.multiplier, 'spherical' : true }
|
||||
GeoResults<Person> findByLocationNear(Point location, Distance distance);
|
||||
|
||||
// {'geoNear' : 'location', 'near' : [x, y] }
|
||||
GeoResults<Person> findByLocationNear(Point location);
|
||||
}</programlisting>
|
||||
</simplesect>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Mongo JSON based query methods and field restriction</title>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user