mirror of
https://github.com/spring-projects/spring-data-elasticsearch.git
synced 2025-06-16 17:12:10 +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.GeoBox;
|
||||||
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
|
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
|
||||||
import org.springframework.data.elasticsearch.core.query.Criteria;
|
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;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -106,20 +109,31 @@ class CriteriaFilterProcessor {
|
|||||||
Object[] valArray = (Object[]) value;
|
Object[] valArray = (Object[]) value;
|
||||||
Assert.noNullElements(valArray, "Geo distance filter takes 2 not null elements array as parameter.");
|
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.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[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, "Second element of a geo distance filter must be 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");
|
||||||
|
|
||||||
|
|
||||||
|
StringBuilder dist = new StringBuilder();
|
||||||
|
|
||||||
|
if(valArray[1] instanceof Distance) {
|
||||||
|
extractDistanceString((Distance)valArray[1], dist);
|
||||||
|
} else {
|
||||||
|
dist.append((String) valArray[1]);
|
||||||
|
}
|
||||||
|
|
||||||
String dist = (String) valArray[1];
|
|
||||||
if (valArray[0] instanceof GeoPoint) {
|
if (valArray[0] instanceof GeoPoint) {
|
||||||
GeoPoint loc = (GeoPoint) valArray[0];
|
GeoPoint loc = (GeoPoint) valArray[0];
|
||||||
((GeoDistanceFilterBuilder) filter).lat(loc.getLat()).lon(loc.getLon()).distance(dist);
|
((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 {
|
} else {
|
||||||
String loc = (String) valArray[0];
|
String loc = (String) valArray[0];
|
||||||
if (loc.contains(",")) {
|
if (loc.contains(",")) {
|
||||||
String c[] = loc.split(",");
|
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 {
|
} else {
|
||||||
((GeoDistanceFilterBuilder) filter).geohash(loc).distance(dist);
|
((GeoDistanceFilterBuilder) filter).geohash(loc).distance(dist.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,6 +165,29 @@ class CriteriaFilterProcessor {
|
|||||||
return filter;
|
return filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
private void oneParameterBBox(GeoBoundingBoxFilterBuilder filter, Object value) {
|
||||||
Assert.isTrue(value instanceof GeoBox, "single-element of boundedBy filter must be type of GeoBox");
|
Assert.isTrue(value instanceof GeoBox, "single-element of boundedBy filter must be type of GeoBox");
|
||||||
GeoBox geoBBox = (GeoBox) value;
|
GeoBox geoBBox = (GeoBox) value;
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.data.elasticsearch.core.geo;
|
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}.
|
* geo-location used for #{@link org.springframework.data.elasticsearch.core.query.Criteria}.
|
||||||
*
|
*
|
||||||
@ -41,4 +43,16 @@ public class GeoPoint {
|
|||||||
public double getLon() {
|
public double getLon() {
|
||||||
return lon;
|
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.dao.InvalidDataAccessApiUsageException;
|
||||||
import org.springframework.data.elasticsearch.core.geo.GeoBox;
|
import org.springframework.data.elasticsearch.core.geo.GeoBox;
|
||||||
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
|
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;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -360,6 +362,21 @@ public class Criteria {
|
|||||||
return this;
|
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}
|
* Creates new CriteriaEntry for {@code geoLocation WITHIN distance}
|
||||||
*
|
*
|
||||||
|
@ -20,9 +20,12 @@ import java.util.Iterator;
|
|||||||
|
|
||||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||||
import org.springframework.data.domain.Sort;
|
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.mapping.ElasticsearchPersistentProperty;
|
||||||
import org.springframework.data.elasticsearch.core.query.Criteria;
|
import org.springframework.data.elasticsearch.core.query.Criteria;
|
||||||
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
|
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.MappingContext;
|
||||||
import org.springframework.data.mapping.context.PersistentPropertyPath;
|
import org.springframework.data.mapping.context.PersistentPropertyPath;
|
||||||
import org.springframework.data.repository.query.ParameterAccessor;
|
import org.springframework.data.repository.query.ParameterAccessor;
|
||||||
@ -120,6 +123,21 @@ public class ElasticsearchQueryCreator extends AbstractQueryCreator<CriteriaQuer
|
|||||||
return criteria.in(asArray(parameters.next()));
|
return criteria.in(asArray(parameters.next()));
|
||||||
case NOT_IN:
|
case NOT_IN:
|
||||||
return criteria.in(asArray(parameters.next())).not();
|
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:
|
default:
|
||||||
throw new InvalidDataAccessApiUsageException("Illegal criteria found '" + type + "'.");
|
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.Id;
|
||||||
import org.springframework.data.annotation.Version;
|
import org.springframework.data.annotation.Version;
|
||||||
import org.springframework.data.elasticsearch.annotations.Document;
|
import org.springframework.data.elasticsearch.annotations.Document;
|
||||||
|
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Rizwan Idrees
|
* @author Rizwan Idrees
|
||||||
@ -35,6 +36,9 @@ public class SampleEntity {
|
|||||||
private int rate;
|
private int rate;
|
||||||
private boolean available;
|
private boolean available;
|
||||||
private String highlightedMessage;
|
private String highlightedMessage;
|
||||||
|
|
||||||
|
private GeoPoint location;
|
||||||
|
|
||||||
@Version
|
@Version
|
||||||
private Long version;
|
private Long version;
|
||||||
|
|
||||||
@ -86,6 +90,14 @@ public class SampleEntity {
|
|||||||
this.highlightedMessage = highlightedMessage;
|
this.highlightedMessage = highlightedMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GeoPoint getLocation() {
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLocation(GeoPoint location) {
|
||||||
|
this.location = location;
|
||||||
|
}
|
||||||
|
|
||||||
public Long getVersion() {
|
public Long getVersion() {
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
@ -30,8 +30,12 @@ import org.springframework.data.domain.Page;
|
|||||||
import org.springframework.data.domain.PageRequest;
|
import org.springframework.data.domain.PageRequest;
|
||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
|
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.entities.SampleEntity;
|
||||||
import org.springframework.data.elasticsearch.repositories.custom.SampleCustomMethodRepository;
|
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.ContextConfiguration;
|
||||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
|
|
||||||
@ -53,6 +57,7 @@ public class CustomMethodRepositoryTests {
|
|||||||
public void before() {
|
public void before() {
|
||||||
elasticsearchTemplate.deleteIndex(SampleEntity.class);
|
elasticsearchTemplate.deleteIndex(SampleEntity.class);
|
||||||
elasticsearchTemplate.createIndex(SampleEntity.class);
|
elasticsearchTemplate.createIndex(SampleEntity.class);
|
||||||
|
elasticsearchTemplate.putMapping(SampleEntity.class);
|
||||||
elasticsearchTemplate.refresh(SampleEntity.class, true);
|
elasticsearchTemplate.refresh(SampleEntity.class, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -486,4 +491,44 @@ public class CustomMethodRepositoryTests {
|
|||||||
assertThat(sampleEntities.isEmpty(), is(false));
|
assertThat(sampleEntities.isEmpty(), is(false));
|
||||||
assertThat(sampleEntities.size(), is(1));
|
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.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.data.elasticsearch.annotations.Query;
|
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.entities.SampleEntity;
|
||||||
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
|
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
|
||||||
|
import org.springframework.data.geo.Distance;
|
||||||
|
import org.springframework.data.geo.Point;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Rizwan Idrees
|
* @author Rizwan Idrees
|
||||||
@ -64,4 +67,8 @@ public interface SampleCustomMethodRepository extends ElasticsearchRepository<Sa
|
|||||||
Page<SampleEntity> findByAvailableFalse(Pageable pageable);
|
Page<SampleEntity> findByAvailableFalse(Pageable pageable);
|
||||||
|
|
||||||
Page<SampleEntity> findByMessageOrderByTypeAsc(String message, 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