Quarkus 整合 Elasticsearch

1、概览

Quarkus 是一个现代框架,它能让你轻松高效地地构建高性能应用。

本文将带你了解如何在 Quarkus 中整合 Elasticsearch,一个著名的全文搜索引擎和 NoSQL 数据库。

2、依赖和配置

首先,你需要在本地主机上运行 Elasticsearch 实例(推荐用 Docker 的方式)。

然后,在 Quarkus 应用中添加依赖:

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-elasticsearch-rest-client</artifactId>
    <version>${quarkus.version}</version>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-elasticsearch-java-client</artifactId>
    <version>${quarkus.version}</version>
</dependency>

在我们的应用中,可以根据需要选择使用合适的客户端。

接下来,将 Elasticsearch HOST 添加到 application.properties 文件中:

quarkus.elasticsearch.hosts=localhost:9200

现在,我们可以开始在 Quarkus 应用中使用 Elasticsearch 了。ElasticsearchRestClientProducerElasticsearchJavaClientProducer 会自动创建所有必要的 Bean。

3、Elasticsearch 低级 REST 客户端

我们可以使用 Elasticsearch 低级 REST 客户端 将应用与 Elasticsearch 集成。这使得我们可以完全控制序列化和反序列化过程,并允许我们使用 JSON 构建 Elasticsearch 的查询。

创建一个要在应用中索引的文档模型:

public class StoreItem {
    private String id;
    private String name;
    private Long price;
    // Getter / Setter 方法省略
}

在这个文档模型中,有一个id 字段。以及其他的一些用于检索的字段。

现在,实现索引 StoreItem 的方法:

private void indexUsingRestClient() throws IOException, InterruptedException {
    iosPhone = new StoreItem();
    iosPhone.setId(UUID.randomUUID().toString());
    iosPhone.setPrice(1000L);
    iosPhone.setName("IOS smartphone");

    Request restRequest = new Request(
      "PUT",
      "/store-items/_doc/" + iosPhone.getId());
    restRequest.setJsonEntity(JsonObject.mapFrom(iosPhone).toString());
    restClient.performRequest(restRequest);
}

如上,创建了一个具有随机 id 和特定名称的 StoreItem。然后,向 /store-items/_doc/{id} 路径执行一个 PUT 请求,以索引我们的文档。现在,我们要创建一个方法来验证我们的文档是否被成功地索引,并根据不同字段进行搜索:

@Test
void givenRestClient_whenSearchInStoreItemsByName_thenExpectedDocumentsFound() throws Exception {
    indexUsingRestClient();

    Request request = new Request(
      "GET",
      "/store-items/_search");

    JsonObject termJson = new JsonObject().put("name", "IOS smartphone");
    JsonObject matchJson = new JsonObject().put("match", termJson);
    JsonObject queryJson = new JsonObject().put("query", matchJson);
    request.setJsonEntity(queryJson.encode());

    Response response = restClient.performRequest(request);
    String responseBody = EntityUtils.toString(response.getEntity());

    JsonObject json = new JsonObject(responseBody);
    JsonArray hits = json.getJsonObject("hits").getJsonArray("hits");
    List<StoreItem> results = new ArrayList<>(hits.size());

    for (int i = 0; i < hits.size(); i++) {
        JsonObject hit = hits.getJsonObject(i);
        StoreItem fruit = hit.getJsonObject("_source").mapTo(StoreItem.class);
        results.add(fruit);
    }

    assertThat(results)
      .hasSize(1)
      .containsExactlyInAnyOrder(iosPhone);
}

我们使用 indexUsingRestClient() 方法索引了 StoreItem。然后,创建 JSON 查询并执行搜索请求。最后对每个搜索结果进行反序列化,并验证其中是否包含 StoreItem

这样,我们就在 Quarkus 应用中使用底层 REST 客户端实现了与 Elasticsearch 的基本集成。如你所见,我们需要自己处理所有序列化和反序列化过程。

4、Elasticsearch Java 客户端

Elasticsearch Java 客户端是更高级别的客户端。我们可以使用它的 DSL 语法更优雅地创建 Elasticsearch 查询。

使用该客户端创建一个方法来索引 StoreItem

private void indexUsingElasticsearchClient() throws IOException, InterruptedException {
    androidPhone = new StoreItem();
    androidPhone.setId(UUID.randomUUID().toString());
    androidPhone.setPrice(500L);
    androidPhone.setName("Android smartphone");

    IndexRequest<StoreItem> request = IndexRequest.of(
      b -> b.index("store-items")
        .id(androidPhone.getId())
        .document(androidPhone));

    elasticsearchClient.index(request);
}

我们创建了另一个 StoreItem 文档,然后构建了 IndexRequest。最后,调用 Elasticsearch Java 客户端的 index() 方法来执行请求。

现在,检索已保存的项目:

@Test
void givenElasticsearchClient_whenSearchInStoreItemsByName_thenExpectedDocumentsFound() throws Exception {
    indexUsingElasticsearchClient();
    Query query = QueryBuilders.match()
      .field("name")
      .query(FieldValue.of("Android smartphone"))
      .build()
      ._toQuery();

    SearchRequest request = SearchRequest.of(
      b -> b.index("store-items")
        .query(query)
    );
    SearchResponse<StoreItem> searchResponse = elasticsearchClient
      .search(request, StoreItem.class);

    HitsMetadata<StoreItem> hits = searchResponse.hits();
    List<StoreItem> results = hits.hits().stream()
      .map(Hit::source)
      .collect(Collectors.toList());

    assertThat(results)
      .hasSize(1)
      .containsExactlyInAnyOrder(androidPhone);
}

我们使用 indexUsingElasticsearchClient() 方法为新文档编制了索引。然后,创建了 SearchRequest,并用 Elasticsearch Java 客户端执行了该请求,并将所有搜索结果收集到 StoreItem 集合中。

我们使用 DSL 语法创建查询,因此不必操心序列化和反序列化问题。

5、总结

如你所见,在 Quarkus 中整合 Elasticsearch 只需添加相关依赖和几行配置即可。你可以选择使用底层的 REST 客户端或高级的 Java 客户端。如果你想要更多控制权,可以自己定义客户端 Bean。


Ref:https://www.baeldung.com/elasticsearch-quarkus-connect