Elasticsearch 操作
Spring Data Elasticsearch 使用多个接口来定义可以针对 Elasticsearch 索引调用的操作(有关反应式接口的描述,请参见 Reactive Elasticsearch Operations)。
-
IndexOperations 定义了在索引级别上的操作,例如创建或删除索引。
-
DocumentOperations 定义了基于实体 ID 存储、更新和检索实体的操作。
-
SearchOperations 定义了使用查询搜索多个实体的操作。
-
ElasticsearchOperations 结合了
DocumentOperations
和SearchOperations
接口。
这些接口对应于 Elasticsearch API 的结构化设计。
接口的默认实现提供了:
-
索引管理功能。
-
针对领域类型的读写映射支持。
-
丰富的查询和条件 API。
-
资源管理和异常转换。
索引管理以及索引和映射的自动创建。
IndexOperations
接口及其提供的实现可以通过 ElasticsearchOperations
实例获得,例如通过调用 operations.indexOps(clazz)
。这些接口和实现使用户能够在 Elasticsearch 集群中创建索引、设置映射或存储模板和别名信息。可以通过使用 @Setting
注解来设置将要创建的索引的详细信息,更多信息请参考 索引设置。
这些操作都不会由 IndexOperations
或 ElasticsearchOperations
的实现自动完成。用户需要自行调用相关方法。
在使用 Spring Data Elasticsearch 存储库时,支持自动创建索引并写入映射,详情请参阅 自动创建索引及相应的映射。
使用示例
该示例展示了如何在 Spring REST 控制器中使用注入的 ElasticsearchOperations
实例。示例假设 Person
是一个用 @Document
、@Id
等注解标注的类(参见 映射注解概述)。
示例 1. ElasticsearchOperations 使用
@RestController
@RequestMapping("/")
public class TestController {
private ElasticsearchOperations elasticsearchOperations;
public TestController(ElasticsearchOperations elasticsearchOperations) { 1
this.elasticsearchOperations = elasticsearchOperations;
}
@PostMapping("/person")
public String save(@RequestBody Person person) { 2
Person savedEntity = elasticsearchOperations.save(person);
return savedEntity.getId();
}
@GetMapping("/person/{id}")
public Person findById(@PathVariable("id") Long id) { 3
Person person = elasticsearchOperations.get(id.toString(), Person.class);
return person;
}
}
让 Spring 在构造函数中注入提供的
ElasticsearchOperations
bean。在 Elasticsearch 集群中存储一些实体。id 是从返回的实体中读取的,因为它在
person
对象中可能为 null,并且是由 Elasticsearch 生成的。通过 id 获取实体。
要查看 ElasticsearchOperations
的全部功能,请参考 API 文档。
搜索结果类型
当使用 DocumentOperations
接口的方法检索文档时,只会返回找到的实体。当使用 SearchOperations
接口的方法进行搜索时,每个实体都会附带额外的信息,例如找到实体的 score 或 sortValues。
为了返回这些信息,每个实体都被包裹在一个 SearchHit
对象中,该对象包含了特定于该实体的附加信息。这些 SearchHit
对象本身则被包含在一个 SearchHits
对象中返回,SearchHits
对象还包含了关于整个搜索的额外信息,例如 maxScore、请求的聚合或完成请求所需的执行时间。以下类和接口现在可用:
SearchHit\<T>
SearchHit\<T>
是一个泛型类型,表示搜索结果中的一个命中项。它通常用于搜索或查询操作中,返回与查询条件匹配的数据项。T
是表示命中项数据类型的泛型参数。
包含以下信息:
-
ID
-
分数
-
排序值
-
高亮字段
-
内部命中(这是一个嵌入的
SearchHits
对象,包含最终返回的内部命中) -
检索到的类型为 <T> 的实体
SearchHits<T>
包含以下信息:
-
总命中数
-
总命中关系
-
最高得分
-
SearchHit<T>
对象列表 -
返回的聚合结果
-
返回的建议结果
SearchPage<T>
定义一个包含 SearchHits<T>
元素的 Spring Data Page
,可用于通过仓库方法进行分页访问。
SearchScrollHits<T>
由 ElasticsearchRestTemplate
中的低级滚动 API 函数返回,它通过 Elasticsearch 滚动 ID 来丰富 SearchHits<T>
。
SearchHitsIterator<T>
由 SearchOperations
接口的流式函数返回的迭代器。
ReactiveSearchHits
ReactiveSearchOperations
提供了一些方法,这些方法返回一个 Mono<ReactiveSearchHits<T>>
,它包含与 SearchHits<T>
对象相同的信息,但会将包含的 SearchHit<T>
对象作为 Flux<SearchHit<T>>
提供,而不是作为列表提供。
查询
SearchOperations
和 ReactiveSearchOperations
接口中定义的几乎所有方法都接受一个 Query
参数,该参数定义了要执行的搜索查询。Query
是一个接口,Spring Data Elasticsearch 提供了三种实现:CriteriaQuery
、StringQuery
和 NativeQuery
。
CriteriaQuery
基于 CriteriaQuery
的查询允许用户在不知道 Elasticsearch 查询语法或基础知识的情况下创建查询。用户可以通过简单地链式和组合 Criteria
对象来构建查询,这些对象指定了所搜索文档必须满足的条件。
在讨论组合条件时的 AND 或 OR 时,请记住,在 Elasticsearch 中,AND 被转换为 must 条件,而 OR 被转换为 should。
Criteria
及其用法最好通过示例来解释(假设我们有一个带有 price
属性的 Book
实体):
示例 2. 获取给定价格的书籍
Criteria criteria = new Criteria("price").is(42.0);
Query query = new CriteriaQuery(criteria);
同一字段的条件可以链式连接,它们将通过逻辑 AND 组合:
示例 3. 获取给定价格的书籍
Criteria criteria = new Criteria("price").greaterThan(42.0).lessThan(34.0);
Query query = new CriteriaQuery(criteria);
在链式调用 Criteria
时,默认使用 AND 逻辑:
示例 4. 获取所有名字为 James 且姓氏为 Miller 的人员:
Criteria criteria = new Criteria("lastname").is("Miller") 1
.and("firstname").is("James") 2
Query query = new CriteriaQuery(criteria);
第一个
Criteria
and()
创建一个新的Criteria
并将其与第一个链接起来。
如果你想创建嵌套查询,你需要使用子查询来实现。假设我们想要查找所有姓氏为 Miller 且名字为 Jack 或 John 的人:
示例 5. 嵌套子查询
Criteria miller = new Criteria("lastName").is("Miller") 1
.subCriteria( 2
new Criteria().or("firstName").is("John") 3
.or("firstName").is("Jack") 4
);
Query query = new CriteriaQuery(criteria);
为姓氏创建第一个
Criteria
这个条件与一个子条件通过 AND 组合
这个子条件是名字为 John 的 OR 组合
和名字为 Jack 的 OR 组合
请参阅 Criteria
类的 API 文档,以全面了解各种可用的操作。
字符串查询
这个类接受一个 Elasticsearch 查询作为 JSON 字符串。以下代码展示了一个查询,用于搜索名字为 "Jack" 的人员:
{
"query": {
"match": {
"firstName": "Jack"
}
}
}
Query query = new StringQuery("{ \"match\": { \"firstname\": { \"query\": \"Jack\" } } } ");
SearchHits<Person> searchHits = operations.search(query, Person.class);
如果你已经有一个 Elasticsearch 查询可以使用,那么使用 StringQuery
可能是合适的。
NativeQuery
NativeQuery
是用于处理复杂查询或无法通过 Criteria
API 表达的查询的类,例如在构建查询和使用聚合时。它允许使用 Elasticsearch 库中的所有不同的 co.elastic.clients.elasticsearch._types.query_dsl.Query
实现,因此被称为“原生”。
以下代码展示了如何搜索具有给定 firstName
的人员,并对找到的文档进行 lastName
的术语聚合,以统计这些人员中 lastName
的出现次数:
Query query = NativeQuery.builder()
.withAggregation("lastNames", Aggregation.of(a -> a
.terms(ta -> ta.field("lastName").size(10))))
.withQuery(q -> q
.match(m -> m
.field("firstName")
.query(firstName)
)
)
.withPageable(pageable)
.build();
SearchHits<Person> searchHits = operations.search(query, Person.class);
SearchTemplateQuery
这是 Query
接口的一个特殊实现,用于与存储的搜索模板结合使用。更多信息请参见搜索模板支持。