mirror of
https://github.com/spring-projects/spring-data-elasticsearch.git
synced 2025-06-01 09:42:11 +00:00
DATAES-706 - CriteriaQueryProcessor must handle nested Criteria definitions.
Original PR: #505
This commit is contained in:
parent
c8c6e7a646
commit
131f0318cc
@ -1,7 +1,8 @@
|
||||
[[preface]]
|
||||
= Preface
|
||||
|
||||
The Spring Data Elasticsearch project applies core Spring concepts to the development of solutions using the Elasticsearch Search Engine. It provides:
|
||||
The Spring Data Elasticsearch project applies core Spring concepts to the development of solutions using the Elasticsearch Search Engine.
|
||||
It provides:
|
||||
|
||||
* _Templates_ as a high-level abstraction for storing, searching, sorting documents and building aggregations.
|
||||
* _Repositories_ which for example enable the user to express queries by defining interfaces having customized method names (for basic information about repositories see <<repositories>>).
|
||||
@ -29,12 +30,13 @@ Requires an installation of https://www.elastic.co/products/elasticsearch[Elasti
|
||||
=== Versions
|
||||
|
||||
The following table shows the Elasticsearch versions that are used by Spring Data release trains and version of Spring Data Elasticsearch included in that, as well as the Spring Boot versions referring to that particular Spring Data release train:
|
||||
|
||||
[cols="^,^,^,^",options="header"]
|
||||
|===
|
||||
| Spring Data Release Train |Spring Data Elasticsearch |Elasticsearch | Spring Boot
|
||||
| 2020.0.0footnote:cdv[Currently in development] |4.1.xfootnote:cdv[]|7.8.1 |2.3.xfootnote:cdv[]
|
||||
| Neumann | 4.0.x | 7.6.2 |2.3.x
|
||||
| Moore | 3.2.x |6.8.12 | 2.2.x
|
||||
| Moore | 3.2.x |6.8.10 | 2.2.x
|
||||
| Lovelace | 3.1.x | 6.2.2 |2.1.x
|
||||
| Kayfootnote:oom[Out of maintenance] | 3.0.xfootnote:oom[] | 5.5.0 | 2.0.xfootnote:oom[]
|
||||
| Ingallsfootnote:oom[] | 2.1.xfootnote:oom[] | 2.4.0 | 1.5.xfootnote:oom[]
|
||||
|
@ -45,7 +45,8 @@ public class TransportClientConfig extends ElasticsearchConfigurationSupport {
|
||||
}
|
||||
}
|
||||
----
|
||||
<1> Setting up the <<elasticsearch.clients.transport>>. Deprecated as of version 4.0.
|
||||
<1> Setting up the <<elasticsearch.clients.transport>>.
|
||||
Deprecated as of version 4.0.
|
||||
<2> Creating the `ElasticsearchTemplate` bean, offering both names, _elasticsearchOperations_ and _elasticsearchTemplate_.
|
||||
====
|
||||
|
||||
@ -75,7 +76,9 @@ public class RestClientConfig extends AbstractElasticsearchConfiguration {
|
||||
[[elasticsearch.operations.usage]]
|
||||
== Usage examples
|
||||
|
||||
As both `ElasticsearchTemplate` and `ElasticsearchRestTemplate` implement the `ElasticsearchOperations` interface, the code to use them is not different. The example shows how to use an injected `ElasticsearchOperations` instance in a Spring REST controller. The decision, if this is using the `TransportClient` or the `RestClient` is made by providing the corresponding Bean with one of the configurations shown above.
|
||||
As both `ElasticsearchTemplate` and `ElasticsearchRestTemplate` implement the `ElasticsearchOperations` interface, the code to use them is not different.
|
||||
The example shows how to use an injected `ElasticsearchOperations` instance in a Spring REST controller.
|
||||
The decision, if this is using the `TransportClient` or the `RestClient` is made by providing the corresponding Bean with one of the configurations shown above.
|
||||
|
||||
.ElasticsearchOperations usage
|
||||
====
|
||||
@ -123,9 +126,12 @@ include::reactive-elasticsearch-operations.adoc[leveloffset=+1]
|
||||
[[elasticsearch.operations.searchresulttypes]]
|
||||
== Search Result Types
|
||||
|
||||
When a document is retrieved with the methods of the `DocumentOperations` interface, just the found entity will be returned. When searching with the methods of the `SearchOperations` interface, additional information is available for each entity, for example the _score_ or the _sortValues_ of the found entity.
|
||||
When a document is retrieved with the methods of the `DocumentOperations` interface, just the found entity will be returned.
|
||||
When searching with the methods of the `SearchOperations` interface, additional information is available for each entity, for example the _score_ or the _sortValues_ of the found entity.
|
||||
|
||||
In order to return this information, each entity is wrapped in a `SearchHit` object that contains this entity-specific additional information. These `SearchHit` objects themselves are returned within a `SearchHits` object which additionally contains informations about the whole search like the _maxScore_ or requested aggregations. The following classes and interfaces are now available:
|
||||
In order to return this information, each entity is wrapped in a `SearchHit` object that contains this entity-specific additional information.
|
||||
These `SearchHit` objects themselves are returned within a `SearchHits` object which additionally contains informations about the whole search like the _maxScore_ or requested aggregations.
|
||||
The following classes and interfaces are now available:
|
||||
|
||||
.SearchHit<T>
|
||||
Contains the following information:
|
||||
@ -155,3 +161,108 @@ Returned by the low level scroll API functions in `ElasticsearchRestTemplate`, i
|
||||
.SearchHitsIterator<T>
|
||||
An Iterator returned by the streaming functions of the `SearchOperations` interface.
|
||||
|
||||
== Queries
|
||||
|
||||
Almost all of the methods defined in the `SearchOperations` and `ReactiveSearchOperations` interface take a `Query` parameter that defines the query to execute for searching. `Query` is an interface and Spring Data Elasticsearch provides three implementations: `CriteriaQuery`, `StringQuery` and `NativeSearchQuery`.
|
||||
|
||||
=== CriteriaQuery
|
||||
|
||||
`CriteriaQuery` based queries allow the creation of queries to search for data without knowing the syntax or basics of Elasticsearch queries. They allow the user to build queries by simply chaining and combining `Criteria` objects that specifiy the criteria the searched documents must fulfill.
|
||||
|
||||
NOTE: when talking about AND or OR when combining criteria keep in mind, that in Elasticsearch AND are converted to a **must** condition and OR to a **should**
|
||||
|
||||
`Criteria` and their usage are best explained by example
|
||||
(let's assume we have a `Book` entity with a `price` property):
|
||||
|
||||
.Get books with a given price
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
Criteria criteria = new Criteria("price").is(42.0);
|
||||
Query query = new CriteriaQuery(criteria);
|
||||
----
|
||||
====
|
||||
|
||||
Conditions for the same field can be chained, they will be combined with a logical AND:
|
||||
|
||||
.Get books with a given price
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
Criteria criteria = new Criteria("price").greaterThan(42.0).lessThan(34.0L);
|
||||
Query query = new CriteriaQuery(criteria);
|
||||
----
|
||||
====
|
||||
|
||||
When chaining `Criteria`, by default a AND logic is used:
|
||||
|
||||
.Get all persons with first name _James_ and last name _Miller_:
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
Criteria criteria = new Criteria("lastname").is("Miller") <1>
|
||||
.and("firstname").is("James") <2>
|
||||
Query query = new CriteriaQuery(criteria);
|
||||
----
|
||||
<1> the first `Criteria`
|
||||
<2> the and() creates a new `Criteria` and chaines it to the first one.
|
||||
====
|
||||
|
||||
If you want to create nested queries, you need to use subqueries for this. Let's assume we want to find all persons with a last name of _Miller_ and a first name of either _Jack_ or _John_:
|
||||
|
||||
.Nested subqueries
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
Criteria miller = new Criteria("lastName").is("Miller") <.>
|
||||
.subCriteria( <.>
|
||||
new Criteria().or("firstName").is("John") <.>
|
||||
.or("firstName").is("Jack") <.>
|
||||
);
|
||||
Query query = new CriteriaQuery(criteria);
|
||||
----
|
||||
<.> create a first `Criteria` for the last name
|
||||
<.> this is combined with AND to a subCriteria
|
||||
<.> This sub Criteria is an OR combination for the first name _John_
|
||||
<.> and the first name Jack
|
||||
====
|
||||
|
||||
Please refer to the API documentation of the `Criteria` class for a complete overview of the different available operations.
|
||||
|
||||
=== StringQuery
|
||||
|
||||
This class takes an Elasticsearch query as JSON String.
|
||||
The following code shows a query that searches for persons having the first name "Jack":
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
|
||||
Query query = new SearchQuery("{ \"match\": { \"firstname\": { \"query\": \"Jack\" } } } ");
|
||||
SearchHits<Person> searchHits = operations.search(query, Person.class);
|
||||
|
||||
----
|
||||
====
|
||||
|
||||
Using `StringQuery` may be appropriate if you already have an Elasticsearch query to use.
|
||||
|
||||
=== NativeSearchQuery
|
||||
|
||||
`NativeSearchQuery` is the class to use when you have a complex query, or a query that cannot be expressed by using the `Criteria` API, for example when building queries and using aggregates.
|
||||
It allows to use all the different `QueryBuilder` implementations from the Elasticsearch library therefore named "native".
|
||||
|
||||
The following code shows how to search for persons with a given firstname and for the found documents have a terms aggregation that counts the number of occurences of the lastnames for these persons:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
Query query = new NativeSearchQueryBuilder()
|
||||
.addAggregation(terms("lastnames").field("lastname").size(10)) //
|
||||
.withQuery(QueryBuilders.matchQuery("firstname", firstName))
|
||||
.build();
|
||||
|
||||
SearchHits<Person> searchHits = operations.search(query, Person.class);
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
@ -17,9 +17,11 @@ package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import static org.springframework.data.elasticsearch.core.query.Criteria.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.elasticsearch.common.geo.GeoDistance;
|
||||
import org.elasticsearch.index.query.BoolQueryBuilder;
|
||||
@ -47,66 +49,56 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
class CriteriaFilterProcessor {
|
||||
|
||||
QueryBuilder createFilterFromCriteria(Criteria criteria) {
|
||||
List<QueryBuilder> fbList = new LinkedList<>();
|
||||
QueryBuilder filter = null;
|
||||
@Nullable
|
||||
QueryBuilder createFilter(Criteria criteria) {
|
||||
|
||||
List<QueryBuilder> filterBuilders = new ArrayList<>();
|
||||
|
||||
for (Criteria chainedCriteria : criteria.getCriteriaChain()) {
|
||||
QueryBuilder fb = null;
|
||||
|
||||
if (chainedCriteria.isOr()) {
|
||||
fb = QueryBuilders.boolQuery();
|
||||
for (QueryBuilder f : createFilterFragmentForCriteria(chainedCriteria)) {
|
||||
((BoolQueryBuilder) fb).should(f);
|
||||
}
|
||||
fbList.add(fb);
|
||||
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
|
||||
queriesForEntries(chainedCriteria).forEach(boolQuery::should);
|
||||
filterBuilders.add(boolQuery);
|
||||
} else if (chainedCriteria.isNegating()) {
|
||||
List<QueryBuilder> negationFilters = buildNegationFilter(criteria.getField().getName(),
|
||||
criteria.getFilterCriteriaEntries().iterator());
|
||||
|
||||
if (!negationFilters.isEmpty()) {
|
||||
fbList.addAll(negationFilters);
|
||||
}
|
||||
filterBuilders.addAll(negationFilters);
|
||||
} else {
|
||||
fbList.addAll(createFilterFragmentForCriteria(chainedCriteria));
|
||||
filterBuilders.addAll(queriesForEntries(chainedCriteria));
|
||||
}
|
||||
}
|
||||
|
||||
if (!fbList.isEmpty()) {
|
||||
if (fbList.size() == 1) {
|
||||
filter = fbList.get(0);
|
||||
QueryBuilder filter = null;
|
||||
|
||||
if (!filterBuilders.isEmpty()) {
|
||||
|
||||
if (filterBuilders.size() == 1) {
|
||||
filter = filterBuilders.get(0);
|
||||
} else {
|
||||
filter = QueryBuilders.boolQuery();
|
||||
for (QueryBuilder f : fbList) {
|
||||
((BoolQueryBuilder) filter).must(f);
|
||||
}
|
||||
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
|
||||
filterBuilders.forEach(boolQuery::must);
|
||||
filter = boolQuery;
|
||||
}
|
||||
}
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
private List<QueryBuilder> createFilterFragmentForCriteria(Criteria chainedCriteria) {
|
||||
Iterator<Criteria.CriteriaEntry> it = chainedCriteria.getFilterCriteriaEntries().iterator();
|
||||
List<QueryBuilder> filterList = new LinkedList<>();
|
||||
private List<QueryBuilder> queriesForEntries(Criteria criteria) {
|
||||
|
||||
String fieldName = chainedCriteria.getField().getName();
|
||||
Assert.notNull(criteria.getField(), "criteria must have a field");
|
||||
String fieldName = criteria.getField().getName();
|
||||
Assert.notNull(fieldName, "Unknown field");
|
||||
QueryBuilder filter = null;
|
||||
|
||||
while (it.hasNext()) {
|
||||
Criteria.CriteriaEntry entry = it.next();
|
||||
filter = processCriteriaEntry(entry.getKey(), entry.getValue(), fieldName);
|
||||
filterList.add(filter);
|
||||
}
|
||||
|
||||
return filterList;
|
||||
return criteria.getFilterCriteriaEntries().stream()
|
||||
.map(entry -> queryFor(entry.getKey(), entry.getValue(), fieldName)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private QueryBuilder processCriteriaEntry(OperationKey key, Object value, String fieldName) {
|
||||
private QueryBuilder queryFor(OperationKey key, Object value, String fieldName) {
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
QueryBuilder filter = null;
|
||||
|
||||
switch (key) {
|
||||
@ -169,8 +161,7 @@ class CriteriaFilterProcessor {
|
||||
// 2x text
|
||||
twoParameterBBox((GeoBoundingBoxQueryBuilder) filter, valArray);
|
||||
} else {
|
||||
// error
|
||||
Assert.isTrue(false,
|
||||
throw new IllegalArgumentException(
|
||||
"Geo distance filter takes a 1-elements array(GeoBox) or 2-elements array(GeoPoints or Strings(format lat,lon or geohash)).");
|
||||
}
|
||||
break;
|
||||
@ -208,8 +199,7 @@ class CriteriaFilterProcessor {
|
||||
|
||||
GeoBox geoBBox;
|
||||
if (value instanceof Box) {
|
||||
Box sdbox = (Box) value;
|
||||
geoBBox = GeoBox.fromBox(sdbox);
|
||||
geoBBox = GeoBox.fromBox((Box) value);
|
||||
} else {
|
||||
geoBBox = (GeoBox) value;
|
||||
}
|
||||
@ -218,7 +208,7 @@ class CriteriaFilterProcessor {
|
||||
geoBBox.getBottomRight().getLon());
|
||||
}
|
||||
|
||||
private static boolean isType(Object[] array, Class clazz) {
|
||||
private static boolean isType(Object[] array, Class<?> clazz) {
|
||||
for (Object o : array) {
|
||||
if (!clazz.isInstance(o)) {
|
||||
return false;
|
||||
@ -247,7 +237,7 @@ class CriteriaFilterProcessor {
|
||||
while (it.hasNext()) {
|
||||
Criteria.CriteriaEntry criteriaEntry = it.next();
|
||||
QueryBuilder notFilter = QueryBuilders.boolQuery()
|
||||
.mustNot(processCriteriaEntry(criteriaEntry.getKey(), criteriaEntry.getValue(), fieldName));
|
||||
.mustNot(queryFor(criteriaEntry.getKey(), criteriaEntry.getValue(), fieldName));
|
||||
notFilterList.add(notFilter);
|
||||
}
|
||||
|
||||
|
@ -21,11 +21,8 @@ import static org.springframework.data.elasticsearch.core.query.Criteria.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
import org.apache.lucene.queryparser.flexible.core.util.StringUtils;
|
||||
import org.apache.lucene.queryparser.flexible.standard.QueryParserUtil;
|
||||
import org.elasticsearch.index.query.BoolQueryBuilder;
|
||||
import org.elasticsearch.index.query.QueryBuilder;
|
||||
@ -46,63 +43,81 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
class CriteriaQueryProcessor {
|
||||
|
||||
QueryBuilder createQueryFromCriteria(Criteria criteria) {
|
||||
@Nullable
|
||||
QueryBuilder createQuery(Criteria criteria) {
|
||||
|
||||
Assert.notNull(criteria, "criteria must not be null");
|
||||
|
||||
List<QueryBuilder> shouldQueryBuilderList = new LinkedList<>();
|
||||
List<QueryBuilder> mustNotQueryBuilderList = new LinkedList<>();
|
||||
List<QueryBuilder> mustQueryBuilderList = new LinkedList<>();
|
||||
|
||||
ListIterator<Criteria> chainIterator = criteria.getCriteriaChain().listIterator();
|
||||
List<QueryBuilder> shouldQueryBuilders = new ArrayList<>();
|
||||
List<QueryBuilder> mustNotQueryBuilders = new ArrayList<>();
|
||||
List<QueryBuilder> mustQueryBuilders = new ArrayList<>();
|
||||
|
||||
QueryBuilder firstQuery = null;
|
||||
boolean negateFirstQuery = false;
|
||||
|
||||
while (chainIterator.hasNext()) {
|
||||
Criteria chainedCriteria = chainIterator.next();
|
||||
QueryBuilder queryFragmentForCriteria = createQueryFragmentForCriteria(chainedCriteria);
|
||||
if (queryFragmentForCriteria != null) {
|
||||
for (Criteria chainedCriteria : criteria.getCriteriaChain()) {
|
||||
QueryBuilder queryFragment = queryForEntries(chainedCriteria);
|
||||
|
||||
if (queryFragment != null) {
|
||||
|
||||
if (firstQuery == null) {
|
||||
firstQuery = queryFragmentForCriteria;
|
||||
firstQuery = queryFragment;
|
||||
negateFirstQuery = chainedCriteria.isNegating();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (chainedCriteria.isOr()) {
|
||||
shouldQueryBuilderList.add(queryFragmentForCriteria);
|
||||
shouldQueryBuilders.add(queryFragment);
|
||||
} else if (chainedCriteria.isNegating()) {
|
||||
mustNotQueryBuilderList.add(queryFragmentForCriteria);
|
||||
mustNotQueryBuilders.add(queryFragment);
|
||||
} else {
|
||||
mustQueryBuilderList.add(queryFragmentForCriteria);
|
||||
mustQueryBuilders.add(queryFragment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Criteria subCriteria : criteria.getSubCriteria()) {
|
||||
|
||||
QueryBuilder subQuery = createQuery(subCriteria);
|
||||
|
||||
if (subQuery != null) {
|
||||
if (criteria.isOr()) {
|
||||
shouldQueryBuilders.add(subQuery);
|
||||
} else if (criteria.isNegating()) {
|
||||
mustNotQueryBuilders.add(subQuery);
|
||||
} else {
|
||||
mustQueryBuilders.add(subQuery);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (firstQuery != null) {
|
||||
if (!shouldQueryBuilderList.isEmpty() && mustNotQueryBuilderList.isEmpty() && mustQueryBuilderList.isEmpty()) {
|
||||
shouldQueryBuilderList.add(0, firstQuery);
|
||||
|
||||
if (!shouldQueryBuilders.isEmpty() && mustNotQueryBuilders.isEmpty() && mustQueryBuilders.isEmpty()) {
|
||||
shouldQueryBuilders.add(0, firstQuery);
|
||||
} else {
|
||||
|
||||
if (negateFirstQuery) {
|
||||
mustNotQueryBuilderList.add(0, firstQuery);
|
||||
mustNotQueryBuilders.add(0, firstQuery);
|
||||
} else {
|
||||
mustQueryBuilderList.add(0, firstQuery);
|
||||
mustQueryBuilders.add(0, firstQuery);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BoolQueryBuilder query = null;
|
||||
|
||||
if (!shouldQueryBuilderList.isEmpty() || !mustNotQueryBuilderList.isEmpty() || !mustQueryBuilderList.isEmpty()) {
|
||||
if (!shouldQueryBuilders.isEmpty() || !mustNotQueryBuilders.isEmpty() || !mustQueryBuilders.isEmpty()) {
|
||||
|
||||
query = boolQuery();
|
||||
|
||||
for (QueryBuilder qb : shouldQueryBuilderList) {
|
||||
for (QueryBuilder qb : shouldQueryBuilders) {
|
||||
query.should(qb);
|
||||
}
|
||||
for (QueryBuilder qb : mustNotQueryBuilderList) {
|
||||
for (QueryBuilder qb : mustNotQueryBuilders) {
|
||||
query.mustNot(qb);
|
||||
}
|
||||
for (QueryBuilder qb : mustQueryBuilderList) {
|
||||
for (QueryBuilder qb : mustQueryBuilders) {
|
||||
query.must(qb);
|
||||
}
|
||||
}
|
||||
@ -111,46 +126,41 @@ class CriteriaQueryProcessor {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private QueryBuilder createQueryFragmentForCriteria(Criteria chainedCriteria) {
|
||||
if (chainedCriteria.getQueryCriteriaEntries().isEmpty())
|
||||
private QueryBuilder queryForEntries(Criteria criteria) {
|
||||
|
||||
if (criteria.getField() == null || criteria.getQueryCriteriaEntries().isEmpty())
|
||||
return null;
|
||||
|
||||
Iterator<Criteria.CriteriaEntry> it = chainedCriteria.getQueryCriteriaEntries().iterator();
|
||||
boolean singeEntryCriteria = (chainedCriteria.getQueryCriteriaEntries().size() == 1);
|
||||
String fieldName = criteria.getField().getName();
|
||||
|
||||
String fieldName = chainedCriteria.getField().getName();
|
||||
Assert.notNull(fieldName, "Unknown field");
|
||||
QueryBuilder query = null;
|
||||
|
||||
if (singeEntryCriteria) {
|
||||
Criteria.CriteriaEntry entry = it.next();
|
||||
query = processCriteriaEntry(entry, fieldName);
|
||||
Iterator<Criteria.CriteriaEntry> it = criteria.getQueryCriteriaEntries().iterator();
|
||||
QueryBuilder query;
|
||||
|
||||
if (criteria.getQueryCriteriaEntries().size() == 1) {
|
||||
query = queryFor(it.next(), fieldName);
|
||||
} else {
|
||||
query = boolQuery();
|
||||
while (it.hasNext()) {
|
||||
Criteria.CriteriaEntry entry = it.next();
|
||||
((BoolQueryBuilder) query).must(processCriteriaEntry(entry, fieldName));
|
||||
((BoolQueryBuilder) query).must(queryFor(entry, fieldName));
|
||||
}
|
||||
}
|
||||
|
||||
addBoost(query, chainedCriteria.getBoost());
|
||||
addBoost(query, criteria.getBoost());
|
||||
return query;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private QueryBuilder processCriteriaEntry(Criteria.CriteriaEntry entry, String fieldName) {
|
||||
private QueryBuilder queryFor(Criteria.CriteriaEntry entry, String fieldName) {
|
||||
OperationKey key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
|
||||
if (value == null) {
|
||||
|
||||
if (key == OperationKey.EXISTS) {
|
||||
return existsQuery(fieldName);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Object value = entry.getValue();
|
||||
String searchText = QueryParserUtil.escape(value.toString());
|
||||
|
||||
QueryBuilder query = null;
|
||||
@ -190,11 +200,23 @@ class CriteriaQueryProcessor {
|
||||
case FUZZY:
|
||||
query = fuzzyQuery(fieldName, searchText);
|
||||
break;
|
||||
case MATCHES:
|
||||
query = matchQuery(fieldName, value).operator(org.elasticsearch.index.query.Operator.OR);
|
||||
break;
|
||||
case MATCHES_ALL:
|
||||
query = matchQuery(fieldName, value).operator(org.elasticsearch.index.query.Operator.AND);
|
||||
break;
|
||||
case IN:
|
||||
query = boolQuery().must(termsQuery(fieldName, toStringList((Iterable<Object>) value)));
|
||||
if (value instanceof Iterable) {
|
||||
Iterable<?> iterable = (Iterable<?>) value;
|
||||
query = boolQuery().must(termsQuery(fieldName, toStringList(iterable)));
|
||||
}
|
||||
break;
|
||||
case NOT_IN:
|
||||
query = boolQuery().mustNot(termsQuery(fieldName, toStringList((Iterable<Object>) value)));
|
||||
if (value instanceof Iterable) {
|
||||
Iterable<?> iterable = (Iterable<?>) value;
|
||||
query = boolQuery().mustNot(termsQuery(fieldName, toStringList(iterable)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
return query;
|
||||
@ -203,15 +225,17 @@ class CriteriaQueryProcessor {
|
||||
private static List<String> toStringList(Iterable<?> iterable) {
|
||||
List<String> list = new ArrayList<>();
|
||||
for (Object item : iterable) {
|
||||
list.add(StringUtils.toString(item));
|
||||
list.add(item != null ? item.toString() : null);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private void addBoost(QueryBuilder query, float boost) {
|
||||
if (Float.isNaN(boost)) {
|
||||
private void addBoost(@Nullable QueryBuilder query, float boost) {
|
||||
|
||||
if (query == null || Float.isNaN(boost)) {
|
||||
return;
|
||||
}
|
||||
|
||||
query.boost(boost);
|
||||
}
|
||||
}
|
||||
|
@ -1513,7 +1513,7 @@ class RequestFactory {
|
||||
elasticsearchQuery = searchQuery.getQuery();
|
||||
} else if (query instanceof CriteriaQuery) {
|
||||
CriteriaQuery criteriaQuery = (CriteriaQuery) query;
|
||||
elasticsearchQuery = new CriteriaQueryProcessor().createQueryFromCriteria(criteriaQuery.getCriteria());
|
||||
elasticsearchQuery = new CriteriaQueryProcessor().createQuery(criteriaQuery.getCriteria());
|
||||
} else if (query instanceof StringQuery) {
|
||||
StringQuery stringQuery = (StringQuery) query;
|
||||
elasticsearchQuery = wrapperQuery(stringQuery.getSource());
|
||||
@ -1533,7 +1533,7 @@ class RequestFactory {
|
||||
elasticsearchFilter = searchQuery.getFilter();
|
||||
} else if (query instanceof CriteriaQuery) {
|
||||
CriteriaQuery criteriaQuery = (CriteriaQuery) query;
|
||||
elasticsearchFilter = new CriteriaFilterProcessor().createFilterFromCriteria(criteriaQuery.getCriteria());
|
||||
elasticsearchFilter = new CriteriaFilterProcessor().createFilter(criteriaQuery.getCriteria());
|
||||
} else if (query instanceof StringQuery) {
|
||||
elasticsearchFilter = null;
|
||||
} else {
|
||||
|
@ -48,6 +48,7 @@ import org.springframework.data.elasticsearch.core.join.JoinField;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentPropertyConverter;
|
||||
import org.springframework.data.elasticsearch.core.query.Criteria;
|
||||
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.SeqNoPrimaryTerm;
|
||||
import org.springframework.data.mapping.PersistentPropertyAccessor;
|
||||
@ -761,7 +762,13 @@ public class MappingElasticsearchConverter
|
||||
ElasticsearchPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(domainClass);
|
||||
|
||||
if (persistentEntity != null) {
|
||||
criteriaQuery.getCriteria().getCriteriaChain().forEach(criteria -> {
|
||||
for (Criteria chainedCriteria : criteriaQuery.getCriteria().getCriteriaChain()) {
|
||||
updateCriteria(chainedCriteria, persistentEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCriteria(Criteria criteria, ElasticsearchPersistentEntity<?> persistentEntity) {
|
||||
String name = criteria.getField().getName();
|
||||
ElasticsearchPersistentProperty property = persistentEntity.getPersistentProperty(name);
|
||||
|
||||
@ -783,7 +790,11 @@ public class MappingElasticsearchConverter
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
for (Criteria subCriteria : criteria.getSubCriteria()) {
|
||||
for (Criteria chainedCriteria : subCriteria.getCriteriaChain()) {
|
||||
updateCriteria(chainedCriteria, persistentEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -18,7 +18,8 @@ package org.springframework.data.elasticsearch.core.query;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* The most trivial implementation of a Field
|
||||
* The most trivial implementation of a Field. The {@link #name} is updateable, so it may be changed during query
|
||||
* preparation by the {@link org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter}.
|
||||
*
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
@ -29,25 +30,27 @@ public class SimpleField implements Field {
|
||||
private String name;
|
||||
|
||||
public SimpleField(String name) {
|
||||
Assert.notNull(name, "name must not be null");
|
||||
|
||||
Assert.hasText(name, "name must not be null");
|
||||
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
Assert.notNull(name, "name must not be null");
|
||||
|
||||
Assert.hasText(name, "name must not be null");
|
||||
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.name;
|
||||
return getName();
|
||||
}
|
||||
}
|
||||
|
@ -96,13 +96,10 @@ public class ElasticsearchQueryCreator extends AbstractQueryCreator<CriteriaQuer
|
||||
return query.addSort(sort);
|
||||
}
|
||||
|
||||
private Criteria from(Part part, Criteria instance, Iterator<?> parameters) {
|
||||
private Criteria from(Part part, Criteria criteria, Iterator<?> parameters) {
|
||||
|
||||
Part.Type type = part.getType();
|
||||
|
||||
Criteria criteria = instance;
|
||||
if (criteria == null) {
|
||||
criteria = new Criteria();
|
||||
}
|
||||
switch (type) {
|
||||
case TRUE:
|
||||
return criteria.is(true);
|
||||
|
@ -56,12 +56,15 @@ public class CriteriaQueryMappingTests {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // DATAES-716
|
||||
void shouldMapNamesAndConvertValuesInCriteriaQuery() throws JSONException {
|
||||
|
||||
// use POJO properties and types in the query building
|
||||
CriteriaQuery criteriaQuery = new CriteriaQuery(new Criteria("birthDate")
|
||||
.between(LocalDate.of(1989, 11, 9), LocalDate.of(1990, 11, 9)).or("birthDate").is(LocalDate.of(2019, 12, 28)));
|
||||
CriteriaQuery criteriaQuery = new CriteriaQuery( //
|
||||
new Criteria("birthDate") //
|
||||
.between(LocalDate.of(1989, 11, 9), LocalDate.of(1990, 11, 9)) //
|
||||
.or("birthDate").is(LocalDate.of(2019, 12, 28)) //
|
||||
);
|
||||
|
||||
// mapped field name and converted parameter
|
||||
String expected = '{' + //
|
||||
@ -90,7 +93,60 @@ public class CriteriaQueryMappingTests {
|
||||
'}'; //
|
||||
|
||||
mappingElasticsearchConverter.updateQuery(criteriaQuery, Person.class);
|
||||
String queryString = new CriteriaQueryProcessor().createQueryFromCriteria(criteriaQuery.getCriteria()).toString();
|
||||
String queryString = new CriteriaQueryProcessor().createQuery(criteriaQuery.getCriteria()).toString();
|
||||
|
||||
assertEquals(expected, queryString, false);
|
||||
}
|
||||
|
||||
@Test // DATAES-706
|
||||
void shouldMapNamesAndValuesInSubCriteriaQuery() throws JSONException {
|
||||
|
||||
CriteriaQuery criteriaQuery = new CriteriaQuery( //
|
||||
new Criteria("firstName").matches("John") //
|
||||
.subCriteria(new Criteria("birthDate") //
|
||||
.between(LocalDate.of(1989, 11, 9), LocalDate.of(1990, 11, 9)) //
|
||||
.or("birthDate").is(LocalDate.of(2019, 12, 28))));
|
||||
|
||||
String expected = "{\n" + //
|
||||
" \"bool\": {\n" + //
|
||||
" \"must\": [\n" + //
|
||||
" {\n" + //
|
||||
" \"match\": {\n" + //
|
||||
" \"first-name\": {\n" + //
|
||||
" \"query\": \"John\"\n" + //
|
||||
" }\n" + //
|
||||
" }\n" + //
|
||||
" },\n" + //
|
||||
" {\n" + //
|
||||
" \"bool\": {\n" + //
|
||||
" \"should\": [\n" + //
|
||||
" {\n" + //
|
||||
" \"range\": {\n" + //
|
||||
" \"birth-date\": {\n" + //
|
||||
" \"from\": \"09.11.1989\",\n" + //
|
||||
" \"to\": \"09.11.1990\",\n" + //
|
||||
" \"include_lower\": true,\n" + //
|
||||
" \"include_upper\": true\n" + //
|
||||
" }\n" + //
|
||||
" }\n" + //
|
||||
" },\n" + //
|
||||
" {\n" + //
|
||||
" \"query_string\": {\n" + //
|
||||
" \"query\": \"28.12.2019\",\n" + //
|
||||
" \"fields\": [\n" + //
|
||||
" \"birth-date^1.0\"\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
" }\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
" }\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
"}\n"; //
|
||||
|
||||
mappingElasticsearchConverter.updateQuery(criteriaQuery, Person.class);
|
||||
String queryString = new CriteriaQueryProcessor().createQuery(criteriaQuery.getCriteria()).toString();
|
||||
|
||||
assertEquals(expected, queryString, false);
|
||||
}
|
||||
|
@ -0,0 +1,341 @@
|
||||
/*
|
||||
* Copyright 2020 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
|
||||
*
|
||||
* https://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 static org.skyscreamer.jsonassert.JSONAssert.*;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.elasticsearch.core.query.Criteria;
|
||||
|
||||
/**
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
class CriteriaQueryProcessorTests {
|
||||
|
||||
private final CriteriaQueryProcessor queryProcessor = new CriteriaQueryProcessor();
|
||||
|
||||
@Test // DATAES-706
|
||||
void shouldProcessTwoCriteriaWithAnd() throws JSONException {
|
||||
|
||||
String expected = "{\n" + //
|
||||
" \"bool\": {\n" + //
|
||||
" \"must\": [\n" + //
|
||||
" {\n" + //
|
||||
" \"query_string\": {\n" + //
|
||||
" \"query\": \"value1\",\n" + //
|
||||
" \"fields\": [\n" + //
|
||||
" \"field1^1.0\"\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
" },\n" + //
|
||||
" {\n" + //
|
||||
" \"query_string\": {\n" + //
|
||||
" \"query\": \"value2\",\n" + //
|
||||
" \"fields\": [\n" + //
|
||||
" \"field2^1.0\"\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
" }\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
"}"; //
|
||||
|
||||
Criteria criteria = new Criteria("field1").is("value1").and("field2").is("value2");
|
||||
|
||||
String query = queryProcessor.createQuery(criteria).toString();
|
||||
|
||||
assertEquals(expected, query, false);
|
||||
}
|
||||
|
||||
@Test // DATAES-706
|
||||
void shouldProcessTwoCriteriaWithOr() throws JSONException {
|
||||
|
||||
String expected = "{\n" + //
|
||||
" \"bool\": {\n" + //
|
||||
" \"should\": [\n" + //
|
||||
" {\n" + //
|
||||
" \"query_string\": {\n" + //
|
||||
" \"query\": \"value1\",\n" + //
|
||||
" \"fields\": [\n" + //
|
||||
" \"field1^1.0\"\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
" },\n" + //
|
||||
" {\n" + //
|
||||
" \"query_string\": {\n" + //
|
||||
" \"query\": \"value2\",\n" + //
|
||||
" \"fields\": [\n" + //
|
||||
" \"field2^1.0\"\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
" }\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
"}"; //
|
||||
|
||||
Criteria criteria = new Criteria("field1").is("value1").or("field2").is("value2");
|
||||
|
||||
String query = queryProcessor.createQuery(criteria).toString();
|
||||
|
||||
assertEquals(expected, query, false);
|
||||
}
|
||||
|
||||
@Test // DATAES-706
|
||||
void shouldProcessMixedCriteriaWithOrAnd() throws JSONException {
|
||||
|
||||
String expected = "{\n" + //
|
||||
" \"bool\": {\n" + //
|
||||
" \"must\": [\n" + //
|
||||
" {\n" + //
|
||||
" \"query_string\": {\n" + //
|
||||
" \"query\": \"value1\",\n" + //
|
||||
" \"fields\": [\n" + //
|
||||
" \"field1^1.0\"\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
" },\n" + //
|
||||
" {\n" + //
|
||||
" \"query_string\": {\n" + //
|
||||
" \"query\": \"value3\",\n" + //
|
||||
" \"fields\": [\n" + //
|
||||
" \"field3^1.0\"\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
" }\n" + //
|
||||
" ],\n" + //
|
||||
" \"should\": [\n" + //
|
||||
" {\n" + //
|
||||
" \"query_string\": {\n" + //
|
||||
" \"query\": \"value2\",\n" + //
|
||||
" \"fields\": [\n" + //
|
||||
" \"field2^1.0\"\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
" },\n" + //
|
||||
" {\n" + //
|
||||
" \"query_string\": {\n" + //
|
||||
" \"query\": \"value4\",\n" + //
|
||||
" \"fields\": [\n" + //
|
||||
" \"field4^1.0\"\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
" }\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
"}\n"; //
|
||||
|
||||
Criteria criteria = new Criteria("field1").is("value1") //
|
||||
.or("field2").is("value2") //
|
||||
.and("field3").is("value3") //
|
||||
.or("field4").is("value4"); //
|
||||
|
||||
String query = queryProcessor.createQuery(criteria).toString();
|
||||
|
||||
assertEquals(expected, query, false);
|
||||
}
|
||||
|
||||
@Test // DATAES-706
|
||||
void shouldAddSubQuery() throws JSONException {
|
||||
|
||||
String expected = "{\n" + //
|
||||
" \"bool\": {\n" + //
|
||||
" \"must\": [\n" + //
|
||||
" {\n" + //
|
||||
" \"query_string\": {\n" + //
|
||||
" \"query\": \"Miller\",\n" + //
|
||||
" \"fields\": [\n" + //
|
||||
" \"lastName^1.0\"\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
" },\n" + //
|
||||
" {\n" + //
|
||||
" \"bool\": {\n" + //
|
||||
" \"should\": [\n" + //
|
||||
" {\n" + //
|
||||
" \"query_string\": {\n" + //
|
||||
" \"query\": \"John\",\n" + //
|
||||
" \"fields\": [\n" + //
|
||||
" \"firstName^1.0\"\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
" },\n" + //
|
||||
" {\n" + //
|
||||
" \"query_string\": {\n" + //
|
||||
" \"query\": \"Jack\",\n" + //
|
||||
" \"fields\": [\n" + //
|
||||
" \"firstName^1.0\"\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
" }\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
" }\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
"}"; //
|
||||
|
||||
Criteria criteria = new Criteria("lastName").is("Miller")
|
||||
.subCriteria(new Criteria().or("firstName").is("John").or("firstName").is("Jack"));
|
||||
|
||||
String query = queryProcessor.createQuery(criteria).toString();
|
||||
|
||||
assertEquals(expected, query, false);
|
||||
}
|
||||
|
||||
@Test // DATAES-706
|
||||
void shouldProcessNestedSubCriteria() throws JSONException {
|
||||
|
||||
String expected = "{\n" + //
|
||||
" \"bool\": {\n" + //
|
||||
" \"should\": [\n" + //
|
||||
" {\n" + //
|
||||
" \"bool\": {\n" + //
|
||||
" \"must\": [\n" + //
|
||||
" {\n" + //
|
||||
" \"query_string\": {\n" + //
|
||||
" \"query\": \"Miller\",\n" + //
|
||||
" \"fields\": [\n" + //
|
||||
" \"lastName^1.0\"\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
" },\n" + //
|
||||
" {\n" + //
|
||||
" \"bool\": {\n" + //
|
||||
" \"should\": [\n" + //
|
||||
" {\n" + //
|
||||
" \"query_string\": {\n" + //
|
||||
" \"query\": \"Jack\",\n" + //
|
||||
" \"fields\": [\n" + //
|
||||
" \"firstName^1.0\"\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
" },\n" + //
|
||||
" {\n" + //
|
||||
" \"query_string\": {\n" + //
|
||||
" \"query\": \"John\",\n" + //
|
||||
" \"fields\": [\n" + //
|
||||
" \"firstName^1.0\"\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
" }\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
" }\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
" },\n" + //
|
||||
" {\n" + //
|
||||
" \"bool\": {\n" + //
|
||||
" \"must\": [\n" + //
|
||||
" {\n" + //
|
||||
" \"query_string\": {\n" + //
|
||||
" \"query\": \"Smith\",\n" + //
|
||||
" \"fields\": [\n" + //
|
||||
" \"lastName^1.0\"\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
" },\n" + //
|
||||
" {\n" + //
|
||||
" \"bool\": {\n" + //
|
||||
" \"should\": [\n" + //
|
||||
" {\n" + //
|
||||
" \"query_string\": {\n" + //
|
||||
" \"query\": \"Emma\",\n" + //
|
||||
" \"fields\": [\n" + //
|
||||
" \"firstName^1.0\"\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
" },\n" + //
|
||||
" {\n" + //
|
||||
" \"query_string\": {\n" + //
|
||||
" \"query\": \"Lucy\",\n" + //
|
||||
" \"fields\": [\n" + //
|
||||
" \"firstName^1.0\"\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
" }\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
" }\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
" }\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
"}"; //
|
||||
|
||||
Criteria criteria = Criteria.or()
|
||||
.subCriteria(new Criteria("lastName").is("Miller")
|
||||
.subCriteria(new Criteria().or("firstName").is("John").or("firstName").is("Jack")))
|
||||
.subCriteria(new Criteria("lastName").is("Smith")
|
||||
.subCriteria(new Criteria().or("firstName").is("Emma").or("firstName").is("Lucy")));
|
||||
|
||||
String query = queryProcessor.createQuery(criteria).toString();
|
||||
|
||||
assertEquals(expected, query, false);
|
||||
}
|
||||
|
||||
@Test // DATAES-706
|
||||
void shouldBuildMatchQuery() throws JSONException {
|
||||
|
||||
String expected = "{\n" + //
|
||||
" \"bool\" : {\n" + //
|
||||
" \"must\" : [\n" + //
|
||||
" {\n" + //
|
||||
" \"match\" : {\n" + //
|
||||
" \"field1\" : {\n" + //
|
||||
" \"query\" : \"value1 value2\",\n" + //
|
||||
" \"operator\" : \"OR\"\n" + //
|
||||
" }\n" + //
|
||||
" }\n" + //
|
||||
" }\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
"}\n"; //
|
||||
|
||||
Criteria criteria = new Criteria("field1").matches("value1 value2");
|
||||
|
||||
String query = queryProcessor.createQuery(criteria).toString();
|
||||
|
||||
assertEquals(expected, query, false);
|
||||
}
|
||||
|
||||
@Test // DATAES-706
|
||||
void shouldBuildMatchAllQuery() throws JSONException {
|
||||
|
||||
String expected = "{\n" + //
|
||||
" \"bool\" : {\n" + //
|
||||
" \"must\" : [\n" + //
|
||||
" {\n" + //
|
||||
" \"match\" : {\n" + //
|
||||
" \"field1\" : {\n" + //
|
||||
" \"query\" : \"value1 value2\",\n" + //
|
||||
" \"operator\" : \"AND\"\n" + //
|
||||
" }\n" + //
|
||||
" }\n" + //
|
||||
" }\n" + //
|
||||
" ]\n" + //
|
||||
" }\n" + //
|
||||
"}\n"; //
|
||||
|
||||
Criteria criteria = new Criteria("field1").matchesAll("value1 value2");
|
||||
|
||||
String query = queryProcessor.createQuery(criteria).toString();
|
||||
|
||||
assertEquals(expected, query, false);
|
||||
}
|
||||
}
|
@ -83,99 +83,72 @@ public class CriteriaQueryTests {
|
||||
indexOperations.delete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldPerformAndOperation() {
|
||||
@Test // ,DATAES-706
|
||||
public void shouldPerformAndOperationOnCriteriaEntries() {
|
||||
|
||||
// given
|
||||
String documentId = nextIdAsString();
|
||||
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);
|
||||
operations.index(indexQuery, index);
|
||||
SampleEntity sampleEntity1 = new SampleEntity();
|
||||
sampleEntity1.setId(nextIdAsString());
|
||||
sampleEntity1.setMessage("some test message");
|
||||
operations.save(sampleEntity1);
|
||||
SampleEntity sampleEntity2 = new SampleEntity();
|
||||
sampleEntity2.setId(nextIdAsString());
|
||||
sampleEntity2.setMessage("some other message");
|
||||
operations.save(sampleEntity2);
|
||||
indexOperations.refresh();
|
||||
|
||||
// when
|
||||
CriteriaQuery criteriaQuery = new CriteriaQuery(
|
||||
new Criteria("message").contains("test").and("message").contains("some"));
|
||||
|
||||
// when
|
||||
SearchHit<SampleEntity> sampleEntity1 = operations.searchOne(criteriaQuery, SampleEntity.class, index);
|
||||
SearchHit<SampleEntity> searchHit = operations.searchOne(criteriaQuery, SampleEntity.class, index);
|
||||
|
||||
// then
|
||||
assertThat(sampleEntity1).isNotNull();
|
||||
assertThat(searchHit).isNotNull();
|
||||
assertThat(searchHit.getId()).isEqualTo(sampleEntity1.id);
|
||||
}
|
||||
|
||||
// @Ignore("DATAES-30")
|
||||
@Test
|
||||
public void shouldPerformOrOperation() {
|
||||
@Test // ,DATAES-706
|
||||
public void shouldPerformOrOperationOnCriteriaEntries() {
|
||||
|
||||
// given
|
||||
List<IndexQuery> indexQueries = new ArrayList<>();
|
||||
|
||||
// first document
|
||||
String documentId = nextIdAsString();
|
||||
SampleEntity sampleEntity1 = new SampleEntity();
|
||||
sampleEntity1.setId(documentId);
|
||||
sampleEntity1.setMessage("some message");
|
||||
sampleEntity1.setVersion(System.currentTimeMillis());
|
||||
|
||||
IndexQuery indexQuery1 = new IndexQuery();
|
||||
indexQuery1.setId(documentId);
|
||||
indexQuery1.setObject(sampleEntity1);
|
||||
indexQueries.add(indexQuery1);
|
||||
|
||||
// second document
|
||||
String documentId2 = nextIdAsString();
|
||||
sampleEntity1.setId(nextIdAsString());
|
||||
sampleEntity1.setMessage("some test message");
|
||||
operations.save(sampleEntity1);
|
||||
SampleEntity sampleEntity2 = new SampleEntity();
|
||||
sampleEntity2.setId(documentId2);
|
||||
sampleEntity2.setMessage("test message");
|
||||
sampleEntity2.setVersion(System.currentTimeMillis());
|
||||
|
||||
IndexQuery indexQuery2 = new IndexQuery();
|
||||
indexQuery2.setId(documentId2);
|
||||
indexQuery2.setObject(sampleEntity2);
|
||||
|
||||
indexQueries.add(indexQuery2);
|
||||
operations.bulkIndex(indexQueries, index);
|
||||
sampleEntity2.setId(nextIdAsString());
|
||||
sampleEntity2.setMessage("some other message");
|
||||
operations.save(sampleEntity2);
|
||||
indexOperations.refresh();
|
||||
CriteriaQuery criteriaQuery = new CriteriaQuery(
|
||||
new Criteria("message").contains("some").or("message").contains("test"));
|
||||
|
||||
// when
|
||||
CriteriaQuery criteriaQuery = new CriteriaQuery(
|
||||
new Criteria("message").contains("test").or("message").contains("other"));
|
||||
SearchHits<SampleEntity> searchHits = operations.search(criteriaQuery, SampleEntity.class, index);
|
||||
|
||||
// then
|
||||
assertThat(searchHits).isNotNull();
|
||||
assertThat(searchHits.getTotalHits()).isGreaterThanOrEqualTo(1);
|
||||
assertThat(searchHits.getSearchHits().stream().map(SearchHit::getId)).containsExactlyInAnyOrder(sampleEntity1.id,
|
||||
sampleEntity2.id);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // ,DATAES-706
|
||||
public void shouldPerformAndOperationWithinCriteria() {
|
||||
|
||||
// given
|
||||
List<IndexQuery> indexQueries = new ArrayList<>();
|
||||
|
||||
// first document
|
||||
String documentId = nextIdAsString();
|
||||
SampleEntity sampleEntity = new SampleEntity();
|
||||
sampleEntity.setId(documentId);
|
||||
sampleEntity.setMessage("some message");
|
||||
sampleEntity.setVersion(System.currentTimeMillis());
|
||||
|
||||
IndexQuery indexQuery = new IndexQuery();
|
||||
indexQuery.setId(documentId);
|
||||
indexQuery.setObject(sampleEntity);
|
||||
indexQueries.add(indexQuery);
|
||||
|
||||
operations.bulkIndex(indexQueries, index);
|
||||
SampleEntity sampleEntity1 = new SampleEntity();
|
||||
sampleEntity1.setId(nextIdAsString());
|
||||
sampleEntity1.setMessage("some test message");
|
||||
operations.save(sampleEntity1);
|
||||
SampleEntity sampleEntity2 = new SampleEntity();
|
||||
sampleEntity2.setId(nextIdAsString());
|
||||
sampleEntity2.setMessage("some other message");
|
||||
operations.save(sampleEntity2);
|
||||
indexOperations.refresh();
|
||||
CriteriaQuery criteriaQuery = new CriteriaQuery(new Criteria().and(new Criteria("message").contains("some")));
|
||||
|
||||
// when
|
||||
|
||||
CriteriaQuery criteriaQuery = new CriteriaQuery(
|
||||
new Criteria("message").contains("test").and(new Criteria("message").contains("some")));
|
||||
SearchHits<SampleEntity> searchHits = operations.search(criteriaQuery, SampleEntity.class, index);
|
||||
|
||||
// then
|
||||
@ -183,34 +156,29 @@ public class CriteriaQueryTests {
|
||||
assertThat(searchHits.getTotalHits()).isGreaterThanOrEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // ,DATAES-706
|
||||
public void shouldPerformOrOperationWithinCriteria() {
|
||||
|
||||
// given
|
||||
List<IndexQuery> indexQueries = new ArrayList<>();
|
||||
|
||||
// first document
|
||||
String documentId = nextIdAsString();
|
||||
SampleEntity sampleEntity = new SampleEntity();
|
||||
sampleEntity.setId(documentId);
|
||||
sampleEntity.setMessage("some message");
|
||||
sampleEntity.setVersion(System.currentTimeMillis());
|
||||
|
||||
IndexQuery indexQuery = new IndexQuery();
|
||||
indexQuery.setId(documentId);
|
||||
indexQuery.setObject(sampleEntity);
|
||||
indexQueries.add(indexQuery);
|
||||
|
||||
operations.bulkIndex(indexQueries, index);
|
||||
SampleEntity sampleEntity1 = new SampleEntity();
|
||||
sampleEntity1.setId(nextIdAsString());
|
||||
sampleEntity1.setMessage("some test message");
|
||||
operations.save(sampleEntity1);
|
||||
SampleEntity sampleEntity2 = new SampleEntity();
|
||||
sampleEntity2.setId(nextIdAsString());
|
||||
sampleEntity2.setMessage("some other message");
|
||||
operations.save(sampleEntity2);
|
||||
indexOperations.refresh();
|
||||
CriteriaQuery criteriaQuery = new CriteriaQuery(new Criteria().or(new Criteria("message").contains("some")));
|
||||
|
||||
// when
|
||||
CriteriaQuery criteriaQuery = new CriteriaQuery(
|
||||
new Criteria("message").contains("test").or(new Criteria("message").contains("other")));
|
||||
SearchHits<SampleEntity> searchHits = operations.search(criteriaQuery, SampleEntity.class, index);
|
||||
|
||||
// then
|
||||
assertThat(searchHits).isNotNull();
|
||||
assertThat(searchHits.getTotalHits()).isGreaterThanOrEqualTo(1);
|
||||
assertThat(searchHits.getSearchHits().stream().map(SearchHit::getId)).containsExactlyInAnyOrder(sampleEntity1.id,
|
||||
sampleEntity2.id);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -78,9 +78,9 @@ public class ReactiveElasticsearchStringQueryUnitTests {
|
||||
public void bindsSimplePropertyCorrectly() throws Exception {
|
||||
|
||||
ReactiveElasticsearchStringQuery elasticsearchStringQuery = createQueryForMethod("findByName", String.class);
|
||||
StubParameterAccessor accesor = new StubParameterAccessor("Luke");
|
||||
StubParameterAccessor accessor = new StubParameterAccessor("Luke");
|
||||
|
||||
org.springframework.data.elasticsearch.core.query.Query query = elasticsearchStringQuery.createQuery(accesor);
|
||||
org.springframework.data.elasticsearch.core.query.Query query = elasticsearchStringQuery.createQuery(accessor);
|
||||
StringQuery reference = new StringQuery("{ 'bool' : { 'must' : { 'term' : { 'name' : 'Luke' } } } }");
|
||||
|
||||
assertThat(query).isInstanceOf(StringQuery.class);
|
||||
@ -93,9 +93,9 @@ public class ReactiveElasticsearchStringQueryUnitTests {
|
||||
|
||||
ReactiveElasticsearchStringQuery elasticsearchStringQuery = createQueryForMethod("findByNameWithExpression",
|
||||
String.class);
|
||||
StubParameterAccessor accesor = new StubParameterAccessor("Luke");
|
||||
StubParameterAccessor accessor = new StubParameterAccessor("Luke");
|
||||
|
||||
org.springframework.data.elasticsearch.core.query.Query query = elasticsearchStringQuery.createQuery(accesor);
|
||||
org.springframework.data.elasticsearch.core.query.Query query = elasticsearchStringQuery.createQuery(accessor);
|
||||
StringQuery reference = new StringQuery("{ 'bool' : { 'must' : { 'term' : { 'name' : 'Luke' } } } }");
|
||||
|
||||
assertThat(query).isInstanceOf(StringQuery.class);
|
||||
|
Loading…
x
Reference in New Issue
Block a user