From 5509c4d819bec2155003c4d905683796460e0c69 Mon Sep 17 00:00:00 2001 From: fmarchand Date: Mon, 1 Apr 2013 23:56:43 +0200 Subject: [PATCH 01/14] adding GeoLocation class and add "within" method (location/distance) for Criteria. --- .../elasticsearch/core/geo/GeoLocation.java | 62 +++++++++++++++++++ .../elasticsearch/core/query/Criteria.java | 25 ++++++-- 2 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/springframework/data/elasticsearch/core/geo/GeoLocation.java 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 0000000..74095b4 --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoLocation.java @@ -0,0 +1,62 @@ +/* + * 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 fmarchand + */ +public class GeoLocation { + private double latitude; + private double longitude; + + public GeoLocation lat(double latitude) { + GeoLocation loc = new GeoLocation(); + loc.setLatitude(latitude); + return loc; + } + + public GeoLocation lon(double longitude) { + GeoLocation loc = new GeoLocation(); + loc.setLongitude(longitude); + return loc; + } + + public GeoLocation() { + } + + public GeoLocation(double latitude, double longitude) { + this.latitude = latitude; + this.longitude = longitude; + } + + public double getLatitude() { + return latitude; + } + + public void setLatitude(double latitude) { + this.latitude = latitude; + } + + public double getLongitude() { + return longitude; + } + + public void setLongitude(double longitude) { + this.longitude = longitude; + } +} 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 a41e2a4..760e229 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,7 @@ import org.apache.commons.lang.StringUtils; import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.data.elasticsearch.core.geo.GeoLocation; import org.springframework.util.Assert; /** @@ -335,7 +336,7 @@ public Criteria in(Object... values) { } /** - * Crates new CriteriaEntry for multiple values {@code (arg0 arg1 arg2 ...)} + * Creates new CriteriaEntry for multiple values {@code (arg0 arg1 arg2 ...)} * * @param values the collection containing the values to match against * @return @@ -343,8 +344,24 @@ public Criteria in(Object... values) { 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; - } + 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"); + criteria.add(new CriteriaEntry(OperationKey.WITHIN, new Object[] { location, distance })); + return this; + } private void assertNoBlankInWildcardedQuery(String searchString, boolean leadingWildcard, boolean trailingWildcard) { @@ -426,7 +443,7 @@ public String getConjunctionOperator() { } public enum OperationKey { - EQUALS, CONTAINS, STARTS_WITH, ENDS_WITH, EXPRESSION, BETWEEN, FUZZY, IN; + EQUALS, CONTAINS, STARTS_WITH, ENDS_WITH, EXPRESSION, BETWEEN, FUZZY, IN, WITHIN, NEAR; } public static class CriteriaEntry { From d5f06cbf7a00ced910e0fe902e73e27857bde498 Mon Sep 17 00:00:00 2001 From: fmarchand Date: Wed, 3 Apr 2013 00:54:33 +0200 Subject: [PATCH 02/14] Query refactoring to handle queries/filters simultaneously --- .../data/elasticsearch/core/query/Criteria.java | 6 ++++++ .../data/elasticsearch/core/query/CriteriaQuery.java | 2 +- .../data/elasticsearch/core/query/Query.java | 2 +- .../data/elasticsearch/core/query/SearchQuery.java | 1 + .../data/elasticsearch/core/query/StringQuery.java | 2 +- 5 files changed, 10 insertions(+), 3 deletions(-) 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 760e229..af61c4f 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 @@ -90,6 +90,12 @@ protected Criteria(List criteriaChain, Field field) { this.field = field; } +// public void splitCriteria(List queries, List filters) { +// for(Criteria c : this.criteriaChain) { +// switch () +// } +// } + /** * Static factory method to create a new Criteria for field with given name * 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 bc70672..fd8a88a 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 @@ -78,4 +78,4 @@ public Criteria getCriteria() { return this.criteria; } -} +} \ No newline at end of file diff --git a/src/main/java/org/springframework/data/elasticsearch/core/query/Query.java b/src/main/java/org/springframework/data/elasticsearch/core/query/Query.java index c9f47d5..b01e66b 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/query/Query.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/query/Query.java @@ -111,4 +111,4 @@ public interface Query { * @return */ List getFields(); -} +} \ No newline at end of file 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 55424c4..7411ea0 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 @@ -18,6 +18,7 @@ import org.elasticsearch.index.query.FilterBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.search.sort.SortBuilder; + /** * NativeSearchQuery * diff --git a/src/main/java/org/springframework/data/elasticsearch/core/query/StringQuery.java b/src/main/java/org/springframework/data/elasticsearch/core/query/StringQuery.java index 7334395..be7aec0 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/query/StringQuery.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/query/StringQuery.java @@ -25,7 +25,7 @@ * @author Rizwan Idrees * @author Mohsin Husen */ -public class StringQuery extends AbstractQuery{ +public class StringQuery extends AbstractQuery { private String source; From 86a45163adbac4d461d1c9ba81884a70e68fee29 Mon Sep 17 00:00:00 2001 From: fmarchand Date: Fri, 5 Apr 2013 01:12:51 +0200 Subject: [PATCH 03/14] create CriteriaFilterProcessor to build filter builders to inject in Query --- .../core/CriteriaFilterProcessor.java | 116 ++++++++++++++++++ .../elasticsearch/core/geo/GeoLocation.java | 10 +- .../elasticsearch/core/query/Criteria.java | 25 ++-- .../data/elasticsearch/core/query/Query.java | 3 +- 4 files changed, 135 insertions(+), 19 deletions(-) create mode 100644 src/main/java/org/springframework/data/elasticsearch/core/CriteriaFilterProcessor.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 0000000..f68172b --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/core/CriteriaFilterProcessor.java @@ -0,0 +1,116 @@ +/* + * 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.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.QueryBuilders.*; +import static org.elasticsearch.index.query.FilterBuilders.*; +import static org.springframework.data.elasticsearch.core.query.Criteria.OperationKey; + +/** + * CriteriaFilterProcessor + * + * @author Franck Marchand + */ +class CriteriaFilterProcessor { + + + List createFilterFromCriteria(Criteria criteria) { + List fbList = new LinkedList(); + + 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()){ + fbList.addAll(buildNegationFilter(criteria.getField().getName(), criteria.getCriteriaEntries().iterator())); + }else{ + fbList.addAll(createFilterFragmentForCriteria(chainedCriteria)); + } + } + return fbList; + } + + + private List createFilterFragmentForCriteria(Criteria chainedCriteria) { + Iterator it = chainedCriteria.getCriteriaEntries().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.getLatitude()).lon(loc.getLongitude()).distance(dist); + 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/geo/GeoLocation.java b/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoLocation.java index 74095b4..4d26739 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoLocation.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoLocation.java @@ -25,15 +25,13 @@ public class GeoLocation { private double longitude; public GeoLocation lat(double latitude) { - GeoLocation loc = new GeoLocation(); - loc.setLatitude(latitude); - return loc; + setLatitude(latitude); + return this; } public GeoLocation lon(double longitude) { - GeoLocation loc = new GeoLocation(); - loc.setLongitude(longitude); - return loc; + setLongitude(longitude); + return this; } public GeoLocation() { 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 af61c4f..362b4b6 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 @@ -49,7 +49,8 @@ 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() { } @@ -178,7 +179,7 @@ public Criteria or(Criteria 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; } @@ -199,7 +200,7 @@ public Criteria or(String fieldName) { * @return */ public Criteria is(Object o) { - criteria.add(new CriteriaEntry(OperationKey.EQUALS, o)); + queryCriteria.add(new CriteriaEntry(OperationKey.EQUALS, o)); return this; } @@ -212,7 +213,7 @@ public Criteria is(Object o) { */ 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; } @@ -224,7 +225,7 @@ public Criteria contains(String s) { */ 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; } @@ -237,7 +238,7 @@ public Criteria startsWith(String s) { */ 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; } @@ -258,7 +259,7 @@ public Criteria not() { * @return */ public Criteria fuzzy(String s) { - criteria.add(new CriteriaEntry(OperationKey.FUZZY, s)); + queryCriteria.add(new CriteriaEntry(OperationKey.FUZZY, s)); return this; } @@ -270,7 +271,7 @@ public Criteria fuzzy(String s) { * @return */ public Criteria expression(String s) { - criteria.add(new CriteriaEntry(OperationKey.EXPRESSION, s)); + queryCriteria.add(new CriteriaEntry(OperationKey.EXPRESSION, s)); return this; } @@ -300,7 +301,7 @@ public Criteria between(Object lowerBound, Object upperBound) { 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; } @@ -349,7 +350,7 @@ public Criteria in(Object... values) { */ public Criteria in(Iterable values) { Assert.notNull(values, "Collection of 'in' values must not be null"); - criteria.add(new CriteriaEntry(OperationKey.IN, values)); + queryCriteria.add(new CriteriaEntry(OperationKey.IN, values)); return this; } @@ -365,7 +366,7 @@ public Criteria in(Iterable values) { 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"); - criteria.add(new CriteriaEntry(OperationKey.WITHIN, new Object[] { location, distance })); + filterCriteria.add(new CriteriaEntry(OperationKey.WITHIN, new Object[]{location, distance})); return this; } @@ -387,7 +388,7 @@ public Field getField() { } public Set getCriteriaEntries() { - return Collections.unmodifiableSet(this.criteria); + return Collections.unmodifiableSet(this.queryCriteria); } /** diff --git a/src/main/java/org/springframework/data/elasticsearch/core/query/Query.java b/src/main/java/org/springframework/data/elasticsearch/core/query/Query.java index b01e66b..d47c72c 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/query/Query.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/query/Query.java @@ -27,6 +27,7 @@ * * @author Rizwan Idrees * @author Mohsin Husen + * @author Franck Marchand */ public interface Query { @@ -111,4 +112,4 @@ public interface Query { * @return */ List getFields(); -} \ No newline at end of file +} From e92a3e88f430c918d39d9dfa353b4e04f241cff1 Mon Sep 17 00:00:00 2001 From: fmarchand Date: Fri, 5 Apr 2013 01:19:40 +0200 Subject: [PATCH 04/14] create CriteriaFilterProcessor to build filter builders to inject in Query --- .../core/CriteriaFilterProcessor.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) 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 f68172b..a2fed4a 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/CriteriaFilterProcessor.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/CriteriaFilterProcessor.java @@ -37,8 +37,9 @@ class CriteriaFilterProcessor { - List createFilterFromCriteria(Criteria criteria) { + FilterBuilder createFilterFromCriteria(Criteria criteria) { List fbList = new LinkedList(); + FilterBuilder filter = null; ListIterator chainIterator = criteria.getCriteriaChain().listIterator(); while (chainIterator.hasNext()) { @@ -53,7 +54,16 @@ List createFilterFromCriteria(Criteria criteria) { fbList.addAll(createFilterFragmentForCriteria(chainedCriteria)); } } - return fbList; + + if(!fbList.isEmpty()) { + if(fbList.size() == 1) { + filter =fbList.get(0); + } else { + filter = andFilter(fbList.toArray(new FilterBuilder[]{ })); + } + } + + return filter; } From 277c818f4b59953c7aa5963e2b920a7d3d757efe Mon Sep 17 00:00:00 2001 From: fmarchand Date: Sun, 7 Apr 2013 23:47:54 +0200 Subject: [PATCH 05/14] refactor back to Query interface and AbstractQuery. create CriteriaFilterProcessor to build filter builders to inject in Query --- .../data/elasticsearch/annotations/Query.java | 4 +- .../core/CriteriaFilterProcessor.java | 6 +- .../core/CriteriaQueryProcessor.java | 51 ++++- .../core/ElasticsearchOperations.java | 1 - .../core/ElasticsearchTemplate.java | 18 +- .../core/query/AbstractQuery.java | 204 +++++++++--------- .../elasticsearch/core/query/Criteria.java | 10 +- .../core/query/CriteriaQuery.java | 13 +- .../core/query/MoreLikeThisQuery.java | 2 +- .../data/elasticsearch/core/query/Query.java | 3 +- .../query/ElasticsearchStringQuery.java | 2 +- .../data/elasticsearch/GeoAuthor.java | 54 +++++ .../core/ElasticsearchTemplateTest.java | 26 +++ 13 files changed, 265 insertions(+), 129 deletions(-) create mode 100644 src/test/java/org/springframework/data/elasticsearch/GeoAuthor.java diff --git a/src/main/java/org/springframework/data/elasticsearch/annotations/Query.java b/src/main/java/org/springframework/data/elasticsearch/annotations/Query.java index 933c06d..a9f1404 100644 --- a/src/main/java/org/springframework/data/elasticsearch/annotations/Query.java +++ b/src/main/java/org/springframework/data/elasticsearch/annotations/Query.java @@ -18,7 +18,7 @@ import java.lang.annotation.*; /** - * Query + * AbstractQuery * * @author Rizwan Idrees * @author Mohsin Husen @@ -37,7 +37,7 @@ String value() default ""; /** - * Named Query Named looked up by repository. + * Named AbstractQuery Named looked up by repository. * * @return */ 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 a2fed4a..8e641dc 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/CriteriaFilterProcessor.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/CriteriaFilterProcessor.java @@ -25,7 +25,6 @@ import java.util.List; import java.util.ListIterator; -import static org.elasticsearch.index.query.QueryBuilders.*; import static org.elasticsearch.index.query.FilterBuilders.*; import static org.springframework.data.elasticsearch.core.query.Criteria.OperationKey; @@ -42,6 +41,7 @@ FilterBuilder createFilterFromCriteria(Criteria criteria) { FilterBuilder filter = null; ListIterator chainIterator = criteria.getCriteriaChain().listIterator(); + while (chainIterator.hasNext()) { FilterBuilder fb = null; Criteria chainedCriteria = chainIterator.next(); @@ -49,7 +49,7 @@ FilterBuilder createFilterFromCriteria(Criteria criteria) { fb = orFilter(createFilterFragmentForCriteria(chainedCriteria).toArray(new FilterBuilder[]{ })); fbList.add(fb); }else if(chainedCriteria.isNegating()){ - fbList.addAll(buildNegationFilter(criteria.getField().getName(), criteria.getCriteriaEntries().iterator())); + fbList.addAll(buildNegationFilter(criteria.getField().getName(), criteria.getQueryCriteriaEntries().iterator())); }else{ fbList.addAll(createFilterFragmentForCriteria(chainedCriteria)); } @@ -68,7 +68,7 @@ FilterBuilder createFilterFromCriteria(Criteria criteria) { private List createFilterFragmentForCriteria(Criteria chainedCriteria) { - Iterator it = chainedCriteria.getCriteriaEntries().iterator(); + Iterator it = chainedCriteria.getQueryCriteriaEntries().iterator(); List filterList = new LinkedList(); String fieldName = chainedCriteria.getField().getName(); 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 01866fa..4d83387 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.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.*; @@ -37,26 +39,57 @@ class CriteriaQueryProcessor { QueryBuilder createQueryFromCriteria(Criteria criteria) { - BoolQueryBuilder query = boolQuery(); + if(criteria == null || criteria.getQueryCriteriaEntries().size() == 0) + return null; + + List shouldQueryBuilderList = new LinkedList(); + List mustNotQueryBuilderList = new LinkedList(); + List mustQueryBuilderList = new LinkedList(); + 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)); + QueryBuilder queryFragmentForCriteria = createQueryFragmentForCriteria(chainedCriteria); + + if(queryFragmentForCriteria!=null) { + if(chainedCriteria.isOr()){ + shouldQueryBuilderList.add(queryFragmentForCriteria); + }else if(chainedCriteria.isNegating()){ + mustNotQueryBuilderList.add(queryFragmentForCriteria); + }else{ + mustQueryBuilderList.add(queryFragmentForCriteria); + } + } + } + + BoolQueryBuilder query = null; + + if(!shouldQueryBuilderList.isEmpty() || !mustNotQueryBuilderList.isEmpty() || !mustQueryBuilderList.isEmpty()) { + + query = boolQuery(); + + for(QueryBuilder qb : shouldQueryBuilderList) { + query.should(qb); + } + for(QueryBuilder qb : mustNotQueryBuilderList) { + query.mustNot(qb); + } + for(QueryBuilder qb : mustQueryBuilderList) { + query.must(qb); } } + return query; } private QueryBuilder createQueryFragmentForCriteria(Criteria chainedCriteria) { - Iterator it = chainedCriteria.getCriteriaEntries().iterator(); - boolean singeEntryCriteria = (chainedCriteria.getCriteriaEntries().size() == 1); + 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"); 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 480f3d5..68605f0 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchOperations.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchOperations.java @@ -89,7 +89,6 @@ public interface ElasticsearchOperations { */ Page queryForPage(SearchQuery query, Class clazz); - /** * Execute the query against elasticsearch and return result as {@link Page} * 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 0b08fe2..ace7ede 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java @@ -36,6 +36,7 @@ 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.sort.SortBuilder; import org.elasticsearch.search.sort.SortOrder; @@ -177,9 +178,20 @@ public List queryForIds(SearchQuery query) { @Override public Page queryForPage(CriteriaQuery criteriaQuery, Class clazz) { - QueryBuilder query = new CriteriaQueryProcessor().createQueryFromCriteria(criteriaQuery.getCriteria()); - SearchResponse response = prepareSearch(criteriaQuery,clazz) - .setQuery(query) + 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/query/AbstractQuery.java b/src/main/java/org/springframework/data/elasticsearch/core/query/AbstractQuery.java index 280654b..888b3cf 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 362b4b6..b373d8f 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 @@ -34,6 +34,7 @@ * * @author Rizwan Idrees * @author Mohsin Husen + * @author Franck Marchand */ public class Criteria { @@ -50,7 +51,8 @@ public class Criteria { private List criteriaChain = new ArrayList(1); private Set queryCriteria = new LinkedHashSet(); - private Set filterCriteria = new LinkedHashSet(); + + private Set filterCriteria = new LinkedHashSet(); public Criteria() { } @@ -387,10 +389,14 @@ public Field getField() { return this.field; } - public Set getCriteriaEntries() { + public Set getQueryCriteriaEntries() { return Collections.unmodifiableSet(this.queryCriteria); } + public Set getFilterCriteria() { + return filterCriteria; + } + /** * Conjunction to be used with this criteria (AND | OR) * 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 fd8a88a..52833ba 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 @@ -16,9 +16,15 @@ package org.springframework.data.elasticsearch.core.query; +import org.elasticsearch.index.query.FilterBuilder; +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.search.sort.SortBuilder; import org.springframework.data.domain.Pageable; import org.springframework.util.Assert; +import java.util.LinkedList; +import java.util.List; + /** * CriteriaQuery * @@ -28,7 +34,8 @@ public class CriteriaQuery extends AbstractQuery{ private Criteria criteria; - private CriteriaQuery() { + + protected CriteriaQuery() { } public CriteriaQuery(Criteria criteria) { @@ -43,7 +50,7 @@ public CriteriaQuery(Criteria criteria, Pageable pageable) { } } - public static final Query fromQuery(CriteriaQuery source) { + public static final CriteriaQuery fromQuery(CriteriaQuery source) { return fromQuery(source, new CriteriaQuery()); } @@ -78,4 +85,4 @@ public Criteria getCriteria() { return this.criteria; } -} \ No newline at end of file +} 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 3133a3a..724e7c9 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 @@ -22,7 +22,7 @@ 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/Query.java b/src/main/java/org/springframework/data/elasticsearch/core/query/Query.java index d47c72c..c9f47d5 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/query/Query.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/query/Query.java @@ -27,7 +27,6 @@ * * @author Rizwan Idrees * @author Mohsin Husen - * @author Franck Marchand */ public interface Query { @@ -112,4 +111,4 @@ public interface Query { * @return */ List getFields(); -} +} diff --git a/src/main/java/org/springframework/data/elasticsearch/repository/query/ElasticsearchStringQuery.java b/src/main/java/org/springframework/data/elasticsearch/repository/query/ElasticsearchStringQuery.java index 1d833bd..5b48ebf 100644 --- a/src/main/java/org/springframework/data/elasticsearch/repository/query/ElasticsearchStringQuery.java +++ b/src/main/java/org/springframework/data/elasticsearch/repository/query/ElasticsearchStringQuery.java @@ -54,7 +54,7 @@ public class ElasticsearchStringQuery extends AbstractElasticsearchRepositoryQue public ElasticsearchStringQuery(ElasticsearchQueryMethod queryMethod, ElasticsearchOperations elasticsearchOperations, String query) { super(queryMethod, elasticsearchOperations); - Assert.notNull(query, "Query cannot be empty"); + Assert.notNull(query, "AbstractQuery cannot be empty"); this.query = query; } 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 0000000..9920997 --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/GeoAuthor.java @@ -0,0 +1,54 @@ +/* + * 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.elasticsearch.core.geo.GeoLocation; + +/** + * @author Rizwan Idrees + * @author Mohsin Husen + */ +public class GeoAuthor { + + 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/ElasticsearchTemplateTest.java b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTest.java index 4b22025..afbe44d 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTest.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTest.java @@ -450,6 +450,32 @@ public void shouldExecuteGivenCriteriaQuery(){ assertThat(sampleEntity1, is(notNullValue())); } +// @Test +// public void shouldExecuteGivenCriteriaQuery(){ +// //given +// String documentId = randomNumeric(5); +// SampleEntity sampleEntity = new SampleEntity(); +// sampleEntity.setId(documentId); +// sampleEntity.setMessage("some test message"); +// sampleEntity.setVersion(System.currentTimeMillis()); +// +// IndexQuery indexQuery = new IndexQuery(); +// indexQuery.setId(documentId); +// indexQuery.setObject(sampleEntity); +// +// elasticsearchTemplate.index(indexQuery); +// elasticsearchTemplate.refresh(SampleEntity.class, true); +// +// Criteria crit = new Criteria("message").contains("test"); +// +// CriteriaQuery query = new CriteriaQuery(crit); +// +// //when +// SampleEntity sampleEntity1 = elasticsearchTemplate.queryForObject(query,SampleEntity.class); +// //then +// assertThat(sampleEntity1, is(notNullValue())); +// } + @Test public void shouldReturnSpecifiedFields(){ //given From 675d8dcc7e3395a461a3e987ecd47002181ebc88 Mon Sep 17 00:00:00 2001 From: fmarchand Date: Wed, 17 Apr 2013 01:01:58 +0200 Subject: [PATCH 06/14] make tests pass for CriteraQuery add geo location (within) query test --- .../core/CriteriaFilterProcessor.java | 2 +- .../elasticsearch/core/MappingBuilder.java | 12 ++++ .../elasticsearch/core/query/Criteria.java | 4 ++ .../data/elasticsearch/GeoAuthor.java | 7 +- .../core/ElasticsearchTemplateTest.java | 71 ++++++++++++++++++- 5 files changed, 92 insertions(+), 4 deletions(-) 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 8e641dc..36743d7 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/CriteriaFilterProcessor.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/CriteriaFilterProcessor.java @@ -68,7 +68,7 @@ FilterBuilder createFilterFromCriteria(Criteria criteria) { private List createFilterFragmentForCriteria(Criteria chainedCriteria) { - Iterator it = chainedCriteria.getQueryCriteriaEntries().iterator(); + Iterator it = chainedCriteria.getFilterCriteriaEntries().iterator(); List filterList = new LinkedList(); String fieldName = chainedCriteria.getField().getName(); 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 8abfe9e..dd5dc50 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 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.springframework.data.elasticsearch.annotations.Field; +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; @@ -65,6 +66,11 @@ private static void mapEntity(XContentBuilder xContentBuilder, if(isEntity(field)){ mapEntity(xContentBuilder, field.getType(), false, EMPTY, field.getName()); } + + if(field.getType() == GeoLocation.class) { + applyGeoLocationFieldMapping(xContentBuilder, field); + } + Field fieldAnnotation = field.getAnnotation(Field.class); if(isRootObject && fieldAnnotation != null && isIdField(field, idFieldName)){ applyDefaultIdFieldMapping(xContentBuilder, field); @@ -79,6 +85,12 @@ private static void mapEntity(XContentBuilder xContentBuilder, } + 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()) .field("type", "string") 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 b373d8f..36d4189 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 @@ -393,6 +393,10 @@ public Set getQueryCriteriaEntries() { return Collections.unmodifiableSet(this.queryCriteria); } + public Set getFilterCriteriaEntries() { + return Collections.unmodifiableSet(this.filterCriteria); + } + public Set getFilterCriteria() { return filterCriteria; } diff --git a/src/test/java/org/springframework/data/elasticsearch/GeoAuthor.java b/src/test/java/org/springframework/data/elasticsearch/GeoAuthor.java index 9920997..671e7d1 100644 --- a/src/test/java/org/springframework/data/elasticsearch/GeoAuthor.java +++ b/src/test/java/org/springframework/data/elasticsearch/GeoAuthor.java @@ -15,14 +15,17 @@ */ 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 Rizwan Idrees - * @author Mohsin Husen + * @author Franck Marchand */ +@Document(indexName = "test-geo-index", type = "test-geo-type") public class GeoAuthor { + @Id private String id; private String name; diff --git a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTest.java b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTest.java index afbe44d..9419d9b 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTest.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTest.java @@ -29,8 +29,10 @@ 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.GeoLocation; import org.springframework.data.elasticsearch.core.query.*; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -48,6 +50,7 @@ /** * @author Rizwan Idrees * @author Mohsin Husen + * @author Franck Marchand */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:elasticsearch-template-test.xml") @@ -592,7 +595,7 @@ public void shouldReturnResultsWithScanAndScroll(){ indexQueries.add(indexQuery2); //when elasticsearchTemplate.bulkIndex(indexQueries); - elasticsearchTemplate.refresh(SampleEntity.class,true); + elasticsearchTemplate.refresh(SampleEntity.class, true); //then SearchQuery searchQuery = new NativeSearchQueryBuilder() @@ -760,5 +763,71 @@ public void shouldDeleteIndexForGivenEntity(){ } + @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(){ + // put mapping before any request + Class entity = GeoAuthor.class; + elasticsearchTemplate.createIndex(entity); + + elasticsearchTemplate.putMapping(entity); + + //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)); + + 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)); + + IndexQuery indexQuery2 = new IndexQuery(); + indexQuery2.setId(documentId2); + indexQuery2.setObject(geoAuthor2); + + indexQueries.add(indexQuery2); + + //second document + String documentId3 = randomNumeric(5); + GeoAuthor geoAuthor3 = new GeoAuthor(); + geoAuthor3.setId(documentId3); + geoAuthor3.setName("Rizwan Idrees"); + geoAuthor3.setLocation(new GeoLocation(51.5171d, 0.1062d)); + 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)); + } } From 7f4c3a89d3b307c84cfafe44e9c372e4dcfb4276 Mon Sep 17 00:00:00 2001 From: fmarchand Date: Mon, 22 Apr 2013 22:38:51 +0200 Subject: [PATCH 07/14] make test pass for CriteraQuery with geo query rename longitude and latitude fields to match with geo_point ES when indexed ("lon" and "lat"). --- .../core/CriteriaFilterProcessor.java | 2 +- .../elasticsearch/core/geo/GeoLocation.java | 32 +++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) 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 36743d7..424776b 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/CriteriaFilterProcessor.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/CriteriaFilterProcessor.java @@ -105,7 +105,7 @@ private FilterBuilder processCriteriaEntry(OperationKey key, Object value, Strin GeoLocation loc = (GeoLocation)valArray[0]; String dist = (String)valArray[1]; - ((GeoDistanceFilterBuilder)filter).lat(loc.getLatitude()).lon(loc.getLongitude()).distance(dist); + ((GeoDistanceFilterBuilder)filter).lat(loc.getLat()).lon(loc.getLon()).distance(dist); break; } 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 index 4d26739..09fe4f0 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoLocation.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoLocation.java @@ -21,16 +21,16 @@ * @author fmarchand */ public class GeoLocation { - private double latitude; - private double longitude; + private double lat; + private double lon; - public GeoLocation lat(double latitude) { - setLatitude(latitude); + public GeoLocation lat(double lat) { + setLat(lat); return this; } - public GeoLocation lon(double longitude) { - setLongitude(longitude); + public GeoLocation lon(double lon) { + setLon(lon); return this; } @@ -38,23 +38,23 @@ public GeoLocation() { } public GeoLocation(double latitude, double longitude) { - this.latitude = latitude; - this.longitude = longitude; + this.lat = latitude; + this.lon = longitude; } - public double getLatitude() { - return latitude; + public double getLat() { + return lat; } - public void setLatitude(double latitude) { - this.latitude = latitude; + public void setLat(double lat) { + this.lat = lat; } - public double getLongitude() { - return longitude; + public double getLon() { + return lon; } - public void setLongitude(double longitude) { - this.longitude = longitude; + public void setLon(double lon) { + this.lon = lon; } } From 3600ffcf4ca1551451b597fe5addcf7a7ae97967 Mon Sep 17 00:00:00 2001 From: fmarchand Date: Mon, 22 Apr 2013 23:17:24 +0200 Subject: [PATCH 08/14] comments typo --- .../data/elasticsearch/core/CriteriaQueryProcessor.java | 1 + .../data/elasticsearch/core/ElasticsearchTemplateTest.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) 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 4d83387..46ff43d 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/CriteriaQueryProcessor.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/CriteriaQueryProcessor.java @@ -34,6 +34,7 @@ * * @author Rizwan Idrees * @author Mohsin Husen + * @author Franck Marchand */ class CriteriaQueryProcessor { diff --git a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTest.java b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTest.java index 9419d9b..ddd0625 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTest.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTest.java @@ -807,7 +807,7 @@ public void shouldReturnListForGivenCriteriaWithGeoLocation(){ indexQueries.add(indexQuery2); - //second document + //third document String documentId3 = randomNumeric(5); GeoAuthor geoAuthor3 = new GeoAuthor(); geoAuthor3.setId(documentId3); From c45426312e5761e84deb2f620c27c7e2d5a7c58a Mon Sep 17 00:00:00 2001 From: fmarchand Date: Tue, 23 Apr 2013 01:13:35 +0200 Subject: [PATCH 09/14] - make test pass for CriteraQuery with geo query : missing mapping in the "before" junit method - fix filter negation bug. --- .../core/CriteriaFilterProcessor.java | 8 ++++++-- .../core/ElasticsearchTemplateTest.java | 19 ++++++++++++++----- 2 files changed, 20 insertions(+), 7 deletions(-) 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 424776b..31cce79 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/CriteriaFilterProcessor.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/CriteriaFilterProcessor.java @@ -49,8 +49,12 @@ FilterBuilder createFilterFromCriteria(Criteria criteria) { fb = orFilter(createFilterFragmentForCriteria(chainedCriteria).toArray(new FilterBuilder[]{ })); fbList.add(fb); }else if(chainedCriteria.isNegating()){ - fbList.addAll(buildNegationFilter(criteria.getField().getName(), criteria.getQueryCriteriaEntries().iterator())); - }else{ + List negationFilters = buildNegationFilter(criteria.getField().getName(), criteria.getFilterCriteriaEntries().iterator()); + + if(!negationFilters.isEmpty()) { + fbList.addAll(negationFilters); + } + }else { fbList.addAll(createFilterFragmentForCriteria(chainedCriteria)); } } diff --git a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTest.java b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTest.java index ddd0625..5077fc1 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTest.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTest.java @@ -66,6 +66,14 @@ public void before(){ deleteQuery.setQuery(matchAllQuery()); elasticsearchTemplate.delete(deleteQuery,SampleEntity.class); elasticsearchTemplate.refresh(SampleEntity.class, true); + + elasticsearchTemplate.createIndex(GeoAuthor.class); + DeleteQuery deleteGeoAuthorQuery = new DeleteQuery(); + deleteGeoAuthorQuery.setElasticsearchQuery(matchAllQuery()); + elasticsearchTemplate.delete(deleteGeoAuthorQuery,GeoAuthor.class); + elasticsearchTemplate.refresh(GeoAuthor.class, true); + + elasticsearchTemplate.putMapping(GeoAuthor.class); } @Test @@ -775,8 +783,8 @@ public void shouldPutMappingForGivenEntityWithGeoLocation()throws Exception{ @Test public void shouldReturnListForGivenCriteriaWithGeoLocation(){ // put mapping before any request - Class entity = GeoAuthor.class; - elasticsearchTemplate.createIndex(entity); + /*Class entity = GeoAuthor.class; + elasticsearchTemplate.createIndex(entity);*/ elasticsearchTemplate.putMapping(entity); @@ -787,7 +795,7 @@ public void shouldReturnListForGivenCriteriaWithGeoLocation(){ GeoAuthor geoAuthor1 = new GeoAuthor(); geoAuthor1.setId(documentId); geoAuthor1.setName("Franck Marchand"); - geoAuthor1.setLocation(new GeoLocation(45.7806d, 3.0875d)); + geoAuthor1.setLocation(new GeoLocation(45.7806d, 3.0875d)); // Clermont-Ferrand IndexQuery indexQuery1 = new IndexQuery(); indexQuery1.setId(documentId); @@ -799,7 +807,7 @@ public void shouldReturnListForGivenCriteriaWithGeoLocation(){ GeoAuthor geoAuthor2 = new GeoAuthor(); geoAuthor2.setId(documentId2); geoAuthor2.setName("Mohsin Husen"); - geoAuthor2.setLocation(new GeoLocation(51.5171d, 0.1062d)); + geoAuthor2.setLocation(new GeoLocation(51.5171d, 0.1062d)); // London IndexQuery indexQuery2 = new IndexQuery(); indexQuery2.setId(documentId2); @@ -812,7 +820,7 @@ public void shouldReturnListForGivenCriteriaWithGeoLocation(){ GeoAuthor geoAuthor3 = new GeoAuthor(); geoAuthor3.setId(documentId3); geoAuthor3.setName("Rizwan Idrees"); - geoAuthor3.setLocation(new GeoLocation(51.5171d, 0.1062d)); + geoAuthor3.setLocation(new GeoLocation(51.5171d, 0.1062d)); // London IndexQuery indexQuery3 = new IndexQuery(); indexQuery3.setId(documentId3); @@ -829,5 +837,6 @@ public void shouldReturnListForGivenCriteriaWithGeoLocation(){ List geoAuthorsForGeoCriteria = elasticsearchTemplate.queryForList(geoLocationCriteriaQuery,GeoAuthor.class); //then assertThat(geoAuthorsForGeoCriteria.size(),is(1)); + assertEquals("Franck Marchand", geoAuthorsForGeoCriteria.get(0).getName()); } } From d72e9128b30db5f208757a2b53b37627a09accc4 Mon Sep 17 00:00:00 2001 From: fmarchand Date: Wed, 24 Apr 2013 00:21:25 +0200 Subject: [PATCH 10/14] DeleteQuery changed. Updated to new DeleteQuery version. --- .../elasticsearch/core/ElasticsearchTemplateTest.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTest.java b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTest.java index 5077fc1..dfd920f 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTest.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTest.java @@ -69,7 +69,7 @@ public void before(){ elasticsearchTemplate.createIndex(GeoAuthor.class); DeleteQuery deleteGeoAuthorQuery = new DeleteQuery(); - deleteGeoAuthorQuery.setElasticsearchQuery(matchAllQuery()); + deleteGeoAuthorQuery.setQuery(matchAllQuery()); elasticsearchTemplate.delete(deleteGeoAuthorQuery,GeoAuthor.class); elasticsearchTemplate.refresh(GeoAuthor.class, true); @@ -782,12 +782,6 @@ public void shouldPutMappingForGivenEntityWithGeoLocation()throws Exception{ @Test public void shouldReturnListForGivenCriteriaWithGeoLocation(){ - // put mapping before any request - /*Class entity = GeoAuthor.class; - elasticsearchTemplate.createIndex(entity);*/ - - elasticsearchTemplate.putMapping(entity); - //given List indexQueries = new ArrayList(); //first document From c69d3f712cbb6afe2350f3228d663081a4378d6c Mon Sep 17 00:00:00 2001 From: fmarchand Date: Wed, 24 Apr 2013 00:53:02 +0200 Subject: [PATCH 11/14] Create GeoPolygon class. --- .../elasticsearch/core/geo/GeoLocation.java | 2 +- .../elasticsearch/core/geo/GeoPolygon.java | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/springframework/data/elasticsearch/core/geo/GeoPolygon.java 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 index 09fe4f0..33a4267 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoLocation.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoLocation.java @@ -18,7 +18,7 @@ /** * geo-location used for #{@link org.springframework.data.elasticsearch.core.query.Criteria}. * - * @author fmarchand + * @author Franck Marchand */ public class GeoLocation { private double lat; 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 0000000..172fc11 --- /dev/null +++ b/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoPolygon.java @@ -0,0 +1,24 @@ +/* + * 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 polygone used for #{@link org.springframework.data.elasticsearch.core.query.Criteria}. + * + * @author Franck Marchand + */ +public class GeoPolygon { +} From baf64ac2ecf8b7888de7f1cc1d2f528c51d3a82e Mon Sep 17 00:00:00 2001 From: fmarchand Date: Wed, 24 Apr 2013 00:58:18 +0200 Subject: [PATCH 12/14] Add GeoPolygon constructor and field. --- .../data/elasticsearch/core/geo/GeoPolygon.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) 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 index 172fc11..46ae5d8 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoPolygon.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/geo/GeoPolygon.java @@ -15,10 +15,17 @@ */ package org.springframework.data.elasticsearch.core.geo; +import java.util.List; + /** - * Geo polygone used for #{@link org.springframework.data.elasticsearch.core.query.Criteria}. + * 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; + } } From 722136ea780d2ca0377114bc1a835004117db4e3 Mon Sep 17 00:00:00 2001 From: fmarchand Date: Thu, 25 Apr 2013 00:45:13 +0200 Subject: [PATCH 13/14] add GeoBBox class and query handling and add Tests for it. --- pom.xml | 8 +++ .../core/CriteriaFilterProcessor.java | 20 +++++++- .../core/CriteriaQueryProcessor.java | 2 +- .../data/elasticsearch/core/geo/GeoBBox.java | 49 +++++++++++++++++++ .../elasticsearch/core/query/Criteria.java | 15 +++++- .../core/ElasticsearchTemplateTest.java | 27 +++++++++- 6 files changed, 116 insertions(+), 5 deletions(-) create mode 100644 src/main/java/org/springframework/data/elasticsearch/core/geo/GeoBBox.java diff --git a/pom.xml b/pom.xml index b5ce879..e619974 100644 --- a/pom.xml +++ b/pom.xml @@ -26,6 +26,7 @@ 2.1 1.0.7 1.9.5 + 2.0M10 1.3 1.0-SP4 1.1.5 @@ -187,6 +188,13 @@ test + + org.easytesting + fest-assert-core + ${fest-assert.version} + test + + org.hamcrest hamcrest-all 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 31cce79..381a427 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/CriteriaFilterProcessor.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/CriteriaFilterProcessor.java @@ -16,6 +16,7 @@ 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; @@ -96,7 +97,7 @@ private FilterBuilder processCriteriaEntry(OperationKey key, Object value, Strin FilterBuilder filter = null; switch (key){ - case WITHIN: + case WITHIN: { filter = geoDistanceFilter(fieldName); Assert.isTrue(value instanceof Object[], "Value of a geo distance filter should be an array of two values."); @@ -113,6 +114,23 @@ private FilterBuilder processCriteriaEntry(OperationKey key, Object value, Strin 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; } 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 46ff43d..818d680 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/CriteriaQueryProcessor.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/CriteriaQueryProcessor.java @@ -40,7 +40,7 @@ class CriteriaQueryProcessor { QueryBuilder createQueryFromCriteria(Criteria criteria) { - if(criteria == null || criteria.getQueryCriteriaEntries().size() == 0) + if(criteria == null) return null; List shouldQueryBuilderList = new LinkedList(); 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 0000000..817398a --- /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/query/Criteria.java b/src/main/java/org/springframework/data/elasticsearch/core/query/Criteria.java index 36d4189..7b2fcd8 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,7 @@ 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; @@ -372,6 +373,18 @@ public Criteria within(GeoLocation location, String 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)) { @@ -460,7 +473,7 @@ public String getConjunctionOperator() { } public enum OperationKey { - EQUALS, CONTAINS, STARTS_WITH, ENDS_WITH, EXPRESSION, BETWEEN, FUZZY, IN, WITHIN, NEAR; + EQUALS, CONTAINS, STARTS_WITH, ENDS_WITH, EXPRESSION, BETWEEN, FUZZY, IN, WITHIN, BBOX, NEAR; } public static class CriteriaEntry { diff --git a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTest.java b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTest.java index dfd920f..a3a08c6 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTest.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTest.java @@ -32,6 +32,7 @@ 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; @@ -45,8 +46,11 @@ 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.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + /** * @author Rizwan Idrees * @author Mohsin Husen @@ -823,14 +827,33 @@ public void shouldReturnListForGivenCriteriaWithGeoLocation(){ indexQueries.add(indexQuery3); //when elasticsearchTemplate.bulkIndex(indexQueries); - elasticsearchTemplate.refresh(GeoAuthor.class,true); + 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")))); } } From d96a5496c1c6b4cf38d6dcd7c212e037f85a755a Mon Sep 17 00:00:00 2001 From: fmarchand Date: Thu, 25 Apr 2013 00:50:29 +0200 Subject: [PATCH 14/14] Remove fest-assert from pom. We already have hamcrest. --- pom.xml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pom.xml b/pom.xml index e619974..b5ce879 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,6 @@ 2.1 1.0.7 1.9.5 - 2.0M10 1.3 1.0-SP4 1.1.5 @@ -188,13 +187,6 @@ test - - org.easytesting - fest-assert-core - ${fest-assert.version} - test - - org.hamcrest hamcrest-all