From 74a4035408c617d6d1993a1983e1e284f00edc2c Mon Sep 17 00:00:00 2001 From: Artur Konczak Date: Thu, 13 Jun 2013 17:31:14 +0100 Subject: [PATCH 1/4] DATAES-12 Merge Geo Location from branch 'master' of https://github.com/fmarchand/spring-data-elasticsearch --- .../core/CriteriaFilterProcessor.java | 148 ++++++++++++ .../core/CriteriaQueryProcessor.java | 211 ++++++++++-------- .../core/ElasticsearchTemplate.java | 19 +- .../elasticsearch/core/MappingBuilder.java | 12 + .../data/elasticsearch/core/geo/GeoBBox.java | 49 ++++ .../elasticsearch/core/geo/GeoLocation.java | 60 +++++ .../elasticsearch/core/geo/GeoPolygon.java | 31 +++ .../core/query/AbstractQuery.java | 204 ++++++++--------- .../elasticsearch/core/query/Criteria.java | 73 ++++-- .../core/query/CriteriaQuery.java | 72 +++--- .../core/query/MoreLikeThisQuery.java | 2 +- .../elasticsearch/core/query/SearchQuery.java | 1 - .../data/elasticsearch/GeoAuthor.java | 57 +++++ .../core/ElasticsearchTemplateTests.java | 103 ++++++++- src/test/resources/infrastructure.xml | 7 +- 15 files changed, 796 insertions(+), 253 deletions(-) create mode 100644 src/main/java/org/springframework/data/elasticsearch/core/CriteriaFilterProcessor.java create mode 100644 src/main/java/org/springframework/data/elasticsearch/core/geo/GeoBBox.java create mode 100644 src/main/java/org/springframework/data/elasticsearch/core/geo/GeoLocation.java create mode 100644 src/main/java/org/springframework/data/elasticsearch/core/geo/GeoPolygon.java create mode 100644 src/test/java/org/springframework/data/elasticsearch/GeoAuthor.java diff --git a/src/main/java/org/springframework/data/elasticsearch/core/CriteriaFilterProcessor.java b/src/main/java/org/springframework/data/elasticsearch/core/CriteriaFilterProcessor.java new file mode 100644 index 000000000..381a4273b --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/core/CriteriaFilterProcessor.java @@ -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 fbList = new LinkedList(); + FilterBuilder filter = null; + + ListIterator 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 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 createFilterFragmentForCriteria(Criteria chainedCriteria) { + Iterator it = chainedCriteria.getFilterCriteriaEntries().iterator(); + List filterList = new LinkedList(); + + 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 buildNegationFilter(String fieldName, Iterator it){ + List notFilterList = new LinkedList(); + + while (it.hasNext()){ + Criteria.CriteriaEntry criteriaEntry = it.next(); + FilterBuilder notFilter = notFilter(processCriteriaEntry(criteriaEntry.getKey(), criteriaEntry.getValue(), fieldName)); + notFilterList.add(notFilter); + } + + return notFilterList; + } +} diff --git a/src/main/java/org/springframework/data/elasticsearch/core/CriteriaQueryProcessor.java b/src/main/java/org/springframework/data/elasticsearch/core/CriteriaQueryProcessor.java index 0da61b4d0..d33259183 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/CriteriaQueryProcessor.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/CriteriaQueryProcessor.java @@ -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 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 it = chainedCriteria.getCriteriaEntries().iterator(); - boolean singeEntryCriteria = (chainedCriteria.getCriteriaEntries().size() == 1); + List shouldQueryBuilderList = new LinkedList(); + List mustNotQueryBuilderList = new LinkedList(); + List mustQueryBuilderList = new LinkedList(); - 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 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 collection = (Iterable) 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 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 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 collection = (Iterable) value; + for (Object item : collection) { + ((BoolQueryBuilder) query).should(fieldQuery(fieldName, item)); + } + break; + } + + return query; + } + + private QueryBuilder buildNegationQuery(String fieldName, Iterator 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); + } + + } } diff --git a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java index 39e11a106..0f2453b55 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java @@ -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 Page queryForPage(CriteriaQuery criteriaQuery, Class 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()); } diff --git a/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java b/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java index ee43c9b99..7cb1f6fc5 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java @@ -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()) diff --git a/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoBBox.java b/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoBBox.java new file mode 100644 index 000000000..817398aba --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoBBox.java @@ -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; + } +} diff --git a/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoLocation.java b/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoLocation.java new file mode 100644 index 000000000..33a4267f8 --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoLocation.java @@ -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; + } +} diff --git a/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoPolygon.java b/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoPolygon.java new file mode 100644 index 000000000..46ae5d862 --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoPolygon.java @@ -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 points; + + public GeoPolygon(List points) { + this.points = points; + } +} diff --git a/src/main/java/org/springframework/data/elasticsearch/core/query/AbstractQuery.java b/src/main/java/org/springframework/data/elasticsearch/core/query/AbstractQuery.java index f0abaa8f2..2bb79698f 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/query/AbstractQuery.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/query/AbstractQuery.java @@ -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 indices = new ArrayList(); - protected List types = new ArrayList(); - protected List fields = new ArrayList(); - - @Override - public Sort getSort() { - return this.sort; - } - - @Override - public Pageable getPageable() { - return this.pageable; - } - - @Override - public final 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 getFields() { - return fields; - } - - @Override - public List 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 getTypes() { - return types; - } - - @SuppressWarnings("unchecked") - public final 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 indices = new ArrayList(); + protected List types = new ArrayList(); + protected List fields = new ArrayList(); + + @Override + public Sort getSort() { + return this.sort; + } + + @Override + public Pageable getPageable() { + return this.pageable; + } + + @Override + public final 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 getFields() { + return fields; + } + + @Override + public List 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 getTypes() { + return types; + } + + @SuppressWarnings("unchecked") + public final 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; + } +} diff --git a/src/main/java/org/springframework/data/elasticsearch/core/query/Criteria.java b/src/main/java/org/springframework/data/elasticsearch/core/query/Criteria.java index f375eb2d8..7acc77b4e 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/query/Criteria.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/query/Criteria.java @@ -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 criteriaChain = new ArrayList(1); - private Set criteria = new LinkedHashSet(); + private Set queryCriteria = new LinkedHashSet(); + + private Set filterCriteria = new LinkedHashSet(); 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 getCriteriaEntries() { - return Collections.unmodifiableSet(this.criteria); + public Set getQueryCriteriaEntries() { + return Collections.unmodifiableSet(this.queryCriteria); } + public Set getFilterCriteriaEntries() { + return Collections.unmodifiableSet(this.filterCriteria); + } + + public Set 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 { diff --git a/src/main/java/org/springframework/data/elasticsearch/core/query/CriteriaQuery.java b/src/main/java/org/springframework/data/elasticsearch/core/query/CriteriaQuery.java index 734e710d2..8dfa99de8 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/query/CriteriaQuery.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/query/CriteriaQuery.java @@ -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 fromQuery(CriteriaQuery source, T destination) { + if (source == null || destination == null) { + return null; + } - public static 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 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 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; + } } diff --git a/src/main/java/org/springframework/data/elasticsearch/core/query/MoreLikeThisQuery.java b/src/main/java/org/springframework/data/elasticsearch/core/query/MoreLikeThisQuery.java index f95099c13..c125e5e12 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/query/MoreLikeThisQuery.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/query/MoreLikeThisQuery.java @@ -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 diff --git a/src/main/java/org/springframework/data/elasticsearch/core/query/SearchQuery.java b/src/main/java/org/springframework/data/elasticsearch/core/query/SearchQuery.java index 21a925073..e625c968a 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/query/SearchQuery.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/query/SearchQuery.java @@ -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; diff --git a/src/test/java/org/springframework/data/elasticsearch/GeoAuthor.java b/src/test/java/org/springframework/data/elasticsearch/GeoAuthor.java new file mode 100644 index 000000000..671e7d101 --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/GeoAuthor.java @@ -0,0 +1,57 @@ +/* + * 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; + +import org.springframework.data.annotation.Id; +import org.springframework.data.elasticsearch.annotations.Document; +import org.springframework.data.elasticsearch.core.geo.GeoLocation; + +/** + * @author Franck Marchand + */ +@Document(indexName = "test-geo-index", type = "test-geo-type") +public class GeoAuthor { + + @Id + private String id; + private String name; + + private GeoLocation location; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public GeoLocation getLocation() { + return location; + } + + public void setLocation(GeoLocation location) { + this.location = location; + } +} diff --git a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java index fde149a6e..bd20de851 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java @@ -25,11 +25,13 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; +import org.springframework.data.elasticsearch.GeoAuthor; import org.springframework.data.elasticsearch.SampleEntity; import org.springframework.data.elasticsearch.SampleMappingEntity; +import org.springframework.data.elasticsearch.core.geo.GeoBBox; +import org.springframework.data.elasticsearch.core.geo.GeoLocation; import org.springframework.data.elasticsearch.core.query.*; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -42,12 +44,14 @@ import static org.elasticsearch.index.query.FilterBuilders.boolFilter; import static org.elasticsearch.index.query.FilterBuilders.termFilter; import static org.elasticsearch.index.query.QueryBuilders.fieldQuery; import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; /** * @author Rizwan Idrees * @author Mohsin Husen + * @author Franck Marchand */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:elasticsearch-template-test.xml") @@ -56,12 +60,18 @@ public class ElasticsearchTemplateTests { @Autowired private ElasticsearchTemplate elasticsearchTemplate; - @Before - public void before() { + @Before + public void before(){ elasticsearchTemplate.deleteIndex(SampleEntity.class); - elasticsearchTemplate.createIndex(SampleEntity.class); - elasticsearchTemplate.refresh(SampleEntity.class, true); - } + elasticsearchTemplate.createIndex(SampleEntity.class); + elasticsearchTemplate.refresh(SampleEntity.class, true); + + elasticsearchTemplate.deleteIndex(GeoAuthor.class); + elasticsearchTemplate.createIndex(GeoAuthor.class); + elasticsearchTemplate.refresh(GeoAuthor.class, true); + + elasticsearchTemplate.putMapping(GeoAuthor.class); + } @Test public void shouldReturnCountForGivenSearchQuery() { @@ -716,4 +726,85 @@ public class ElasticsearchTemplateTests { assertThat(elasticsearchTemplate.indexExists(clazz), is(false)); } + @Test + public void shouldPutMappingForGivenEntityWithGeoLocation()throws Exception{ + //given + Class entity = GeoAuthor.class; + elasticsearchTemplate.createIndex(entity); + //when + assertThat(elasticsearchTemplate.putMapping(entity) , is(true)) ; + } + + @Test + public void shouldReturnListForGivenCriteriaWithGeoLocation(){ + //given + List indexQueries = new ArrayList(); + //first document + String documentId = randomNumeric(5); + GeoAuthor geoAuthor1 = new GeoAuthor(); + geoAuthor1.setId(documentId); + geoAuthor1.setName("Franck Marchand"); + geoAuthor1.setLocation(new GeoLocation(45.7806d, 3.0875d)); // Clermont-Ferrand + + IndexQuery indexQuery1 = new IndexQuery(); + indexQuery1.setId(documentId); + indexQuery1.setObject(geoAuthor1); + indexQueries.add(indexQuery1); + + //second document + String documentId2 = randomNumeric(5); + GeoAuthor geoAuthor2 = new GeoAuthor(); + geoAuthor2.setId(documentId2); + geoAuthor2.setName("Mohsin Husen"); + geoAuthor2.setLocation(new GeoLocation(51.5171d, 0.1062d)); // London + + IndexQuery indexQuery2 = new IndexQuery(); + indexQuery2.setId(documentId2); + indexQuery2.setObject(geoAuthor2); + + indexQueries.add(indexQuery2); + + //third document + String documentId3 = randomNumeric(5); + GeoAuthor geoAuthor3 = new GeoAuthor(); + geoAuthor3.setId(documentId3); + geoAuthor3.setName("Rizwan Idrees"); + geoAuthor3.setLocation(new GeoLocation(51.5171d, 0.1062d)); // London + + IndexQuery indexQuery3 = new IndexQuery(); + indexQuery3.setId(documentId3); + indexQuery3.setObject(geoAuthor3); + + indexQueries.add(indexQuery3); + //when + elasticsearchTemplate.bulkIndex(indexQueries); + elasticsearchTemplate.refresh(GeoAuthor.class, true); + //when + CriteriaQuery geoLocationCriteriaQuery = new CriteriaQuery( + new Criteria("location").within(new GeoLocation(45.7806d, 3.0875d), "20km")); + + + List geoAuthorsForGeoCriteria = elasticsearchTemplate.queryForList(geoLocationCriteriaQuery,GeoAuthor.class); + //then + assertThat(geoAuthorsForGeoCriteria.size(),is(1)); + assertEquals("Franck Marchand", geoAuthorsForGeoCriteria.get(0).getName()); + + // query/filter geo distance mixed query + CriteriaQuery geoLocationCriteriaQuery2 = new CriteriaQuery( + new Criteria("name").is("Mohsin Husen").and("location").within(new GeoLocation(51.5171d, 0.1062d), "20km")); + List geoAuthorsForGeoCriteria2 = elasticsearchTemplate.queryForList(geoLocationCriteriaQuery2,GeoAuthor.class); + + assertThat(geoAuthorsForGeoCriteria2.size(),is(1)); + assertEquals("Mohsin Husen", geoAuthorsForGeoCriteria2.get(0).getName()); + + // bbox query + CriteriaQuery geoLocationCriteriaQuery3 = new CriteriaQuery( + new Criteria("location").bbox( + new GeoBBox(new GeoLocation(53.5171d, 0), + new GeoLocation(49.5171d, 0.2062d)))); + List geoAuthorsForGeoCriteria3 = elasticsearchTemplate.queryForList(geoLocationCriteriaQuery3,GeoAuthor.class); + + assertThat(geoAuthorsForGeoCriteria3.size(),is(2)); + assertThat(geoAuthorsForGeoCriteria3, containsInAnyOrder(hasProperty("name", equalTo("Mohsin Husen")), hasProperty("name",equalTo("Rizwan Idrees")))); + } } diff --git a/src/test/resources/infrastructure.xml b/src/test/resources/infrastructure.xml index 747841218..c9207190f 100644 --- a/src/test/resources/infrastructure.xml +++ b/src/test/resources/infrastructure.xml @@ -5,7 +5,10 @@ xsi:schemaLocation="http://www.springframework.org/schema/data/elasticsearch http://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> - + + + + + \ No newline at end of file From 5a499315e858980830fba6b414f244a06b3b3a40 Mon Sep 17 00:00:00 2001 From: Artur Konczak Date: Mon, 24 Jun 2013 12:55:52 +0100 Subject: [PATCH 2/4] DATAES-12 added support for GeoPoint --- .../GeoPointField.java} | 19 +- .../core/CriteriaFilterProcessor.java | 104 ++- .../core/ElasticsearchOperations.java | 10 + .../core/ElasticsearchTemplate.java | 5 + .../elasticsearch/core/MappingBuilder.java | 19 +- .../geo/{GeoBBox.java => GeoEnvelope.java} | 22 +- .../geo/{GeoLocation.java => GeoPoint.java} | 25 +- .../elasticsearch/core/query/Criteria.java | 769 ++++++++++-------- .../data/elasticsearch/ArticleBuilder.java | 47 -- .../core/ElasticsearchTemplateTests.java | 94 +-- .../facet/ArticleEntity.java} | 8 +- .../core/facet/ArticleEntityBuilder.java | 47 ++ .../ElasticsearchTemplateFacetTests.java | 50 +- .../core/geo/AuthorMarkerAnnotatedEntity.java | 77 ++ .../AuthorMarkerAnnotatedEntityBuilder.java | 53 ++ .../geo/AuthorMarkerEntity.java} | 21 +- .../core/geo/AuthorMarkerEntityBuilder.java | 48 ++ .../geo/ElasticsearchTemplateGeoTests.java | 236 ++++++ src/test/resources/infrastructure.xml | 6 +- 19 files changed, 1038 insertions(+), 622 deletions(-) rename src/main/java/org/springframework/data/elasticsearch/{core/geo/GeoPolygon.java => annotations/GeoPointField.java} (59%) rename src/main/java/org/springframework/data/elasticsearch/core/geo/{GeoBBox.java => GeoEnvelope.java} (67%) rename src/main/java/org/springframework/data/elasticsearch/core/geo/{GeoLocation.java => GeoPoint.java} (70%) delete mode 100644 src/test/java/org/springframework/data/elasticsearch/ArticleBuilder.java rename src/test/java/org/springframework/data/elasticsearch/{Article.java => core/facet/ArticleEntity.java} (92%) create mode 100644 src/test/java/org/springframework/data/elasticsearch/core/facet/ArticleEntityBuilder.java rename src/test/java/org/springframework/data/elasticsearch/core/{ => facet}/ElasticsearchTemplateFacetTests.java (86%) create mode 100644 src/test/java/org/springframework/data/elasticsearch/core/geo/AuthorMarkerAnnotatedEntity.java create mode 100644 src/test/java/org/springframework/data/elasticsearch/core/geo/AuthorMarkerAnnotatedEntityBuilder.java rename src/test/java/org/springframework/data/elasticsearch/{GeoAuthor.java => core/geo/AuthorMarkerEntity.java} (68%) create mode 100644 src/test/java/org/springframework/data/elasticsearch/core/geo/AuthorMarkerEntityBuilder.java create mode 100644 src/test/java/org/springframework/data/elasticsearch/core/geo/ElasticsearchTemplateGeoTests.java diff --git a/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoPolygon.java b/src/main/java/org/springframework/data/elasticsearch/annotations/GeoPointField.java similarity index 59% rename from src/main/java/org/springframework/data/elasticsearch/core/geo/GeoPolygon.java rename to src/main/java/org/springframework/data/elasticsearch/annotations/GeoPointField.java index 46ae5d862..bdb8025bb 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoPolygon.java +++ b/src/main/java/org/springframework/data/elasticsearch/annotations/GeoPointField.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -13,19 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.elasticsearch.core.geo; +package org.springframework.data.elasticsearch.annotations; -import java.util.List; +import java.lang.annotation.*; /** - * Geo polygon used for #{@link org.springframework.data.elasticsearch.core.query.Criteria}. - * - * @author Franck Marchand + * @author Artur Konczak */ -public class GeoPolygon { - private List points; +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@Documented +public @interface GeoPointField { - public GeoPolygon(List points) { - this.points = points; - } } diff --git a/src/main/java/org/springframework/data/elasticsearch/core/CriteriaFilterProcessor.java b/src/main/java/org/springframework/data/elasticsearch/core/CriteriaFilterProcessor.java index 381a4273b..0d2cdc9e7 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/CriteriaFilterProcessor.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/CriteriaFilterProcessor.java @@ -16,8 +16,8 @@ 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.geo.GeoEnvelope; +import org.springframework.data.elasticsearch.core.geo.GeoPoint; import org.springframework.data.elasticsearch.core.query.Criteria; import org.springframework.util.Assert; @@ -46,25 +46,25 @@ class CriteriaFilterProcessor { while (chainIterator.hasNext()) { FilterBuilder fb = null; Criteria chainedCriteria = chainIterator.next(); - if(chainedCriteria.isOr()){ - fb = orFilter(createFilterFragmentForCriteria(chainedCriteria).toArray(new FilterBuilder[]{ })); + if (chainedCriteria.isOr()) { + fb = orFilter(createFilterFragmentForCriteria(chainedCriteria).toArray(new FilterBuilder[]{})); fbList.add(fb); - }else if(chainedCriteria.isNegating()){ + } else if (chainedCriteria.isNegating()) { List negationFilters = buildNegationFilter(criteria.getField().getName(), criteria.getFilterCriteriaEntries().iterator()); - if(!negationFilters.isEmpty()) { + if (!negationFilters.isEmpty()) { fbList.addAll(negationFilters); } - }else { + } else { fbList.addAll(createFilterFragmentForCriteria(chainedCriteria)); } } - if(!fbList.isEmpty()) { - if(fbList.size() == 1) { - filter =fbList.get(0); + if (!fbList.isEmpty()) { + if (fbList.size() == 1) { + filter = fbList.get(0); } else { - filter = andFilter(fbList.toArray(new FilterBuilder[]{ })); + filter = andFilter(fbList.toArray(new FilterBuilder[]{})); } } @@ -77,10 +77,10 @@ class CriteriaFilterProcessor { List filterList = new LinkedList(); String fieldName = chainedCriteria.getField().getName(); - Assert.notNull(fieldName,"Unknown field"); + Assert.notNull(fieldName, "Unknown field"); FilterBuilder filter = null; - while (it.hasNext()){ + while (it.hasNext()) { Criteria.CriteriaEntry entry = it.next(); filter = processCriteriaEntry(entry.getKey(), entry.getValue(), fieldName); filterList.add(filter); @@ -96,36 +96,53 @@ class CriteriaFilterProcessor { } FilterBuilder filter = null; - switch (key){ - case WITHIN: { + 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[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"); - GeoLocation loc = (GeoLocation)valArray[0]; - String dist = (String)valArray[1]; + 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); + } 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); + } else { + ((GeoDistanceFilterBuilder) filter).geohash(loc).distance(dist); + } + + } - ((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."); + Assert.isTrue(value instanceof Object[], "Value of a bbox filter should be an array of one or 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()); + if (valArray.length == 1) { + //GeoEnvelop + oneParameterBBox((GeoBoundingBoxFilterBuilder) filter, valArray[0]); + } else if (valArray.length == 2) { + //2x GeoPoint + //2x String + twoParameterBBox((GeoBoundingBoxFilterBuilder) filter, valArray); + } else { + //error + Assert.isTrue(false, "Geo distance filter takes a 1-elements array(GeoEnvelop) or 2-elements array(GeoPoints or Strings(geohash))."); + } break; } @@ -134,12 +151,43 @@ class CriteriaFilterProcessor { return filter; } - private List buildNegationFilter(String fieldName, Iterator it){ + private void oneParameterBBox(GeoBoundingBoxFilterBuilder filter, Object value) { + Assert.isTrue(value instanceof GeoEnvelope, "single-element of a geo bbox filter must be type of GeoEnvelop"); + GeoEnvelope geoBBox = (GeoEnvelope) value; + filter.topLeft(geoBBox.getTopLeft().getLat(), geoBBox.getTopLeft().getLon()); + filter.bottomRight(geoBBox.getBottomRight().getLat(), geoBBox.getBottomRight().getLon()); + } + + private static boolean isType(Object[] array, Class clazz) { + for (Object o : array) { + if (!clazz.isInstance(o)) { + return false; + } + } + return true; + } + + private void twoParameterBBox(GeoBoundingBoxFilterBuilder filter, Object[] values) { + Assert.isTrue(isType(values, GeoPoint.class) || isType(values, String.class), " both elements of geo bbox filter must be type of GeoPoint or String(geohash)"); + if (values[0] instanceof GeoPoint) { + GeoPoint topLeft = (GeoPoint) values[0]; + GeoPoint bottomRight = (GeoPoint) values[1]; + filter.topLeft(topLeft.getLat(), topLeft.getLon()); + filter.bottomRight(bottomRight.getLat(), bottomRight.getLon()); + } else { + String topLeft = (String) values[0]; + String bottomRight = (String) values[1]; + filter.topLeft(topLeft); + filter.bottomRight(bottomRight); + } + } + + private List buildNegationFilter(String fieldName, Iterator it) { List notFilterList = new LinkedList(); - while (it.hasNext()){ + while (it.hasNext()) { Criteria.CriteriaEntry criteriaEntry = it.next(); - FilterBuilder notFilter = notFilter(processCriteriaEntry(criteriaEntry.getKey(), criteriaEntry.getValue(), fieldName)); + FilterBuilder notFilter = notFilter(processCriteriaEntry(criteriaEntry.getKey(), criteriaEntry.getValue(), fieldName)); notFilterList.add(notFilter); } diff --git a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchOperations.java b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchOperations.java index e8b0a0fed..4db37b176 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchOperations.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchOperations.java @@ -133,6 +133,16 @@ public interface ElasticsearchOperations { */ List queryForList(StringQuery query, Class clazz); + /** + * Execute the search query against elasticsearch and return result as {@link List} + * + * @param query + * @param clazz + * @param + * @return + */ + List queryForList(SearchQuery query, Class clazz); + /** * Execute the query against elasticsearch and return ids * diff --git a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java index 0f2453b55..4d6bb04eb 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java @@ -170,6 +170,11 @@ public class ElasticsearchTemplate implements ElasticsearchOperations { return queryForPage(query, clazz).getContent(); } + @Override + public List queryForList(SearchQuery query, Class clazz) { + return queryForPage(query, clazz).getContent(); + } + @Override public List queryForIds(SearchQuery query) { SearchRequestBuilder request = prepareSearch(query).setQuery(query.getQuery()).setNoFields(); diff --git a/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java b/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java index 7cb1f6fc5..cc6de328f 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/MappingBuilder.java @@ -18,7 +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.elasticsearch.core.geo.GeoPoint; import org.springframework.data.mapping.model.SimpleTypeHolder; import org.springframework.data.util.ClassTypeInformation; import org.springframework.data.util.TypeInformation; @@ -48,6 +48,7 @@ class MappingBuilder { public static final String INDEX_VALUE_NOT_ANALYZED = "not_analyzed"; public static final String TYPE_VALUE_STRING = "string"; public static final String TYPE_VALUE_OBJECT = "object"; + public static final String TYPE_VALUE_GEO_POINT = "geo_point"; private static SimpleTypeHolder SIMPLE_TYPE_HOLDER = new SimpleTypeHolder(); @@ -73,12 +74,14 @@ class MappingBuilder { 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); + GeoPointField geoPoint = field.getAnnotation(GeoPointField.class); + + if (field.getType() == GeoPoint.class || geoPoint != null) { + applyGeoPointFieldMapping(xContentBuilder, field); + } + if (isRootObject && singleField != null && isIdField(field, idFieldName)) { applyDefaultIdFieldMapping(xContentBuilder, field); } else if (multiField != null) { @@ -94,10 +97,10 @@ class MappingBuilder { } - private static void applyGeoLocationFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field) throws IOException { + private static void applyGeoPointFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field) throws IOException { xContentBuilder.startObject(field.getName()); - xContentBuilder.field("type", "geo_point") - .endObject(); + xContentBuilder.field(FIELD_TYPE, TYPE_VALUE_GEO_POINT) + .endObject(); } private static void applyDefaultIdFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field) diff --git a/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoBBox.java b/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoEnvelope.java similarity index 67% rename from src/main/java/org/springframework/data/elasticsearch/core/geo/GeoBBox.java rename to src/main/java/org/springframework/data/elasticsearch/core/geo/GeoEnvelope.java index 817398aba..16c00f248 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoBBox.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoEnvelope.java @@ -15,35 +15,27 @@ */ 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 class GeoEnvelope { - public GeoBBox(GeoLocation topLeft, GeoLocation bottomRight) { + private GeoPoint topLeft; + private GeoPoint bottomRight; + + public GeoEnvelope(GeoPoint topLeft, GeoPoint bottomRight) { this.topLeft = topLeft; this.bottomRight = bottomRight; } - public GeoLocation getTopLeft() { + public GeoPoint getTopLeft() { return topLeft; } - public void setTopLeft(GeoLocation topLeft) { - this.topLeft = topLeft; - } - - public GeoLocation getBottomRight() { + public GeoPoint getBottomRight() { return bottomRight; } - public void setBottomRight(GeoLocation bottomRight) { - this.bottomRight = bottomRight; - } } diff --git a/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoLocation.java b/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoPoint.java similarity index 70% rename from src/main/java/org/springframework/data/elasticsearch/core/geo/GeoLocation.java rename to src/main/java/org/springframework/data/elasticsearch/core/geo/GeoPoint.java index 33a4267f8..75eae5ce1 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoLocation.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoPoint.java @@ -20,24 +20,16 @@ package org.springframework.data.elasticsearch.core.geo; * * @author Franck Marchand */ -public class GeoLocation { +public class GeoPoint { + private double lat; private double lon; - public GeoLocation lat(double lat) { - setLat(lat); - return this; + private GeoPoint() { + //required by mapper to instantiate object } - public GeoLocation lon(double lon) { - setLon(lon); - return this; - } - - public GeoLocation() { - } - - public GeoLocation(double latitude, double longitude) { + public GeoPoint(double latitude, double longitude) { this.lat = latitude; this.lon = longitude; } @@ -46,15 +38,8 @@ public class GeoLocation { return lat; } - public void setLat(double lat) { - this.lat = lat; - } - public double getLon() { return lon; } - public void setLon(double lon) { - this.lon = lon; - } } diff --git a/src/main/java/org/springframework/data/elasticsearch/core/query/Criteria.java b/src/main/java/org/springframework/data/elasticsearch/core/query/Criteria.java index 7acc77b4e..42bc08712 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/query/Criteria.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/query/Criteria.java @@ -25,341 +25,341 @@ 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.data.elasticsearch.core.geo.GeoEnvelope; +import org.springframework.data.elasticsearch.core.geo.GeoPoint; import org.springframework.util.Assert; /** * Criteria is the central class when constructing queries. It follows more or less a fluent API style, which allows to * easily chain together multiple criteria. - * + * * @author Rizwan Idrees * @author Mohsin Husen * @author Franck Marchand */ public class Criteria { - public static final String WILDCARD = "*"; - public static final String CRITERIA_VALUE_SEPERATOR = " "; + public static final String WILDCARD = "*"; + public static final String CRITERIA_VALUE_SEPERATOR = " "; - private static final String OR_OPERATOR = " OR "; - private static final String AND_OPERATOR = " AND "; + private static final String OR_OPERATOR = " OR "; + private static final String AND_OPERATOR = " AND "; - private Field field; - private float boost = Float.NaN; - private boolean negating = false; + private Field field; + private float boost = Float.NaN; + private boolean negating = false; - private List criteriaChain = new ArrayList(1); + private List criteriaChain = new ArrayList(1); - private Set queryCriteria = new LinkedHashSet(); + private Set queryCriteria = new LinkedHashSet(); private Set filterCriteria = new LinkedHashSet(); - public Criteria() { - } + public Criteria() { + } - /** - * Creates a new CriterSimpleFieldia for the Filed with provided name - * - * @param fieldname - */ - public Criteria(String fieldname) { - this(new SimpleField(fieldname)); - } + /** + * Creates a new CriterSimpleFieldia for the Filed with provided name + * + * @param fieldname + */ + public Criteria(String fieldname) { + this(new SimpleField(fieldname)); + } - /** - * Creates a new Criteria for the given field - * - * @param field - */ - public Criteria(Field field) { - Assert.notNull(field, "Field for criteria must not be null"); - Assert.hasText(field.getName(), "Field.name for criteria must not be null/empty"); + /** + * Creates a new Criteria for the given field + * + * @param field + */ + public Criteria(Field field) { + Assert.notNull(field, "Field for criteria must not be null"); + Assert.hasText(field.getName(), "Field.name for criteria must not be null/empty"); - this.criteriaChain.add(this); - this.field = field; - } + this.criteriaChain.add(this); + this.field = field; + } - protected Criteria(List criteriaChain, String fieldname) { - this(criteriaChain, new SimpleField(fieldname)); - } + protected Criteria(List criteriaChain, String fieldname) { + this(criteriaChain, new SimpleField(fieldname)); + } - protected Criteria(List criteriaChain, Field field) { - Assert.notNull(criteriaChain, "CriteriaChain must not be null"); - Assert.notNull(field, "Field for criteria must not be null"); - Assert.hasText(field.getName(), "Field.name for criteria must not be null/empty"); + protected Criteria(List criteriaChain, Field field) { + Assert.notNull(criteriaChain, "CriteriaChain must not be null"); + Assert.notNull(field, "Field for criteria must not be null"); + Assert.hasText(field.getName(), "Field.name for criteria must not be null/empty"); - this.criteriaChain.addAll(criteriaChain); - this.criteriaChain.add(this); - this.field = field; - } + this.criteriaChain.addAll(criteriaChain); + this.criteriaChain.add(this); + this.field = field; + } - /** - * Static factory method to create a new Criteria for field with given name - * - * @param field - * @return - */ - public static Criteria where(String field) { - return where(new SimpleField(field)); - } + /** + * Static factory method to create a new Criteria for field with given name + * + * @param field + * @return + */ + public static Criteria where(String field) { + return where(new SimpleField(field)); + } - /** - * Static factory method to create a new Criteria for provided field - * - * @param field - * @return - */ - public static Criteria where(Field field) { - return new Criteria(field); - } + /** + * Static factory method to create a new Criteria for provided field + * + * @param field + * @return + */ + public static Criteria where(Field field) { + return new Criteria(field); + } - /** - * Chain using {@code AND} - * - * @param field - * @return - */ - public Criteria and(Field field) { - return new Criteria(this.criteriaChain, field); - } + /** + * Chain using {@code AND} + * + * @param field + * @return + */ + public Criteria and(Field field) { + return new Criteria(this.criteriaChain, field); + } - /** - * Chain using {@code AND} - * - * @param fieldName - * @return - */ - public Criteria and(String fieldName) { - return new Criteria(this.criteriaChain, fieldName); - } + /** + * Chain using {@code AND} + * + * @param fieldName + * @return + */ + public Criteria and(String fieldName) { + return new Criteria(this.criteriaChain, fieldName); + } - /** - * Chain using {@code AND} - * - * @param criteria - * @return - */ - public Criteria and(Criteria criteria) { - this.criteriaChain.add(criteria); - return this; - } + /** + * Chain using {@code AND} + * + * @param criteria + * @return + */ + public Criteria and(Criteria criteria) { + this.criteriaChain.add(criteria); + return this; + } - /** - * Chain using {@code AND} - * - * @param criterias - * @return - */ - public Criteria and(Criteria... criterias) { - this.criteriaChain.addAll(Arrays.asList(criterias)); - return this; - } + /** + * Chain using {@code AND} + * + * @param criterias + * @return + */ + public Criteria and(Criteria... criterias) { + this.criteriaChain.addAll(Arrays.asList(criterias)); + return this; + } - /** - * Chain using {@code OR} - * - * @param field - * @return - */ - public Criteria or(Field field) { - return new OrCriteria(this.criteriaChain, field); - } + /** + * Chain using {@code OR} + * + * @param field + * @return + */ + public Criteria or(Field field) { + return new OrCriteria(this.criteriaChain, field); + } - /** - * Chain using {@code OR} - * - * @param criteria - * @return - */ - public Criteria or(Criteria criteria) { - Assert.notNull(criteria, "Cannot chain 'null' criteria."); + /** + * Chain using {@code OR} + * + * @param criteria + * @return + */ + public Criteria or(Criteria criteria) { + Assert.notNull(criteria, "Cannot chain 'null' criteria."); - Criteria orConnectedCritiera = new OrCriteria(this.criteriaChain, criteria.getField()); - orConnectedCritiera.queryCriteria.addAll(criteria.queryCriteria); - return orConnectedCritiera; - } + Criteria orConnectedCritiera = new OrCriteria(this.criteriaChain, criteria.getField()); + orConnectedCritiera.queryCriteria.addAll(criteria.queryCriteria); + return orConnectedCritiera; + } - /** - * Chain using {@code OR} - * - * @param fieldName - * @return - */ - public Criteria or(String fieldName) { - return or(new SimpleField(fieldName)); - } + /** + * Chain using {@code OR} + * + * @param fieldName + * @return + */ + public Criteria or(String fieldName) { + return or(new SimpleField(fieldName)); + } - /** - * Crates new CriteriaEntry without any wildcards - * - * @param o - * @return - */ - public Criteria is(Object o) { - queryCriteria.add(new CriteriaEntry(OperationKey.EQUALS, o)); - return this; - } + /** + * Crates new CriteriaEntry without any wildcards + * + * @param o + * @return + */ + public Criteria is(Object o) { + queryCriteria.add(new CriteriaEntry(OperationKey.EQUALS, o)); + return this; + } - /** - * Crates new CriteriaEntry with leading and trailing wildcards
- * NOTE: mind your schema as leading wildcards may not be supported and/or execution might be slow. - * - * @param s - * @return - */ - public Criteria contains(String s) { - assertNoBlankInWildcardedQuery(s, true, true); - queryCriteria.add(new CriteriaEntry(OperationKey.CONTAINS, s)); - return this; - } + /** + * Crates new CriteriaEntry with leading and trailing wildcards
+ * NOTE: mind your schema as leading wildcards may not be supported and/or execution might be slow. + * + * @param s + * @return + */ + public Criteria contains(String s) { + assertNoBlankInWildcardedQuery(s, true, true); + queryCriteria.add(new CriteriaEntry(OperationKey.CONTAINS, s)); + return this; + } - /** - * Crates new CriteriaEntry with trailing wildcard - * - * @param s - * @return - */ - public Criteria startsWith(String s) { - assertNoBlankInWildcardedQuery(s, true, false); - queryCriteria.add(new CriteriaEntry(OperationKey.STARTS_WITH, s)); - return this; - } + /** + * Crates new CriteriaEntry with trailing wildcard + * + * @param s + * @return + */ + public Criteria startsWith(String s) { + assertNoBlankInWildcardedQuery(s, true, false); + queryCriteria.add(new CriteriaEntry(OperationKey.STARTS_WITH, s)); + return this; + } - /** - * Crates new CriteriaEntry with leading wildcard
- * NOTE: mind your schema and execution times as leading wildcards may not be supported. - * - * @param s - * @return - */ - public Criteria endsWith(String s) { - assertNoBlankInWildcardedQuery(s, false, true); - queryCriteria.add(new CriteriaEntry(OperationKey.ENDS_WITH, s)); - return this; - } + /** + * Crates new CriteriaEntry with leading wildcard
+ * NOTE: mind your schema and execution times as leading wildcards may not be supported. + * + * @param s + * @return + */ + public Criteria endsWith(String s) { + assertNoBlankInWildcardedQuery(s, false, true); + queryCriteria.add(new CriteriaEntry(OperationKey.ENDS_WITH, s)); + return this; + } - /** - * Crates new CriteriaEntry with trailing - - * - * @return - */ - public Criteria not() { - this.negating = true; - return this; - } + /** + * Crates new CriteriaEntry with trailing - + * + * @return + */ + public Criteria not() { + this.negating = true; + return this; + } - /** - * Crates new CriteriaEntry with trailing ~ - * - * @param s - * @return - */ - public Criteria fuzzy(String s) { + /** + * Crates new CriteriaEntry with trailing ~ + * + * @param s + * @return + */ + public Criteria fuzzy(String s) { queryCriteria.add(new CriteriaEntry(OperationKey.FUZZY, s)); return this; - } + } - /** - * Crates new CriteriaEntry allowing native elasticsearch expressions - * - * @param s - * @return - */ - public Criteria expression(String s) { - queryCriteria.add(new CriteriaEntry(OperationKey.EXPRESSION, s)); - return this; - } + /** + * Crates new CriteriaEntry allowing native elasticsearch expressions + * + * @param s + * @return + */ + public Criteria expression(String s) { + queryCriteria.add(new CriteriaEntry(OperationKey.EXPRESSION, s)); + return this; + } - /** - * Boost positive hit with given factor. eg. ^2.3 - * - * @param boost - * @return - */ - public Criteria boost(float boost) { - if (boost < 0) { - throw new InvalidDataAccessApiUsageException("Boost must not be negative."); - } - this.boost = boost; - return this; - } + /** + * Boost positive hit with given factor. eg. ^2.3 + * + * @param boost + * @return + */ + public Criteria boost(float boost) { + if (boost < 0) { + throw new InvalidDataAccessApiUsageException("Boost must not be negative."); + } + this.boost = boost; + return this; + } - /** - * Crates new CriteriaEntry for {@code RANGE [lowerBound TO upperBound]} - * - * @param lowerBound - * @param upperBound - * @return - */ - public Criteria between(Object lowerBound, Object upperBound) { - if (lowerBound == null && upperBound == null) { - throw new InvalidDataAccessApiUsageException("Range [* TO *] is not allowed"); - } + /** + * Crates new CriteriaEntry for {@code RANGE [lowerBound TO upperBound]} + * + * @param lowerBound + * @param upperBound + * @return + */ + public Criteria between(Object lowerBound, Object upperBound) { + if (lowerBound == null && upperBound == null) { + throw new InvalidDataAccessApiUsageException("Range [* TO *] is not allowed"); + } - queryCriteria.add(new CriteriaEntry(OperationKey.BETWEEN, new Object[]{lowerBound, upperBound})); - return this; - } + queryCriteria.add(new CriteriaEntry(OperationKey.BETWEEN, new Object[]{lowerBound, upperBound})); + return this; + } - /** - * Crates new CriteriaEntry for {@code RANGE [* TO upperBound]} - * - * @param upperBound - * @return - */ - public Criteria lessThanEqual(Object upperBound) { - between(null, upperBound); - return this; - } + /** + * Crates new CriteriaEntry for {@code RANGE [* TO upperBound]} + * + * @param upperBound + * @return + */ + public Criteria lessThanEqual(Object upperBound) { + between(null, upperBound); + return this; + } - /** - * Crates new CriteriaEntry for {@code RANGE [lowerBound TO *]} - * - * @param lowerBound - * @return - */ - public Criteria greaterThanEqual(Object lowerBound) { - between(lowerBound, null); - return this; - } + /** + * Crates new CriteriaEntry for {@code RANGE [lowerBound TO *]} + * + * @param lowerBound + * @return + */ + public Criteria greaterThanEqual(Object lowerBound) { + between(lowerBound, null); + return this; + } - /** - * Crates new CriteriaEntry for multiple values {@code (arg0 arg1 arg2 ...)} - * - * @param values - * @return - */ - public Criteria in(Object... values) { - if (values.length == 0 || (values.length > 1 && values[1] instanceof Collection)) { - throw new InvalidDataAccessApiUsageException("At least one element " - + (values.length > 0 ? ("of argument of type " + values[1].getClass().getName()) : "") - + " has to be present."); - } - return in(Arrays.asList(values)); - } + /** + * Crates new CriteriaEntry for multiple values {@code (arg0 arg1 arg2 ...)} + * + * @param values + * @return + */ + public Criteria in(Object... values) { + if (values.length == 0 || (values.length > 1 && values[1] instanceof Collection)) { + throw new InvalidDataAccessApiUsageException("At least one element " + + (values.length > 0 ? ("of argument of type " + values[1].getClass().getName()) : "") + + " has to be present."); + } + return in(Arrays.asList(values)); + } - /** - * Crates new CriteriaEntry for multiple values {@code (arg0 arg1 arg2 ...)} - * - * @param values the collection containing the values to match against - * @return - */ - public Criteria in(Iterable values) { - Assert.notNull(values, "Collection of 'in' values must not be null"); + /** + * Crates new CriteriaEntry for multiple values {@code (arg0 arg1 arg2 ...)} + * + * @param values the collection containing the values to match against + * @return + */ + public Criteria in(Iterable values) { + Assert.notNull(values, "Collection of 'in' values must not be null"); 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 * + * @param location {@link org.springframework.data.elasticsearch.core.geo.GeoPoint} 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) { + public Criteria within(GeoPoint 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})); @@ -367,125 +367,172 @@ public class Criteria { } /** - * Creates new CriteriaEntry for {@code location BBOX bounding box} - * @param bbox {@link org.springframework.data.elasticsearch.core.geo.GeoBBox} center coordinates + * Creates new CriteriaEntry for {@code geoLocation WITHIN distance} * + * @param geoLocation {@link String} center point + * supported formats: + * lat on = > "41.2,45.1", + * geohash = > "asd9as0d" + * @param distance {@link String} radius as a string (e.g. : '100km'). + * Distance unit : + * either mi/miles or km can be set + * @return + */ + public Criteria within(String geoLocation, String distance) { + Assert.isTrue(StringUtils.isNotBlank(geoLocation), "geoLocation value must not be null"); + filterCriteria.add(new CriteriaEntry(OperationKey.WITHIN, new Object[]{geoLocation, distance})); + return this; + } + + /** + * Creates new CriteriaEntry for {@code location BBOX bounding box} + * + * @param bbox {@link org.springframework.data.elasticsearch.core.geo.GeoEnvelope} bounding box(left top corner + right bottom corner) * @return Criteria the chaind criteria with the new 'bbox' criteria included. */ - public Criteria bbox(GeoBBox bbox) { + public Criteria bbox(GeoEnvelope 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)) { - throw new InvalidDataAccessApiUsageException("Cannot constructQuery '" + (leadingWildcard ? "*" : "") + "\"" - + searchString + "\"" + (trailingWildcard ? "*" : "") + "'. Use epxression or mulitple clauses instead."); - } - } - /** - * Field targeted by this Criteria - * - * @return - */ - public Field getField() { - return this.field; - } + /** + * Creates new CriteriaEntry for bounding box created from points + * + * @param topLeft left top corner of bounding box + * @param bottomRight right bottom corner of bounding box + * @return Criteria the chaind criteria with the new 'bbox' criteria included. + */ + public Criteria bbox(String topLeft, String bottomRight) { + Assert.isTrue(StringUtils.isNotBlank(topLeft), "topLeft point must not be empty"); + Assert.isTrue(StringUtils.isNotBlank(bottomRight), "bottomRight point must not be empty"); + filterCriteria.add(new CriteriaEntry(OperationKey.BBOX, new Object[]{topLeft, bottomRight})); + return this; + } - public Set getQueryCriteriaEntries() { - return Collections.unmodifiableSet(this.queryCriteria); - } + /** + * Creates new CriteriaEntry for bounding box created from points + * + * @param topLeft left top corner of bounding box + * @param bottomRight right bottom corner of bounding box + * @return Criteria the chaind criteria with the new 'bbox' criteria included. + */ + public Criteria bbox(GeoPoint topLeft, GeoPoint bottomRight) { + Assert.notNull(topLeft, "topLeft point must not be null"); + Assert.notNull(bottomRight, "bottomRight point must not be null"); + filterCriteria.add(new CriteriaEntry(OperationKey.BBOX, new Object[]{topLeft, bottomRight})); + return this; + } - public Set getFilterCriteriaEntries() { - return Collections.unmodifiableSet(this.filterCriteria); - } + private void assertNoBlankInWildcardedQuery(String searchString, boolean leadingWildcard, boolean trailingWildcard) { + if (StringUtils.contains(searchString, CRITERIA_VALUE_SEPERATOR)) { + throw new InvalidDataAccessApiUsageException("Cannot constructQuery '" + (leadingWildcard ? "*" : "") + "\"" + + searchString + "\"" + (trailingWildcard ? "*" : "") + "'. Use epxression or mulitple clauses instead."); + } + } + + /** + * Field targeted by this Criteria + * + * @return + */ + public Field getField() { + return this.field; + } + + public Set getQueryCriteriaEntries() { + return Collections.unmodifiableSet(this.queryCriteria); + } + + public Set getFilterCriteriaEntries() { + return Collections.unmodifiableSet(this.filterCriteria); + } public Set getFilterCriteria() { return filterCriteria; } - /** - * Conjunction to be used with this criteria (AND | OR) - * - * @return - */ - public String getConjunctionOperator() { - return AND_OPERATOR; - } + /** + * Conjunction to be used with this criteria (AND | OR) + * + * @return + */ + public String getConjunctionOperator() { + return AND_OPERATOR; + } - public List getCriteriaChain() { - return Collections.unmodifiableList(this.criteriaChain); - } + public List getCriteriaChain() { + return Collections.unmodifiableList(this.criteriaChain); + } - public boolean isNegating() { - return this.negating; - } + public boolean isNegating() { + return this.negating; + } - public boolean isAnd() { - return AND_OPERATOR == getConjunctionOperator(); - } + public boolean isAnd() { + return AND_OPERATOR == getConjunctionOperator(); + } - public boolean isOr() { - return OR_OPERATOR == getConjunctionOperator(); - } + public boolean isOr() { + return OR_OPERATOR == getConjunctionOperator(); + } - public float getBoost() { - return this.boost; - } + public float getBoost() { + return this.boost; + } - static class OrCriteria extends Criteria { + static class OrCriteria extends Criteria { - public OrCriteria() { - super(); - } + public OrCriteria() { + super(); + } - public OrCriteria(Field field) { - super(field); - } + public OrCriteria(Field field) { + super(field); + } - public OrCriteria(List criteriaChain, Field field) { - super(criteriaChain, field); - } + public OrCriteria(List criteriaChain, Field field) { + super(criteriaChain, field); + } - public OrCriteria(List criteriaChain, String fieldname) { - super(criteriaChain, fieldname); - } + public OrCriteria(List criteriaChain, String fieldname) { + super(criteriaChain, fieldname); + } - public OrCriteria(String fieldname) { - super(fieldname); - } + public OrCriteria(String fieldname) { + super(fieldname); + } - @Override - public String getConjunctionOperator() { - return OR_OPERATOR; - } + @Override + public String getConjunctionOperator() { + return OR_OPERATOR; + } - } + } - public enum OperationKey { - EQUALS, CONTAINS, STARTS_WITH, ENDS_WITH, EXPRESSION, BETWEEN, FUZZY, IN, WITHIN, BBOX, NEAR; - } + public enum OperationKey { + EQUALS, CONTAINS, STARTS_WITH, ENDS_WITH, EXPRESSION, BETWEEN, FUZZY, IN, WITHIN, BBOX, NEAR; + } - public static class CriteriaEntry { + public static class CriteriaEntry { - private OperationKey key; - private Object value; + private OperationKey key; + private Object value; - CriteriaEntry(OperationKey key, Object value) { - this.key = key; - this.value = value; - } + CriteriaEntry(OperationKey key, Object value) { + this.key = key; + this.value = value; + } - public OperationKey getKey() { - return key; - } + public OperationKey getKey() { + return key; + } - public Object getValue() { - return value; - } + public Object getValue() { + return value; + } - } + } } diff --git a/src/test/java/org/springframework/data/elasticsearch/ArticleBuilder.java b/src/test/java/org/springframework/data/elasticsearch/ArticleBuilder.java deleted file mode 100644 index bba9a5844..000000000 --- a/src/test/java/org/springframework/data/elasticsearch/ArticleBuilder.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.springframework.data.elasticsearch; - -import org.springframework.data.elasticsearch.core.query.IndexQuery; - -/** - * Simple type to test facets - */ -public class ArticleBuilder { - - private Article resutl; - - public ArticleBuilder(String id) { - resutl = new Article(id); - } - - public ArticleBuilder title(String title) { - resutl.setTitle(title); - return this; - } - - public ArticleBuilder addAuthor(String author) { - resutl.getAuthors().add(author); - return this; - } - - public ArticleBuilder addPublishedYear(Integer year) { - resutl.getPublishedYears().add(year); - return this; - } - - public ArticleBuilder score(int score) { - resutl.setScore(score); - return this; - } - - public Article build() { - return resutl; - } - - public IndexQuery buildIndex() { - IndexQuery indexQuery = new IndexQuery(); - indexQuery.setId(resutl.getId()); - indexQuery.setObject(resutl); - return indexQuery; - } - -} diff --git a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java index bd20de851..06bc93ad1 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java @@ -27,11 +27,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; -import org.springframework.data.elasticsearch.GeoAuthor; import org.springframework.data.elasticsearch.SampleEntity; import org.springframework.data.elasticsearch.SampleMappingEntity; -import org.springframework.data.elasticsearch.core.geo.GeoBBox; -import org.springframework.data.elasticsearch.core.geo.GeoLocation; import org.springframework.data.elasticsearch.core.query.*; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -46,7 +43,8 @@ import static org.elasticsearch.index.query.QueryBuilders.fieldQuery; import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; /** * @author Rizwan Idrees @@ -65,12 +63,6 @@ public class ElasticsearchTemplateTests { elasticsearchTemplate.deleteIndex(SampleEntity.class); elasticsearchTemplate.createIndex(SampleEntity.class); elasticsearchTemplate.refresh(SampleEntity.class, true); - - elasticsearchTemplate.deleteIndex(GeoAuthor.class); - elasticsearchTemplate.createIndex(GeoAuthor.class); - elasticsearchTemplate.refresh(GeoAuthor.class, true); - - elasticsearchTemplate.putMapping(GeoAuthor.class); } @Test @@ -725,86 +717,4 @@ public class ElasticsearchTemplateTests { // then assertThat(elasticsearchTemplate.indexExists(clazz), is(false)); } - - @Test - public void shouldPutMappingForGivenEntityWithGeoLocation()throws Exception{ - //given - Class entity = GeoAuthor.class; - elasticsearchTemplate.createIndex(entity); - //when - assertThat(elasticsearchTemplate.putMapping(entity) , is(true)) ; - } - - @Test - public void shouldReturnListForGivenCriteriaWithGeoLocation(){ - //given - List indexQueries = new ArrayList(); - //first document - String documentId = randomNumeric(5); - GeoAuthor geoAuthor1 = new GeoAuthor(); - geoAuthor1.setId(documentId); - geoAuthor1.setName("Franck Marchand"); - geoAuthor1.setLocation(new GeoLocation(45.7806d, 3.0875d)); // Clermont-Ferrand - - IndexQuery indexQuery1 = new IndexQuery(); - indexQuery1.setId(documentId); - indexQuery1.setObject(geoAuthor1); - indexQueries.add(indexQuery1); - - //second document - String documentId2 = randomNumeric(5); - GeoAuthor geoAuthor2 = new GeoAuthor(); - geoAuthor2.setId(documentId2); - geoAuthor2.setName("Mohsin Husen"); - geoAuthor2.setLocation(new GeoLocation(51.5171d, 0.1062d)); // London - - IndexQuery indexQuery2 = new IndexQuery(); - indexQuery2.setId(documentId2); - indexQuery2.setObject(geoAuthor2); - - indexQueries.add(indexQuery2); - - //third document - String documentId3 = randomNumeric(5); - GeoAuthor geoAuthor3 = new GeoAuthor(); - geoAuthor3.setId(documentId3); - geoAuthor3.setName("Rizwan Idrees"); - geoAuthor3.setLocation(new GeoLocation(51.5171d, 0.1062d)); // London - - IndexQuery indexQuery3 = new IndexQuery(); - indexQuery3.setId(documentId3); - indexQuery3.setObject(geoAuthor3); - - indexQueries.add(indexQuery3); - //when - elasticsearchTemplate.bulkIndex(indexQueries); - elasticsearchTemplate.refresh(GeoAuthor.class, true); - //when - CriteriaQuery geoLocationCriteriaQuery = new CriteriaQuery( - new Criteria("location").within(new GeoLocation(45.7806d, 3.0875d), "20km")); - - - List geoAuthorsForGeoCriteria = elasticsearchTemplate.queryForList(geoLocationCriteriaQuery,GeoAuthor.class); - //then - assertThat(geoAuthorsForGeoCriteria.size(),is(1)); - assertEquals("Franck Marchand", geoAuthorsForGeoCriteria.get(0).getName()); - - // query/filter geo distance mixed query - CriteriaQuery geoLocationCriteriaQuery2 = new CriteriaQuery( - new Criteria("name").is("Mohsin Husen").and("location").within(new GeoLocation(51.5171d, 0.1062d), "20km")); - List geoAuthorsForGeoCriteria2 = elasticsearchTemplate.queryForList(geoLocationCriteriaQuery2,GeoAuthor.class); - - assertThat(geoAuthorsForGeoCriteria2.size(),is(1)); - assertEquals("Mohsin Husen", geoAuthorsForGeoCriteria2.get(0).getName()); - - // bbox query - CriteriaQuery geoLocationCriteriaQuery3 = new CriteriaQuery( - new Criteria("location").bbox( - new GeoBBox(new GeoLocation(53.5171d, 0), - new GeoLocation(49.5171d, 0.2062d)))); - List geoAuthorsForGeoCriteria3 = elasticsearchTemplate.queryForList(geoLocationCriteriaQuery3,GeoAuthor.class); - - assertThat(geoAuthorsForGeoCriteria3.size(),is(2)); - assertThat(geoAuthorsForGeoCriteria3, containsInAnyOrder(hasProperty("name", equalTo("Mohsin Husen")), hasProperty("name",equalTo("Rizwan Idrees")))); - } } diff --git a/src/test/java/org/springframework/data/elasticsearch/Article.java b/src/test/java/org/springframework/data/elasticsearch/core/facet/ArticleEntity.java similarity index 92% rename from src/test/java/org/springframework/data/elasticsearch/Article.java rename to src/test/java/org/springframework/data/elasticsearch/core/facet/ArticleEntity.java index 89b46ca2b..77847b317 100644 --- a/src/test/java/org/springframework/data/elasticsearch/Article.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/facet/ArticleEntity.java @@ -1,4 +1,4 @@ -package org.springframework.data.elasticsearch; +package org.springframework.data.elasticsearch.core.facet; import org.springframework.data.annotation.Id; import org.springframework.data.elasticsearch.annotations.*; @@ -15,7 +15,7 @@ import static org.springframework.data.elasticsearch.annotations.FieldType.Strin * Simple type to test facets */ @Document(indexName = "articles", type = "article", shards = 1, replicas = 0, refreshInterval = "-1") -public class Article { +public class ArticleEntity { @Id private String id; @@ -36,11 +36,11 @@ public class Article { private int score; - public Article() { + private ArticleEntity(){ } - public Article(String id) { + public ArticleEntity(String id) { this.id = id; } diff --git a/src/test/java/org/springframework/data/elasticsearch/core/facet/ArticleEntityBuilder.java b/src/test/java/org/springframework/data/elasticsearch/core/facet/ArticleEntityBuilder.java new file mode 100644 index 000000000..4b55b7328 --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/core/facet/ArticleEntityBuilder.java @@ -0,0 +1,47 @@ +package org.springframework.data.elasticsearch.core.facet; + +import org.springframework.data.elasticsearch.core.query.IndexQuery; + +/** + * Simple type to test facets + */ +public class ArticleEntityBuilder { + + private ArticleEntity result; + + public ArticleEntityBuilder(String id) { + result = new ArticleEntity(id); + } + + public ArticleEntityBuilder title(String title) { + result.setTitle(title); + return this; + } + + public ArticleEntityBuilder addAuthor(String author) { + result.getAuthors().add(author); + return this; + } + + public ArticleEntityBuilder addPublishedYear(Integer year) { + result.getPublishedYears().add(year); + return this; + } + + public ArticleEntityBuilder score(int score) { + result.setScore(score); + return this; + } + + public ArticleEntity build() { + return result; + } + + public IndexQuery buildIndex() { + IndexQuery indexQuery = new IndexQuery(); + indexQuery.setId(result.getId()); + indexQuery.setObject(result); + return indexQuery; + } + +} diff --git a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateFacetTests.java b/src/test/java/org/springframework/data/elasticsearch/core/facet/ElasticsearchTemplateFacetTests.java similarity index 86% rename from src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateFacetTests.java rename to src/test/java/org/springframework/data/elasticsearch/core/facet/ElasticsearchTemplateFacetTests.java index 47c29c16d..dda6f7f80 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateFacetTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/facet/ElasticsearchTemplateFacetTests.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.elasticsearch.core; +package org.springframework.data.elasticsearch.core.facet; import org.elasticsearch.index.query.FilterBuilders; import org.elasticsearch.search.facet.FacetBuilders; @@ -21,8 +21,8 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.elasticsearch.Article; -import org.springframework.data.elasticsearch.ArticleBuilder; +import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; +import org.springframework.data.elasticsearch.core.FacetedPage; import org.springframework.data.elasticsearch.core.facet.request.NativeFacetRequest; import org.springframework.data.elasticsearch.core.facet.request.RangeFacetRequestBuilder; import org.springframework.data.elasticsearch.core.facet.request.TermFacetRequestBuilder; @@ -62,21 +62,21 @@ public class ElasticsearchTemplateFacetTests { @Before public void before() { - elasticsearchTemplate.deleteIndex(Article.class); - elasticsearchTemplate.createIndex(Article.class); - elasticsearchTemplate.putMapping(Article.class); - elasticsearchTemplate.refresh(Article.class, true); + elasticsearchTemplate.deleteIndex(ArticleEntity.class); + elasticsearchTemplate.createIndex(ArticleEntity.class); + elasticsearchTemplate.putMapping(ArticleEntity.class); + elasticsearchTemplate.refresh(ArticleEntity.class, true); - IndexQuery article1 = new ArticleBuilder("1").title("article four").addAuthor(RIZWAN_IDREES).addAuthor(ARTUR_KONCZAK).addAuthor(MOHSIN_HUSEN).addAuthor(JONATHAN_YAN).score(10).buildIndex(); - IndexQuery article2 = new ArticleBuilder("2").title("article three").addAuthor(RIZWAN_IDREES).addAuthor(ARTUR_KONCZAK).addAuthor(MOHSIN_HUSEN).addPublishedYear(YEAR_2000).score(20).buildIndex(); - IndexQuery article3 = new ArticleBuilder("3").title("article two").addAuthor(RIZWAN_IDREES).addAuthor(ARTUR_KONCZAK).addPublishedYear(YEAR_2001).addPublishedYear(YEAR_2000).score(30).buildIndex(); - IndexQuery article4 = new ArticleBuilder("4").title("article one").addAuthor(RIZWAN_IDREES).addPublishedYear(YEAR_2002).addPublishedYear(YEAR_2001).addPublishedYear(YEAR_2000).score(40).buildIndex(); + IndexQuery article1 = new ArticleEntityBuilder("1").title("article four").addAuthor(RIZWAN_IDREES).addAuthor(ARTUR_KONCZAK).addAuthor(MOHSIN_HUSEN).addAuthor(JONATHAN_YAN).score(10).buildIndex(); + IndexQuery article2 = new ArticleEntityBuilder("2").title("article three").addAuthor(RIZWAN_IDREES).addAuthor(ARTUR_KONCZAK).addAuthor(MOHSIN_HUSEN).addPublishedYear(YEAR_2000).score(20).buildIndex(); + IndexQuery article3 = new ArticleEntityBuilder("3").title("article two").addAuthor(RIZWAN_IDREES).addAuthor(ARTUR_KONCZAK).addPublishedYear(YEAR_2001).addPublishedYear(YEAR_2000).score(30).buildIndex(); + IndexQuery article4 = new ArticleEntityBuilder("4").title("article one").addAuthor(RIZWAN_IDREES).addPublishedYear(YEAR_2002).addPublishedYear(YEAR_2001).addPublishedYear(YEAR_2000).score(40).buildIndex(); elasticsearchTemplate.index(article1); elasticsearchTemplate.index(article2); elasticsearchTemplate.index(article3); elasticsearchTemplate.index(article4); - elasticsearchTemplate.refresh(Article.class, true); + elasticsearchTemplate.refresh(ArticleEntity.class, true); } @Test @@ -86,7 +86,7 @@ public class ElasticsearchTemplateFacetTests { String facetName = "fauthors"; SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).withFacet(new TermFacetRequestBuilder(facetName).fields("authors.untouched").build()).build(); // when - FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); + FacetedPage result = elasticsearchTemplate.queryForPage(searchQuery, ArticleEntity.class); // then assertThat(result.getNumberOfElements(), is(equalTo(4))); @@ -117,7 +117,7 @@ public class ElasticsearchTemplateFacetTests { .withFilter(FilterBuilders.notFilter(FilterBuilders.termFilter("title", "four"))) .withFacet(new TermFacetRequestBuilder(facetName).applyQueryFilter().fields("authors.untouched").build()).build(); // when - FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); + FacetedPage result = elasticsearchTemplate.queryForPage(searchQuery, ArticleEntity.class); // then assertThat(result.getNumberOfElements(), is(equalTo(3))); @@ -143,7 +143,7 @@ public class ElasticsearchTemplateFacetTests { .withFilter(FilterBuilders.notFilter(FilterBuilders.termFilter("title", "four"))) .withFacet(new TermFacetRequestBuilder(facetName).applyQueryFilter().fields("authors.untouched").excludeTerms(RIZWAN_IDREES, ARTUR_KONCZAK).build()).build(); // when - FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); + FacetedPage result = elasticsearchTemplate.queryForPage(searchQuery, ArticleEntity.class); // then assertThat(result.getNumberOfElements(), is(equalTo(3))); @@ -165,7 +165,7 @@ public class ElasticsearchTemplateFacetTests { SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) .withFacet(new TermFacetRequestBuilder(facetName).fields("authors.untouched").ascTerm().build()).build(); // when - FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); + FacetedPage result = elasticsearchTemplate.queryForPage(searchQuery, ArticleEntity.class); // then assertThat(result.getNumberOfElements(), is(equalTo(4))); @@ -196,7 +196,7 @@ public class ElasticsearchTemplateFacetTests { SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) .withFacet(new TermFacetRequestBuilder(facetName).fields("authors.untouched").ascCount().build()).build(); // when - FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); + FacetedPage result = elasticsearchTemplate.queryForPage(searchQuery, ArticleEntity.class); // then assertThat(result.getNumberOfElements(), is(equalTo(4))); @@ -226,7 +226,7 @@ public class ElasticsearchTemplateFacetTests { SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) .withFacet(new TermFacetRequestBuilder(facetName).fields("publishedYears").descCount().build()).build(); // when - FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); + FacetedPage result = elasticsearchTemplate.queryForPage(searchQuery, ArticleEntity.class); // then assertThat(result.getNumberOfElements(), is(equalTo(4))); @@ -256,7 +256,7 @@ public class ElasticsearchTemplateFacetTests { SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) .withFacet(new TermFacetRequestBuilder(facetName).fields("publishedYears", "authors.untouched").ascTerm().build()).build(); // when - FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); + FacetedPage result = elasticsearchTemplate.queryForPage(searchQuery, ArticleEntity.class); // then assertThat(result.getNumberOfElements(), is(equalTo(4))); @@ -305,7 +305,7 @@ public class ElasticsearchTemplateFacetTests { .withFacet(new TermFacetRequestBuilder(stringFacetName).fields("authors.untouched").ascTerm().build()) .build(); // when - FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); + FacetedPage result = elasticsearchTemplate.queryForPage(searchQuery, ArticleEntity.class); // then assertThat(result.getNumberOfElements(), is(equalTo(4))); @@ -352,7 +352,7 @@ public class ElasticsearchTemplateFacetTests { SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) .withFacet(new NativeFacetRequest(FacetBuilders.termsFacet(facetName).field("publishedYears"))).build(); // when - FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); + FacetedPage result = elasticsearchTemplate.queryForPage(searchQuery, ArticleEntity.class); // then assertThat(result.getNumberOfElements(), is(equalTo(4))); @@ -381,7 +381,7 @@ public class ElasticsearchTemplateFacetTests { .withFilter(FilterBuilders.notFilter(FilterBuilders.termFilter("title", "four"))) .withFacet(new TermFacetRequestBuilder(facetName).applyQueryFilter().fields("authors.untouched").regex("Art.*").build()).build(); // when - FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); + FacetedPage result = elasticsearchTemplate.queryForPage(searchQuery, ArticleEntity.class); // then assertThat(result.getNumberOfElements(), is(equalTo(3))); @@ -403,7 +403,7 @@ public class ElasticsearchTemplateFacetTests { .withFilter(FilterBuilders.notFilter(FilterBuilders.termFilter("title", "four"))) .withFacet(new TermFacetRequestBuilder(facetName).applyQueryFilter().fields("authors.untouched").allTerms().build()).build(); // when - FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); + FacetedPage result = elasticsearchTemplate.queryForPage(searchQuery, ArticleEntity.class); // then assertThat(result.getNumberOfElements(), is(equalTo(3))); @@ -423,7 +423,7 @@ public class ElasticsearchTemplateFacetTests { .to(YEAR_2000).range(YEAR_2000, YEAR_2002).from(YEAR_2002).build() ).build(); // when - FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); + FacetedPage result = elasticsearchTemplate.queryForPage(searchQuery, ArticleEntity.class); // then assertThat(result.getNumberOfElements(), is(equalTo(4))); @@ -460,7 +460,7 @@ public class ElasticsearchTemplateFacetTests { .to(YEAR_2000).range(YEAR_2000, YEAR_2002).from(YEAR_2002).build() ).build(); // when - FacetedPage
result = elasticsearchTemplate.queryForPage(searchQuery, Article.class); + FacetedPage result = elasticsearchTemplate.queryForPage(searchQuery, ArticleEntity.class); // then assertThat(result.getNumberOfElements(), is(equalTo(4))); diff --git a/src/test/java/org/springframework/data/elasticsearch/core/geo/AuthorMarkerAnnotatedEntity.java b/src/test/java/org/springframework/data/elasticsearch/core/geo/AuthorMarkerAnnotatedEntity.java new file mode 100644 index 000000000..fad86fbca --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/core/geo/AuthorMarkerAnnotatedEntity.java @@ -0,0 +1,77 @@ +/* + * 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 org.springframework.data.annotation.Id; +import org.springframework.data.elasticsearch.annotations.Document; +import org.springframework.data.elasticsearch.annotations.GeoPointField; + +/** + * @author Franck Marchand + */ +@Document(indexName = "test-geo-index", type = "geo-annotation-point-type", indexStoreType = "memory", shards = 1, replicas = 0, refreshInterval = "-1") +public class AuthorMarkerAnnotatedEntity { + + @Id + private String id; + private String name; + + @GeoPointField + private String location; + + @GeoPointField + private double[] additionalLocation; + + private AuthorMarkerAnnotatedEntity() { + + } + + public AuthorMarkerAnnotatedEntity(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLocation() { + return location; + } + + public void setLocation(String location) { + this.location = location; + } + + public double[] getAdditionalLocation() { + return additionalLocation; + } + + public void setAdditionalLocation(double... additionalLocation) { + this.additionalLocation = additionalLocation; + } +} diff --git a/src/test/java/org/springframework/data/elasticsearch/core/geo/AuthorMarkerAnnotatedEntityBuilder.java b/src/test/java/org/springframework/data/elasticsearch/core/geo/AuthorMarkerAnnotatedEntityBuilder.java new file mode 100644 index 000000000..2912d88f5 --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/core/geo/AuthorMarkerAnnotatedEntityBuilder.java @@ -0,0 +1,53 @@ +/* + * 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 org.springframework.data.elasticsearch.core.query.IndexQuery; + +public class AuthorMarkerAnnotatedEntityBuilder { + + private AuthorMarkerAnnotatedEntity result; + + public AuthorMarkerAnnotatedEntityBuilder(String id) { + result = new AuthorMarkerAnnotatedEntity(id); + } + + public AuthorMarkerAnnotatedEntityBuilder name(String name) { + result.setName(name); + return this; + } + + public AuthorMarkerAnnotatedEntityBuilder location(String location) { + result.setLocation(location); + return this; + } + + public AuthorMarkerAnnotatedEntityBuilder additionalLocation(double... location) { + result.setAdditionalLocation(location); + return this; + } + + public AuthorMarkerAnnotatedEntity build() { + return result; + } + + public IndexQuery buildIndex() { + IndexQuery indexQuery = new IndexQuery(); + indexQuery.setId(result.getId()); + indexQuery.setObject(result); + return indexQuery; + } +} diff --git a/src/test/java/org/springframework/data/elasticsearch/GeoAuthor.java b/src/test/java/org/springframework/data/elasticsearch/core/geo/AuthorMarkerEntity.java similarity index 68% rename from src/test/java/org/springframework/data/elasticsearch/GeoAuthor.java rename to src/test/java/org/springframework/data/elasticsearch/core/geo/AuthorMarkerEntity.java index 671e7d101..007e04955 100644 --- a/src/test/java/org/springframework/data/elasticsearch/GeoAuthor.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/geo/AuthorMarkerEntity.java @@ -13,23 +13,30 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.elasticsearch; +package org.springframework.data.elasticsearch.core.geo; import org.springframework.data.annotation.Id; import org.springframework.data.elasticsearch.annotations.Document; -import org.springframework.data.elasticsearch.core.geo.GeoLocation; +import org.springframework.data.elasticsearch.core.geo.GeoPoint; /** * @author Franck Marchand */ -@Document(indexName = "test-geo-index", type = "test-geo-type") -public class GeoAuthor { +@Document(indexName = "test-geo-index", type = "geo-class-point-type", indexStoreType = "memory", shards = 1, replicas = 0, refreshInterval = "-1") +public class AuthorMarkerEntity { @Id private String id; private String name; - private GeoLocation location; + private GeoPoint location; + + private AuthorMarkerEntity(){ + } + + public AuthorMarkerEntity(String id){ + this.id = id; + } public String getId() { return id; @@ -47,11 +54,11 @@ public class GeoAuthor { this.name = name; } - public GeoLocation getLocation() { + public GeoPoint getLocation() { return location; } - public void setLocation(GeoLocation location) { + public void setLocation(GeoPoint location) { this.location = location; } } diff --git a/src/test/java/org/springframework/data/elasticsearch/core/geo/AuthorMarkerEntityBuilder.java b/src/test/java/org/springframework/data/elasticsearch/core/geo/AuthorMarkerEntityBuilder.java new file mode 100644 index 000000000..294b6fd8c --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/core/geo/AuthorMarkerEntityBuilder.java @@ -0,0 +1,48 @@ +/* + * 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 org.springframework.data.elasticsearch.core.query.IndexQuery; + +public class AuthorMarkerEntityBuilder { + + private AuthorMarkerEntity result; + + public AuthorMarkerEntityBuilder(String id) { + result = new AuthorMarkerEntity(id); + } + + public AuthorMarkerEntityBuilder name(String name) { + result.setName(name); + return this; + } + + public AuthorMarkerEntityBuilder location(double latitude, double longitude) { + result.setLocation(new GeoPoint(latitude, longitude)); + return this; + } + + public AuthorMarkerEntity build() { + return result; + } + + public IndexQuery buildIndex() { + IndexQuery indexQuery = new IndexQuery(); + indexQuery.setId(result.getId()); + indexQuery.setObject(result); + return indexQuery; + } +} diff --git a/src/test/java/org/springframework/data/elasticsearch/core/geo/ElasticsearchTemplateGeoTests.java b/src/test/java/org/springframework/data/elasticsearch/core/geo/ElasticsearchTemplateGeoTests.java new file mode 100644 index 000000000..d2222b158 --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/core/geo/ElasticsearchTemplateGeoTests.java @@ -0,0 +1,236 @@ +/* + * 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 org.elasticsearch.index.query.FilterBuilders; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; +import org.springframework.data.elasticsearch.core.query.Criteria; +import org.springframework.data.elasticsearch.core.query.CriteriaQuery; +import org.springframework.data.elasticsearch.core.query.IndexQuery; +import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.ArrayList; +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.assertEquals; + +/** + * @author Rizwan Idrees + * @author Mohsin Husen + * @author Franck Marchand + * @author Artur Konczak + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:elasticsearch-template-test.xml") +public class ElasticsearchTemplateGeoTests { + + @Autowired + private ElasticsearchTemplate elasticsearchTemplate; + + @Before + public void before() { + + } + + private void loadClassBaseEntities() { + elasticsearchTemplate.deleteIndex(AuthorMarkerEntity.class); + elasticsearchTemplate.createIndex(AuthorMarkerEntity.class); + elasticsearchTemplate.refresh(AuthorMarkerEntity.class, true); + elasticsearchTemplate.putMapping(AuthorMarkerEntity.class); + + List indexQueries = new ArrayList(); + indexQueries.add(new AuthorMarkerEntityBuilder("1").name("Franck Marchand").location(45.7806d, 3.0875d).buildIndex()); + indexQueries.add(new AuthorMarkerEntityBuilder("2").name("Mohsin Husen").location(51.5171d, 0.1062d).buildIndex()); + indexQueries.add(new AuthorMarkerEntityBuilder("3").name("Rizwan Idrees").location(51.5171d, 0.1062d).buildIndex()); + elasticsearchTemplate.bulkIndex(indexQueries); + elasticsearchTemplate.refresh(AuthorMarkerEntity.class, true); + } + + private void loadAnnotationBaseEntities() { + elasticsearchTemplate.deleteIndex(AuthorMarkerAnnotatedEntity.class); + elasticsearchTemplate.createIndex(AuthorMarkerAnnotatedEntity.class); + elasticsearchTemplate.refresh(AuthorMarkerAnnotatedEntity.class, true); + elasticsearchTemplate.putMapping(AuthorMarkerAnnotatedEntity.class); + + List indexQueries = new ArrayList(); + double[] latLonArray = {0.100000, 51.000000}; + String lonLatString = "51.000000, 0.100000"; + String geohash = "u1044k2bd6u"; + indexQueries.add(new AuthorMarkerAnnotatedEntityBuilder("2").name("Mohsin Husen").location(geohash.substring(3)).additionalLocation(latLonArray).buildIndex()); + indexQueries.add(new AuthorMarkerAnnotatedEntityBuilder("1").name("Artur Konczak").location(lonLatString).additionalLocation(latLonArray).buildIndex()); + indexQueries.add(new AuthorMarkerAnnotatedEntityBuilder("3").name("Rizwan Idrees").location(geohash).additionalLocation(latLonArray).buildIndex()); + + elasticsearchTemplate.bulkIndex(indexQueries); + elasticsearchTemplate.refresh(AuthorMarkerEntity.class, true); + } + + @Test + public void shouldPutMappingForGivenEntityWithGeoLocation() throws Exception { + //given + Class entity = AuthorMarkerEntity.class; + elasticsearchTemplate.createIndex(entity); + //when + assertThat(elasticsearchTemplate.putMapping(entity), is(true)); + } + + @Test + public void shouldFindAuthorMarkersInRangeForGivenCriteriaQuery() { + //given + loadClassBaseEntities(); + CriteriaQuery geoLocationCriteriaQuery = new CriteriaQuery( + new Criteria("location").within(new GeoPoint(45.7806d, 3.0875d), "20km")); + //when + List geoAuthorsForGeoCriteria = elasticsearchTemplate.queryForList(geoLocationCriteriaQuery, AuthorMarkerEntity.class); + + //then + assertThat(geoAuthorsForGeoCriteria.size(), is(1)); + assertEquals("Franck Marchand", geoAuthorsForGeoCriteria.get(0).getName()); + } + + @Test + public void shouldFindSelectedAuthorMarkerInRangeForGivenCriteriaQuery() { + //given + loadClassBaseEntities(); + CriteriaQuery geoLocationCriteriaQuery2 = new CriteriaQuery( + new Criteria("name").is("Mohsin Husen").and("location").within(new GeoPoint(51.5171d, 0.1062d), "20km")); + //when + List geoAuthorsForGeoCriteria2 = elasticsearchTemplate.queryForList(geoLocationCriteriaQuery2, AuthorMarkerEntity.class); + + //then + assertThat(geoAuthorsForGeoCriteria2.size(), is(1)); + assertEquals("Mohsin Husen", geoAuthorsForGeoCriteria2.get(0).getName()); + } + + @Test + public void shouldFindStringAnnotatedGeoMarkersInRangeForGivenCriteriaQuery() { + //given + loadAnnotationBaseEntities(); + CriteriaQuery geoLocationCriteriaQuery = new CriteriaQuery( + new Criteria("location").within(new GeoPoint(51.000000, 0.100000), "1km")); + //when + List geoAuthorsForGeoCriteria = elasticsearchTemplate.queryForList(geoLocationCriteriaQuery, AuthorMarkerAnnotatedEntity.class); + + //then + assertThat(geoAuthorsForGeoCriteria.size(), is(1)); + //TODO: result should be 3 not 1 - geohash points don't work + assertEquals("Artur Konczak", geoAuthorsForGeoCriteria.get(0).getName()); + } + + @Test + public void shouldFindDoubleAnnotatedGeoMarkersInRangeForGivenCriteriaQuery() { + //given + loadAnnotationBaseEntities(); + CriteriaQuery geoLocationCriteriaQuery = new CriteriaQuery( + new Criteria("additionalLocation").within(new GeoPoint(51.001000, 0.10100), "1km")); + //when + List geoAuthorsForGeoCriteria = elasticsearchTemplate.queryForList(geoLocationCriteriaQuery, AuthorMarkerAnnotatedEntity.class); + + //then + assertThat(geoAuthorsForGeoCriteria.size(), is(3)); + } + + @Test + public void shouldFindAnnotatedGeoMarkersInRangeForGivenCriteriaQuery() { + //given + loadAnnotationBaseEntities(); + CriteriaQuery geoLocationCriteriaQuery = new CriteriaQuery( + new Criteria("additionalLocation").within("51.001000, 0.10100", "1km")); + //when + List geoAuthorsForGeoCriteria = elasticsearchTemplate.queryForList(geoLocationCriteriaQuery, AuthorMarkerAnnotatedEntity.class); + + //then + assertThat(geoAuthorsForGeoCriteria.size(), is(3)); + } + + @Test + public void shouldFindAnnotatedGeoMarkersInRangeForGivenCriteriaQueryUsingGeohashLocation() { + //given + loadAnnotationBaseEntities(); + CriteriaQuery geoLocationCriteriaQuery = new CriteriaQuery( + new Criteria("additionalLocation").within("u1044", "1km")); + //when + List geoAuthorsForGeoCriteria = elasticsearchTemplate.queryForList(geoLocationCriteriaQuery, AuthorMarkerAnnotatedEntity.class); + + //then + assertThat(geoAuthorsForGeoCriteria.size(), is(3)); + } + + @Test + public void shouldFindAllMarkersForNativeSearchQuery() { + //Given + loadAnnotationBaseEntities(); + NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder().withFilter(FilterBuilders.geoBoundingBoxFilter("additionalLocation").topLeft("52, -1").bottomRight("50,1")); + //When + List geoAuthorsForGeoCriteria = elasticsearchTemplate.queryForList(queryBuilder.build(), AuthorMarkerAnnotatedEntity.class); + //Then + assertThat(geoAuthorsForGeoCriteria.size(), is(3)); + } + + @Test + public void shouldFindAuthorMarkersInBoxForGivenCriteriaQueryUsingGeoEnvelop() { + //given + loadClassBaseEntities(); + CriteriaQuery geoLocationCriteriaQuery3 = new CriteriaQuery( + new Criteria("location").bbox( + new GeoEnvelope(new GeoPoint(53.5171d, 0), + new GeoPoint(49.5171d, 0.2062d)))); + //when + List geoAuthorsForGeoCriteria3 = elasticsearchTemplate.queryForList(geoLocationCriteriaQuery3, AuthorMarkerEntity.class); + + //then + assertThat(geoAuthorsForGeoCriteria3.size(), is(2)); + assertThat(geoAuthorsForGeoCriteria3, containsInAnyOrder(hasProperty("name", equalTo("Mohsin Husen")), hasProperty("name", equalTo("Rizwan Idrees")))); + } + + @Test + public void shouldFindAuthorMarkersInBoxForGivenCriteriaQueryUsingString() { + //given + loadClassBaseEntities(); + CriteriaQuery geoLocationCriteriaQuery3 = new CriteriaQuery( + new Criteria("location").bbox("53.5171d, 0", "49.5171d, 0.2062d")); + //when + List geoAuthorsForGeoCriteria3 = elasticsearchTemplate.queryForList(geoLocationCriteriaQuery3, AuthorMarkerEntity.class); + + //then + assertThat(geoAuthorsForGeoCriteria3.size(), is(2)); + assertThat(geoAuthorsForGeoCriteria3, containsInAnyOrder(hasProperty("name", equalTo("Mohsin Husen")), hasProperty("name", equalTo("Rizwan Idrees")))); + } + + @Test + public void shouldFindAuthorMarkersInBoxForGivenCriteriaQueryUsingGeoPoints() { + //given + loadClassBaseEntities(); + CriteriaQuery geoLocationCriteriaQuery3 = new CriteriaQuery( + new Criteria("location").bbox( + new GeoPoint(53.5171d, 0), + new GeoPoint(49.5171d, 0.2062d))); + //when + List geoAuthorsForGeoCriteria3 = elasticsearchTemplate.queryForList(geoLocationCriteriaQuery3, AuthorMarkerEntity.class); + + //then + assertThat(geoAuthorsForGeoCriteria3.size(), is(2)); + assertThat(geoAuthorsForGeoCriteria3, containsInAnyOrder(hasProperty("name", equalTo("Mohsin Husen")), hasProperty("name", equalTo("Rizwan Idrees")))); + } + +} diff --git a/src/test/resources/infrastructure.xml b/src/test/resources/infrastructure.xml index c9207190f..7c17a094d 100644 --- a/src/test/resources/infrastructure.xml +++ b/src/test/resources/infrastructure.xml @@ -5,10 +5,8 @@ xsi:schemaLocation="http://www.springframework.org/schema/data/elasticsearch http://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> - - - - + + \ No newline at end of file From 714bcf87fefdd2cba2f357ee8d235d29537e375a Mon Sep 17 00:00:00 2001 From: Artur Konczak Date: Wed, 14 Aug 2013 22:38:50 +0100 Subject: [PATCH 3/4] Merge branch 'master' into geo_location --- pom.xml | 2 +- .../elasticsearch/annotations/FieldType.java | 2 +- .../client/TransportClientFactoryBean.java | 6 - .../TransportClientBeanDefinitionParser.java | 1 - .../core/ElasticsearchOperations.java | 35 +++- .../core/ElasticsearchTemplate.java | 53 +++++- .../elasticsearch/core/FactedPageImpl.java | 52 ------ .../elasticsearch/core/query/DeleteQuery.java | 20 ++- .../core/query/NativeSearchQuery.java | 15 +- .../core/query/NativeSearchQueryBuilder.java | 9 +- .../elasticsearch/core/query/SearchQuery.java | 3 + .../elasticsearch/core/query/UpdateQuery.java | 80 +++++++++ .../core/query/UpdateQueryBuilder.java | 73 ++++++++ .../config/spring-elasticsearch-1.0.xsd | 5 - .../data/elasticsearch/SampleEntity.java | 9 + .../core/ElasticsearchTemplateTests.java | 170 +++++++++++++++++- .../core/facet/ArticleEntity.java | 2 +- .../geo/ElasticsearchTemplateGeoTests.java | 6 +- 18 files changed, 462 insertions(+), 81 deletions(-) delete mode 100644 src/main/java/org/springframework/data/elasticsearch/core/FactedPageImpl.java create mode 100644 src/main/java/org/springframework/data/elasticsearch/core/query/UpdateQuery.java create mode 100644 src/main/java/org/springframework/data/elasticsearch/core/query/UpdateQueryBuilder.java diff --git a/pom.xml b/pom.xml index 51d100828..b74901c26 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ 3.2.1 2.6 - 0.90.0 + 0.90.2 1.9.2 1.6.0.BUILD-SNAPSHOT diff --git a/src/main/java/org/springframework/data/elasticsearch/annotations/FieldType.java b/src/main/java/org/springframework/data/elasticsearch/annotations/FieldType.java index 4073d78bb..ef982b601 100644 --- a/src/main/java/org/springframework/data/elasticsearch/annotations/FieldType.java +++ b/src/main/java/org/springframework/data/elasticsearch/annotations/FieldType.java @@ -3,5 +3,5 @@ package org.springframework.data.elasticsearch.annotations; /** */ public enum FieldType { - String, Integer, Long, Date, Object, Auto + String, Integer, Long, Date, Float, Double, Boolean, Object, Auto } diff --git a/src/main/java/org/springframework/data/elasticsearch/client/TransportClientFactoryBean.java b/src/main/java/org/springframework/data/elasticsearch/client/TransportClientFactoryBean.java index f2868d234..f7a175409 100644 --- a/src/main/java/org/springframework/data/elasticsearch/client/TransportClientFactoryBean.java +++ b/src/main/java/org/springframework/data/elasticsearch/client/TransportClientFactoryBean.java @@ -42,7 +42,6 @@ public class TransportClientFactoryBean implements FactoryBean, private static final Logger logger = LoggerFactory.getLogger(TransportClientFactoryBean.class); private String clusterNodes; private String clusterName; - private Boolean enableHttp; private Boolean clientTransportSniff; private TransportClient client; private Properties properties; @@ -102,7 +101,6 @@ public class TransportClientFactoryBean implements FactoryBean, return settingsBuilder() .put("cluster.name", clusterName) .put("client.transport.sniff", clientTransportSniff) - .put("http.enabled", enableHttp) .build(); } @@ -114,10 +112,6 @@ public class TransportClientFactoryBean implements FactoryBean, this.clusterName = clusterName; } - public void setEnableHttp(Boolean enableHttp) { - this.enableHttp = enableHttp; - } - public void setClientTransportSniff(Boolean clientTransportSniff) { this.clientTransportSniff = clientTransportSniff; } diff --git a/src/main/java/org/springframework/data/elasticsearch/config/TransportClientBeanDefinitionParser.java b/src/main/java/org/springframework/data/elasticsearch/config/TransportClientBeanDefinitionParser.java index b51b26040..5b0475c40 100644 --- a/src/main/java/org/springframework/data/elasticsearch/config/TransportClientBeanDefinitionParser.java +++ b/src/main/java/org/springframework/data/elasticsearch/config/TransportClientBeanDefinitionParser.java @@ -43,7 +43,6 @@ public class TransportClientBeanDefinitionParser extends AbstractBeanDefinitionP private void setClusterNodes(Element element, BeanDefinitionBuilder builder) { builder.addPropertyValue("clusterNodes", element.getAttribute("cluster-nodes")); builder.addPropertyValue("clusterName", element.getAttribute("cluster-name")); - builder.addPropertyValue("enableHttp", Boolean.valueOf(element.getAttribute("http-enabled"))); builder.addPropertyValue("clientTransportSniff", Boolean.valueOf(element.getAttribute("client-transport-sniff"))); } diff --git a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchOperations.java b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchOperations.java index 4db37b176..bae6e6848 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchOperations.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchOperations.java @@ -15,6 +15,7 @@ */ package org.springframework.data.elasticsearch.core; +import org.elasticsearch.action.update.UpdateResponse; import org.springframework.data.domain.Page; import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter; import org.springframework.data.elasticsearch.core.query.*; @@ -168,6 +169,14 @@ public interface ElasticsearchOperations { */ String index(IndexQuery query); + /** + * Partial update of the document + * + * @param updateQuery + * @return + */ + UpdateResponse update(UpdateQuery updateQuery); + /** * Bulk index all objects. Will do save or update * @@ -202,15 +211,30 @@ public interface ElasticsearchOperations { */ void delete(DeleteQuery query, Class clazz); + /** + * Delete all records matching the query + * + * @param query + */ + void delete(DeleteQuery query); + /** * Deletes an index for given entity - * + * * @param clazz * @param * @return */ boolean deleteIndex(Class clazz); + /** + * Deletes a type in an index + * + * @param index + * @param type + */ + void deleteType(String index, String type); + /** * check if index is exists * @@ -220,6 +244,15 @@ public interface ElasticsearchOperations { */ boolean indexExists(Class clazz); + /** + * check if type is exists in an index + * + * @param index + * @param type + * @return + */ + boolean typeExists(String index, String type); + /** * refresh the index * diff --git a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java index 4d6bb04eb..87976af0b 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java @@ -19,6 +19,7 @@ import org.apache.commons.collections.CollectionUtils; import org.codehaus.jackson.map.DeserializationConfig; import org.codehaus.jackson.map.ObjectMapper; import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; +import org.elasticsearch.action.admin.indices.mapping.delete.DeleteMappingRequest; import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequestBuilder; import org.elasticsearch.action.bulk.BulkItemResponse; import org.elasticsearch.action.bulk.BulkRequestBuilder; @@ -29,6 +30,8 @@ import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.action.mlt.MoreLikeThisRequestBuilder; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.action.update.UpdateRequestBuilder; +import org.elasticsearch.action.update.UpdateResponse; import org.elasticsearch.client.Client; import org.elasticsearch.client.Requests; import org.elasticsearch.common.collect.MapBuilder; @@ -40,6 +43,7 @@ import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.facet.Facet; import org.elasticsearch.search.facet.FacetBuilder; +import org.elasticsearch.search.highlight.HighlightBuilder; import org.elasticsearch.search.sort.SortOrder; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -101,7 +105,6 @@ public class ElasticsearchTemplate implements ElasticsearchOperations { @Override public boolean createIndex(Class clazz) { - ElasticsearchPersistentEntity persistentEntity = getPersistentEntityFor(clazz); return createIndexIfNotCreated(clazz); } @@ -227,6 +230,25 @@ public class ElasticsearchTemplate implements ElasticsearchOperations { return prepareIndex(query).execute().actionGet().getId(); } + @Override + public UpdateResponse update(UpdateQuery query) { + String indexName = isNotBlank(query.getIndexName()) ? query.getIndexName() : getPersistentEntityFor(query.getClazz()).getIndexName(); + String type = isNotBlank(query.getType()) ? query.getType() : getPersistentEntityFor(query.getClazz()).getIndexType(); + Assert.notNull(indexName, "No index defined for Query"); + Assert.notNull(type, "No type define for Query"); + Assert.notNull(query.getId(), "No Id define for Query"); + Assert.notNull(query.getIndexRequest(), "No IndexRequest define for Query"); + UpdateRequestBuilder updateRequestBuilder = client.prepareUpdate(indexName, type, query.getId()); + if(query.DoUpsert()){ + updateRequestBuilder.setDocAsUpsert(true) + .setUpsertRequest(query.getIndexRequest()).setDoc(query.getIndexRequest()); + } else { + updateRequestBuilder.setDoc(query.getIndexRequest()); + } + return updateRequestBuilder.execute().actionGet(); + } + + @Override public void bulkIndex(List queries) { BulkRequestBuilder bulkRequest = client.prepareBulk(); @@ -251,6 +273,12 @@ public class ElasticsearchTemplate implements ElasticsearchOperations { return indexExists(getPersistentEntityFor(clazz).getIndexName()); } + @Override + public boolean typeExists(String index, String type) { + return client.admin().cluster().prepareState().execute().actionGet() + .getState().metaData().index(index).mappings().containsKey(type); + } + @Override public boolean deleteIndex(Class clazz) { String indexName = getPersistentEntityFor(clazz).getIndexName(); @@ -260,6 +288,15 @@ public class ElasticsearchTemplate implements ElasticsearchOperations { return false; } + @Override + public void deleteType(String index, String type){ + Map mappings = client.admin().cluster().prepareState().execute().actionGet() + .getState().metaData().index(index).mappings(); + if (mappings.containsKey(type)) { + client.admin().indices().deleteMapping(new DeleteMappingRequest(index).type(type)).actionGet(); + } + } + @Override public String delete(String indexName, String type, String id) { return client.prepareDelete(indexName, type, id).execute().actionGet().getId(); @@ -278,6 +315,14 @@ public class ElasticsearchTemplate implements ElasticsearchOperations { .setQuery(deleteQuery.getQuery()).execute().actionGet(); } + @Override + public void delete(DeleteQuery deleteQuery) { + Assert.notNull(deleteQuery.getIndex(), "No index defined for Query"); + Assert.notNull(deleteQuery.getType(), "No type define for Query"); + client.prepareDeleteByQuery(deleteQuery.getIndex()).setTypes(deleteQuery.getType()) + .setQuery(deleteQuery.getQuery()).execute().actionGet(); + } + @Override public String scan(SearchQuery searchQuery, long scrollTimeInMillis, boolean noFields) { Assert.notNull(searchQuery.getIndices(), "No index defined for Query"); @@ -388,6 +433,12 @@ public class ElasticsearchTemplate implements ElasticsearchOperations { } } + if(searchQuery.getHighlightFields() != null) { + for(HighlightBuilder.Field highlightField : searchQuery.getHighlightFields()){ + searchRequest.addHighlightedField(highlightField); + } + } + return searchRequest.setQuery(searchQuery.getQuery()).execute().actionGet(); } diff --git a/src/main/java/org/springframework/data/elasticsearch/core/FactedPageImpl.java b/src/main/java/org/springframework/data/elasticsearch/core/FactedPageImpl.java deleted file mode 100644 index cc59ded31..000000000 --- a/src/main/java/org/springframework/data/elasticsearch/core/FactedPageImpl.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.springframework.data.elasticsearch.core; - -import org.apache.commons.collections.CollectionUtils; -import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.Pageable; -import org.springframework.data.elasticsearch.core.facet.FacetResult; - -import java.util.List; -import java.util.Map; - -/** - * @author Rizwan Idrees - * @author Mohsin Husen - * @author Artur Konczak - * @author Jonathan Yan - */ -public class FactedPageImpl extends PageImpl implements FacetedPage { - - private List facets; - private Map mapOfFacets; - - public FactedPageImpl(List content) { - super(content); - } - - public FactedPageImpl(List content, Pageable pageable, long total) { - super(content, pageable, total); - } - - public FactedPageImpl(List content, Pageable pageable, long total, List facets) { - super(content, pageable, total); - this.facets = facets; - for (FacetResult facet : facets) { - mapOfFacets.put(facet.getName(), facet); - } - } - - @Override - public boolean hasFacets() { - return CollectionUtils.isNotEmpty(facets); - } - - @Override - public List getFacets() { - return facets; - } - - @Override - public FacetResult getFacet(String name) { - return mapOfFacets.get(name); - } -} diff --git a/src/main/java/org/springframework/data/elasticsearch/core/query/DeleteQuery.java b/src/main/java/org/springframework/data/elasticsearch/core/query/DeleteQuery.java index 4578c834c..b02c773fc 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/query/DeleteQuery.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/query/DeleteQuery.java @@ -19,13 +19,15 @@ import org.elasticsearch.index.query.QueryBuilder; /** * DeleteQuery - * + * * @author Rizwan Idrees * @author Mohsin Husen */ public class DeleteQuery { private QueryBuilder query; + private String index; + private String type; public QueryBuilder getQuery() { return query; @@ -33,5 +35,21 @@ public class DeleteQuery { public void setQuery(QueryBuilder query) { this.query = query; + } + + public String getIndex() { + return index; + } + + public void setIndex(String index) { + this.index = index; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; } } diff --git a/src/main/java/org/springframework/data/elasticsearch/core/query/NativeSearchQuery.java b/src/main/java/org/springframework/data/elasticsearch/core/query/NativeSearchQuery.java index 46f80e264..0b4ca5121 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/query/NativeSearchQuery.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/query/NativeSearchQuery.java @@ -17,7 +17,7 @@ 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.highlight.HighlightBuilder; import org.elasticsearch.search.sort.SortBuilder; import org.springframework.data.elasticsearch.core.facet.FacetRequest; @@ -37,6 +37,7 @@ public class NativeSearchQuery extends AbstractQuery implements SearchQuery { private FilterBuilder filter; private SortBuilder sort; private List facets; + private HighlightBuilder.Field[] highlightFields; public NativeSearchQuery(QueryBuilder query) { @@ -54,6 +55,13 @@ public class NativeSearchQuery extends AbstractQuery implements SearchQuery { this.sort = sort; } + public NativeSearchQuery(QueryBuilder query, FilterBuilder filter, SortBuilder sort, HighlightBuilder.Field[] highlightFields) { + this.query = query; + this.filter = filter; + this.sort = sort; + this.highlightFields = highlightFields; + } + public QueryBuilder getQuery() { return query; } @@ -64,6 +72,11 @@ public class NativeSearchQuery extends AbstractQuery implements SearchQuery { public SortBuilder getElasticsearchSort() { return sort; + } + + @Override + public HighlightBuilder.Field[] getHighlightFields() { + return highlightFields; } public void addFacet(FacetRequest facetRequest){ diff --git a/src/main/java/org/springframework/data/elasticsearch/core/query/NativeSearchQueryBuilder.java b/src/main/java/org/springframework/data/elasticsearch/core/query/NativeSearchQueryBuilder.java index 6d8e354c4..db667a245 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/query/NativeSearchQueryBuilder.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/query/NativeSearchQueryBuilder.java @@ -18,6 +18,7 @@ package org.springframework.data.elasticsearch.core.query; import org.apache.commons.collections.CollectionUtils; import org.elasticsearch.index.query.FilterBuilder; import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.search.highlight.HighlightBuilder; import org.elasticsearch.search.sort.SortBuilder; import org.springframework.data.domain.Pageable; import org.springframework.data.elasticsearch.core.facet.FacetRequest; @@ -39,6 +40,7 @@ public class NativeSearchQueryBuilder { private FilterBuilder filterBuilder; private SortBuilder sortBuilder; private List facetRequests = new ArrayList(); + private HighlightBuilder.Field[] highlightFields; private Pageable pageable; private String[] indices; private String[] types; @@ -64,6 +66,11 @@ public class NativeSearchQueryBuilder { return this; } + public NativeSearchQueryBuilder withHighlightFields(HighlightBuilder.Field... highlightFields){ + this.highlightFields = highlightFields; + return this; + } + public NativeSearchQueryBuilder withPageable(Pageable pageable) { this.pageable = pageable; return this; @@ -85,7 +92,7 @@ public class NativeSearchQueryBuilder { } public NativeSearchQuery build() { - NativeSearchQuery nativeSearchQuery = new NativeSearchQuery(queryBuilder, filterBuilder, sortBuilder); + NativeSearchQuery nativeSearchQuery = new NativeSearchQuery(queryBuilder, filterBuilder, sortBuilder, highlightFields); if (pageable != null) { nativeSearchQuery.setPageable(pageable); } diff --git a/src/main/java/org/springframework/data/elasticsearch/core/query/SearchQuery.java b/src/main/java/org/springframework/data/elasticsearch/core/query/SearchQuery.java index e625c968a..2d823d720 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/query/SearchQuery.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/query/SearchQuery.java @@ -17,6 +17,7 @@ package org.springframework.data.elasticsearch.core.query; import org.elasticsearch.index.query.FilterBuilder; import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.search.highlight.HighlightBuilder; import org.elasticsearch.search.sort.SortBuilder; import org.springframework.data.elasticsearch.core.facet.FacetRequest; @@ -37,4 +38,6 @@ public interface SearchQuery extends Query { SortBuilder getElasticsearchSort(); List getFacets(); + + HighlightBuilder.Field[] getHighlightFields(); } diff --git a/src/main/java/org/springframework/data/elasticsearch/core/query/UpdateQuery.java b/src/main/java/org/springframework/data/elasticsearch/core/query/UpdateQuery.java new file mode 100644 index 000000000..2eca04462 --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/core/query/UpdateQuery.java @@ -0,0 +1,80 @@ +/* + * 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.elasticsearch.action.index.IndexRequest; + +/** + * @author Rizwan Idrees + * @author Mohsin Husen + */ +public class UpdateQuery { + + private String id; + private IndexRequest indexRequest; + private String indexName; + private String type; + private Class clazz; + private boolean doUpsert; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public IndexRequest getIndexRequest() { + return indexRequest; + } + + public void setIndexRequest(IndexRequest indexRequest) { + this.indexRequest = indexRequest; + } + + public String getIndexName() { + return indexName; + } + + public void setIndexName(String indexName) { + this.indexName = indexName; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Class getClazz() { + return clazz; + } + + public void setClazz(Class clazz) { + this.clazz = clazz; + } + + public boolean DoUpsert() { + return doUpsert; + } + + public void setDoUpsert(boolean doUpsert) { + this.doUpsert = doUpsert; + } +} diff --git a/src/main/java/org/springframework/data/elasticsearch/core/query/UpdateQueryBuilder.java b/src/main/java/org/springframework/data/elasticsearch/core/query/UpdateQueryBuilder.java new file mode 100644 index 000000000..773a645cf --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/core/query/UpdateQueryBuilder.java @@ -0,0 +1,73 @@ +/* + * 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.elasticsearch.action.index.IndexRequest; + +/** + * @author Rizwan Idrees + * @author Mohsin Husen + */ +public class UpdateQueryBuilder { + + private String id; + private IndexRequest indexRequest; + private String indexName; + private String type; + private Class clazz; + private boolean doUpsert; + + public UpdateQueryBuilder withId(String id){ + this.id = id; + return this; + } + + public UpdateQueryBuilder withIndexRequest(IndexRequest indexRequest){ + this.indexRequest = indexRequest; + return this; + } + + public UpdateQueryBuilder withIndexName(String indexName){ + this.indexName = indexName; + return this; + } + + public UpdateQueryBuilder withType(String type){ + this.type = type; + return this; + } + + public UpdateQueryBuilder withClass(Class clazz){ + this.clazz = clazz; + return this; + } + + public UpdateQueryBuilder withDoUpsert(boolean doUpsert){ + this.doUpsert = doUpsert; + return this; + } + + public UpdateQuery build(){ + UpdateQuery updateQuery = new UpdateQuery(); + updateQuery.setId(id); + updateQuery.setIndexName(indexName); + updateQuery.setType(type); + updateQuery.setClazz(clazz); + updateQuery.setIndexRequest(indexRequest); + updateQuery.setDoUpsert(doUpsert); + return updateQuery; + } +} diff --git a/src/main/resources/org/springframework/data/elasticsearch/config/spring-elasticsearch-1.0.xsd b/src/main/resources/org/springframework/data/elasticsearch/config/spring-elasticsearch-1.0.xsd index 668aa618a..22b9d2f0f 100644 --- a/src/main/resources/org/springframework/data/elasticsearch/config/spring-elasticsearch-1.0.xsd +++ b/src/main/resources/org/springframework/data/elasticsearch/config/spring-elasticsearch-1.0.xsd @@ -84,11 +84,6 @@ - - - - - diff --git a/src/test/java/org/springframework/data/elasticsearch/SampleEntity.java b/src/test/java/org/springframework/data/elasticsearch/SampleEntity.java index dc890d840..aceb9588a 100644 --- a/src/test/java/org/springframework/data/elasticsearch/SampleEntity.java +++ b/src/test/java/org/springframework/data/elasticsearch/SampleEntity.java @@ -34,6 +34,7 @@ public class SampleEntity { private String message; private int rate; private boolean available; + private String highlightedMessage; @Version private Long version; @@ -77,6 +78,14 @@ public class SampleEntity { this.available = available; } + public String getHighlightedMessage() { + return highlightedMessage; + } + + public void setHighlightedMessage(String highlightedMessage) { + this.highlightedMessage = highlightedMessage; + } + public Long getVersion() { return version; } diff --git a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java index 06bc93ad1..6cdc46204 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java @@ -15,8 +15,11 @@ */ package org.springframework.data.elasticsearch.core; +import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.index.engine.DocumentMissingException; import org.elasticsearch.search.SearchHit; +import org.elasticsearch.search.highlight.HighlightBuilder; import org.elasticsearch.search.sort.FieldSortBuilder; import org.elasticsearch.search.sort.SortOrder; import org.junit.Before; @@ -39,12 +42,9 @@ import java.util.List; import static org.apache.commons.lang.RandomStringUtils.randomNumeric; import static org.elasticsearch.index.query.FilterBuilders.boolFilter; import static org.elasticsearch.index.query.FilterBuilders.termFilter; -import static org.elasticsearch.index.query.QueryBuilders.fieldQuery; -import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; -import static org.hamcrest.MatcherAssert.assertThat; +import static org.elasticsearch.index.query.QueryBuilders.*; import static org.hamcrest.Matchers.*; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.*; /** * @author Rizwan Idrees @@ -717,4 +717,164 @@ public class ElasticsearchTemplateTests { // then assertThat(elasticsearchTemplate.indexExists(clazz), is(false)); } + + @Test + public void shouldDoPartialUpdateForExistingDocument() { + //given + String documentId = randomNumeric(5); + String messageBeforeUpdate = "some test message"; + String messageAfterUpdate = "test message"; + + SampleEntity sampleEntity = new SampleEntity(); + sampleEntity.setId(documentId); + sampleEntity.setMessage(messageBeforeUpdate); + sampleEntity.setVersion(System.currentTimeMillis()); + + IndexQuery indexQuery = new IndexQuery(); + indexQuery.setId(documentId); + indexQuery.setObject(sampleEntity); + + elasticsearchTemplate.index(indexQuery); + elasticsearchTemplate.refresh(SampleEntity.class, true); + + IndexRequest indexRequest = new IndexRequest(); + indexRequest.source("message", messageAfterUpdate); + UpdateQuery updateQuery = new UpdateQueryBuilder().withId(documentId) + .withClass(SampleEntity.class).withIndexRequest(indexRequest).build(); + // when + elasticsearchTemplate.update(updateQuery); + //then + GetQuery getQuery = new GetQuery(); + getQuery.setId(documentId); + SampleEntity indexedEntity = elasticsearchTemplate.queryForObject(getQuery, SampleEntity.class); + assertThat(indexedEntity.getMessage(), is(messageAfterUpdate)); + } + + @Test(expected = DocumentMissingException.class) + public void shouldThrowExceptionIfDocumentDoesNotExistWhileDoingPartialUpdate(){ + // when + IndexRequest indexRequest = new IndexRequest(); + UpdateQuery updateQuery = new UpdateQueryBuilder().withId(randomNumeric(5)) + .withClass(SampleEntity.class).withIndexRequest(indexRequest).build(); + elasticsearchTemplate.update(updateQuery); + } + + @Test + public void shouldDoUpsertIfDocumentDoesNotExist(){ + //given + String documentId = randomNumeric(5); + String message = "test message"; + IndexRequest indexRequest = new IndexRequest(); + indexRequest.source("message", message); + UpdateQuery updateQuery = new UpdateQueryBuilder().withId(documentId) + .withDoUpsert(true).withClass(SampleEntity.class) + .withIndexRequest(indexRequest).build(); + //when + elasticsearchTemplate.update(updateQuery); + //then + GetQuery getQuery = new GetQuery(); + getQuery.setId(documentId); + SampleEntity indexedEntity = elasticsearchTemplate.queryForObject(getQuery, SampleEntity.class); + assertThat(indexedEntity.getMessage(), is(message)); + } + + @Test + public void shouldReturnHighlightedFieldsForGivenQueryAndFields(){ + + //given + String documentId = randomNumeric(5); + String actualMessage = "some test message"; + String highlightedMessage = "some test message"; + + SampleEntity sampleEntity = new SampleEntity(); + sampleEntity.setId(documentId); + sampleEntity.setMessage(actualMessage); + sampleEntity.setVersion(System.currentTimeMillis()); + + IndexQuery indexQuery = new IndexQuery(); + indexQuery.setId(documentId); + indexQuery.setObject(sampleEntity); + + elasticsearchTemplate.index(indexQuery); + elasticsearchTemplate.refresh(SampleEntity.class, true); + + SearchQuery searchQuery = new NativeSearchQueryBuilder() + .withQuery(termQuery("message", "test")) + .withHighlightFields(new HighlightBuilder.Field("message")) + .build(); + + Page sampleEntities = elasticsearchTemplate.queryForPage(searchQuery, new ResultsMapper() { + @Override + public FacetedPage mapResults(SearchResponse response) { + List chunk = new ArrayList(); + for (SearchHit searchHit : response.getHits()) { + if (response.getHits().getHits().length <= 0) { + return null; + } + SampleEntity user = new SampleEntity(); + user.setId(searchHit.getId()); + user.setMessage((String) searchHit.getSource().get("message")); + user.setHighlightedMessage(searchHit.getHighlightFields().get("message").fragments()[0].toString()); + chunk.add(user); + } + if(chunk.size() > 0){ + return new FacetedPageImpl(chunk); + } + return null; + } + }); + + assertThat(sampleEntities.getContent().get(0).getHighlightedMessage(), is(highlightedMessage)); + + } + + @Test + public void shouldDeleteSpecifiedTypeFromAnIndex() { + // given + String documentId = randomNumeric(5); + SampleEntity sampleEntity = new SampleEntity(); + sampleEntity.setId(documentId); + sampleEntity.setMessage("some message"); + sampleEntity.setVersion(System.currentTimeMillis()); + + IndexQuery indexQuery = new IndexQuery(); + indexQuery.setId(documentId); + indexQuery.setObject(sampleEntity); + + elasticsearchTemplate.index(indexQuery); + + // when + elasticsearchTemplate.deleteType("test-index","test-type"); + + //then + boolean typeExists = elasticsearchTemplate.typeExists("test-index", "test-type"); + assertThat(typeExists, is(false)); + } + + @Test + public void shouldDeleteDocumentBySpecifiedTypeUsingDeleteQuery(){ + // given + String documentId = randomNumeric(5); + SampleEntity sampleEntity = new SampleEntity(); + sampleEntity.setId(documentId); + sampleEntity.setMessage("some message"); + sampleEntity.setVersion(System.currentTimeMillis()); + + IndexQuery indexQuery = new IndexQuery(); + indexQuery.setId(documentId); + indexQuery.setObject(sampleEntity); + + elasticsearchTemplate.index(indexQuery); + // when + DeleteQuery deleteQuery = new DeleteQuery(); + deleteQuery.setQuery(fieldQuery("id", documentId)); + deleteQuery.setIndex("test-index"); + deleteQuery.setType("test-type"); + elasticsearchTemplate.delete(deleteQuery); + // then + SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(fieldQuery("id", documentId)).build(); + Page sampleEntities = elasticsearchTemplate.queryForPage(searchQuery, SampleEntity.class); + assertThat(sampleEntities.getTotalElements(), equalTo(0L)); + } + } diff --git a/src/test/java/org/springframework/data/elasticsearch/core/facet/ArticleEntity.java b/src/test/java/org/springframework/data/elasticsearch/core/facet/ArticleEntity.java index 77847b317..6d6c1df1d 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/facet/ArticleEntity.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/facet/ArticleEntity.java @@ -14,7 +14,7 @@ import static org.springframework.data.elasticsearch.annotations.FieldType.Strin /** * Simple type to test facets */ -@Document(indexName = "articles", type = "article", shards = 1, replicas = 0, refreshInterval = "-1") +@Document(indexName = "articles", type = "article", shards = 1, replicas = 0, refreshInterval = "-1", indexStoreType = "memory") public class ArticleEntity { @Id diff --git a/src/test/java/org/springframework/data/elasticsearch/core/geo/ElasticsearchTemplateGeoTests.java b/src/test/java/org/springframework/data/elasticsearch/core/geo/ElasticsearchTemplateGeoTests.java index d2222b158..1d22b4bfc 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/geo/ElasticsearchTemplateGeoTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/geo/ElasticsearchTemplateGeoTests.java @@ -77,7 +77,7 @@ public class ElasticsearchTemplateGeoTests { double[] latLonArray = {0.100000, 51.000000}; String lonLatString = "51.000000, 0.100000"; String geohash = "u1044k2bd6u"; - indexQueries.add(new AuthorMarkerAnnotatedEntityBuilder("2").name("Mohsin Husen").location(geohash.substring(3)).additionalLocation(latLonArray).buildIndex()); + indexQueries.add(new AuthorMarkerAnnotatedEntityBuilder("2").name("Mohsin Husen").location(geohash.substring(0,5)).additionalLocation(latLonArray).buildIndex()); indexQueries.add(new AuthorMarkerAnnotatedEntityBuilder("1").name("Artur Konczak").location(lonLatString).additionalLocation(latLonArray).buildIndex()); indexQueries.add(new AuthorMarkerAnnotatedEntityBuilder("3").name("Rizwan Idrees").location(geohash).additionalLocation(latLonArray).buildIndex()); @@ -132,9 +132,7 @@ public class ElasticsearchTemplateGeoTests { List geoAuthorsForGeoCriteria = elasticsearchTemplate.queryForList(geoLocationCriteriaQuery, AuthorMarkerAnnotatedEntity.class); //then - assertThat(geoAuthorsForGeoCriteria.size(), is(1)); - //TODO: result should be 3 not 1 - geohash points don't work - assertEquals("Artur Konczak", geoAuthorsForGeoCriteria.get(0).getName()); + assertThat(geoAuthorsForGeoCriteria.size(), is(3)); } @Test From bb81fa75dbacbd72af29e921cf84f8e6b35d9549 Mon Sep 17 00:00:00 2001 From: Artur Konczak Date: Fri, 27 Sep 2013 22:58:26 +0100 Subject: [PATCH 4/4] Renamed GeoEnvelope to GeoBox --- .../data/elasticsearch/core/CriteriaFilterProcessor.java | 6 +++--- .../core/geo/{GeoEnvelope.java => GeoBox.java} | 4 ++-- .../data/elasticsearch/core/query/Criteria.java | 6 +++--- .../core/geo/ElasticsearchTemplateGeoTests.java | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) rename src/main/java/org/springframework/data/elasticsearch/core/geo/{GeoEnvelope.java => GeoBox.java} (92%) diff --git a/src/main/java/org/springframework/data/elasticsearch/core/CriteriaFilterProcessor.java b/src/main/java/org/springframework/data/elasticsearch/core/CriteriaFilterProcessor.java index 0d2cdc9e7..45f42d880 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/CriteriaFilterProcessor.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/CriteriaFilterProcessor.java @@ -16,7 +16,7 @@ package org.springframework.data.elasticsearch.core; import org.elasticsearch.index.query.*; -import org.springframework.data.elasticsearch.core.geo.GeoEnvelope; +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.util.Assert; @@ -152,8 +152,8 @@ class CriteriaFilterProcessor { } private void oneParameterBBox(GeoBoundingBoxFilterBuilder filter, Object value) { - Assert.isTrue(value instanceof GeoEnvelope, "single-element of a geo bbox filter must be type of GeoEnvelop"); - GeoEnvelope geoBBox = (GeoEnvelope) value; + Assert.isTrue(value instanceof GeoBox, "single-element of a geo bbox filter must be type of GeoEnvelop"); + GeoBox geoBBox = (GeoBox) value; filter.topLeft(geoBBox.getTopLeft().getLat(), geoBBox.getTopLeft().getLon()); filter.bottomRight(geoBBox.getBottomRight().getLat(), geoBBox.getBottomRight().getLon()); } diff --git a/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoEnvelope.java b/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoBox.java similarity index 92% rename from src/main/java/org/springframework/data/elasticsearch/core/geo/GeoEnvelope.java rename to src/main/java/org/springframework/data/elasticsearch/core/geo/GeoBox.java index 16c00f248..c8317b094 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoEnvelope.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoBox.java @@ -20,12 +20,12 @@ package org.springframework.data.elasticsearch.core.geo; * * @author Franck Marchand */ -public class GeoEnvelope { +public class GeoBox { private GeoPoint topLeft; private GeoPoint bottomRight; - public GeoEnvelope(GeoPoint topLeft, GeoPoint bottomRight) { + public GeoBox(GeoPoint topLeft, GeoPoint bottomRight) { this.topLeft = topLeft; this.bottomRight = bottomRight; } diff --git a/src/main/java/org/springframework/data/elasticsearch/core/query/Criteria.java b/src/main/java/org/springframework/data/elasticsearch/core/query/Criteria.java index 42bc08712..c46cb8e84 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/query/Criteria.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/query/Criteria.java @@ -25,7 +25,7 @@ import java.util.Set; import org.apache.commons.lang.StringUtils; import org.springframework.dao.InvalidDataAccessApiUsageException; -import org.springframework.data.elasticsearch.core.geo.GeoEnvelope; +import org.springframework.data.elasticsearch.core.geo.GeoBox; import org.springframework.data.elasticsearch.core.geo.GeoPoint; import org.springframework.util.Assert; @@ -387,10 +387,10 @@ public class Criteria { /** * Creates new CriteriaEntry for {@code location BBOX bounding box} * - * @param bbox {@link org.springframework.data.elasticsearch.core.geo.GeoEnvelope} bounding box(left top corner + right bottom corner) + * @param bbox {@link org.springframework.data.elasticsearch.core.geo.GeoBox} bounding box(left top corner + right bottom corner) * @return Criteria the chaind criteria with the new 'bbox' criteria included. */ - public Criteria bbox(GeoEnvelope bbox) { + public Criteria bbox(GeoBox bbox) { Assert.notNull(bbox, "bbox value for bbox criteria must not be null"); filterCriteria.add(new CriteriaEntry(OperationKey.BBOX, new Object[]{bbox})); return this; diff --git a/src/test/java/org/springframework/data/elasticsearch/core/geo/ElasticsearchTemplateGeoTests.java b/src/test/java/org/springframework/data/elasticsearch/core/geo/ElasticsearchTemplateGeoTests.java index 1d22b4bfc..133f2ba6f 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/geo/ElasticsearchTemplateGeoTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/geo/ElasticsearchTemplateGeoTests.java @@ -191,7 +191,7 @@ public class ElasticsearchTemplateGeoTests { loadClassBaseEntities(); CriteriaQuery geoLocationCriteriaQuery3 = new CriteriaQuery( new Criteria("location").bbox( - new GeoEnvelope(new GeoPoint(53.5171d, 0), + new GeoBox(new GeoPoint(53.5171d, 0), new GeoPoint(49.5171d, 0.2062d)))); //when List geoAuthorsForGeoCriteria3 = elasticsearchTemplate.queryForList(geoLocationCriteriaQuery3, AuthorMarkerEntity.class);