DATAES-89 - implements missing "within" in ElasticsearchQueryCreator

This commit is contained in:
Franck MARCHAND
2014-06-03 01:13:40 +02:00
parent cb7983120e
commit 5faf54b67c
7 changed files with 159 additions and 9 deletions

View File

@@ -29,6 +29,9 @@ import org.elasticsearch.index.query.GeoDistanceFilterBuilder;
import org.springframework.data.elasticsearch.core.geo.GeoBox;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
import org.springframework.util.Assert;
/**
@@ -106,20 +109,31 @@ class CriteriaFilterProcessor {
Object[] valArray = (Object[]) value;
Assert.noNullElements(valArray, "Geo distance filter takes 2 not null elements array as parameter.");
Assert.isTrue(valArray.length == 2, "Geo distance filter takes a 2-elements array as parameter.");
Assert.isTrue(valArray[0] instanceof GeoPoint || valArray[0] instanceof String, "First element of a geo distance filter must be a GeoLocation or String");
Assert.isTrue(valArray[1] instanceof String, "Second element of a geo distance filter must be a String");
Assert.isTrue(valArray[0] instanceof GeoPoint || valArray[0] instanceof String || valArray[0] instanceof Point, "First element of a geo distance filter must be a GeoPoint, a Point or a String");
Assert.isTrue(valArray[1] instanceof String || valArray[1] instanceof Distance, "Second element of a geo distance filter must be a String or a Distance");
String dist = (String) valArray[1];
if (valArray[0] instanceof GeoPoint) {
GeoPoint loc = (GeoPoint) valArray[0];
((GeoDistanceFilterBuilder) filter).lat(loc.getLat()).lon(loc.getLon()).distance(dist);
StringBuilder dist = new StringBuilder();
if(valArray[1] instanceof Distance) {
extractDistanceString((Distance)valArray[1], dist);
} else {
dist.append((String) valArray[1]);
}
if (valArray[0] instanceof GeoPoint) {
GeoPoint loc = (GeoPoint) valArray[0];
((GeoDistanceFilterBuilder) filter).lat(loc.getLat()).lon(loc.getLon()).distance(dist.toString());
} else if (valArray[0] instanceof Point) {
GeoPoint loc = GeoPoint.fromPoint((Point)valArray[0]);
((GeoDistanceFilterBuilder) filter).lat(loc.getLat()).lon(loc.getLon()).distance(dist.toString());
} else {
String loc = (String) valArray[0];
if (loc.contains(",")) {
String c[] = loc.split(",");
((GeoDistanceFilterBuilder) filter).lat(Double.parseDouble(c[0])).lon(Double.parseDouble(c[1])).distance(dist);
((GeoDistanceFilterBuilder) filter).lat(Double.parseDouble(c[0])).lon(Double.parseDouble(c[1])).distance(dist.toString());
} else {
((GeoDistanceFilterBuilder) filter).geohash(loc).distance(dist);
((GeoDistanceFilterBuilder) filter).geohash(loc).distance(dist.toString());
}
}
@@ -151,7 +165,30 @@ class CriteriaFilterProcessor {
return filter;
}
private void oneParameterBBox(GeoBoundingBoxFilterBuilder filter, Object value) {
/**
* extract the distance string from a {@link org.springframework.data.geo.Distance} object.
*
* @param distance distance object to extract string from
* @param sb StringBuilder to build the distance string
*/
private void extractDistanceString(Distance distance, StringBuilder sb) {
// handle Distance object
sb.append((int) distance.getValue());
Metrics metric = (Metrics) distance.getMetric();
switch (metric) {
case KILOMETERS :
sb.append("km");
break;
case MILES:
sb.append("mi");
break;
}
}
private void oneParameterBBox(GeoBoundingBoxFilterBuilder filter, Object value) {
Assert.isTrue(value instanceof GeoBox, "single-element of boundedBy filter must be type of GeoBox");
GeoBox geoBBox = (GeoBox) value;
filter.topLeft(geoBBox.getTopLeft().getLat(), geoBBox.getTopLeft().getLon());

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.elasticsearch.core.geo;
import org.springframework.data.geo.Point;
/**
* geo-location used for #{@link org.springframework.data.elasticsearch.core.query.Criteria}.
*
@@ -41,4 +43,16 @@ public class GeoPoint {
public double getLon() {
return lon;
}
/**
* build a GeoPoint from a {@link org.springframework.data.geo.Point}
*
* @param point {@link org.springframework.data.geo.Point}
* @return a {@link org.springframework.data.elasticsearch.core.geo.GeoPoint}
*/
public static GeoPoint fromPoint(Point point) {
return new GeoPoint(point.getY(), point.getX());
}
}

View File

@@ -21,6 +21,8 @@ import org.apache.commons.lang.StringUtils;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.elasticsearch.core.geo.GeoBox;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
import org.springframework.util.Assert;
/**
@@ -360,6 +362,21 @@ public class Criteria {
return this;
}
/**
* Creates new CriteriaEntry for {@code location WITHIN distance}
*
* @param location {@link org.springframework.data.geo.Point} center coordinates
* @param distance {@link org.springframework.data.geo.Distance} radius
* .
* @return Criteria the chaind criteria with the new 'within' criteria included.
*/
public Criteria within(Point location, Distance distance) {
Assert.notNull(location, "Location value for near criteria must not be null");
Assert.notNull(location, "Distance value for near criteria must not be null");
filterCriteria.add(new CriteriaEntry(OperationKey.WITHIN, new Object[]{location, distance}));
return this;
}
/**
* Creates new CriteriaEntry for {@code geoLocation WITHIN distance}
*

View File

@@ -20,9 +20,12 @@ import java.util.Iterator;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.context.PersistentPropertyPath;
import org.springframework.data.repository.query.ParameterAccessor;
@@ -120,6 +123,21 @@ public class ElasticsearchQueryCreator extends AbstractQueryCreator<CriteriaQuer
return criteria.in(asArray(parameters.next()));
case NOT_IN:
return criteria.in(asArray(parameters.next())).not();
case WITHIN: {
Object firstParameter = parameters.next();
Object secondParameter = parameters.next();
if(firstParameter instanceof GeoPoint && secondParameter instanceof String)
return criteria.within((GeoPoint)firstParameter, (String)secondParameter);
if(firstParameter instanceof Point && secondParameter instanceof Distance)
return criteria.within((Point)firstParameter, (Distance)secondParameter);
if(firstParameter instanceof String && secondParameter instanceof String)
return criteria.within((String)firstParameter, (String)secondParameter);
}
default:
throw new InvalidDataAccessApiUsageException("Illegal criteria found '" + type + "'.");
}