DATAES-12 Merge Geo Location from branch 'master' of https://github.com/fmarchand/spring-data-elasticsearch

This commit is contained in:
Artur Konczak
2013-06-13 17:31:14 +01:00
parent aa6a5e3753
commit 74a4035408
15 changed files with 796 additions and 253 deletions

View File

@@ -0,0 +1,148 @@
/*
* Copyright 2013 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.elasticsearch.core;
import org.elasticsearch.index.query.*;
import org.springframework.data.elasticsearch.core.geo.GeoBBox;
import org.springframework.data.elasticsearch.core.geo.GeoLocation;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.util.Assert;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import static org.elasticsearch.index.query.FilterBuilders.*;
import static org.springframework.data.elasticsearch.core.query.Criteria.OperationKey;
/**
* CriteriaFilterProcessor
*
* @author Franck Marchand
*/
class CriteriaFilterProcessor {
FilterBuilder createFilterFromCriteria(Criteria criteria) {
List<FilterBuilder> fbList = new LinkedList<FilterBuilder>();
FilterBuilder filter = null;
ListIterator<Criteria> chainIterator = criteria.getCriteriaChain().listIterator();
while (chainIterator.hasNext()) {
FilterBuilder fb = null;
Criteria chainedCriteria = chainIterator.next();
if(chainedCriteria.isOr()){
fb = orFilter(createFilterFragmentForCriteria(chainedCriteria).toArray(new FilterBuilder[]{ }));
fbList.add(fb);
}else if(chainedCriteria.isNegating()){
List<FilterBuilder> negationFilters = buildNegationFilter(criteria.getField().getName(), criteria.getFilterCriteriaEntries().iterator());
if(!negationFilters.isEmpty()) {
fbList.addAll(negationFilters);
}
}else {
fbList.addAll(createFilterFragmentForCriteria(chainedCriteria));
}
}
if(!fbList.isEmpty()) {
if(fbList.size() == 1) {
filter =fbList.get(0);
} else {
filter = andFilter(fbList.toArray(new FilterBuilder[]{ }));
}
}
return filter;
}
private List<FilterBuilder> createFilterFragmentForCriteria(Criteria chainedCriteria) {
Iterator<Criteria.CriteriaEntry> it = chainedCriteria.getFilterCriteriaEntries().iterator();
List<FilterBuilder> filterList = new LinkedList<FilterBuilder>();
String fieldName = chainedCriteria.getField().getName();
Assert.notNull(fieldName,"Unknown field");
FilterBuilder filter = null;
while (it.hasNext()){
Criteria.CriteriaEntry entry = it.next();
filter = processCriteriaEntry(entry.getKey(), entry.getValue(), fieldName);
filterList.add(filter);
}
return filterList;
}
private FilterBuilder processCriteriaEntry(OperationKey key, Object value, String fieldName) {
if (value == null) {
return null;
}
FilterBuilder filter = null;
switch (key){
case WITHIN: {
filter = geoDistanceFilter(fieldName);
Assert.isTrue(value instanceof Object[], "Value of a geo distance filter should be an array of two values.");
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 GeoLocation, "First element of a geo distance filter must be a GeoLocation");
Assert.isTrue(valArray[1] instanceof String, "Second element of a geo distance filter must be a String");
GeoLocation loc = (GeoLocation)valArray[0];
String dist = (String)valArray[1];
((GeoDistanceFilterBuilder)filter).lat(loc.getLat()).lon(loc.getLon()).distance(dist);
break;
}
case BBOX: {
filter = geoBoundingBoxFilter(fieldName);
Assert.isTrue(value instanceof Object[], "Value of a geo distance filter should be an array of two values.");
Object[] valArray = (Object[]) value;
Assert.noNullElements(valArray, "Geo bbox filter takes a not null element array as parameter.");
Assert.isTrue(valArray.length == 1, "Geo distance filter takes a 1-elements array as parameter.");
Assert.isTrue(valArray[0] instanceof GeoBBox, "single-element of a geo bbox filter must be a GeoBBox");
GeoBBox geoBBox = (GeoBBox)valArray[0];
((GeoBoundingBoxFilterBuilder)filter).topLeft(geoBBox.getTopLeft().getLat(), geoBBox.getTopLeft().getLon());
((GeoBoundingBoxFilterBuilder)filter).bottomRight(geoBBox.getBottomRight().getLat(), geoBBox.getBottomRight().getLon());
break;
}
}
return filter;
}
private List<FilterBuilder> buildNegationFilter(String fieldName, Iterator<Criteria.CriteriaEntry> it){
List<FilterBuilder> notFilterList = new LinkedList<FilterBuilder>();
while (it.hasNext()){
Criteria.CriteriaEntry criteriaEntry = it.next();
FilterBuilder notFilter = notFilter(processCriteriaEntry(criteriaEntry.getKey(), criteriaEntry.getValue(), fieldName));
notFilterList.add(notFilter);
}
return notFilterList;
}
}

View File

@@ -22,6 +22,8 @@ import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.util.Assert;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import static org.elasticsearch.index.query.QueryBuilders.*;
@@ -32,106 +34,141 @@ import static org.springframework.data.elasticsearch.core.query.Criteria.Operati
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Franck Marchand
*/
class CriteriaQueryProcessor {
QueryBuilder createQueryFromCriteria(Criteria criteria) {
BoolQueryBuilder query = boolQuery();
ListIterator<Criteria> chainIterator = criteria.getCriteriaChain().listIterator();
while (chainIterator.hasNext()) {
Criteria chainedCriteria = chainIterator.next();
if (chainedCriteria.isOr()) {
query.should(createQueryFragmentForCriteria(chainedCriteria));
} else if (chainedCriteria.isNegating()) {
query.mustNot(createQueryFragmentForCriteria(chainedCriteria));
} else {
query.must(createQueryFragmentForCriteria(chainedCriteria));
}
}
return query;
}
QueryBuilder createQueryFromCriteria(Criteria criteria) {
if(criteria == null)
return null;
private QueryBuilder createQueryFragmentForCriteria(Criteria chainedCriteria) {
Iterator<Criteria.CriteriaEntry> it = chainedCriteria.getCriteriaEntries().iterator();
boolean singeEntryCriteria = (chainedCriteria.getCriteriaEntries().size() == 1);
List<QueryBuilder> shouldQueryBuilderList = new LinkedList<QueryBuilder>();
List<QueryBuilder> mustNotQueryBuilderList = new LinkedList<QueryBuilder>();
List<QueryBuilder> mustQueryBuilderList = new LinkedList<QueryBuilder>();
String fieldName = chainedCriteria.getField().getName();
Assert.notNull(fieldName, "Unknown field");
QueryBuilder query = null;
if (singeEntryCriteria) {
Criteria.CriteriaEntry entry = it.next();
query = processCriteriaEntry(entry.getKey(), entry.getValue(), fieldName);
} else {
query = boolQuery();
while (it.hasNext()) {
Criteria.CriteriaEntry entry = it.next();
((BoolQueryBuilder) query).must(processCriteriaEntry(entry.getKey(), entry.getValue(), fieldName));
}
}
ListIterator<Criteria> chainIterator = criteria.getCriteriaChain().listIterator();
while (chainIterator.hasNext()) {
Criteria chainedCriteria = chainIterator.next();
QueryBuilder queryFragmentForCriteria = createQueryFragmentForCriteria(chainedCriteria);
addBoost(query, chainedCriteria.getBoost());
return query;
}
if(queryFragmentForCriteria!=null) {
if(chainedCriteria.isOr()){
shouldQueryBuilderList.add(queryFragmentForCriteria);
}else if(chainedCriteria.isNegating()){
mustNotQueryBuilderList.add(queryFragmentForCriteria);
}else{
mustQueryBuilderList.add(queryFragmentForCriteria);
}
}
}
private QueryBuilder processCriteriaEntry(OperationKey key, Object value, String fieldName) {
if (value == null) {
return null;
}
QueryBuilder query = null;
BoolQueryBuilder query = null;
switch (key) {
case EQUALS:
query = fieldQuery(fieldName, value);
break;
case CONTAINS:
query = fieldQuery(fieldName, "*" + value + "*").analyzeWildcard(true);
break;
case STARTS_WITH:
query = fieldQuery(fieldName, value + "*").analyzeWildcard(true);
break;
case ENDS_WITH:
query = fieldQuery(fieldName, "*" + value).analyzeWildcard(true);
break;
case EXPRESSION:
query = queryString((String) value).field(fieldName);
break;
case BETWEEN:
Object[] ranges = (Object[]) value;
query = rangeQuery(fieldName).from(ranges[0]).to(ranges[1]);
break;
case FUZZY:
query = fuzzyQuery(fieldName, (String) value);
break;
case IN:
query = boolQuery();
Iterable<Object> collection = (Iterable<Object>) value;
for (Object item : collection) {
((BoolQueryBuilder) query).should(fieldQuery(fieldName, item));
}
break;
}
if(!shouldQueryBuilderList.isEmpty() || !mustNotQueryBuilderList.isEmpty() || !mustQueryBuilderList.isEmpty()) {
return query;
}
query = boolQuery();
private QueryBuilder buildNegationQuery(String fieldName, Iterator<Criteria.CriteriaEntry> it) {
BoolQueryBuilder notQuery = boolQuery();
while (it.hasNext()) {
notQuery.mustNot(fieldQuery(fieldName, it.next().getValue()));
}
return notQuery;
}
for(QueryBuilder qb : shouldQueryBuilderList) {
query.should(qb);
}
for(QueryBuilder qb : mustNotQueryBuilderList) {
query.mustNot(qb);
}
for(QueryBuilder qb : mustQueryBuilderList) {
query.must(qb);
}
}
private void addBoost(QueryBuilder query, float boost) {
if (Float.isNaN(boost)) {
return;
}
if (query instanceof BoostableQueryBuilder) {
((BoostableQueryBuilder) query).boost(boost);
}
return query;
}
}
private QueryBuilder createQueryFragmentForCriteria(Criteria chainedCriteria) {
if(chainedCriteria.getQueryCriteriaEntries().isEmpty())
return null;
Iterator<Criteria.CriteriaEntry> it = chainedCriteria.getQueryCriteriaEntries().iterator();
boolean singeEntryCriteria = (chainedCriteria.getQueryCriteriaEntries().size() == 1);
String fieldName = chainedCriteria.getField().getName();
Assert.notNull(fieldName,"Unknown field");
QueryBuilder query = null;
if(singeEntryCriteria){
Criteria.CriteriaEntry entry = it.next();
query = processCriteriaEntry(entry.getKey(), entry.getValue(), fieldName);
}else{
query = boolQuery();
while (it.hasNext()){
Criteria.CriteriaEntry entry = it.next();
((BoolQueryBuilder)query).must(processCriteriaEntry(entry.getKey(), entry.getValue(), fieldName));
}
}
addBoost(query, chainedCriteria.getBoost());
return query;
}
private QueryBuilder processCriteriaEntry(OperationKey key, Object value, String fieldName) {
if (value == null) {
return null;
}
QueryBuilder query = null;
switch (key) {
case EQUALS:
query = fieldQuery(fieldName, value);
break;
case CONTAINS:
query = fieldQuery(fieldName, "*" + value + "*").analyzeWildcard(true);
break;
case STARTS_WITH:
query = fieldQuery(fieldName, value + "*").analyzeWildcard(true);
break;
case ENDS_WITH:
query = fieldQuery(fieldName, "*" + value).analyzeWildcard(true);
break;
case EXPRESSION:
query = queryString((String) value).field(fieldName);
break;
case BETWEEN:
Object[] ranges = (Object[]) value;
query = rangeQuery(fieldName).from(ranges[0]).to(ranges[1]);
break;
case FUZZY:
query = fuzzyQuery(fieldName, (String) value);
break;
case IN:
query = boolQuery();
Iterable<Object> collection = (Iterable<Object>) value;
for (Object item : collection) {
((BoolQueryBuilder) query).should(fieldQuery(fieldName, item));
}
break;
}
return query;
}
private QueryBuilder buildNegationQuery(String fieldName, Iterator<Criteria.CriteriaEntry> it) {
BoolQueryBuilder notQuery = boolQuery();
while (it.hasNext()) {
notQuery.mustNot(fieldQuery(fieldName, it.next().getValue()));
}
return notQuery;
}
private void addBoost(QueryBuilder query, float boost) {
if (Float.isNaN(boost)) {
return;
}
if (query instanceof BoostableQueryBuilder) {
((BoostableQueryBuilder) query).boost(boost);
}
}
}

View File

@@ -34,7 +34,9 @@ import org.elasticsearch.client.Requests;
import org.elasticsearch.common.collect.MapBuilder;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.query.FilterBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.facet.Facet;
import org.elasticsearch.search.facet.FacetBuilder;
@@ -180,8 +182,21 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
@Override
public <T> Page<T> queryForPage(CriteriaQuery criteriaQuery, Class<T> clazz) {
QueryBuilder query = new CriteriaQueryProcessor().createQueryFromCriteria(criteriaQuery.getCriteria());
SearchResponse response = prepareSearch(criteriaQuery, clazz).setQuery(query).execute().actionGet();
QueryBuilder elasticsearchQuery = new CriteriaQueryProcessor().createQueryFromCriteria(criteriaQuery.getCriteria());
FilterBuilder elasticsearchFilter = new CriteriaFilterProcessor().createFilterFromCriteria(criteriaQuery.getCriteria());
SearchRequestBuilder searchRequestBuilder = prepareSearch(criteriaQuery, clazz);
if (elasticsearchQuery != null) {
searchRequestBuilder.setQuery(elasticsearchQuery);
} else {
searchRequestBuilder.setQuery(QueryBuilders.matchAllQuery());
}
if (elasticsearchFilter != null)
searchRequestBuilder.setFilter(elasticsearchFilter);
SearchResponse response = searchRequestBuilder
.execute().actionGet();
return mapResults(response, clazz, criteriaQuery.getPageable());
}

View File

@@ -18,6 +18,7 @@ package org.springframework.data.elasticsearch.core;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.springframework.data.elasticsearch.annotations.*;
import org.springframework.data.elasticsearch.core.facet.FacetRequest;
import org.springframework.data.elasticsearch.core.geo.GeoLocation;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
@@ -71,6 +72,11 @@ class MappingBuilder {
if (isEntity(field)) {
mapEntity(xContentBuilder, field.getType(), false, EMPTY, field.getName());
}
if(field.getType() == GeoLocation.class) {
applyGeoLocationFieldMapping(xContentBuilder, field);
}
Field singleField = field.getAnnotation(Field.class);
MultiField multiField = field.getAnnotation(MultiField.class);
if (isRootObject && singleField != null && isIdField(field, idFieldName)) {
@@ -88,6 +94,12 @@ class MappingBuilder {
}
private static void applyGeoLocationFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field) throws IOException {
xContentBuilder.startObject(field.getName());
xContentBuilder.field("type", "geo_point")
.endObject();
}
private static void applyDefaultIdFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field)
throws IOException {
xContentBuilder.startObject(field.getName())

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2013 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.elasticsearch.core.geo;
import java.util.List;
/**
* Geo bbox used for #{@link org.springframework.data.elasticsearch.core.query.Criteria}.
*
* @author Franck Marchand
*/
public class GeoBBox {
private GeoLocation topLeft;
private GeoLocation bottomRight;
public GeoBBox(GeoLocation topLeft, GeoLocation bottomRight) {
this.topLeft = topLeft;
this.bottomRight = bottomRight;
}
public GeoLocation getTopLeft() {
return topLeft;
}
public void setTopLeft(GeoLocation topLeft) {
this.topLeft = topLeft;
}
public GeoLocation getBottomRight() {
return bottomRight;
}
public void setBottomRight(GeoLocation bottomRight) {
this.bottomRight = bottomRight;
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2013 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.elasticsearch.core.geo;
/**
* geo-location used for #{@link org.springframework.data.elasticsearch.core.query.Criteria}.
*
* @author Franck Marchand
*/
public class GeoLocation {
private double lat;
private double lon;
public GeoLocation lat(double lat) {
setLat(lat);
return this;
}
public GeoLocation lon(double lon) {
setLon(lon);
return this;
}
public GeoLocation() {
}
public GeoLocation(double latitude, double longitude) {
this.lat = latitude;
this.lon = longitude;
}
public double getLat() {
return lat;
}
public void setLat(double lat) {
this.lat = lat;
}
public double getLon() {
return lon;
}
public void setLon(double lon) {
this.lon = lon;
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2013 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.elasticsearch.core.geo;
import java.util.List;
/**
* Geo polygon used for #{@link org.springframework.data.elasticsearch.core.query.Criteria}.
*
* @author Franck Marchand
*/
public class GeoPolygon {
private List<GeoLocation> points;
public GeoPolygon(List<GeoLocation> points) {
this.points = points;
}
}

View File

@@ -1,102 +1,102 @@
/*
* Copyright 2013 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.elasticsearch.core.query;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.util.Assert;
import java.util.ArrayList;
import java.util.List;
import static org.apache.commons.collections.CollectionUtils.addAll;
/**
* AbstractQuery
*
* @author Rizwan Idrees
* @author Mohsin Husen
*/
abstract class AbstractQuery implements Query {
protected Pageable pageable = DEFAULT_PAGE;
protected Sort sort;
protected List<String> indices = new ArrayList<String>();
protected List<String> types = new ArrayList<String>();
protected List<String> fields = new ArrayList<String>();
@Override
public Sort getSort() {
return this.sort;
}
@Override
public Pageable getPageable() {
return this.pageable;
}
@Override
public final <T extends Query> T setPageable(Pageable pageable) {
Assert.notNull(pageable);
this.pageable = pageable;
return (T) this.addSort(pageable.getSort());
}
@Override
public void addFields(String... fields) {
addAll(this.fields, fields);
}
@Override
public List<String> getFields() {
return fields;
}
@Override
public List<String> getIndices() {
return indices;
}
@Override
public void addIndices(String... indices) {
addAll(this.indices, indices);
}
@Override
public void addTypes(String... types) {
addAll(this.types, types);
}
@Override
public List<String> getTypes() {
return types;
}
@SuppressWarnings("unchecked")
public final <T extends Query> T addSort(Sort sort) {
if (sort == null) {
return (T) this;
}
if (this.sort == null) {
this.sort = sort;
} else {
this.sort = this.sort.and(sort);
}
return (T) this;
}
}
/*
* Copyright 2013 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.elasticsearch.core.query;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.util.Assert;
import java.util.ArrayList;
import java.util.List;
import static org.apache.commons.collections.CollectionUtils.addAll;
/**
* AbstractQuery
*
* @author Rizwan Idrees
* @author Mohsin Husen
*/
abstract class AbstractQuery implements Query {
protected Pageable pageable = DEFAULT_PAGE;
protected Sort sort;
protected List<String> indices = new ArrayList<String>();
protected List<String> types = new ArrayList<String>();
protected List<String> fields = new ArrayList<String>();
@Override
public Sort getSort() {
return this.sort;
}
@Override
public Pageable getPageable() {
return this.pageable;
}
@Override
public final <T extends Query> T setPageable(Pageable pageable) {
Assert.notNull(pageable);
this.pageable = pageable;
return (T) this.addSort(pageable.getSort());
}
@Override
public void addFields(String... fields) {
addAll(this.fields, fields);
}
@Override
public List<String> getFields() {
return fields;
}
@Override
public List<String> getIndices() {
return indices;
}
@Override
public void addIndices(String... indices) {
addAll(this.indices, indices);
}
@Override
public void addTypes(String... types) {
addAll(this.types, types);
}
@Override
public List<String> getTypes() {
return types;
}
@SuppressWarnings("unchecked")
public final <T extends Query> T addSort(Sort sort) {
if (sort == null) {
return (T) this;
}
if (this.sort == null) {
this.sort = sort;
} else {
this.sort = this.sort.and(sort);
}
return (T) this;
}
}

View File

@@ -25,6 +25,8 @@ import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.elasticsearch.core.geo.GeoBBox;
import org.springframework.data.elasticsearch.core.geo.GeoLocation;
import org.springframework.util.Assert;
/**
@@ -33,6 +35,7 @@ import org.springframework.util.Assert;
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Franck Marchand
*/
public class Criteria {
@@ -48,7 +51,9 @@ public class Criteria {
private List<Criteria> criteriaChain = new ArrayList<Criteria>(1);
private Set<CriteriaEntry> criteria = new LinkedHashSet<CriteriaEntry>();
private Set<CriteriaEntry> queryCriteria = new LinkedHashSet<CriteriaEntry>();
private Set<CriteriaEntry> filterCriteria = new LinkedHashSet<CriteriaEntry>();
public Criteria() {
}
@@ -171,7 +176,7 @@ public class Criteria {
Assert.notNull(criteria, "Cannot chain 'null' criteria.");
Criteria orConnectedCritiera = new OrCriteria(this.criteriaChain, criteria.getField());
orConnectedCritiera.criteria.addAll(criteria.criteria);
orConnectedCritiera.queryCriteria.addAll(criteria.queryCriteria);
return orConnectedCritiera;
}
@@ -192,7 +197,7 @@ public class Criteria {
* @return
*/
public Criteria is(Object o) {
criteria.add(new CriteriaEntry(OperationKey.EQUALS, o));
queryCriteria.add(new CriteriaEntry(OperationKey.EQUALS, o));
return this;
}
@@ -205,7 +210,7 @@ public class Criteria {
*/
public Criteria contains(String s) {
assertNoBlankInWildcardedQuery(s, true, true);
criteria.add(new CriteriaEntry(OperationKey.CONTAINS, s));
queryCriteria.add(new CriteriaEntry(OperationKey.CONTAINS, s));
return this;
}
@@ -217,7 +222,7 @@ public class Criteria {
*/
public Criteria startsWith(String s) {
assertNoBlankInWildcardedQuery(s, true, false);
criteria.add(new CriteriaEntry(OperationKey.STARTS_WITH, s));
queryCriteria.add(new CriteriaEntry(OperationKey.STARTS_WITH, s));
return this;
}
@@ -230,7 +235,7 @@ public class Criteria {
*/
public Criteria endsWith(String s) {
assertNoBlankInWildcardedQuery(s, false, true);
criteria.add(new CriteriaEntry(OperationKey.ENDS_WITH, s));
queryCriteria.add(new CriteriaEntry(OperationKey.ENDS_WITH, s));
return this;
}
@@ -251,8 +256,8 @@ public class Criteria {
* @return
*/
public Criteria fuzzy(String s) {
criteria.add(new CriteriaEntry(OperationKey.FUZZY, s));
return this;
queryCriteria.add(new CriteriaEntry(OperationKey.FUZZY, s));
return this;
}
/**
@@ -262,7 +267,7 @@ public class Criteria {
* @return
*/
public Criteria expression(String s) {
criteria.add(new CriteriaEntry(OperationKey.EXPRESSION, s));
queryCriteria.add(new CriteriaEntry(OperationKey.EXPRESSION, s));
return this;
}
@@ -292,7 +297,7 @@ public class Criteria {
throw new InvalidDataAccessApiUsageException("Range [* TO *] is not allowed");
}
criteria.add(new CriteriaEntry(OperationKey.BETWEEN, new Object[] { lowerBound, upperBound }));
queryCriteria.add(new CriteriaEntry(OperationKey.BETWEEN, new Object[]{lowerBound, upperBound}));
return this;
}
@@ -341,9 +346,37 @@ public class Criteria {
*/
public Criteria in(Iterable<?> values) {
Assert.notNull(values, "Collection of 'in' values must not be null");
criteria.add(new CriteriaEntry(OperationKey.IN, values));
return this;
}
queryCriteria.add(new CriteriaEntry(OperationKey.IN, values));
return this;
}
/**
* Creates new CriteriaEntry for {@code location WITHIN distance}
* @param location {@link GeoLocation} center coordinates
* @param distance {@link String} radius as a string (e.g. : '100km').
* Distance unit :
* either mi/miles or km can be set
*
* @return Criteria the chaind criteria with the new 'within' criteria included.
*/
public Criteria within(GeoLocation location, String 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 location BBOX bounding box}
* @param bbox {@link org.springframework.data.elasticsearch.core.geo.GeoBBox} center coordinates
*
* @return Criteria the chaind criteria with the new 'bbox' criteria included.
*/
public Criteria bbox(GeoBBox bbox) {
Assert.notNull(bbox, "bbox value for bbox criteria must not be null");
filterCriteria.add(new CriteriaEntry(OperationKey.BBOX, new Object[]{bbox}));
return this;
}
private void assertNoBlankInWildcardedQuery(String searchString, boolean leadingWildcard, boolean trailingWildcard) {
if (StringUtils.contains(searchString, CRITERIA_VALUE_SEPERATOR)) {
@@ -361,10 +394,18 @@ public class Criteria {
return this.field;
}
public Set<CriteriaEntry> getCriteriaEntries() {
return Collections.unmodifiableSet(this.criteria);
public Set<CriteriaEntry> getQueryCriteriaEntries() {
return Collections.unmodifiableSet(this.queryCriteria);
}
public Set<CriteriaEntry> getFilterCriteriaEntries() {
return Collections.unmodifiableSet(this.filterCriteria);
}
public Set<CriteriaEntry> getFilterCriteria() {
return filterCriteria;
}
/**
* Conjunction to be used with this criteria (AND | OR)
*
@@ -424,7 +465,7 @@ public class Criteria {
}
public enum OperationKey {
EQUALS, CONTAINS, STARTS_WITH, ENDS_WITH, EXPRESSION, BETWEEN, FUZZY, IN;
EQUALS, CONTAINS, STARTS_WITH, ENDS_WITH, EXPRESSION, BETWEEN, FUZZY, IN, WITHIN, BBOX, NEAR;
}
public static class CriteriaEntry {

View File

@@ -34,48 +34,48 @@ public class CriteriaQuery extends AbstractQuery {
public CriteriaQuery(Criteria criteria) {
this(criteria, null);
}
public CriteriaQuery(Criteria criteria, Pageable pageable) {
this.criteria = criteria;
this.pageable = pageable;
if (pageable != null) {
this.addSort(pageable.getSort());
}
}
public CriteriaQuery(Criteria criteria, Pageable pageable) {
this.criteria = criteria;
this.pageable = pageable;
if (pageable != null) {
this.addSort(pageable.getSort());
}
}
public static final Query fromQuery(CriteriaQuery source) {
return fromQuery(source, new CriteriaQuery());
}
public static final Query fromQuery(CriteriaQuery source) {
return fromQuery(source, new CriteriaQuery());
}
public static <T extends CriteriaQuery> T fromQuery(CriteriaQuery source, T destination) {
if (source == null || destination == null) {
return null;
}
public static <T extends CriteriaQuery> T fromQuery(CriteriaQuery source, T destination) {
if (source == null || destination == null) {
return null;
}
if (source.getCriteria() != null) {
destination.addCriteria(source.getCriteria());
}
if (source.getCriteria() != null) {
destination.addCriteria(source.getCriteria());
}
if (source.getSort() != null) {
destination.addSort(source.getSort());
}
if (source.getSort() != null) {
destination.addSort(source.getSort());
}
return destination;
}
return destination;
}
@SuppressWarnings("unchecked")
public final <T extends CriteriaQuery> T addCriteria(Criteria criteria) {
Assert.notNull(criteria, "Cannot add null criteria.");
if (this.criteria == null) {
this.criteria = criteria;
} else {
this.criteria.and(criteria);
}
return (T) this;
}
@SuppressWarnings("unchecked")
public final <T extends CriteriaQuery> T addCriteria(Criteria criteria) {
Assert.notNull(criteria, "Cannot add null criteria.");
if (this.criteria == null) {
this.criteria = criteria;
} else {
this.criteria.and(criteria);
}
return (T) this;
}
public Criteria getCriteria() {
return this.criteria;
}
public Criteria getCriteria() {
return this.criteria;
}
}

View File

@@ -21,7 +21,7 @@ import java.util.ArrayList;
import java.util.List;
import static org.apache.commons.collections.CollectionUtils.addAll;
import static org.springframework.data.elasticsearch.core.query.Query.DEFAULT_PAGE;
import static org.springframework.data.elasticsearch.core.query.AbstractQuery.DEFAULT_PAGE;
/**
* MoreLikeThisQuery

View File

@@ -17,7 +17,6 @@ package org.springframework.data.elasticsearch.core.query;
import org.elasticsearch.index.query.FilterBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.facet.FacetBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.springframework.data.elasticsearch.core.facet.FacetRequest;