Skip to content

Commit 49708d3

Browse files
committed
DATAES-171 - added support for missing query keywords
1 parent d921263 commit 49708d3

File tree

10 files changed

+354
-141
lines changed

10 files changed

+354
-141
lines changed

pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,13 @@
121121
<scope>test</scope>
122122
</dependency>
123123

124+
<dependency>
125+
<groupId>org.projectlombok</groupId>
126+
<artifactId>lombok</artifactId>
127+
<version>1.16.4</version>
128+
<scope>test</scope>
129+
</dependency>
130+
124131
</dependencies>
125132

126133
<build>

src/main/java/org/springframework/data/elasticsearch/core/CriteriaQueryProcessor.java

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@
2424
import java.util.ListIterator;
2525

2626
import org.apache.lucene.queryparser.flexible.core.util.StringUtils;
27-
import org.elasticsearch.index.query.*;
28-
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
27+
import org.elasticsearch.index.query.BoolQueryBuilder;
28+
import org.elasticsearch.index.query.BoostableQueryBuilder;
29+
import org.elasticsearch.index.query.QueryBuilder;
30+
import org.elasticsearch.index.query.QueryStringQueryBuilder;
2931
import org.springframework.data.elasticsearch.core.query.Criteria;
30-
import org.springframework.data.geo.Point;
3132
import org.springframework.util.Assert;
3233

3334
/**
@@ -36,6 +37,7 @@
3637
* @author Rizwan Idrees
3738
* @author Mohsin Husen
3839
* @author Franck Marchand
40+
* @author Artur Konczak
3941
*/
4042
class CriteriaQueryProcessor {
4143

@@ -49,11 +51,19 @@ QueryBuilder createQueryFromCriteria(Criteria criteria) {
4951
List<QueryBuilder> mustQueryBuilderList = new LinkedList<QueryBuilder>();
5052

5153
ListIterator<Criteria> chainIterator = criteria.getCriteriaChain().listIterator();
54+
55+
QueryBuilder firstQuery = null;
56+
boolean negateFirstQuery = false;
57+
5258
while (chainIterator.hasNext()) {
5359
Criteria chainedCriteria = chainIterator.next();
5460
QueryBuilder queryFragmentForCriteria = createQueryFragmentForCriteria(chainedCriteria);
55-
5661
if (queryFragmentForCriteria != null) {
62+
if (firstQuery == null) {
63+
firstQuery = queryFragmentForCriteria;
64+
negateFirstQuery = chainedCriteria.isNegating();
65+
continue;
66+
}
5767
if (chainedCriteria.isOr()) {
5868
shouldQueryBuilderList.add(queryFragmentForCriteria);
5969
} else if (chainedCriteria.isNegating()) {
@@ -64,6 +74,18 @@ QueryBuilder createQueryFromCriteria(Criteria criteria) {
6474
}
6575
}
6676

77+
if (firstQuery != null) {
78+
if (!shouldQueryBuilderList.isEmpty() && mustNotQueryBuilderList.isEmpty() && mustQueryBuilderList.isEmpty()) {
79+
shouldQueryBuilderList.add(0, firstQuery);
80+
} else {
81+
if (negateFirstQuery) {
82+
mustNotQueryBuilderList.add(0, firstQuery);
83+
} else {
84+
mustQueryBuilderList.add(0, firstQuery);
85+
}
86+
}
87+
}
88+
6789
BoolQueryBuilder query = null;
6890

6991
if (!shouldQueryBuilderList.isEmpty() || !mustNotQueryBuilderList.isEmpty() || !mustQueryBuilderList.isEmpty()) {
@@ -98,12 +120,12 @@ private QueryBuilder createQueryFragmentForCriteria(Criteria chainedCriteria) {
98120

99121
if (singeEntryCriteria) {
100122
Criteria.CriteriaEntry entry = it.next();
101-
query = processCriteriaEntry(entry.getKey(), entry.getValue(), fieldName);
123+
query = processCriteriaEntry(entry, fieldName);
102124
} else {
103125
query = boolQuery();
104126
while (it.hasNext()) {
105127
Criteria.CriteriaEntry entry = it.next();
106-
((BoolQueryBuilder) query).must(processCriteriaEntry(entry.getKey(), entry.getValue(), fieldName));
128+
((BoolQueryBuilder) query).must(processCriteriaEntry(entry, fieldName));
107129
}
108130
}
109131

@@ -112,14 +134,18 @@ private QueryBuilder createQueryFragmentForCriteria(Criteria chainedCriteria) {
112134
}
113135

114136

115-
private QueryBuilder processCriteriaEntry(OperationKey key, Object value, String fieldName) {
137+
private QueryBuilder processCriteriaEntry(Criteria.CriteriaEntry entry,/* OperationKey key, Object value,*/ String fieldName) {
138+
Object value = entry.getValue();
116139
if (value == null) {
117140
return null;
118141
}
142+
OperationKey key = entry.getKey();
119143
QueryBuilder query = null;
120144

121145
String searchText = StringUtils.toString(value);
122146

147+
Iterable<Object> collection = null;
148+
123149
switch (key) {
124150
case EQUALS:
125151
query = queryString(searchText).field(fieldName).defaultOperator(QueryStringQueryBuilder.Operator.AND);
@@ -134,24 +160,42 @@ private QueryBuilder processCriteriaEntry(OperationKey key, Object value, String
134160
query = queryString("*" + searchText).field(fieldName).analyzeWildcard(true);
135161
break;
136162
case EXPRESSION:
137-
query = queryString((String) value).field(fieldName);
163+
query = queryString(searchText).field(fieldName);
164+
break;
165+
case LESS_EQUAL:
166+
query = rangeQuery(fieldName).lte(value);
167+
break;
168+
case GREATER_EQUAL:
169+
query = rangeQuery(fieldName).gte(value);
138170
break;
139171
case BETWEEN:
140172
Object[] ranges = (Object[]) value;
141173
query = rangeQuery(fieldName).from(ranges[0]).to(ranges[1]);
142174
break;
175+
case LESS:
176+
query = rangeQuery(fieldName).lt(value);
177+
break;
178+
case GREATER:
179+
query = rangeQuery(fieldName).gt(value);
180+
break;
143181
case FUZZY:
144-
query = fuzzyQuery(fieldName, (String) value);
182+
query = fuzzyQuery(fieldName, searchText);
145183
break;
146184
case IN:
147185
query = boolQuery();
148-
Iterable<Object> collection = (Iterable<Object>) value;
186+
collection = (Iterable<Object>) value;
149187
for (Object item : collection) {
150-
((BoolQueryBuilder) query).should(queryString((String) item).field(fieldName));
188+
((BoolQueryBuilder) query).should(queryString(item.toString()).field(fieldName));
189+
}
190+
break;
191+
case NOT_IN:
192+
query = boolQuery();
193+
collection = (Iterable<Object>) value;
194+
for (Object item : collection) {
195+
((BoolQueryBuilder) query).mustNot(queryString(item.toString()).field(fieldName));
151196
}
152197
break;
153198
}
154-
155199
return query;
156200
}
157201

src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,21 @@
1515
*/
1616
package org.springframework.data.elasticsearch.core;
1717

18+
import static org.apache.commons.collections.CollectionUtils.isNotEmpty;
19+
import static org.apache.commons.lang.StringUtils.*;
20+
import static org.elasticsearch.action.search.SearchType.*;
21+
import static org.elasticsearch.client.Requests.*;
22+
import static org.elasticsearch.cluster.metadata.AliasAction.Type.*;
23+
import static org.elasticsearch.common.collect.Sets.*;
24+
import static org.elasticsearch.index.VersionType.*;
25+
import static org.springframework.data.elasticsearch.core.MappingBuilder.*;
26+
27+
import java.io.BufferedReader;
28+
import java.io.IOException;
29+
import java.io.InputStreamReader;
30+
import java.lang.reflect.Method;
31+
import java.util.*;
32+
1833
import org.apache.commons.collections.CollectionUtils;
1934
import org.elasticsearch.action.ListenableActionFuture;
2035
import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest;
@@ -81,23 +96,6 @@
8196
import org.springframework.data.util.CloseableIterator;
8297
import org.springframework.util.Assert;
8398

84-
import java.io.BufferedReader;
85-
import java.io.IOException;
86-
import java.io.InputStreamReader;
87-
import java.lang.reflect.Method;
88-
import java.util.*;
89-
90-
import static org.apache.commons.collections.CollectionUtils.isNotEmpty;
91-
import static org.apache.commons.lang.StringUtils.isBlank;
92-
import static org.apache.commons.lang.StringUtils.isNotBlank;
93-
import static org.elasticsearch.action.search.SearchType.SCAN;
94-
import static org.elasticsearch.client.Requests.indicesExistsRequest;
95-
import static org.elasticsearch.client.Requests.refreshRequest;
96-
import static org.elasticsearch.cluster.metadata.AliasAction.Type.ADD;
97-
import static org.elasticsearch.common.collect.Sets.newHashSet;
98-
import static org.elasticsearch.index.VersionType.EXTERNAL;
99-
import static org.springframework.data.elasticsearch.core.MappingBuilder.buildMapping;
100-
10199
/**
102100
* ElasticsearchTemplate
103101
*
@@ -314,6 +312,9 @@ public <T> Page<T> queryForPage(CriteriaQuery criteriaQuery, Class<T> clazz) {
314312

315313
if (elasticsearchFilter != null)
316314
searchRequestBuilder.setPostFilter(elasticsearchFilter);
315+
if (logger.isDebugEnabled()) {
316+
logger.debug("doSearch query:\n" + searchRequestBuilder.toString());
317+
}
317318

318319
SearchResponse response = getSearchResponse(searchRequestBuilder
319320
.execute());
@@ -401,7 +402,6 @@ public T next() {
401402
}
402403
throw new NoSuchElementException();
403404
}
404-
405405
};
406406
}
407407

@@ -536,7 +536,7 @@ private UpdateRequestBuilder prepareUpdate(UpdateQuery query) {
536536
Assert.notNull(query.getUpdateRequest(), "No IndexRequest define for Query");
537537
UpdateRequestBuilder updateRequestBuilder = client.prepareUpdate(indexName, type, query.getId());
538538

539-
if(query.getUpdateRequest().script() == null) {
539+
if (query.getUpdateRequest().script() == null) {
540540
// doc
541541
if (query.DoUpsert()) {
542542
updateRequestBuilder.setDocAsUpsert(true)

src/main/java/org/springframework/data/elasticsearch/core/query/Criteria.java

Lines changed: 64 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,17 @@
3535
*/
3636
public class Criteria {
3737

38+
@Override
39+
public String toString() {
40+
return "Criteria{" +
41+
"field=" + field.getName() +
42+
", boost=" + boost +
43+
", negating=" + negating +
44+
", queryCriteria=" + StringUtils.join(queryCriteria, '|') +
45+
", filterCriteria=" + StringUtils.join(filterCriteria, '|') +
46+
'}';
47+
}
48+
3849
public static final String WILDCARD = "*";
3950
public static final String CRITERIA_VALUE_SEPERATOR = " ";
4051

@@ -71,7 +82,6 @@ public Criteria(String fieldname) {
7182
public Criteria(Field field) {
7283
Assert.notNull(field, "Field for criteria must not be null");
7384
Assert.hasText(field.getName(), "Field.name for criteria must not be null/empty");
74-
7585
this.criteriaChain.add(this);
7686
this.field = field;
7787
}
@@ -304,7 +314,18 @@ public Criteria between(Object lowerBound, Object upperBound) {
304314
* @return
305315
*/
306316
public Criteria lessThanEqual(Object upperBound) {
307-
between(null, upperBound);
317+
if (upperBound == null) {
318+
throw new InvalidDataAccessApiUsageException("UpperBound can't be null");
319+
}
320+
queryCriteria.add(new CriteriaEntry(OperationKey.LESS_EQUAL, upperBound));
321+
return this;
322+
}
323+
324+
public Criteria lessThan(Object upperBound) {
325+
if (upperBound == null) {
326+
throw new InvalidDataAccessApiUsageException("UpperBound can't be null");
327+
}
328+
queryCriteria.add(new CriteriaEntry(OperationKey.LESS, upperBound));
308329
return this;
309330
}
310331

@@ -315,7 +336,18 @@ public Criteria lessThanEqual(Object upperBound) {
315336
* @return
316337
*/
317338
public Criteria greaterThanEqual(Object lowerBound) {
318-
between(lowerBound, null);
339+
if (lowerBound == null) {
340+
throw new InvalidDataAccessApiUsageException("LowerBound can't be null");
341+
}
342+
queryCriteria.add(new CriteriaEntry(OperationKey.GREATER_EQUAL, lowerBound));
343+
return this;
344+
}
345+
346+
public Criteria greaterThan(Object lowerBound) {
347+
if (lowerBound == null) {
348+
throw new InvalidDataAccessApiUsageException("LowerBound can't be null");
349+
}
350+
queryCriteria.add(new CriteriaEntry(OperationKey.GREATER, lowerBound));
319351
return this;
320352
}
321353

@@ -326,12 +358,7 @@ public Criteria greaterThanEqual(Object lowerBound) {
326358
* @return
327359
*/
328360
public Criteria in(Object... values) {
329-
if (values.length == 0 || (values.length > 1 && values[1] instanceof Collection)) {
330-
throw new InvalidDataAccessApiUsageException("At least one element "
331-
+ (values.length > 0 ? ("of argument of type " + values[1].getClass().getName()) : "")
332-
+ " has to be present.");
333-
}
334-
return in(Arrays.asList(values));
361+
return in(toCollection(values));
335362
}
336363

337364
/**
@@ -346,6 +373,25 @@ public Criteria in(Iterable<?> values) {
346373
return this;
347374
}
348375

376+
private List<Object> toCollection(Object... values) {
377+
if (values.length == 0 || (values.length > 1 && values[1] instanceof Collection)) {
378+
throw new InvalidDataAccessApiUsageException("At least one element "
379+
+ (values.length > 0 ? ("of argument of type " + values[1].getClass().getName()) : "")
380+
+ " has to be present.");
381+
}
382+
return Arrays.asList(values);
383+
}
384+
385+
public Criteria notIn(Object... values) {
386+
return notIn(toCollection(values));
387+
}
388+
389+
public Criteria notIn(Iterable<?> values) {
390+
Assert.notNull(values, "Collection of 'NotIn' values must not be null");
391+
queryCriteria.add(new CriteriaEntry(OperationKey.NOT_IN, values));
392+
return this;
393+
}
394+
349395
/**
350396
* Creates new CriteriaEntry for {@code location WITHIN distance}
351397
*
@@ -522,7 +568,7 @@ public String getConjunctionOperator() {
522568
}
523569

524570
public enum OperationKey {
525-
EQUALS, CONTAINS, STARTS_WITH, ENDS_WITH, EXPRESSION, BETWEEN, FUZZY, IN, WITHIN, BBOX, NEAR;
571+
EQUALS, CONTAINS, STARTS_WITH, ENDS_WITH, EXPRESSION, BETWEEN, FUZZY, IN, NOT_IN, WITHIN, BBOX, NEAR, LESS, LESS_EQUAL, GREATER, GREATER_EQUAL;
526572
}
527573

528574
public static class CriteriaEntry {
@@ -542,5 +588,13 @@ public OperationKey getKey() {
542588
public Object getValue() {
543589
return value;
544590
}
591+
592+
@Override
593+
public String toString() {
594+
return "CriteriaEntry{" +
595+
"key=" + key +
596+
", value=" + value +
597+
'}';
598+
}
545599
}
546600
}

src/main/java/org/springframework/data/elasticsearch/repository/query/parser/ElasticsearchQueryCreator.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
* @author Rizwan Idrees
4242
* @author Mohsin Husen
4343
* @author Franck Marchand
44+
* @author Artur Konczak
4445
*/
4546
public class ElasticsearchQueryCreator extends AbstractQueryCreator<CriteriaQuery, CriteriaQuery> {
4647

@@ -112,20 +113,22 @@ private Criteria from(Part part, Criteria instance, Iterator<?> parameters) {
112113
return criteria.endsWith(parameters.next().toString());
113114
case CONTAINING:
114115
return criteria.contains(parameters.next().toString());
115-
case AFTER:
116116
case GREATER_THAN:
117+
return criteria.greaterThan(parameters.next());
118+
case AFTER:
117119
case GREATER_THAN_EQUAL:
118120
return criteria.greaterThanEqual(parameters.next());
119-
case BEFORE:
120121
case LESS_THAN:
122+
return criteria.lessThan(parameters.next());
123+
case BEFORE:
121124
case LESS_THAN_EQUAL:
122125
return criteria.lessThanEqual(parameters.next());
123126
case BETWEEN:
124127
return criteria.between(parameters.next(), parameters.next());
125128
case IN:
126129
return criteria.in(asArray(parameters.next()));
127130
case NOT_IN:
128-
return criteria.in(asArray(parameters.next())).not();
131+
return criteria.notIn(asArray(parameters.next()));
129132
case SIMPLE_PROPERTY:
130133
case WITHIN: {
131134
Object firstParameter = parameters.next();

0 commit comments

Comments
 (0)