mirror of
https://github.com/spring-projects/spring-data-elasticsearch.git
synced 2025-06-14 08:02:11 +00:00
DATAES-89 - implements missing "within" in ElasticsearchQueryCreator
This commit is contained in:
parent
cb7983120e
commit
5faf54b67c
@ -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());
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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}
|
||||
*
|
||||
|
@ -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 + "'.");
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import org.apache.commons.lang.builder.HashCodeBuilder;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.Version;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
|
||||
|
||||
/**
|
||||
* @author Rizwan Idrees
|
||||
@ -35,6 +36,9 @@ public class SampleEntity {
|
||||
private int rate;
|
||||
private boolean available;
|
||||
private String highlightedMessage;
|
||||
|
||||
private GeoPoint location;
|
||||
|
||||
@Version
|
||||
private Long version;
|
||||
|
||||
@ -86,6 +90,14 @@ public class SampleEntity {
|
||||
this.highlightedMessage = highlightedMessage;
|
||||
}
|
||||
|
||||
public GeoPoint getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public void setLocation(GeoPoint location) {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public Long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
@ -30,8 +30,12 @@ import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
|
||||
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
|
||||
import org.springframework.data.elasticsearch.entities.SampleEntity;
|
||||
import org.springframework.data.elasticsearch.repositories.custom.SampleCustomMethodRepository;
|
||||
import org.springframework.data.geo.Distance;
|
||||
import org.springframework.data.geo.Metrics;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
@ -53,6 +57,7 @@ public class CustomMethodRepositoryTests {
|
||||
public void before() {
|
||||
elasticsearchTemplate.deleteIndex(SampleEntity.class);
|
||||
elasticsearchTemplate.createIndex(SampleEntity.class);
|
||||
elasticsearchTemplate.putMapping(SampleEntity.class);
|
||||
elasticsearchTemplate.refresh(SampleEntity.class, true);
|
||||
}
|
||||
|
||||
@ -486,4 +491,44 @@ public class CustomMethodRepositoryTests {
|
||||
assertThat(sampleEntities.isEmpty(), is(false));
|
||||
assertThat(sampleEntities.size(), is(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldExecuteCustomMethodWithWithinGeoPoint() {
|
||||
// given
|
||||
String documentId = randomNumeric(5);
|
||||
SampleEntity sampleEntity = new SampleEntity();
|
||||
sampleEntity.setId(documentId);
|
||||
sampleEntity.setType("test");
|
||||
sampleEntity.setRate(10);
|
||||
sampleEntity.setMessage("foo");
|
||||
sampleEntity.setLocation(new GeoPoint(45.7806d, 3.0875d));
|
||||
|
||||
repository.save(sampleEntity);
|
||||
|
||||
// when
|
||||
Page<SampleEntity> page = repository.findByLocationWithin(new GeoPoint(45.7806d, 3.0875d), "2km", new PageRequest(0, 10));
|
||||
// then
|
||||
assertThat(page, is(notNullValue()));
|
||||
assertThat(page.getTotalElements(), is(equalTo(1L)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldExecuteCustomMethodWithWithinPoint() {
|
||||
// given
|
||||
String documentId = randomNumeric(5);
|
||||
SampleEntity sampleEntity = new SampleEntity();
|
||||
sampleEntity.setId(documentId);
|
||||
sampleEntity.setType("test");
|
||||
sampleEntity.setRate(10);
|
||||
sampleEntity.setMessage("foo");
|
||||
sampleEntity.setLocation(new GeoPoint(45.7806d, 3.0875d));
|
||||
|
||||
repository.save(sampleEntity);
|
||||
|
||||
// when
|
||||
Page<SampleEntity> page = repository.findByLocationWithin(new Point(3.0875d, 45.7806d), new Distance(2, Metrics.KILOMETERS), new PageRequest(0, 10));
|
||||
// then
|
||||
assertThat(page, is(notNullValue()));
|
||||
assertThat(page.getTotalElements(), is(equalTo(1L)));
|
||||
}
|
||||
}
|
||||
|
@ -20,8 +20,11 @@ import java.util.List;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.elasticsearch.annotations.Query;
|
||||
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
|
||||
import org.springframework.data.elasticsearch.entities.SampleEntity;
|
||||
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
|
||||
import org.springframework.data.geo.Distance;
|
||||
import org.springframework.data.geo.Point;
|
||||
|
||||
/**
|
||||
* @author Rizwan Idrees
|
||||
@ -64,4 +67,8 @@ public interface SampleCustomMethodRepository extends ElasticsearchRepository<Sa
|
||||
Page<SampleEntity> findByAvailableFalse(Pageable pageable);
|
||||
|
||||
Page<SampleEntity> findByMessageOrderByTypeAsc(String message, Pageable pageable);
|
||||
|
||||
Page<SampleEntity> findByLocationWithin(GeoPoint point, String distance, Pageable pageable);
|
||||
|
||||
Page<SampleEntity> findByLocationWithin(Point point, Distance distance, Pageable pageable);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user