Skip to content

Commit 7e74ec6

Browse files
christophstroblodrotbohm
authored andcommitted
DATAMONGO-1110 - Add support for $minDistance.
We now support $minDistance for NearQuery and Criteria. Please keep in mind that minDistance is only available for MongoDB 2.6 and better and can only be combined with $near or $nearSphere operator depending on the defined index type. Usage of $minDistance with NearQuery is only possible when a 2dsphere index is present. We also make sure $minDistance operator gets correctly nested when using GeoJSON types. It is now possible to use a Range<Distance> parameter within the repository queries. This allows to define near queries like: findByLocationNear(Point point, Range<Distance> distances); The lower bound of the range is treated as the minimum distance while the upper one defines the maximum distance from the given point. In case a Distance parameter is provided it will serve as maxDistance. Original pull request: spring-projects#277.
1 parent b887fa7 commit 7e74ec6

21 files changed

+494
-29
lines changed

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

+24-5
Original file line numberDiff line numberDiff line change
@@ -441,14 +441,34 @@ public Criteria nearSphere(Point point) {
441441
*/
442442
public Criteria maxDistance(double maxDistance) {
443443

444-
if (createNearCriteriaForCommand("$near", maxDistance) || createNearCriteriaForCommand("$nearSphere", maxDistance)) {
444+
if (createNearCriteriaForCommand("$near", "$maxDistance", maxDistance)
445+
|| createNearCriteriaForCommand("$nearSphere", "$maxDistance", maxDistance)) {
445446
return this;
446447
}
447448

448449
criteria.put("$maxDistance", maxDistance);
449450
return this;
450451
}
451452

453+
/**
454+
* Creates a geospatial criterion using a {@literal $minDistance} operation, for use with {@literal $near} or
455+
* {@literal $nearSphere}.
456+
*
457+
* @param minDistance
458+
* @return
459+
* @since 1.7
460+
*/
461+
public Criteria minDistance(double minDistance) {
462+
463+
if (createNearCriteriaForCommand("$near", "$minDistance", minDistance)
464+
|| createNearCriteriaForCommand("$nearSphere", "$minDistance", minDistance)) {
465+
return this;
466+
}
467+
468+
criteria.put("$minDistance", minDistance);
469+
return this;
470+
}
471+
452472
/**
453473
* Creates a criterion using the {@literal $elemMatch} operator
454474
*
@@ -605,7 +625,7 @@ private void setValue(DBObject dbo, String key, Object value) {
605625
}
606626
}
607627

608-
private boolean createNearCriteriaForCommand(String command, double maxDistance) {
628+
private boolean createNearCriteriaForCommand(String command, String operation, double maxDistance) {
609629

610630
if (!criteria.containsKey(command)) {
611631
return false;
@@ -615,14 +635,13 @@ private boolean createNearCriteriaForCommand(String command, double maxDistance)
615635

616636
if (existingNearOperationValue instanceof DBObject) {
617637

618-
((DBObject) existingNearOperationValue).put("$maxDistance", maxDistance);
638+
((DBObject) existingNearOperationValue).put(operation, maxDistance);
619639

620640
return true;
621641

622642
} else if (existingNearOperationValue instanceof GeoJson) {
623643

624-
BasicDBObject dbo = new BasicDBObject("$geometry", existingNearOperationValue)
625-
.append("$maxDistance", maxDistance);
644+
BasicDBObject dbo = new BasicDBObject("$geometry", existingNearOperationValue).append(operation, maxDistance);
626645
criteria.put(command, dbo);
627646

628647
return true;

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

+74-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2011-2014 the original author or authors.
2+
* Copyright 2011-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -40,6 +40,7 @@ public final class NearQuery {
4040
private final Point point;
4141
private Query query;
4242
private Distance maxDistance;
43+
private Distance minDistance;
4344
private Metric metric;
4445
private boolean spherical;
4546
private Integer num;
@@ -211,6 +212,63 @@ public NearQuery maxDistance(Distance distance) {
211212
return this;
212213
}
213214

215+
/**
216+
* Sets the minimum distance results shall have from the configured origin. If a {@link Metric} was set before the
217+
* given value will be interpreted as being a value in that metric. E.g.
218+
*
219+
* <pre>
220+
* NearQuery query = near(10.0, 20.0, Metrics.KILOMETERS).minDistance(150);
221+
* </pre>
222+
*
223+
* Will set the minimum distance to 150 kilometers.
224+
*
225+
* @param minDistance
226+
* @return
227+
* @since 1.7
228+
*/
229+
public NearQuery minDistance(double minDistance) {
230+
return minDistance(new Distance(minDistance, getMetric()));
231+
}
232+
233+
/**
234+
* Sets the minimum distance supplied in a given metric. Will normalize the distance but not reconfigure the query's
235+
* result {@link Metric} if one was configured before.
236+
*
237+
* @param minDistance
238+
* @param metric must not be {@literal null}.
239+
* @return
240+
* @since 1.7
241+
*/
242+
public NearQuery minDistance(double minDistance, Metric metric) {
243+
244+
Assert.notNull(metric);
245+
return minDistance(new Distance(minDistance, metric));
246+
}
247+
248+
/**
249+
* Sets the minimum distance to the given {@link Distance}. Will set the returned {@link Metric} to be the one of the
250+
* given {@link Distance} if no {@link Metric} was set before.
251+
*
252+
* @param distance must not be {@literal null}.
253+
* @return
254+
* @since 1.7
255+
*/
256+
public NearQuery minDistance(Distance distance) {
257+
258+
Assert.notNull(distance);
259+
260+
if (distance.getMetric() != Metrics.NEUTRAL) {
261+
this.spherical(true);
262+
}
263+
264+
if (this.metric == null) {
265+
in(distance.getMetric());
266+
}
267+
268+
this.minDistance = distance;
269+
return this;
270+
}
271+
214272
/**
215273
* Returns the maximum {@link Distance}.
216274
*
@@ -220,6 +278,16 @@ public Distance getMaxDistance() {
220278
return this.maxDistance;
221279
}
222280

281+
/**
282+
* Returns the maximum {@link Distance}.
283+
*
284+
* @return
285+
* @since 1.7
286+
*/
287+
public Distance getMinDistance() {
288+
return this.minDistance;
289+
}
290+
223291
/**
224292
* Configures a {@link CustomMetric} with the given multiplier.
225293
*
@@ -352,7 +420,11 @@ public DBObject toDBObject() {
352420
}
353421

354422
if (maxDistance != null) {
355-
dbObject.put("maxDistance", this.maxDistance.getNormalizedValue());
423+
dbObject.put("maxDistance", maxDistance.getNormalizedValue());
424+
}
425+
426+
if (minDistance != null) {
427+
dbObject.put("minDistance", minDistance.getNormalizedValue());
356428
}
357429

358430
if (metric != null) {

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java

+5
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,11 @@ private GeoResults<Object> doExecuteQuery(Query query) {
366366
nearQuery.maxDistance(maxDistance).in(maxDistance.getMetric());
367367
}
368368

369+
Distance minDistance = accessor.getMinDistance();
370+
if (minDistance != null) {
371+
nearQuery.minDistance(minDistance).in(minDistance.getMetric());
372+
}
373+
369374
Pageable pageable = accessor.getPageable();
370375
if (pageable != null) {
371376
nearQuery.with(pageable);

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessor.java

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2011-2014 the original author or authors.
2+
* Copyright 2011-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -104,6 +104,15 @@ public Distance getMaxDistance() {
104104
return delegate.getMaxDistance();
105105
}
106106

107+
/*
108+
* (non-Javadoc)
109+
* @see org.springframework.data.mongodb.repository.query.MongoParameterAccessor#getMinDistance()
110+
*/
111+
@Override
112+
public Distance getMinDistance() {
113+
return delegate.getMinDistance();
114+
}
115+
107116
/*
108117
* (non-Javadoc)
109118
* @see org.springframework.data.mongodb.repository.MongoParameterAccessor#getGeoNearLocation()
@@ -252,4 +261,5 @@ public interface PotentiallyConvertingIterator extends Iterator<Object> {
252261
*/
253262
Object nextConverted(MongoPersistentProperty property);
254263
}
264+
255265
}

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameterAccessor.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2011-2014 the original author or authors.
2+
* Copyright 2011-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -50,4 +50,12 @@ public interface MongoParameterAccessor extends ParameterAccessor {
5050
* @since 1.6
5151
*/
5252
TextCriteria getFullText();
53+
54+
/**
55+
* Returns a {@link Distance} to be applied to {@literal $minDistance} for MongoDB geo queries.
56+
*
57+
* @return
58+
* @since 1.7
59+
*/
60+
Distance getMinDistance();
5361
}

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameters.java

+57-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2011-2014 the original author or authors.
2+
* Copyright 2011-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -36,7 +36,8 @@
3636
*/
3737
public class MongoParameters extends Parameters<MongoParameters, MongoParameter> {
3838

39-
private final Integer distanceIndex;
39+
private final Integer minDistanceIndex;
40+
private final Integer maxDistanceIndex;
4041
private final Integer fullTextIndex;
4142

4243
private Integer nearIndex;
@@ -51,9 +52,13 @@ public MongoParameters(Method method, boolean isGeoNearMethod) {
5152

5253
super(method);
5354
List<Class<?>> parameterTypes = Arrays.asList(method.getParameterTypes());
54-
this.distanceIndex = parameterTypes.indexOf(Distance.class);
55+
5556
this.fullTextIndex = parameterTypes.indexOf(TextCriteria.class);
5657

58+
int[] distances = distances(parameterTypes);
59+
this.minDistanceIndex = distances[0];
60+
this.maxDistanceIndex = distances[1];
61+
5762
if (this.nearIndex == null && isGeoNearMethod) {
5863
this.nearIndex = getNearIndex(parameterTypes);
5964
} else if (this.nearIndex == null) {
@@ -62,13 +67,14 @@ public MongoParameters(Method method, boolean isGeoNearMethod) {
6267
}
6368

6469
private MongoParameters(List<MongoParameter> parameters, Integer distanceIndex, Integer nearIndex,
65-
Integer fullTextIndex) {
70+
Integer fullTextIndex, Integer minDistanceIndex) {
6671

6772
super(parameters);
6873

69-
this.distanceIndex = distanceIndex;
74+
this.maxDistanceIndex = distanceIndex;
7075
this.nearIndex = nearIndex;
7176
this.fullTextIndex = fullTextIndex;
77+
this.minDistanceIndex = minDistanceIndex;
7278
}
7379

7480
private final int getNearIndex(List<Class<?>> parameterTypes) {
@@ -115,9 +121,21 @@ protected MongoParameter createParameter(MethodParameter parameter) {
115121
* Returns the index of a {@link Distance} parameter to be used for geo queries.
116122
*
117123
* @return
124+
* @deprecated since 1.7. Please use {@link #getMaxDistanceParameterIndex()} instead.
118125
*/
126+
@Deprecated
119127
public int getDistanceIndex() {
120-
return distanceIndex;
128+
return getMaxDistanceParameterIndex();
129+
}
130+
131+
/**
132+
* Returns the index of the {@link Distance} parameter to be used for max distance in geo queries.
133+
*
134+
* @return
135+
* @since 1.7
136+
*/
137+
public int getMaxDistanceParameterIndex() {
138+
return maxDistanceIndex;
121139
}
122140

123141
/**
@@ -147,13 +165,45 @@ public boolean hasFullTextParameter() {
147165
return this.fullTextIndex != null && this.fullTextIndex.intValue() >= 0;
148166
}
149167

168+
/**
169+
* @return
170+
* @since 1.7
171+
*/
172+
public boolean hasMinDistanceParameter() {
173+
return minDistanceIndex != null && minDistanceIndex.intValue() >= 0;
174+
}
175+
176+
/**
177+
* @return
178+
* @since 1.7
179+
*/
180+
public int getMinDistanceParameterIndex() {
181+
return minDistanceIndex != null ? minDistanceIndex.intValue() : -1;
182+
}
183+
150184
/*
151185
* (non-Javadoc)
152186
* @see org.springframework.data.repository.query.Parameters#createFrom(java.util.List)
153187
*/
154188
@Override
155189
protected MongoParameters createFrom(List<MongoParameter> parameters) {
156-
return new MongoParameters(parameters, this.distanceIndex, this.nearIndex, this.fullTextIndex);
190+
return new MongoParameters(parameters, this.maxDistanceIndex, this.nearIndex, this.fullTextIndex,
191+
this.minDistanceIndex);
192+
}
193+
194+
private int[] distances(List<Class<?>> paramTypes) {
195+
196+
int maxDistance = paramTypes.lastIndexOf(Distance.class);
197+
if (maxDistance == -1) {
198+
return new int[] { -1, -1 };
199+
}
200+
201+
int minDistance = paramTypes.indexOf(Distance.class);
202+
if (minDistance == maxDistance) {
203+
return new int[] { -1, maxDistance };
204+
}
205+
206+
return new int[] { minDistance, maxDistance };
157207
}
158208

159209
/**

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParametersParameterAccessor.java

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2011-2014 the original author or authors.
2+
* Copyright 2011-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -53,6 +53,16 @@ public Distance getMaxDistance() {
5353
return index == -1 ? null : (Distance) getValue(index);
5454
}
5555

56+
/*
57+
* (non-Javadoc)
58+
* @see org.springframework.data.mongodb.repository.query.MongoParameterAccessor#getMinDistance()
59+
*/
60+
@Override
61+
public Distance getMinDistance() {
62+
int index = method.getParameters().getMinDistanceParameterIndex();
63+
return index == -1 ? null : (Distance) getValue(index);
64+
}
65+
5666
/*
5767
* (non-Javadoc)
5868
* @see org.springframework.data.mongodb.repository.MongoParameterAccessor#getGeoNearLocation()

0 commit comments

Comments
 (0)