查询方法

查询查找策略

Elasticsearch 模块支持所有基本的查询构建功能,包括字符串查询、原生搜索查询、基于条件的查询,或从方法名派生的查询。spring-doc.cadn.net.cn

声明的查询

仅从方法名推导查询并不总是足够的,或者可能导致方法名难以阅读。 在这种情况下,可以使用 @Query 注解(参见 使用 @Query 注解)。spring-doc.cadn.net.cn

另一种可能性是使用搜索模板(参见 使用 @SearchTemplateQuery 注解)。spring-doc.cadn.net.cn

查询创建

通常,Elasticsearch 的查询创建机制如 定义查询方法 中所述。 以下是一个简短示例,展示 Elasticsearch 查询方法的转换结果:spring-doc.cadn.net.cn

示例 1. 从方法名创建查询
interface BookRepository extends Repository<Book, String> {
  List<Book> findByNameAndPrice(String name, Integer price);
}

上述方法名将被转换为以下 Elasticsearch JSON 查询spring-doc.cadn.net.cn

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

下方展示了 Elasticsearch 支持的关键字列表。spring-doc.cadn.net.cn

表1. 方法名中支持的关键字
关键字 示例 Elasticsearch 查询字符串

Andspring-doc.cadn.net.cn

findByNameAndPricespring-doc.cadn.net.cn

{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } }, { "query_string" : { "query" : "?", "fields" : [ "price" ] } } ] } }}spring-doc.cadn.net.cn

Orspring-doc.cadn.net.cn

findByNameOrPricespring-doc.cadn.net.cn

{ "query" : { "bool" : { "should" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } }, { "query_string" : { "query" : "?", "fields" : [ "price" ] } } ] } }}spring-doc.cadn.net.cn

Isspring-doc.cadn.net.cn

findByNamespring-doc.cadn.net.cn

{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } } ] } }}spring-doc.cadn.net.cn

Notspring-doc.cadn.net.cn

findByNameNotspring-doc.cadn.net.cn

{ "query" : { "bool" : { "must_not" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } } ] } }}spring-doc.cadn.net.cn

Betweenspring-doc.cadn.net.cn

findByPriceBetweenspring-doc.cadn.net.cn

{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : ?, "include_lower" : true, "include_upper" : true } } } ] } }}spring-doc.cadn.net.cn

LessThanspring-doc.cadn.net.cn

findByPriceLessThanspring-doc.cadn.net.cn

{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : false } } } ] } }}spring-doc.cadn.net.cn

LessThanEqualspring-doc.cadn.net.cn

findByPriceLessThanEqualspring-doc.cadn.net.cn

{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : true } } } ] } }}spring-doc.cadn.net.cn

GreaterThanspring-doc.cadn.net.cn

findByPriceGreaterThanspring-doc.cadn.net.cn

{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : false, "include_upper" : true } } } ] } }}spring-doc.cadn.net.cn

GreaterThanEqualspring-doc.cadn.net.cn

findByPriceGreaterThanEqualspring-doc.cadn.net.cn

{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : true, "include_upper" : true } } } ] } }}spring-doc.cadn.net.cn

Beforespring-doc.cadn.net.cn

findByPriceBeforespring-doc.cadn.net.cn

{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : true } } } ] } }}spring-doc.cadn.net.cn

Afterspring-doc.cadn.net.cn

findByPriceAfterspring-doc.cadn.net.cn

{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : true, "include_upper" : true } } } ] } }}spring-doc.cadn.net.cn

Likespring-doc.cadn.net.cn

findByNameLikespring-doc.cadn.net.cn

{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?*", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}spring-doc.cadn.net.cn

StartingWithspring-doc.cadn.net.cn

findByNameStartingWithspring-doc.cadn.net.cn

{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?*", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}spring-doc.cadn.net.cn

EndingWithspring-doc.cadn.net.cn

findByNameEndingWithspring-doc.cadn.net.cn

{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "*?", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}spring-doc.cadn.net.cn

Contains/Containingspring-doc.cadn.net.cn

findByNameContainingspring-doc.cadn.net.cn

{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "*?*", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}spring-doc.cadn.net.cn

In(当标注为 FieldType.Keyword 时)spring-doc.cadn.net.cn

findByNameIn(Collection<String>names)spring-doc.cadn.net.cn

{ "query" : { "bool" : { "must" : [ {"bool" : {"must" : [ {"terms" : {"name" : ["?","?"]}} ] } } ] } }}spring-doc.cadn.net.cn

Inspring-doc.cadn.net.cn

findByNameIn(Collection<String>names)spring-doc.cadn.net.cn

{ "query": {"bool": {"must": [{"query_string":{"query": "\"?\" \"?\"", "fields": ["name"]}}]}}}spring-doc.cadn.net.cn

NotIn(当标注为 FieldType.Keyword 时)spring-doc.cadn.net.cn

findByNameNotIn(Collection<String>names)spring-doc.cadn.net.cn

{ "query" : { "bool" : { "must" : [ {"bool" : {"must_not" : [ {"terms" : {"name" : ["?","?"]}} ] } } ] } }}spring-doc.cadn.net.cn

NotInspring-doc.cadn.net.cn

findByNameNotIn(Collection<String>names)spring-doc.cadn.net.cn

{"query": {"bool": {"must": [{"query_string": {"query": "NOT(\"?\" \"?\")", "fields": ["name"]}}]}}}spring-doc.cadn.net.cn

Truespring-doc.cadn.net.cn

findByAvailableTruespring-doc.cadn.net.cn

{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "true", "fields" : [ "available" ] } } ] } }}spring-doc.cadn.net.cn

Falsespring-doc.cadn.net.cn

findByAvailableFalsespring-doc.cadn.net.cn

{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "false", "fields" : [ "available" ] } } ] } }}spring-doc.cadn.net.cn

OrderByspring-doc.cadn.net.cn

findByAvailableTrueOrderByNameDescspring-doc.cadn.net.cn

{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "true", "fields" : [ "available" ] } } ] } }, "sort":[{"name":{"order":"desc"}}] }spring-doc.cadn.net.cn

Existsspring-doc.cadn.net.cn

findByNameExistsspring-doc.cadn.net.cn

{"query":{"bool":{"must":[{"exists":{"field":"name"}}]}}}spring-doc.cadn.net.cn

IsNullspring-doc.cadn.net.cn

findByNameIsNullspring-doc.cadn.net.cn

{"query":{"bool":{"must_not":[{"exists":{"field":"name"}}]}}}spring-doc.cadn.net.cn

IsNotNullspring-doc.cadn.net.cn

findByNameIsNotNullspring-doc.cadn.net.cn

{"query":{"bool":{"must":[{"exists":{"field":"name"}}]}}}spring-doc.cadn.net.cn

IsEmptyspring-doc.cadn.net.cn

findByNameIsEmptyspring-doc.cadn.net.cn

{"query":{"bool":{"must":[{"bool":{"must":[{"exists":{"field":"name"}}],"must_not":[{"wildcard":{"name":{"wildcard":"*"}}}]}}]}}}spring-doc.cadn.net.cn

IsNotEmptyspring-doc.cadn.net.cn

findByNameIsNotEmptyspring-doc.cadn.net.cn

{"query":{"bool":{"must":[{"wildcard":{"name":{"wildcard":"*"}}}]}}}spring-doc.cadn.net.cn

不支持构建使用 GeoJson 个参数的 Geo-shape 查询的方法名。 如果您需要在仓库中包含此类函数,请在自定义仓库实现中使用带有 CriteriaQueryElasticsearchOperations

方法返回类型

仓库方法可以定义为具有以下返回类型,用于返回多个元素:spring-doc.cadn.net.cn

使用 @Query 注解

示例 2. 使用 @Query 注解在方法上声明查询。

传递给方法的参数可以插入到查询字符串的占位符中。 占位符的形式为 ?0?1?2 等,分别对应第一个、第二个、第三个参数,依此类推。spring-doc.cadn.net.cn

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

设置为注解参数的字符串必须是有效的 Elasticsearch JSON 查询。 它将作为 query 元素的值发送到 Elasticsearch;例如,如果函数使用参数 John 调用,则会生成以下查询体:spring-doc.cadn.net.cn

{
  "query": {
    "match": {
      "name": {
        "query": "John"
      }
    }
  }
}
示例 3. 在接收 Collection 参数的方法上使用 @Query 注解

一个仓库方法,例如spring-doc.cadn.net.cn

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

将执行一个 IDs 查询 以返回所有匹配的文档。 因此,使用 List["id1", "id2", "id3"] 调用该方法将生成查询体spring-doc.cadn.net.cn

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

使用 SpEL 表达式

示例 4. 使用带有 SpEL 表达式的 @Query 注解在方法上声明查询。

SpEL 表达式@Query 中定义查询时也受支持。spring-doc.cadn.net.cn

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

例如,如果使用该参数 John 调用函数,它将生成以下查询主体:spring-doc.cadn.net.cn

{
  "bool":{
    "must":[
      {
        "term":{
          "name": "John"
        }
      }
    ]
  }
}
示例 5. 访问参数属性。

假设我们有以下类作为查询参数类型:spring-doc.cadn.net.cn

public record QueryParameter(String value) {
}

通过 # 符号可以轻松访问参数,然后使用简单的 . 引用属性 valuespring-doc.cadn.net.cn

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

我们现在可以传递 new QueryParameter("John") 作为参数,它将生成与上述相同的查询字符串。spring-doc.cadn.net.cn

示例 6. 访问 Bean 属性。

Bean 属性 也支持访问。 假设存在一个名为 queryParameter、类型为 QueryParameter 的 Bean,我们可以使用符号 @ 而非 # 来访问该 Bean,并且在查询方法中无需声明类型为 QueryParameter 的参数:spring-doc.cadn.net.cn

interface BookRepository extends ElasticsearchRepository<Book, String> {
    @Query("""
            {
              "bool":{
                "must":[
                  {
                    "term":{
                      "name": "#{@queryParameter.value}"
                    }
                  }
                ]
              }
            }
            """)
    Page<Book> findByName(Pageable pageable);
}
示例 7. SpEL 和 Collection 个参数。

Collection 参数也受支持,并且像普通 String 一样易于使用,例如以下 terms 查询:spring-doc.cadn.net.cn

interface BookRepository extends ElasticsearchRepository<Book, String> {
    @Query("""
            {
              "bool":{
                "must":[
                  {
                    "terms":{
                      "name": #{#names}
                    }
                  }
                ]
              }
            }
            """)
    Page<Book> findByName(Collection<String> names, Pageable pageable);
}
声明 Elasticsearch JSON 查询时,集合值不应加引号。

一个包含 names 个像 List.of("name1", "name2") 这样的元素的集合将生成以下 terms 查询:spring-doc.cadn.net.cn

{
  "bool":{
    "must":[
      {
        "terms":{
          "name": ["name1", "name2"]
        }
      }
    ]
  }
}
示例 8:在 Collection 参数中访问属性。

SpEL 集合投影Collection 参数中的值不是普通 String 时非常方便:spring-doc.cadn.net.cn

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

这将把 value 属性的所有值从 QueryParameter 集合中提取为一个新的 Collection,因此效果与上述相同。spring-doc.cadn.net.cn

示例 9. 使用 @Param 更改参数名称

当通过 SpEL 访问参数时,在 Spring Data 中使用 @Param 注解将参数名更改为另一个名称也非常有用:spring-doc.cadn.net.cn

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

使用 @SearchTemplateQuery 注解

当使用 Elasticsearch 搜索模板时(参见 搜索模板支持),可以通过在该方法上添加 @SearchTemplateQuery 注解,指定仓库方法应使用模板。spring-doc.cadn.net.cn

假设存在一个名为"book-by-title"的搜索模板,且该模板需要一个名为"title"的参数,那么可以使用该搜索模板定义如下仓库方法:spring-doc.cadn.net.cn

interface BookRepository extends ElasticsearchRepository<Book, String> {
    @SearchTemplateQuery(id = "book-by-title")
    SearchHits<Book> findByTitle(String title);
}

仓库方法的参数会以键/值对的形式发送到搜索模板中,其中键是参数名称,值则取自方法调用时的实际值。spring-doc.cadn.net.cn