Query methods

Query lookup strategies

Elasticsearch 模块支持所有基本查询构建功能,例如字符串查询、原生搜索查询、基于标准的查询或者从方法名派生查询。

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.

Declared queries

总是从方法名称派生查询并不足够,或者可能导致无法读取的方法名称。在这种情况下,人们可以使用`@Query`注解(请参阅Using @Query Annotation)。

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 Using @Query Annotation ).

Query creation

总的来说,Elasticsearch 的查询创建机制的工作方式如 repositories/query-methods-details.adoc 中所述。下面是一个有关 Elasticsearch 查询方法如何转换的简短示例:

Generally the query creation mechanism for Elasticsearch works as described in repositories/query-methods-details.adoc. Here’s a short example of what a Elasticsearch query method translates into:

Example 1. Query creation from method names
interface BookRepository extends Repository<Book, String> {
  List<Book> findByNameAndPrice(String name, Integer price);
}

上面的方法名将转换成以下 Elasticsearch JSON 查询

The method name above will be translated into the following Elasticsearch json query

{
    "query": {
        "bool" : {
            "must" : [
                { "query_string" : { "query" : "?", "fields" : [ "name" ] } },
                { "query_string" : { "query" : "?", "fields" : [ "price" ] } }
            ]
        }
    }
}

下面列出了 Elasticsearch 支持的关键字列表。

A list of supported keywords for Elasticsearch is shown below.

Table 1. Supported keywords inside method names
Keyword Sample Elasticsearch Query String

And

findByNameAndPrice

{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } }, { "query_string" : { "query" : "?", "fields" : [ "price" ] } } ] } }}

Or

findByNameOrPrice

{ "query" : { "bool" : { "should" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } }, { "query_string" : { "query" : "?", "fields" : [ "price" ] } } ] } }}

Is

findByName

{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } } ] } }}

Not

findByNameNot

{ "query" : { "bool" : { "must_not" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } } ] } }}

Between

findByPriceBetween

{ "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

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

findByPriceGreaterThanEqual

{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : true, "include_upper" : true } } } ] } }}

Before

findByPriceBefore

{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : true } } } ] } }}

After

findByPriceAfter

{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : true, "include_upper" : true } } } ] } }}

Like

findByNameLike

{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?*", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}

StartingWith

findByNameStartingWith

{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?*", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}

EndingWith

findByNameEndingWith

{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "*?", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}

Contains/Containing

findByNameContaining

{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}

In (when annotated as FieldType.Keyword)

findByNameIn(Collection<String>names)

{ "query" : { "bool" : { "must" : [ {"bool" : {"must" : [ {"terms" : {"name" : ["?","?"]}} ] } } ] } }}

In

findByNameIn(Collection<String>names)

{ "query": {"bool": {"must": [{"query_string":{"query": "\"?\" \"?\"", "fields": ["name"]}}]}}}

NotIn (when annotated as FieldType.Keyword)

findByNameNotIn(Collection<String>names)

{ "query" : { "bool" : { "must" : [ {"bool" : {"must_not" : [ {"terms" : {"name" : ["?","?"]}} ] } } ] } }}

NotIn

findByNameNotIn(Collection<String>names)

{"query": {"bool": {"must": [{"query_string": {"query": "NOT(\"?\" \"?\")", "fields": ["name"]}}]}}}

True

findByAvailableTrue

{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "true", "fields" : [ "available" ] } } ] } }}

False

findByAvailableFalse

{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "false", "fields" : [ "available" ] } } ] } }}

OrderBy

findByAvailableTrueOrderByNameDesc

{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "true", "fields" : [ "available" ] } } ] } }, "sort":[{"name":{"order":"desc"}}] }

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":"*"}}}]}}}

如果需要在存储库中使用此类函数,则不支持生成采用 GeoJson 参数的地理空间查询的方法名称。在自定义存储库实现中使用 ElasticsearchOperationsCriteriaQuery

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.

Method return types

可以定义存储库方法以具有以下返回类型,以返回多个元素:

Repository methods can be defined to have the following return types for returning multiple Elements:

  • List<T>

  • Stream<T>

  • SearchHits<T>

  • List<SearchHit<T>>

  • Stream<SearchHit<T>>

  • SearchPage<T>

Using @Query Annotation

Example 2. Declare query on the method using the @Query annotation.

传递给该方法的参数可以插入到查询字符串中的占位符中。对于第一个、第二个、第三个参数等,占位符的格式为 ?0?1?2 等。

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.

interface BookRepository extends ElasticsearchRepository<Book, String> {
    @Query("{\"match\": {\"name\": {\"query\": \"?0\"}}}")
    Page<Book> findByName(String name,Pageable pageable);
}

作为注解参数设置的 String 必须是有效的 Elasticsearch JSON 查询。它将作为查询元素的值发送到 Elasticsearch 中;例如,如果使用参数 John 调用该函数,它将生成以下查询体:

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:

{
  "query": {
    "match": {
      "name": {
        "query": "John"
      }
    }
  }
}
Example 3. @Query annotation on a method taking a Collection argument

这样的存储库方法

A repository method such as

@Query("{\"ids\": {\"values\": ?0 }}")
List<SampleEntity> getByIds(Collection<String> ids);

将执行 IDs query 以返回所有匹配的文档。因此,使用 List["id1", "id2", "id3"] 调用方法将产生请求正文

would make an IDs query to return all the matching documents. So calling the method with a List of ["id1", "id2", "id3"] would produce the query body

{
  "query": {
    "ids": {
      "values": ["id1", "id2", "id3"]
    }
  }
}

Using SpEL Expressions

Example 4. Declare query on the method using the @Query annotation with SpEL expression.

@Query 中定义查询时也支持 SpEL expression

SpEL expression is also supported when defining query in @Query.

interface BookRepository extends ElasticsearchRepository<Book, String> {
    @Query("""
        {
          "bool":{
            "must":[
              {
                "term":{
                  "name": "#{#name}"
                }
              }
            ]
          }
        }
        """)
    Page<Book> findByName(String name, Pageable pageable);
}

例如,如果使用参数 John 调用该函数,它将生成以下查询体:

If for example the function is called with the parameter John, it would produce the following query body:

{
  "bool":{
    "must":[
      {
        "term":{
          "name": "John"
        }
      }
    ]
  }
}
Example 5. accessing parameter property.

假设我们有以下类作为查询参数类型:

Supposing that we have the following class as query parameter type:

public record QueryParameter(String value) {
}

我们可以通过 # 符号轻松地访问参数,然后使用简单的 . 引用属性 value

It’s easy to access the parameter by # symbol, then reference the property value with a simple .:

interface BookRepository extends ElasticsearchRepository<Book, String> {
    @Query("""
            {
              "bool":{
                "must":[
                  {
                    "term":{
                      "name": "#{#parameter.value}"
                    }
                  }
                ]
              }
            }
            """)
    Page<Book> findByName(QueryParameter parameter, Pageable pageable);
}

现在,我们可以将 new QueryParameter("John") 作为参数传递,它将生成与上面相同的查询字符串。

We can pass new QueryParameter("John") as the parameter now, and it will produce the same query string as above.

Example 6. accessing bean property.

也支持访问 Bean property。假设有一个名为 queryParameter 的类型为 QueryParameter 的 Bean,我们可以用符号 @ 而不是 # 访问 Bean,并且无需在查询方法中声明类型为 QueryParameter 的参数:

Bean property is also supported to access. Given that there is a bean named queryParameter of type QueryParameter, we can access the bean with symbol @ rather than #, and there is no need to declare a parameter of type QueryParameter in the query method:

interface BookRepository extends ElasticsearchRepository<Book, String> {
    @Query("""
            {
              "bool":{
                "must":[
                  {
                    "term":{
                      "name": "#{@queryParameter.value}"
                    }
                  }
                ]
              }
            }
            """)
    Page<Book> findByName(Pageable pageable);
}
Example 7. SpEL and Collection param.

Collection 参数也受支持,并且与普通 String 一样容易使用,例如以下 terms 查询:

Collection parameter is also supported and is as easy to use as normal String, such as the following terms query:

interface BookRepository extends ElasticsearchRepository<Book, String> {
    @Query("""
            {
              "bool":{
                "must":[
                  {
                    "terms":{
                      "name": #{#names}
                    }
                  }
                ]
              }
            }
            """)
    Page<Book> findByName(Collection<String> names, Pageable pageable);
}

声明 Elasticsearch JSON 查询时,不应该对集合值加上引号。

collection values should not be quoted when declaring the elasticsearch json query.

类似 List.of("name1", "name2")names 集合将生成以下 terms 查询:

A collection of names like List.of("name1", "name2") will produce the following terms query:

{
  "bool":{
    "must":[
      {
        "terms":{
          "name": ["name1", "name2"]
        }
      }
    ]
  }
}
Example 8. access property in the Collection param.

Collection 参数中的值不是简单的 String 时,使用 SpEL Collection Projection 很方便:

SpEL Collection Projection is convenient to use when values in the Collection parameter is not plain String:

interface BookRepository extends ElasticsearchRepository<Book, String> {
    @Query("""
            {
              "bool":{
                "must":[
                  {
                    "terms":{
                      "name": #{#parameters.![value]}
                    }
                  }
                ]
              }
            }
            """)
    Page<Book> findByName(Collection<QueryParameter> parameters, Pageable pageable);
}

这将从 QueryParameter 集合中提取所有 value 属性值作为新的 Collection,从而产生与上述相同的效果。

This will extract all the value property values as a new Collection from QueryParameter collection, thus takes the same effect as above.

Example 9. alter parameter name by using @Param

通过 SpEL 访问参数时,在 Sping Data 中通过 @Param 注解将参数名更改为另一个参数名也很有用:

When accessing the parameter by SpEL, it’s also useful to alter the parameter name to another one by @Param annotation in Sping Data:

interface BookRepository extends ElasticsearchRepository<Book, String> {
    @Query("""
            {
              "bool":{
                "must":[
                  {
                    "terms":{
                      "name": #{#another.![value]}
                    }
                  }
                ]
              }
            }
            """)
    Page<Book> findByName(@Param("another") Collection<QueryParameter> parameters, Pageable pageable);
}