查询方法
查询查找策略
Elasticsearch 模块支持所有基本查询构建功能,例如字符串查询、原生搜索查询、基于 Criteria 的查询,或者从方法名称派生查询。
声明式查询
从方法名称派生查询并非总是足够,并且/或者可能导致方法名称不可读。在这种情况下,可以使用 `@Query` 注解(参见使用 @Query 注解)。
查询创建
一般来说,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 注解
传递给方法的参数可以插入到查询字符串中的占位符中。占位符的形式为 `?0`、`?1`、`?2` 等,分别代表第一个、第二个、第三个参数,依此类推。
interface BookRepository extends ElasticsearchRepository<Book, String> {
@Query("{\"match\": {\"name\": {\"query\": \"?0\"}}}")
Page<Book> findByName(String name,Pageable pageable);
}
设置为注解参数的字符串必须是有效的 Elasticsearch JSON 查询。它将作为查询元素的值发送到 Elasticsearch;例如,如果使用参数 John 调用该函数,它将生成以下查询体
{
"query": {
"match": {
"name": {
"query": "John"
}
}
}
}
一个仓库方法,例如
@Query("{\"ids\": {\"values\": ?0 }}")
List<SampleEntity> getByIds(Collection<String> ids);
将发起一个 IDs query 来返回所有匹配的文档。因此,使用包含 `["id1", "id2", "id3"]` 的 `List` 调用该方法将生成查询体
{
"query": {
"ids": {
"values": ["id1", "id2", "id3"]
}
}
}
使用 SpEL 表达式
在 `@Query` 中定义查询时,也支持 SpEL 表达式。
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` 参数,并且像普通 `String` 一样易于使用,例如下面的 `terms` 查询
interface BookRepository extends ElasticsearchRepository<Book, String> {
@Query("""
{
"bool":{
"must":[
{
"terms":{
"name": #{#names}
}
}
]
}
}
""")
Page<Book> findByName(Collection<String> names, Pageable pageable);
}
在声明 elasticsearch json 查询时,集合值不应加引号。 |
一个像 `List.of("name1", "name2")` 这样的 `names` 集合将生成以下 terms 查询
{
"bool":{
"must":[
{
"terms":{
"name": ["name1", "name2"]
}
}
]
}
}
当 `Collection` 参数中的值不是纯 `String` 时,使用 SpEL 集合投影 会很方便
interface BookRepository extends ElasticsearchRepository<Book, String> {
@Query("""
{
"bool":{
"must":[
{
"terms":{
"name": #{#parameters.![value]}
}
}
]
}
}
""")
Page<Book> findByName(Collection<QueryParameter> parameters, Pageable pageable);
}
这将从 `QueryParameter` 集合中提取所有 `value` 属性值作为新的 `Collection`,从而产生与上面相同的效果。
通过 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);
}