DATAES-171 - added support for missing query keywords

This commit is contained in:
Artur Konczak
2015-09-17 09:11:57 +01:00
parent d9212630b9
commit 49708d38c0
10 changed files with 354 additions and 141 deletions

View File

@@ -24,10 +24,11 @@ import java.util.List;
import java.util.ListIterator;
import org.apache.lucene.queryparser.flexible.core.util.StringUtils;
import org.elasticsearch.index.query.*;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.BoostableQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryStringQueryBuilder;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.geo.Point;
import org.springframework.util.Assert;
/**
@@ -36,6 +37,7 @@ import org.springframework.util.Assert;
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Franck Marchand
* @author Artur Konczak
*/
class CriteriaQueryProcessor {
@@ -49,11 +51,19 @@ class CriteriaQueryProcessor {
List<QueryBuilder> mustQueryBuilderList = new LinkedList<QueryBuilder>();
ListIterator<Criteria> chainIterator = criteria.getCriteriaChain().listIterator();
QueryBuilder firstQuery = null;
boolean negateFirstQuery = false;
while (chainIterator.hasNext()) {
Criteria chainedCriteria = chainIterator.next();
QueryBuilder queryFragmentForCriteria = createQueryFragmentForCriteria(chainedCriteria);
if (queryFragmentForCriteria != null) {
if (firstQuery == null) {
firstQuery = queryFragmentForCriteria;
negateFirstQuery = chainedCriteria.isNegating();
continue;
}
if (chainedCriteria.isOr()) {
shouldQueryBuilderList.add(queryFragmentForCriteria);
} else if (chainedCriteria.isNegating()) {
@@ -64,6 +74,18 @@ class CriteriaQueryProcessor {
}
}
if (firstQuery != null) {
if (!shouldQueryBuilderList.isEmpty() && mustNotQueryBuilderList.isEmpty() && mustQueryBuilderList.isEmpty()) {
shouldQueryBuilderList.add(0, firstQuery);
} else {
if (negateFirstQuery) {
mustNotQueryBuilderList.add(0, firstQuery);
} else {
mustQueryBuilderList.add(0, firstQuery);
}
}
}
BoolQueryBuilder query = null;
if (!shouldQueryBuilderList.isEmpty() || !mustNotQueryBuilderList.isEmpty() || !mustQueryBuilderList.isEmpty()) {
@@ -98,12 +120,12 @@ class CriteriaQueryProcessor {
if (singeEntryCriteria) {
Criteria.CriteriaEntry entry = it.next();
query = processCriteriaEntry(entry.getKey(), entry.getValue(), fieldName);
query = processCriteriaEntry(entry, fieldName);
} else {
query = boolQuery();
while (it.hasNext()) {
Criteria.CriteriaEntry entry = it.next();
((BoolQueryBuilder) query).must(processCriteriaEntry(entry.getKey(), entry.getValue(), fieldName));
((BoolQueryBuilder) query).must(processCriteriaEntry(entry, fieldName));
}
}
@@ -112,14 +134,18 @@ class CriteriaQueryProcessor {
}
private QueryBuilder processCriteriaEntry(OperationKey key, Object value, String fieldName) {
private QueryBuilder processCriteriaEntry(Criteria.CriteriaEntry entry,/* OperationKey key, Object value,*/ String fieldName) {
Object value = entry.getValue();
if (value == null) {
return null;
}
OperationKey key = entry.getKey();
QueryBuilder query = null;
String searchText = StringUtils.toString(value);
Iterable<Object> collection = null;
switch (key) {
case EQUALS:
query = queryString(searchText).field(fieldName).defaultOperator(QueryStringQueryBuilder.Operator.AND);
@@ -134,24 +160,42 @@ class CriteriaQueryProcessor {
query = queryString("*" + searchText).field(fieldName).analyzeWildcard(true);
break;
case EXPRESSION:
query = queryString((String) value).field(fieldName);
query = queryString(searchText).field(fieldName);
break;
case LESS_EQUAL:
query = rangeQuery(fieldName).lte(value);
break;
case GREATER_EQUAL:
query = rangeQuery(fieldName).gte(value);
break;
case BETWEEN:
Object[] ranges = (Object[]) value;
query = rangeQuery(fieldName).from(ranges[0]).to(ranges[1]);
break;
case LESS:
query = rangeQuery(fieldName).lt(value);
break;
case GREATER:
query = rangeQuery(fieldName).gt(value);
break;
case FUZZY:
query = fuzzyQuery(fieldName, (String) value);
query = fuzzyQuery(fieldName, searchText);
break;
case IN:
query = boolQuery();
Iterable<Object> collection = (Iterable<Object>) value;
collection = (Iterable<Object>) value;
for (Object item : collection) {
((BoolQueryBuilder) query).should(queryString((String) item).field(fieldName));
((BoolQueryBuilder) query).should(queryString(item.toString()).field(fieldName));
}
break;
case NOT_IN:
query = boolQuery();
collection = (Iterable<Object>) value;
for (Object item : collection) {
((BoolQueryBuilder) query).mustNot(queryString(item.toString()).field(fieldName));
}
break;
}
return query;
}

View File

@@ -15,6 +15,21 @@
*/
package org.springframework.data.elasticsearch.core;
import static org.apache.commons.collections.CollectionUtils.isNotEmpty;
import static org.apache.commons.lang.StringUtils.*;
import static org.elasticsearch.action.search.SearchType.*;
import static org.elasticsearch.client.Requests.*;
import static org.elasticsearch.cluster.metadata.AliasAction.Type.*;
import static org.elasticsearch.common.collect.Sets.*;
import static org.elasticsearch.index.VersionType.*;
import static org.springframework.data.elasticsearch.core.MappingBuilder.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.util.*;
import org.apache.commons.collections.CollectionUtils;
import org.elasticsearch.action.ListenableActionFuture;
import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest;
@@ -81,23 +96,6 @@ import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.util.CloseableIterator;
import org.springframework.util.Assert;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.util.*;
import static org.apache.commons.collections.CollectionUtils.isNotEmpty;
import static org.apache.commons.lang.StringUtils.isBlank;
import static org.apache.commons.lang.StringUtils.isNotBlank;
import static org.elasticsearch.action.search.SearchType.SCAN;
import static org.elasticsearch.client.Requests.indicesExistsRequest;
import static org.elasticsearch.client.Requests.refreshRequest;
import static org.elasticsearch.cluster.metadata.AliasAction.Type.ADD;
import static org.elasticsearch.common.collect.Sets.newHashSet;
import static org.elasticsearch.index.VersionType.EXTERNAL;
import static org.springframework.data.elasticsearch.core.MappingBuilder.buildMapping;
/**
* ElasticsearchTemplate
*
@@ -314,6 +312,9 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
if (elasticsearchFilter != null)
searchRequestBuilder.setPostFilter(elasticsearchFilter);
if (logger.isDebugEnabled()) {
logger.debug("doSearch query:\n" + searchRequestBuilder.toString());
}
SearchResponse response = getSearchResponse(searchRequestBuilder
.execute());
@@ -401,7 +402,6 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
}
throw new NoSuchElementException();
}
};
}
@@ -536,7 +536,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati
Assert.notNull(query.getUpdateRequest(), "No IndexRequest define for Query");
UpdateRequestBuilder updateRequestBuilder = client.prepareUpdate(indexName, type, query.getId());
if(query.getUpdateRequest().script() == null) {
if (query.getUpdateRequest().script() == null) {
// doc
if (query.DoUpsert()) {
updateRequestBuilder.setDocAsUpsert(true)

View File

@@ -35,6 +35,17 @@ import org.springframework.util.Assert;
*/
public class Criteria {
@Override
public String toString() {
return "Criteria{" +
"field=" + field.getName() +
", boost=" + boost +
", negating=" + negating +
", queryCriteria=" + StringUtils.join(queryCriteria, '|') +
", filterCriteria=" + StringUtils.join(filterCriteria, '|') +
'}';
}
public static final String WILDCARD = "*";
public static final String CRITERIA_VALUE_SEPERATOR = " ";
@@ -71,7 +82,6 @@ public class Criteria {
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;
}
@@ -304,7 +314,18 @@ public class Criteria {
* @return
*/
public Criteria lessThanEqual(Object upperBound) {
between(null, upperBound);
if (upperBound == null) {
throw new InvalidDataAccessApiUsageException("UpperBound can't be null");
}
queryCriteria.add(new CriteriaEntry(OperationKey.LESS_EQUAL, upperBound));
return this;
}
public Criteria lessThan(Object upperBound) {
if (upperBound == null) {
throw new InvalidDataAccessApiUsageException("UpperBound can't be null");
}
queryCriteria.add(new CriteriaEntry(OperationKey.LESS, upperBound));
return this;
}
@@ -315,7 +336,18 @@ public class Criteria {
* @return
*/
public Criteria greaterThanEqual(Object lowerBound) {
between(lowerBound, null);
if (lowerBound == null) {
throw new InvalidDataAccessApiUsageException("LowerBound can't be null");
}
queryCriteria.add(new CriteriaEntry(OperationKey.GREATER_EQUAL, lowerBound));
return this;
}
public Criteria greaterThan(Object lowerBound) {
if (lowerBound == null) {
throw new InvalidDataAccessApiUsageException("LowerBound can't be null");
}
queryCriteria.add(new CriteriaEntry(OperationKey.GREATER, lowerBound));
return this;
}
@@ -326,12 +358,7 @@ public class Criteria {
* @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));
return in(toCollection(values));
}
/**
@@ -346,6 +373,25 @@ public class Criteria {
return this;
}
private List<Object> toCollection(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 Arrays.asList(values);
}
public Criteria notIn(Object... values) {
return notIn(toCollection(values));
}
public Criteria notIn(Iterable<?> values) {
Assert.notNull(values, "Collection of 'NotIn' values must not be null");
queryCriteria.add(new CriteriaEntry(OperationKey.NOT_IN, values));
return this;
}
/**
* Creates new CriteriaEntry for {@code location WITHIN distance}
*
@@ -522,7 +568,7 @@ public class Criteria {
}
public enum OperationKey {
EQUALS, CONTAINS, STARTS_WITH, ENDS_WITH, EXPRESSION, BETWEEN, FUZZY, IN, WITHIN, BBOX, NEAR;
EQUALS, CONTAINS, STARTS_WITH, ENDS_WITH, EXPRESSION, BETWEEN, FUZZY, IN, NOT_IN, WITHIN, BBOX, NEAR, LESS, LESS_EQUAL, GREATER, GREATER_EQUAL;
}
public static class CriteriaEntry {
@@ -542,5 +588,13 @@ public class Criteria {
public Object getValue() {
return value;
}
@Override
public String toString() {
return "CriteriaEntry{" +
"key=" + key +
", value=" + value +
'}';
}
}
}

View File

@@ -41,6 +41,7 @@ import org.springframework.data.repository.query.parser.PartTree;
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Franck Marchand
* @author Artur Konczak
*/
public class ElasticsearchQueryCreator extends AbstractQueryCreator<CriteriaQuery, CriteriaQuery> {
@@ -112,12 +113,14 @@ public class ElasticsearchQueryCreator extends AbstractQueryCreator<CriteriaQuer
return criteria.endsWith(parameters.next().toString());
case CONTAINING:
return criteria.contains(parameters.next().toString());
case AFTER:
case GREATER_THAN:
return criteria.greaterThan(parameters.next());
case AFTER:
case GREATER_THAN_EQUAL:
return criteria.greaterThanEqual(parameters.next());
case BEFORE:
case LESS_THAN:
return criteria.lessThan(parameters.next());
case BEFORE:
case LESS_THAN_EQUAL:
return criteria.lessThanEqual(parameters.next());
case BETWEEN:
@@ -125,7 +128,7 @@ public class ElasticsearchQueryCreator extends AbstractQueryCreator<CriteriaQuer
case IN:
return criteria.in(asArray(parameters.next()));
case NOT_IN:
return criteria.in(asArray(parameters.next())).not();
return criteria.notIn(asArray(parameters.next()));
case SIMPLE_PROPERTY:
case WITHIN: {
Object firstParameter = parameters.next();