Elasticsearch 操作

Spring Data Elasticsearch 使用多个接口来定义可以对 Elasticsearch 索引调用的操作(有关响应式接口的描述,请参阅 响应式 Elasticsearch 操作)。

这些接口对应于 Elasticsearch API 的结构。

接口的默认实现提供了

  • 索引管理功能。

  • 领域类型的读/写映射支持。

  • 丰富的查询和 Criteria API。

  • 资源管理和异常转换。

索引管理以及索引和映射的自动创建。

IndexOperations 接口及其提供的实现(可以从 ElasticsearchOperations 实例获取,例如通过调用 operations.indexOps(clazz))使用户能够创建索引、设置映射或在 Elasticsearch 集群中存储模板和别名信息。可以通过使用 @Setting 注解设置将要创建的索引的详细信息,更多信息请参阅 索引设置

IndexOperationsElasticsearchOperations 的实现不会自动执行这些操作。调用这些方法是用户的责任。

使用 Spring Data Elasticsearch Repository 时,支持自动创建索引和写入映射,请参阅 使用对应映射自动创建索引

使用示例

此示例展示了如何在 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;
  }
}
1 让 Spring 在构造函数中注入提供的 ElasticsearchOperations Bean。
2 在 Elasticsearch 集群中存储一些实体。ID 从返回的实体中读取,因为它在 person 对象中可能为 null,并由 Elasticsearch 创建。
3 通过 ID 获取实体。

要查看 ElasticsearchOperations 的全部可能性,请参阅 API 文档。

搜索结果类型

使用 DocumentOperations 接口的方法检索文档时,只会返回找到的实体。使用 SearchOperations 接口的方法进行搜索时,每个实体都有额外的信息可用,例如找到的实体的 scoresortValues

为了返回这些信息,每个实体都被包装在一个包含特定于实体的额外信息的 SearchHit 对象中。这些 SearchHit 对象本身在 SearchHits 对象中返回,该对象还包含关于整个搜索的信息,例如 maxScore、请求的聚合或完成请求所花费的执行时间。现在提供以下类和接口

SearchHit<T>

包含以下信息

  • ID

  • 评分

  • 排序值

  • 高亮字段

  • Inner hits(这是一个嵌入的 SearchHits 对象,包含最终返回的 inner hits)

  • 检索到的类型为 <T> 的实体

SearchHits<T>

包含以下信息

  • 总命中数

  • 总命中数关系

  • 最大评分

  • 一个 SearchHit<T> 对象列表

  • 返回的聚合结果

  • 返回的建议结果

SearchPage<T>

定义了一个包含 SearchHits<T> 元素的 Spring Data Page,可用于使用 repository 方法进行分页访问。

SearchScrollHits<T>

ElasticsearchRestTemplate 中的低级 scroll API 函数返回,它用 Elasticsearch scroll id 丰富了 SearchHits<T>

SearchHitsIterator<T>

SearchOperations 接口的流式函数返回的 Iterator。

ReactiveSearchHits

ReactiveSearchOperations 有返回 Mono<ReactiveSearchHits<T>> 的方法,这包含与 SearchHits<T> 对象相同的信息,但会将包含的 SearchHit<T> 对象作为 Flux<SearchHit<T>> 提供,而不是作为列表。

查询

SearchOperationsReactiveSearchOperations 接口中定义的大多数方法都接受一个 Query 参数,该参数定义要执行的搜索查询。Query 是一个接口,Spring Data Elasticsearch 提供了三种实现:CriteriaQueryStringQueryNativeQuery

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);
1 第一个 Criteria
2 the and() 创建一个新的 Criteria 并将其链式连接到第一个。

如果要创建嵌套查询,需要使用子查询。假设我们要查找所有姓氏为 Miller 且名字为 JackJohn 的人员

示例 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);
1 为姓氏创建一个第一个 Criteria
2 这与 AND 组合成一个子 Criteria
3 这个子 Criteria 是名字 John 的 OR 组合
4 以及名字 Jack

有关不同可用操作的完整概述,请参阅 Criteria 类的 API 文档。

StringQuery

此类接受一个 Elasticsearch 查询作为 JSON 字符串。以下代码展示了一个搜索名字为 "Jack" 的人员的查询

Query query = new StringQuery("{ \"match\": { \"firstname\": { \"query\": \"Jack\" } } } ");
SearchHits<Person> searchHits = operations.search(query, Person.class);

如果您已经有一个要使用的 Elasticsearch 查询,使用 StringQuery 可能很合适。

NativeQuery

当您有复杂的查询,或者无法使用 Criteria API 表达的查询时(例如构建查询和使用聚合时),NativeQuery 是要使用的类。它允许使用 Elasticsearch 库中所有不同的 co.elastic.clients.elasticsearch._types.query_dsl.Query 实现,因此命名为 "native"。

以下代码展示了如何搜索具有给定 firstName 的人员,以及如何对找到的文档执行 terms 聚合,以计算这些人员的 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 接口的一种特殊实现,用于与存储的搜索模板结合使用。更多信息请参阅 搜索模板支持