跳到主要内容

查询方法

DeepSeek V3 中英对照 Query methods

查询查找策略

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

声明查询

从方法名称派生查询并不总是足够的,并且/或者可能导致方法名称难以阅读。在这种情况下,可以使用 @Query 注解(参见 使用 @Query 注解)。

查询创建

通常,Elasticsearch 的查询创建机制如定义查询方法中所述。以下是一个简短的示例,展示了 Elasticsearch 查询方法如何转换为实际查询:

示例 1. 从方法名称创建查询

interface BookRepository extends Repository<Book, String> {
List<Book> findByNameAndPrice(String name, Integer price);
}
java

上面的方法名称将被翻译为以下 Elasticsearch JSON 查询

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

以下是 Elasticsearch 支持的关键字列表。

表 1. 方法名称中支持的关键字

关键词示例Elasticsearch 查询字符串
AndfindByNameAndPrice{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } }, { "query_string" : { "query" : "?", "fields" : [ "price" ] } } ] } }}
OrfindByNameOrPrice{ "query" : { "bool" : { "should" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } }, { "query_string" : { "query" : "?", "fields" : [ "price" ] } } ] } }}
IsfindByName{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } } ] } }}
NotfindByNameNot{ "query" : { "bool" : { "must_not" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } } ] } }}
BetweenfindByPriceBetween{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : ?, "include_lower" : true, "include_upper" : true } } } ] } }}
LessThanfindByPriceLessThan{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : false } } } ] } }}
LessThanEqualfindByPriceLessThanEqual{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : true } } } ] } }}
GreaterThanfindByPriceGreaterThan{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : false, "include_upper" : true } } } ] } }}
GreaterThanEqualfindByPriceGreaterThanEqual{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : true, "include_upper" : true } } } ] } }}
BeforefindByPriceBefore{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : true } } } ] } }}
AfterfindByPriceAfter{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : true, "include_upper" : true } } } ] } }}
LikefindByNameLike{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?*", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}
StartingWithfindByNameStartingWith{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?*", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}
EndingWithfindByNameEndingWith{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "*?", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}
Contains/ContainingfindByNameContaining{ "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" : ["?","?"]}} ] } } ] } }}
InfindByNameIn(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" : ["?","?"]}} ] } } ] } }}
NotInfindByNameNotIn(Collection<String>names){"query": {"bool": {"must": [{"query_string": {"query": "NOT(\"?\" \"?\")", "fields": ["name"]}}]}}}
TruefindByAvailableTrue{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "true", "fields" : [ "available" ] } } ] } }}
FalsefindByAvailableFalse{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "false", "fields" : [ "available" ] } } ] } }}
OrderByfindByAvailableTrueOrderByNameDesc{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "true", "fields" : [ "available" ] } } ] } }, "sort":[{"name":{"order":"desc"}}] }
ExistsfindByNameExists{"query":{"bool":{"must":[{"exists":{"field":"name"}}]}}}
IsNullfindByNameIsNull{"query":{"bool":{"must_not":[{"exists":{"field":"name"}}]}}}
IsNotNullfindByNameIsNotNull{"query":{"bool":{"must":[{"exists":{"field":"name"}}]}}}
IsEmptyfindByNameIsEmpty{"query":{"bool":{"must":[{"bool":{"must":[{"exists":{"field":"name"}}],"must_not":[{"wildcard":{"name":{"wildcard":"*"}}}]}}]}}}
IsNotEmptyfindByNameIsNotEmpty{"query":{"bool":{"must":[{"wildcard":{"name":{"wildcard":"*"}}}]}}}
备注

不支持使用 GeoJson 参数构建地理形状查询的方法名称。如果你需要在仓库中实现此类功能,请在自定义仓库实现中使用 ElasticsearchOperationsCriteriaQuery

方法返回类型

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

  • List<T> - 列表

  • Stream<T> - 流

  • SearchHits<T> - 搜索结果集

  • List<SearchHit<T>> - 搜索结果列表

  • Stream<SearchHit<T>> - 搜索结果流

  • SearchPage<T> - 搜索页

使用 @Query 注解

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

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

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

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

{
"query": {
"match": {
"name": {
"query": "John"
}
}
}
}
json

示例 3. 在方法上使用 @Query 注解,该方法接受一个 Collection 参数

一个仓库方法,例如

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

将会执行一个 IDs 查询,以返回所有匹配的文档。因此,使用包含 ["id1", "id2", "id3"]List 调用该方法将会生成以下查询体

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

使用 SpEL 表达式

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

@Query 中定义查询时,也支持 SpEL 表达式

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

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

{
"bool":{
"must":[
{
"term":{
"name": "John"
}
}
]
}
}
json

示例 5. 访问参数属性。

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

public record QueryParameter(String value) {
}
java

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

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

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

示例 6. 访问 bean 属性。

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

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

示例 7. SpEL 和 Collection 参数。

Collection 参数同样被支持,并且与普通的 String 一样易于使用,例如下面的 terms 查询:

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

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

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

{
"bool":{
"must":[
{
"terms":{
"name": ["name1", "name2"]
}
}
]
}
}
json

示例 8. 访问 Collection 参数中的属性。

SpEL 集合投影Collection 参数中的值不是简单的 String 时非常方便使用:

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

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

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

在使用 SpEL 访问参数时,通过 Spring Data 中的 @Param 注解将参数名称更改为另一个名称也非常有用:

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