2019-06-28 21:40:59 +02:00
[[elasticsearch.query-methods]]
= Query methods
[[elasticsearch.query-methods.finders]]
== Query lookup strategies
The Elasticsearch module supports all basic query building feature as string queries, native search queries, criteria based queries or have it being derived from the method name.
2022-09-28 22:21:23 +02:00
[[elasticsearch.query-methods.finders.declared]]
2019-06-28 21:40:59 +02:00
=== Declared queries
2020-10-21 23:05:18 +02:00
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.at-query>> ).
2019-06-28 21:40:59 +02:00
[[elasticsearch.query-methods.criterions]]
== Query creation
2020-10-21 23:05:18 +02:00
Generally the query creation mechanism for Elasticsearch works as described in <<repositories.query-methods>>.
Here's a short example of what a Elasticsearch query method translates into:
2019-06-28 21:40:59 +02:00
.Query creation from method names
====
[source,java]
----
interface BookRepository extends Repository<Book, String> {
List<Book> findByNameAndPrice(String name, Integer price);
}
----
====
The method name above will be translated into the following Elasticsearch json query
[source]
----
2020-01-02 13:27:32 +01:00
{
"query": {
"bool" : {
"must" : [
{ "query_string" : { "query" : "?", "fields" : [ "name" ] } },
{ "query_string" : { "query" : "?", "fields" : [ "price" ] } }
]
}
2019-06-28 21:40:59 +02:00
}
}
----
A list of supported keywords for Elasticsearch is shown below.
2020-10-21 23:05:18 +02:00
[cols="1,2,3",options="header"]
2019-06-28 21:40:59 +02:00
.Supported keywords inside method names
|===
| Keyword
| Sample
2020-08-24 07:02:43 +02:00
| Elasticsearch Query String
| `And`
2019-06-28 21:40:59 +02:00
| `findByNameAndPrice`
2020-01-02 13:27:32 +01:00
| `{ "query" : {
"bool" : {
"must" : [
2020-10-21 23:05:18 +02:00
{ "query_string" : { "query" : "?", "fields" : [ "name" ] } },
{ "query_string" : { "query" : "?", "fields" : [ "price" ] } }
]
}
2020-01-02 13:27:32 +01:00
}}`
2019-06-28 21:40:59 +02:00
| `Or`
| `findByNameOrPrice`
2020-01-02 13:27:32 +01:00
| `{ "query" : {
"bool" : {
"should" : [
2020-10-21 23:05:18 +02:00
{ "query_string" : { "query" : "?", "fields" : [ "name" ] } },
{ "query_string" : { "query" : "?", "fields" : [ "price" ] } }
]
}
2020-01-02 13:27:32 +01:00
}}`
2019-06-28 21:40:59 +02:00
| `Is`
| `findByName`
2020-01-02 13:27:32 +01:00
| `{ "query" : {
"bool" : {
"must" : [
2020-10-21 23:05:18 +02:00
{ "query_string" : { "query" : "?", "fields" : [ "name" ] } }
]
}
2020-01-02 13:27:32 +01:00
}}`
2019-06-28 21:40:59 +02:00
| `Not`
| `findByNameNot`
2020-01-02 13:27:32 +01:00
| `{ "query" : {
"bool" : {
"must_not" : [
2020-10-21 23:05:18 +02:00
{ "query_string" : { "query" : "?", "fields" : [ "name" ] } }
]
}
2020-01-02 13:27:32 +01:00
}}`
2019-06-28 21:40:59 +02:00
| `Between`
| `findByPriceBetween`
2020-01-02 13:27:32 +01:00
| `{ "query" : {
"bool" : {
"must" : [
2020-10-21 23:05:18 +02:00
{"range" : {"price" : {"from" : ?, "to" : ?, "include_lower" : true, "include_upper" : true } } }
]
}
2020-01-02 13:27:32 +01:00
}}`
2019-06-28 21:40:59 +02:00
2020-01-02 13:27:32 +01:00
| `LessThan`
2019-06-28 21:40:59 +02:00
| `findByPriceLessThan`
2020-01-02 13:27:32 +01:00
| `{ "query" : {
"bool" : {
"must" : [
2020-10-21 23:05:18 +02:00
{"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : false } } }
]
}
2020-01-02 13:27:32 +01:00
}}`
| `LessThanEqual`
| `findByPriceLessThanEqual`
| `{ "query" : {
"bool" : {
"must" : [
2020-10-21 23:05:18 +02:00
{"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : true } } }
]
}
2020-01-02 13:27:32 +01:00
}}`
| `GreaterThan`
| `findByPriceGreaterThan`
| `{ "query" : {
"bool" : {
"must" : [
2020-10-21 23:05:18 +02:00
{"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : false, "include_upper" : true } } }
]
}
2020-01-02 13:27:32 +01:00
}}`
2019-06-28 21:40:59 +02:00
| `GreaterThanEqual`
| `findByPriceGreaterThan`
2020-01-02 13:27:32 +01:00
| `{ "query" : {
"bool" : {
"must" : [
2020-10-21 23:05:18 +02:00
{"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : true, "include_upper" : true } } }
]
}
2020-01-02 13:27:32 +01:00
}}`
2019-06-28 21:40:59 +02:00
| `Before`
| `findByPriceBefore`
2020-01-02 13:27:32 +01:00
| `{ "query" : {
"bool" : {
"must" : [
2020-10-21 23:05:18 +02:00
{"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : true } } }
]
}
2020-01-02 13:27:32 +01:00
}}`
2019-06-28 21:40:59 +02:00
| `After`
| `findByPriceAfter`
2020-01-02 13:27:32 +01:00
| `{ "query" : {
"bool" : {
"must" : [
2020-10-21 23:05:18 +02:00
{"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : true, "include_upper" : true } } }
]
}
2020-01-02 13:27:32 +01:00
}}`
2019-06-28 21:40:59 +02:00
| `Like`
| `findByNameLike`
2020-01-02 13:27:32 +01:00
| `{ "query" : {
"bool" : {
"must" : [
2020-10-21 23:05:18 +02:00
{ "query_string" : { "query" : "?*", "fields" : [ "name" ] }, "analyze_wildcard": true }
]
}
2020-01-02 13:27:32 +01:00
}}`
2019-06-28 21:40:59 +02:00
| `StartingWith`
| `findByNameStartingWith`
2020-01-02 13:27:32 +01:00
| `{ "query" : {
"bool" : {
"must" : [
2020-10-21 23:05:18 +02:00
{ "query_string" : { "query" : "?*", "fields" : [ "name" ] }, "analyze_wildcard": true }
]
}
2020-01-02 13:27:32 +01:00
}}`
2019-06-28 21:40:59 +02:00
| `EndingWith`
| `findByNameEndingWith`
2020-01-02 13:27:32 +01:00
| `{ "query" : {
"bool" : {
"must" : [
2020-10-21 23:05:18 +02:00
{ "query_string" : { "query" : "*?", "fields" : [ "name" ] }, "analyze_wildcard": true }
]
}
2020-01-02 13:27:32 +01:00
}}`
2019-06-28 21:40:59 +02:00
| `Contains/Containing`
| `findByNameContaining`
2020-01-02 13:27:32 +01:00
| `{ "query" : {
"bool" : {
"must" : [
2020-10-21 23:05:18 +02:00
{ "query_string" : { "query" : "\*?*", "fields" : [ "name" ] }, "analyze_wildcard": true }
]
}
2020-01-02 13:27:32 +01:00
}}`
2019-06-28 21:40:59 +02:00
2020-08-24 07:02:43 +02:00
| `In` (when annotated as FieldType.Keyword)
2019-06-28 21:40:59 +02:00
| `findByNameIn(Collection<String>names)`
2020-01-02 13:27:32 +01:00
| `{ "query" : {
"bool" : {
"must" : [
2020-10-21 23:05:18 +02:00
{"bool" : {"must" : [
{"terms" : {"name" : ["?","?"]}}
]
}
}
]
}
2020-01-02 13:27:32 +01:00
}}`
2019-06-28 21:40:59 +02:00
2020-08-24 07:02:43 +02:00
| `In`
| `findByNameIn(Collection<String>names)`
| `{ "query": {"bool": {"must": [{"query_string":{"query": "\"?\" \"?\"", "fields": ["name"]}}]}}}`
| `NotIn` (when annotated as FieldType.Keyword)
2019-06-28 21:40:59 +02:00
| `findByNameNotIn(Collection<String>names)`
2020-01-02 13:27:32 +01:00
| `{ "query" : {
"bool" : {
"must" : [
2020-10-21 23:05:18 +02:00
{"bool" : {"must_not" : [
{"terms" : {"name" : ["?","?"]}}
]
}
}
]
}
2020-01-02 13:27:32 +01:00
}}`
2019-06-28 21:40:59 +02:00
2020-08-24 07:02:43 +02:00
| `NotIn`
| `findByNameNotIn(Collection<String>names)`
| `{"query": {"bool": {"must": [{"query_string": {"query": "NOT(\"?\" \"?\")", "fields": ["name"]}}]}}}`
2019-06-28 21:40:59 +02:00
| `True`
| `findByAvailableTrue`
2020-01-02 13:27:32 +01:00
| `{ "query" : {
"bool" : {
"must" : [
2020-10-21 23:05:18 +02:00
{ "query_string" : { "query" : "true", "fields" : [ "available" ] } }
]
}
2020-01-02 13:27:32 +01:00
}}`
2019-06-28 21:40:59 +02:00
| `False`
| `findByAvailableFalse`
2020-01-02 13:27:32 +01:00
| `{ "query" : {
"bool" : {
"must" : [
2020-10-21 23:05:18 +02:00
{ "query_string" : { "query" : "false", "fields" : [ "available" ] } }
]
}
2020-01-02 13:27:32 +01:00
}}`
2019-06-28 21:40:59 +02:00
| `OrderBy`
| `findByAvailableTrueOrderByNameDesc`
2020-01-02 13:27:32 +01:00
| `{ "query" : {
"bool" : {
"must" : [
2020-10-21 23:05:18 +02:00
{ "query_string" : { "query" : "true", "fields" : [ "available" ] } }
]
}
2020-01-02 13:27:32 +01:00
}, "sort":[{"name":{"order":"desc"}}]
}`
2021-09-25 14:51:40 +02:00
| `Exists`
| `findByNameExists`
| `{"query":{"bool":{"must":[{"exists":{"field":"name"}}]}}}`
| `IsNull`
| `findByNameIsNull`
| `{"query":{"bool":{"must_not":[{"exists":{"field":"name"}}]}}}`
| `IsNotNull`
| `findByNameIsNotNull`
| `{"query":{"bool":{"must":[{"exists":{"field":"name"}}]}}}`
| `IsEmpty`
| `findByNameIsEmpty`
| `{"query":{"bool":{"must":[{"bool":{"must":[{"exists":{"field":"name"}}],"must_not":[{"wildcard":{"name":{"wildcard":"*"}}}]}}]}}}`
| `IsNotEmpty`
| `findByNameIsNotEmpty`
| `{"query":{"bool":{"must":[{"wildcard":{"name":{"wildcard":"*"}}}]}}}`
2019-06-28 21:40:59 +02:00
|===
2020-10-21 23:05:18 +02:00
NOTE: Methods names to build Geo-shape queries taking `GeoJson` parameters are not supported.
Use `ElasticsearchOperations` with `CriteriaQuery` in a custom repository implementation if you need to have such a function in a repository.
2022-09-28 22:21:23 +02:00
[[elasticsearch.query-methods.return-types]]
2020-01-03 23:20:17 +01:00
== Method return types
Repository methods can be defined to have the following return types for returning multiple Elements:
* `List<T>`
* `Stream<T>`
* `SearchHits<T>`
2020-05-06 08:19:48 +02:00
* `List<SearchHit<T>>`
2020-03-06 19:08:49 +01:00
* `Stream<SearchHit<T>>`
* `SearchPage<T>`
2020-01-03 23:20:17 +01:00
2019-06-28 21:40:59 +02:00
[[elasticsearch.query-methods.at-query]]
== Using @Query Annotation
2022-01-20 18:35:40 +00:00
.Declare query on the method using the `@Query` annotation.
2019-06-28 21:40:59 +02:00
====
2022-01-20 18:35:40 +00:00
The arguments passed to the method can be inserted into placeholders in the query string. the placeholders are of the form `?0`, `?1`, `?2` etc. for the first, second, third parameter and so on.
2019-06-28 21:40:59 +02:00
[source,java]
----
interface BookRepository extends ElasticsearchRepository<Book, String> {
2020-03-06 19:08:49 +01:00
@Query("{\"match\": {\"name\": {\"query\": \"?0\"}}}")
2019-06-28 21:40:59 +02:00
Page<Book> findByName(String name,Pageable pageable);
}
----
2020-10-21 23:05:18 +02:00
The String that is set as the annotation argument must be a valid Elasticsearch JSON query.
It will be sent to Easticsearch as value of the query element; if for example the function is called with the parameter _John_, it would produce the following query body:
2020-03-06 19:08:49 +01:00
[source,json]
----
{
"query": {
"match": {
"name": {
"query": "John"
}
}
}
}
----
2019-06-28 21:40:59 +02:00
====
2022-01-20 18:35:40 +00:00
.`@Query` annotation on a method taking a Collection argument
====
A repository method such as
[source,java]
----
@Query("{\"ids\": {\"values\": ?0 }}")
List<SampleEntity> getByIds(Collection<String> ids);
----
would make an https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-ids-query.html[IDs query] to return all the matching documents. So calling the method with a `List` of `["id1", "id2", "id3"]` would produce the query body
[source,json]
----
{
"query": {
"ids": {
"values": ["id1", "id2", "id3"]
}
}
}
----
====