diff --git a/src/main/asciidoc/reference/elasticsearch-repository-queries.adoc b/src/main/asciidoc/reference/elasticsearch-repository-queries.adoc index ae0a126d6..4e578ce9c 100644 --- a/src/main/asciidoc/reference/elasticsearch-repository-queries.adoc +++ b/src/main/asciidoc/reference/elasticsearch-repository-queries.adoc @@ -8,7 +8,7 @@ The Elasticsearch module supports all basic query building feature as string que === Declared queries -Deriving the query from the method name is not always sufficient and/or may result in unreadable method names. In this case one might make either use of `@Query` annotation (see <> ). +Deriving the query from the method name is not always sufficient and/or may result in unreadable method names. In this case one might make use of the `@Query` annotation (see <> ). [[elasticsearch.query-methods.criterions]] == Query creation @@ -29,12 +29,14 @@ The method name above will be translated into the following Elasticsearch json q [source] ---- -{ "bool" : - { "must" : - [ - { "field" : {"name" : "?"} }, - { "field" : {"price" : "?"} } - ] +{ + "query": { + "bool" : { + "must" : [ + { "query_string" : { "query" : "?", "fields" : [ "name" ] } }, + { "query_string" : { "query" : "?", "fields" : [ "price" ] } } + ] + } } } ---- @@ -48,80 +50,184 @@ A list of supported keywords for Elasticsearch is shown below. | Sample | Elasticsearch Query String| `And` | `findByNameAndPrice` -| `{"bool" : {"must" : [ {"field" : {"name" : "?"}}, - {"field" : {"price" : "?"}} ]}}` +| `{ "query" : { +"bool" : { +"must" : [ + { "query_string" : { "query" : "?", "fields" : [ "name" ] } }, + { "query_string" : { "query" : "?", "fields" : [ "price" ] } } + ] + } +}}` | `Or` | `findByNameOrPrice` -| `{"bool" : {"should" : [ {"field" : {"name" : "?"}}, - {"field" : {"price" : "?"}} ]}}` +| `{ "query" : { +"bool" : { +"should" : [ + { "query_string" : { "query" : "?", "fields" : [ "name" ] } }, + { "query_string" : { "query" : "?", "fields" : [ "price" ] } } + ] + } +}}` | `Is` | `findByName` -| `{"bool" : {"must" : {"field" : {"name" : "?"}}}}` +| `{ "query" : { +"bool" : { +"must" : [ + { "query_string" : { "query" : "?", "fields" : [ "name" ] } } + ] + } +}}` | `Not` | `findByNameNot` -| `{"bool" : {"must_not" : {"field" : {"name" : "?"}}}}` +| `{ "query" : { +"bool" : { +"must_not" : [ + { "query_string" : { "query" : "?", "fields" : [ "name" ] } } + ] + } +}}` | `Between` | `findByPriceBetween` -| `{"bool" : {"must" : {"range" : {"price" : {"from" : - ?,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}` +| `{ "query" : { +"bool" : { +"must" : [ + {"range" : {"price" : {"from" : ?, "to" : ?, "include_lower" : true, "include_upper" : true } } } + ] + } +}}` + +| `LessThan` +| `findByPriceLessThan` +| `{ "query" : { +"bool" : { +"must" : [ + {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : false } } } + ] + } +}}` | `LessThanEqual` -| `findByPriceLessThan` -| `{"bool" : {"must" : {"range" : {"price" : {"from" : - null,"to" : ?,"include_lower" : true,"include_upper" : - true}}}}}` +| `findByPriceLessThanEqual` +| `{ "query" : { +"bool" : { +"must" : [ + {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : true } } } + ] + } +}}` + +| `GreaterThan` +| `findByPriceGreaterThan` +| `{ "query" : { +"bool" : { +"must" : [ + {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : false, "include_upper" : true } } } + ] + } +}}` + | `GreaterThanEqual` | `findByPriceGreaterThan` -| `{"bool" : {"must" : {"range" : {"price" : {"from" : - ?,"to" : null,"include_lower" : true,"include_upper" : - true}}}}}` +| `{ "query" : { +"bool" : { +"must" : [ + {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : true, "include_upper" : true } } } + ] + } +}}` | `Before` | `findByPriceBefore` -| `{"bool" : {"must" : {"range" : {"price" : {"from" : - null,"to" : ?,"include_lower" : true,"include_upper" : - true}}}}}` +| `{ "query" : { +"bool" : { +"must" : [ + {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : true } } } + ] + } +}}` | `After` | `findByPriceAfter` -| `{"bool" : {"must" : {"range" : {"price" : {"from" : - ?,"to" : null,"include_lower" : true,"include_upper" : - true}}}}}` +| `{ "query" : { +"bool" : { +"must" : [ + {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : true, "include_upper" : true } } } + ] + } +}}` | `Like` | `findByNameLike` -| `{"bool" : {"must" : {"field" : {"name" : {"query" : - "?*","analyze_wildcard" : true}}}}}` +| `{ "query" : { +"bool" : { +"must" : [ + { "query_string" : { "query" : "?*", "fields" : [ "name" ] }, "analyze_wildcard": true } + ] + } +}}` | `StartingWith` | `findByNameStartingWith` -| `{"bool" : {"must" : {"field" : {"name" : {"query" : - "?*","analyze_wildcard" : true}}}}}` +| `{ "query" : { +"bool" : { +"must" : [ + { "query_string" : { "query" : "?*", "fields" : [ "name" ] }, "analyze_wildcard": true } + ] + } +}}` | `EndingWith` | `findByNameEndingWith` -| `{"bool" : {"must" : {"field" : {"name" : {"query" : - "*?","analyze_wildcard" : true}}}}}` +| `{ "query" : { +"bool" : { +"must" : [ + { "query_string" : { "query" : "*?", "fields" : [ "name" ] }, "analyze_wildcard": true } + ] + } +}}` | `Contains/Containing` | `findByNameContaining` -| `{"bool" : {"must" : {"field" : {"name" : {"query" : - "*?*","analyze_wildcard" : true}}}}}` +| `{ "query" : { +"bool" : { +"must" : [ + { "query_string" : { "query" : "\*?*", "fields" : [ "name" ] }, "analyze_wildcard": true } + ] + } +}}` | `In` | `findByNameIn(Collectionnames)` -| `{"bool" : {"must" : {"bool" : {"should" : [ {"field" : - {"name" : "?"}}, {"field" : {"name" : "?"}} ]}}}}` +| `{ "query" : { +"bool" : { +"must" : [ + {"bool" : {"must" : [ + {"terms" : {"name" : ["?","?"]}} + ] + } + } + ] + } +}}` | `NotIn` | `findByNameNotIn(Collectionnames)` -| `{"bool" : {"must_not" : {"bool" : {"should" : {"field" : - {"name" : "?"}}}}}}` +| `{ "query" : { +"bool" : { +"must" : [ + {"bool" : {"must_not" : [ + {"terms" : {"name" : ["?","?"]}} + ] + } + } + ] + } +}}` | `Near` | `findByStoreNear` @@ -129,16 +235,35 @@ A list of supported keywords for Elasticsearch is shown below. | `True` | `findByAvailableTrue` -| `{"bool" : {"must" : {"field" : {"available" : true}}}}` +| `{ "query" : { +"bool" : { +"must" : [ + { "query_string" : { "query" : "true", "fields" : [ "available" ] } } + ] + } +}}` | `False` | `findByAvailableFalse` -| `{"bool" : {"must" : {"field" : {"available" : false}}}}` +| `{ "query" : { +"bool" : { +"must" : [ + { "query_string" : { "query" : "false", "fields" : [ "available" ] } } + ] + } +}}` | `OrderBy` | `findByAvailableTrueOrderByNameDesc` -| `{"sort" : [{ "name" : {"order" : "desc"} }],"bool" : - {"must" : {"field" : {"available" : true}}}}` +| `{ "query" : { +"bool" : { +"must" : [ + { "query_string" : { "query" : "true", "fields" : [ "available" ] } } + ] + } +}, "sort":[{"name":{"order":"desc"}}] +}` + |=== [[elasticsearch.query-methods.at-query]] diff --git a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchPartQueryTests.java b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchPartQueryTests.java new file mode 100644 index 000000000..7e631c3a1 --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchPartQueryTests.java @@ -0,0 +1,589 @@ +/* + * 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.mockito.Mockito.*; +import static org.skyscreamer.jsonassert.JSONAssert.*; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.elasticsearch.search.builder.SearchSourceBuilder; +import org.json.JSONException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.data.annotation.Id; +import org.springframework.data.elasticsearch.annotations.Field; +import org.springframework.data.elasticsearch.annotations.FieldType; +import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter; +import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter; +import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; +import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext; +import org.springframework.data.elasticsearch.core.query.CriteriaQuery; +import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; +import org.springframework.data.elasticsearch.repository.query.ElasticsearchPartQuery; +import org.springframework.data.elasticsearch.repository.query.ElasticsearchQueryMethod; +import org.springframework.data.projection.SpelAwareProxyProjectionFactory; +import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; +import org.springframework.data.repository.query.ParametersParameterAccessor; + +/** + * Tests for {@link ElasticsearchPartQuery}. Resides in the core package, as we need an instance of the + * {@link RequestFactory} class for the tests. The tests make sure that queries are built according to the method + * naming. + * + * @author Peter-Josef Meisch + */ +@ExtendWith(MockitoExtension.class) +class ElasticsearchPartQueryTests { + + public static final String BOOK_TITLE = "Title"; + public static final int BOOK_PRICE = 42; + + private @Mock ElasticsearchOperations operations; + private ElasticsearchConverter converter; + + @BeforeEach + public void setUp() { + converter = new MappingElasticsearchConverter(new SimpleElasticsearchMappingContext()); + when(operations.getElasticsearchConverter()).thenReturn(converter); + } + + @Test + void findByName() throws NoSuchMethodException, JSONException { + String methodName = "findByName"; + Class[] parameterClasses = new Class[] { String.class }; + Object[] parameters = new Object[] { BOOK_TITLE }; + + String query = getQueryBuilder(methodName, parameterClasses, parameters); + + String expected = "{\"query\": {" + // + " \"bool\" : {" + // + " \"must\" : [" + // + " {\"query_string\" : {\"query\" : \"" + BOOK_TITLE + "\", \"fields\" : [\"name^1.0\"]}}" + // + " ]" + // + " }" + // + "}}"; // + + assertEquals(expected, query, false); + } + + @Test + void findByNameNot() throws NoSuchMethodException, JSONException { + String methodName = "findByNameNot"; + Class[] parameterClasses = new Class[] { String.class }; + Object[] parameters = new Object[] { BOOK_TITLE }; + + String query = getQueryBuilder(methodName, parameterClasses, parameters); + + String expected = "{\"query\": {" + // + " \"bool\" : {" + // + " \"must_not\" : [" + // + " {\"query_string\" : {\"query\" : \"" + BOOK_TITLE + "\", \"fields\" : [\"name^1.0\"]}}" + // + " ]" + // + " }" + // + "}}"; // + + assertEquals(expected, query, false); + } + + @Test + void findByNameLike() throws NoSuchMethodException, JSONException { + String methodName = "findByNameLike"; + Class[] parameterClasses = new Class[] { String.class }; + Object[] parameters = new Object[] { BOOK_TITLE }; + + String query = getQueryBuilder(methodName, parameterClasses, parameters); + + String expected = "{\"query\": {" + // + " \"bool\" : {" + // + " \"must\" : [" + // + " {\"query_string\" : {\"query\" : \"" + BOOK_TITLE + + "*\", \"fields\" : [\"name^1.0\"], \"analyze_wildcard\": true }}" + // + " ]" + // + " }" + // + "}}"; // + + assertEquals(expected, query, false); + } + + @Test + void findByNameStartingWith() throws NoSuchMethodException, JSONException { + String methodName = "findByNameStartingWith"; + Class[] parameterClasses = new Class[] { String.class }; + Object[] parameters = new Object[] { BOOK_TITLE }; + + String query = getQueryBuilder(methodName, parameterClasses, parameters); + + String expected = "{\"query\": {" + // + " \"bool\" : {" + // + " \"must\" : [" + // + " {\"query_string\" : {\"query\" : \"" + BOOK_TITLE + + "*\", \"fields\" : [\"name^1.0\"], \"analyze_wildcard\": true }}" + // + " ]" + // + " }" + // + "}}"; // + + assertEquals(expected, query, false); + } + + @Test + void findByNameEndingWith() throws NoSuchMethodException, JSONException { + String methodName = "findByNameEndingWith"; + Class[] parameterClasses = new Class[] { String.class }; + Object[] parameters = new Object[] { BOOK_TITLE }; + + String query = getQueryBuilder(methodName, parameterClasses, parameters); + + String expected = "{\"query\": {" + // + " \"bool\" : {" + // + " \"must\" : [" + // + " {\"query_string\" : {\"query\" : \"*" + BOOK_TITLE + + "\", \"fields\" : [\"name^1.0\"], \"analyze_wildcard\": true }}" + // + " ]" + // + " }" + // + "}}"; // + + assertEquals(expected, query, false); + } + + @Test + void findByNameContaining() throws NoSuchMethodException, JSONException { + String methodName = "findByNameContaining"; + Class[] parameterClasses = new Class[] { String.class }; + Object[] parameters = new Object[] { BOOK_TITLE }; + + String query = getQueryBuilder(methodName, parameterClasses, parameters); + + String expected = "{\"query\": {" + // + " \"bool\" : {" + // + " \"must\" : [" + // + " {\"query_string\" : {\"query\" : \"*" + BOOK_TITLE + + "*\", \"fields\" : [\"name^1.0\"], \"analyze_wildcard\": true }}" + // + " ]" + // + " }" + // + "}}"; // + + assertEquals(expected, query, false); + } + + @Test + void findByNameIn() throws NoSuchMethodException, JSONException { + String methodName = "findByNameIn"; + Class[] parameterClasses = new Class[] { Collection.class }; + List names = new ArrayList<>(); + names.add(BOOK_TITLE); + names.add(BOOK_TITLE + "2"); + Object[] parameters = new Object[] { names }; + + String query = getQueryBuilder(methodName, parameterClasses, parameters); + + String expected = "{\"query\": {" + // + " \"bool\" : {" + // + " \"must\" : [" + // + " {\"bool\" : {\"must\" : [{\"terms\" : {\"name\" : [\"" + names.get(0) + "\", \"" + names.get(1) + + "\"]}}]}}" + // + " ]" + // + " }" + // + "}}"; // + + assertEquals(expected, query, false); + } + + @Test + void findByNameNotIn() throws NoSuchMethodException, JSONException { + String methodName = "findByNameNotIn"; + Class[] parameterClasses = new Class[] { Collection.class }; + List names = new ArrayList<>(); + names.add(BOOK_TITLE); + names.add(BOOK_TITLE + "2"); + Object[] parameters = new Object[] { names }; + + String query = getQueryBuilder(methodName, parameterClasses, parameters); + + String expected = "{\"query\": {" + // + " \"bool\" : {" + // + " \"must\" : [" + // + " {\"bool\" : {\"must_not\" : [{\"terms\" : {\"name\" : [\"" + names.get(0) + "\", \"" + names.get(1) + + "\"]}}]}}" + // + " ]" + // + " }" + // + "}}"; // + + assertEquals(expected, query, false); + } + + @Test + void findByPrice() throws NoSuchMethodException, JSONException { + String methodName = "findByPrice"; + Class[] parameterClasses = new Class[] { Integer.class }; + Object[] parameters = new Object[] { 42 }; + + String query = getQueryBuilder(methodName, parameterClasses, parameters); + + String expected = "{\"query\": {" + // + " \"bool\" : {" + // + " \"must\" : [" + // + " {\"query_string\" : {\"query\" : \"" + BOOK_PRICE + "\", \"fields\" : [\"price^1.0\"]}}" + // + " ]" + // + " }" + // + "}}"; // + + assertEquals(expected, query, false); + } + + @Test + void findByNameAndPrice() throws NoSuchMethodException, JSONException { + String methodName = "findByNameAndPrice"; + Class[] parameterClasses = new Class[] { String.class, Integer.class }; + Object[] parameters = new Object[] { BOOK_TITLE, BOOK_PRICE }; + + String query = getQueryBuilder(methodName, parameterClasses, parameters); + + String expected = "{\"query\": {" + // + " \"bool\" : {" + // + " \"must\" : [" + // + " {\"query_string\" : {\"query\" : \"" + BOOK_TITLE + "\", \"fields\" : [\"name^1.0\"]}}," + // + " {\"query_string\" : {\"query\" : \"" + BOOK_PRICE + "\", \"fields\" : [\"price^1.0\"]}}" + // + " ]" + // + " }" + // + "}}"; // + + assertEquals(expected, query, false); + } + + @Test + void findByNameOrPrice() throws NoSuchMethodException, JSONException { + String methodName = "findByNameOrPrice"; + Class[] parameterClasses = new Class[] { String.class, Integer.class }; + Object[] parameters = new Object[] { BOOK_TITLE, BOOK_PRICE }; + + String query = getQueryBuilder(methodName, parameterClasses, parameters); + + String expected = "{\"query\": {" + // + " \"bool\" : {" + // + " \"should\" : [" + // + " {\"query_string\" : {\"query\" : \"" + BOOK_TITLE + "\", \"fields\" : [\"name^1.0\"]}}," + // + " {\"query_string\" : {\"query\" : \"" + BOOK_PRICE + "\", \"fields\" : [\"price^1.0\"]}}" + // + " ]" + // + " }" + // + "}}"; // + + assertEquals(expected, query, false); + } + + @Test + void findByPriceBetween() throws NoSuchMethodException, JSONException { + String methodName = "findByPriceBetween"; + Class[] parameterClasses = new Class[] { Integer.class, Integer.class }; + Object[] parameters = new Object[] { BOOK_PRICE - 10, BOOK_PRICE + 10 }; + + String query = getQueryBuilder(methodName, parameterClasses, parameters); + + String expected = "{\"query\": {" + // + " \"bool\" : {" + // + " \"must\" : [" + // + " {\"range\" : {\"price\" : {\"from\" : 32, \"to\" : 52, \"include_lower\" : true, \"include_upper\" : true } } }" + + // + " ]" + // + " }" + // + "}}"; // + + assertEquals(expected, query, false); + } + + @Test + void findByPriceLessThan() throws NoSuchMethodException, JSONException { + String methodName = "findByPriceLessThan"; + Class[] parameterClasses = new Class[] { Integer.class }; + Object[] parameters = new Object[] { BOOK_PRICE }; + + String query = getQueryBuilder(methodName, parameterClasses, parameters); + + String expected = "{\"query\": {" + // + " \"bool\" : {" + // + " \"must\" : [" + // + " {\"range\" : {\"price\" : {\"from\" : null, \"to\" : 42, \"include_lower\" : true, \"include_upper\" : false } } }" + + // + " ]" + // + " }" + // + "}}"; // + + assertEquals(expected, query, false); + } + + @Test + void findByPriceLessThanEqual() throws NoSuchMethodException, JSONException { + String methodName = "findByPriceLessThanEqual"; + Class[] parameterClasses = new Class[] { Integer.class }; + Object[] parameters = new Object[] { BOOK_PRICE }; + + String query = getQueryBuilder(methodName, parameterClasses, parameters); + + String expected = "{\"query\": {" + // + " \"bool\" : {" + // + " \"must\" : [" + // + " {\"range\" : {\"price\" : {\"from\" : null, \"to\" : 42, \"include_lower\" : true, \"include_upper\" : true } } }" + + // + " ]" + // + " }" + // + "}}"; // + + assertEquals(expected, query, false); + } + + @Test + void findByPriceGreaterThan() throws NoSuchMethodException, JSONException { + String methodName = "findByPriceGreaterThan"; + Class[] parameterClasses = new Class[] { Integer.class }; + Object[] parameters = new Object[] { BOOK_PRICE }; + + String query = getQueryBuilder(methodName, parameterClasses, parameters); + + String expected = "{\"query\": {" + // + " \"bool\" : {" + // + " \"must\" : [" + // + " {\"range\" : {\"price\" : {\"from\" : 42, \"to\" : null, \"include_lower\" : false, \"include_upper\" : true } } }" + + // + " ]" + // + " }" + // + "}}"; // + + assertEquals(expected, query, false); + } + + @Test + void findByPriceGreaterThanEqual() throws NoSuchMethodException, JSONException { + String methodName = "findByPriceGreaterThanEqual"; + Class[] parameterClasses = new Class[] { Integer.class }; + Object[] parameters = new Object[] { BOOK_PRICE }; + + String query = getQueryBuilder(methodName, parameterClasses, parameters); + + String expected = "{\"query\": {" + // + " \"bool\" : {" + // + " \"must\" : [" + // + " {\"range\" : {\"price\" : {\"from\" : 42, \"to\" : null, \"include_lower\" : true, \"include_upper\" : true } } }" + + // + " ]" + // + " }" + // + "}}"; // + + assertEquals(expected, query, false); + } + + @Test + void findByPriceBefore() throws NoSuchMethodException, JSONException { + String methodName = "findByPriceBefore"; + Class[] parameterClasses = new Class[] { Integer.class }; + Object[] parameters = new Object[] { BOOK_PRICE }; + + String query = getQueryBuilder(methodName, parameterClasses, parameters); + + String expected = "{\"query\": {" + // + " \"bool\" : {" + // + " \"must\" : [" + // + " {\"range\" : {\"price\" : {\"from\" : null, \"to\" : 42, \"include_lower\" : true, \"include_upper\" : true } } }" + + // + " ]" + // + " }" + // + "}}"; // + + assertEquals(expected, query, false); + } + + @Test + void findByPriceAfter() throws NoSuchMethodException, JSONException { + String methodName = "findByPriceAfter"; + Class[] parameterClasses = new Class[] { Integer.class }; + Object[] parameters = new Object[] { BOOK_PRICE }; + + String query = getQueryBuilder(methodName, parameterClasses, parameters); + + String expected = "{\"query\": {" + // + " \"bool\" : {" + // + " \"must\" : [" + // + " {\"range\" : {\"price\" : {\"from\" : 42, \"to\" : null, \"include_lower\" : true, \"include_upper\" : true } } }" + + // + " ]" + // + " }" + // + "}}"; // + + assertEquals(expected, query, false); + } + + @Test + void findByAvailableTrue() throws NoSuchMethodException, JSONException { + String methodName = "findByAvailableTrue"; + Class[] parameterClasses = new Class[] {}; + Object[] parameters = new Object[] {}; + + String query = getQueryBuilder(methodName, parameterClasses, parameters); + + String expected = "{\"query\": {" + // + " \"bool\" : {" + // + " \"must\" : [" + // + " {\"query_string\" : {\"query\" : \"true\", \"fields\" : [\"available^1.0\"]}}" + // + " ]" + // + " }" + // + "}}"; // + + assertEquals(expected, query, false); + } + + @Test + void findByAvailableFalse() throws NoSuchMethodException, JSONException { + String methodName = "findByAvailableFalse"; + Class[] parameterClasses = new Class[] {}; + Object[] parameters = new Object[] {}; + + String query = getQueryBuilder(methodName, parameterClasses, parameters); + + String expected = "{\"query\": {" + // + " \"bool\" : {" + // + " \"must\" : [" + // + " {\"query_string\" : {\"query\" : \"false\", \"fields\" : [\"available^1.0\"]}}" + // + " ]" + // + " }" + // + "}}"; // + + assertEquals(expected, query, false); + } + + @Test + void findByAvailableTrueOrderByNameDesc() throws NoSuchMethodException, JSONException { + String methodName = "findByAvailableTrueOrderByNameDesc"; + Class[] parameterClasses = new Class[] {}; + Object[] parameters = new Object[] {}; + + String query = getQueryBuilder(methodName, parameterClasses, parameters); + + String expected = "{\"query\": {" + // + " \"bool\" : {" + // + " \"must\" : [" + // + " {\"query_string\" : {\"query\" : \"true\", \"fields\" : [\"available^1.0\"]}}" + // + " ]" + // + " }" + // + "}," + // + "\"sort\":[{\"name\":{\"order\":\"desc\"}}]" + // + '}'; // + + assertEquals(expected, query, false); + } + + private String getQueryBuilder(String methodName, Class[] parameterClasses, Object[] parameters) + throws NoSuchMethodException { + Method method = SampleRepository.class.getMethod(methodName, parameterClasses); + ElasticsearchQueryMethod queryMethod = new ElasticsearchQueryMethod(method, + new DefaultRepositoryMetadata(SampleRepository.class), new SpelAwareProxyProjectionFactory(), + converter.getMappingContext()); + ElasticsearchPartQuery partQuery = new ElasticsearchPartQuery(queryMethod, operations); + CriteriaQuery criteriaQuery = partQuery + .createQuery(new ParametersParameterAccessor(queryMethod.getParameters(), parameters)); + SearchSourceBuilder source = new RequestFactory(converter) + .searchRequest(criteriaQuery, Book.class, IndexCoordinates.of("dummy")).source(); + return source.toString(); + } + + private interface SampleRepository extends ElasticsearchRepository { + List findByName(String name); + + List findByNameNot(String name); + + List findByNameLike(String name); + + List findByNameStartingWith(String name); + + List findByNameEndingWith(String name); + + List findByNameContaining(String name); + + List findByNameIn(Collection names); + + List findByNameNotIn(Collection names); + + List findByNameAndPrice(String name, Integer price); + + List findByNameOrPrice(String name, Integer price); + + List findByPrice(Integer price); + + List findByPriceBetween(Integer lower, Integer upper); + + List findByPriceLessThan(Integer price); + + List findByPriceLessThanEqual(Integer price); + + List findByPriceGreaterThan(Integer price); + + List findByPriceGreaterThanEqual(Integer price); + + List findByPriceBefore(Integer price); + + List findByPriceAfter(Integer price); + + List findByAvailableTrue(); + + List findByAvailableFalse(); + + List findByAvailableTrueOrderByNameDesc(); + + } + + static class Book { + @Id private String id; + private String name; + private Integer price; + @Field(type = FieldType.Boolean) private boolean available; + + 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 Integer getPrice() { + return price; + } + + public void setPrice(Integer price) { + this.price = price; + } + + public Boolean getAvailable() { + return available; + } + + public void setAvailable(Boolean available) { + this.available = available; + } + } +}