查询方法
查询查找策略
Elasticsearch 模块支持所有基本的查询构建功能,包括字符串查询、原生搜索查询、基于条件的查询,或从方法名派生的查询。
声明的查询
仅从方法名推导查询并不总是足够的,或者可能导致方法名难以阅读。
在这种情况下,可以使用 @Query 注解(参见 使用 @Query 注解)。
另一种可能性是使用搜索模板(参见 使用 @SearchTemplateQuery 注解)。
查询创建
通常,Elasticsearch 的查询创建机制如 定义查询方法 中所述。 以下是一个简短示例,展示 Elasticsearch 查询方法的转换结果:
interface BookRepository extends Repository<Book, String> {
List<Book> findByNameAndPrice(String name, Integer price);
}
上述方法名将被转换为以下 Elasticsearch JSON 查询
{
"query": {
"bool" : {
"must" : [
{ "query_string" : { "query" : "?", "fields" : [ "name" ] } },
{ "query_string" : { "query" : "?", "fields" : [ "price" ] } }
]
}
}
}
下方展示了 Elasticsearch 支持的关键字列表。
| 关键字 | 示例 | Elasticsearch 查询字符串 |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
不支持构建使用 GeoJson 个参数的 Geo-shape 查询的方法名。
如果您需要在仓库中包含此类函数,请在自定义仓库实现中使用带有 CriteriaQuery 的 ElasticsearchOperations。 |
方法返回类型
仓库方法可以定义为具有以下返回类型,用于返回多个元素:
-
List<T> -
Stream<T> -
SearchHits<T> -
List<SearchHit<T>> -
Stream<SearchHit<T>> -
SearchPage<T>
使用 @Query 注解
@Query 注解在方法上声明查询。传递给方法的参数可以插入到查询字符串的占位符中。
占位符的形式为 ?0、?1、?2 等,分别对应第一个、第二个、第三个参数,依此类推。
interface BookRepository extends ElasticsearchRepository<Book, String> {
@Query("{\"match\": {\"name\": {\"query\": \"?0\"}}}")
Page<Book> findByName(String name,Pageable pageable);
}
设置为注解参数的字符串必须是有效的 Elasticsearch JSON 查询。 它将作为 query 元素的值发送到 Elasticsearch;例如,如果函数使用参数 John 调用,则会生成以下查询体:
{
"query": {
"match": {
"name": {
"query": "John"
}
}
}
}
@Query 注解一个仓库方法,例如
@Query("{\"ids\": {\"values\": ?0 }}")
List<SampleEntity> getByIds(Collection<String> ids);
将执行一个 IDs 查询 以返回所有匹配的文档。
因此,使用 List 为 ["id1", "id2", "id3"] 调用该方法将生成查询体
{
"query": {
"ids": {
"values": ["id1", "id2", "id3"]
}
}
}
使用 SpEL 表达式
@Query 注解在方法上声明查询。SpEL 表达式 在 @Query 中定义查询时也受支持。
interface BookRepository extends ElasticsearchRepository<Book, String> {
@Query("""
{
"bool":{
"must":[
{
"term":{
"name": "#{#name}"
}
}
]
}
}
""")
Page<Book> findByName(String name, Pageable pageable);
}
例如,如果使用该参数 John 调用函数,它将生成以下查询主体:
{
"bool":{
"must":[
{
"term":{
"name": "John"
}
}
]
}
}
假设我们有以下类作为查询参数类型:
public record QueryParameter(String value) {
}
通过 # 符号可以轻松访问参数,然后使用简单的 . 引用属性 value:
interface BookRepository extends ElasticsearchRepository<Book, String> {
@Query("""
{
"bool":{
"must":[
{
"term":{
"name": "#{#parameter.value}"
}
}
]
}
}
""")
Page<Book> findByName(QueryParameter parameter, Pageable pageable);
}
我们现在可以传递 new QueryParameter("John") 作为参数,它将生成与上述相同的查询字符串。
Bean 属性 也支持访问。
假设存在一个名为 queryParameter、类型为 QueryParameter 的 Bean,我们可以使用符号 @ 而非 # 来访问该 Bean,并且在查询方法中无需声明类型为 QueryParameter 的参数:
interface BookRepository extends ElasticsearchRepository<Book, String> {
@Query("""
{
"bool":{
"must":[
{
"term":{
"name": "#{@queryParameter.value}"
}
}
]
}
}
""")
Page<Book> findByName(Pageable pageable);
}
Collection 个参数。Collection 参数也受支持,并且像普通 String 一样易于使用,例如以下 terms 查询:
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 查询:
{
"bool":{
"must":[
{
"terms":{
"name": ["name1", "name2"]
}
}
]
}
}
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);
}
这将把 value 属性的所有值从 QueryParameter 集合中提取为一个新的 Collection,因此效果与上述相同。
@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);
}
使用 @SearchTemplateQuery 注解
当使用 Elasticsearch 搜索模板时(参见 搜索模板支持),可以通过在该方法上添加 @SearchTemplateQuery 注解,指定仓库方法应使用模板。
假设存在一个名为"book-by-title"的搜索模板,且该模板需要一个名为"title"的参数,那么可以使用该搜索模板定义如下仓库方法:
interface BookRepository extends ElasticsearchRepository<Book, String> {
@SearchTemplateQuery(id = "book-by-title")
SearchHits<Book> findByTitle(String title);
}
仓库方法的参数会以键/值对的形式发送到搜索模板中,其中键是参数名称,值则取自方法调用时的实际值。