1. 简介

如果你曾经使用过 Elasticsearch,你一定知道通过其 RESTful 搜索 API 构建查询是一件繁琐且容易出错的事情。

本文我们将介绍 Jest,一个基于 HTTP 的 Elasticsearch Java 客户端。虽然 Elasticsearch 提供了官方的 Java 客户端,但 Jest 提供了更流畅的 API 和更简洁的接口

2. Maven 依赖

首先,我们需要在 POM 中引入 Jest 库

<dependency>
    <groupId>io.searchbox</groupId>
    <artifactId>jest</artifactId>
    <version>6.3.1</version>
</dependency>

Jest 的版本号与 Elasticsearch 主版本保持同步,这有助于确保客户端与服务端的兼容性。

引入 Jest 后,Elasticsearch 相关依赖会自动作为传递依赖被引入。

3. 使用 Jest 客户端

本节将介绍如何使用 Jest 客户端执行常见的 Elasticsearch 操作。

使用 Jest 时,我们通过 JestClientFactory 创建一个 JestClient 实例。⚠️ 这些对象创建代价较高,但线程安全,建议在应用中使用单例模式共享一个实例:

public JestClient jestClient() {
    JestClientFactory factory = new JestClientFactory();
    factory.setHttpClientConfig(
      new HttpClientConfig.Builder("http://localhost:9200")
        .multiThreaded(true)
        .defaultMaxTotalConnectionPerRoute(2)
        .maxTotalConnection(10)
        .build());
    return factory.getObject();
}

这段代码创建了一个连接到本地 Elasticsearch 实例的 Jest 客户端。虽然这是最简单的连接方式,但 Jest 同样支持代理、SSL、认证甚至节点发现

JestClient 接口非常简洁,主要方法是 execute,它接受一个 Action 接口的实现。Jest 提供了多个构建器类用于创建不同类型的 Elasticsearch 操作。

所有 Jest 调用的结果都是 JestResult 实例。可以通过 isSucceeded 方法判断是否成功,失败时可通过 getErrorMessage 获取详细信息:

JestResult jestResult = jestClient.execute(new Delete.Builder("1").index("employees").build());

if (jestResult.isSucceeded()) {
    System.out.println("Success!");
}
else {
    System.out.println("Error: " + jestResult.getErrorMessage());
}

3.1. 管理索引

检查索引是否存在,使用 IndicesExists 操作:

JestResult result = jestClient.execute(new IndicesExists.Builder("employees").build())

创建索引使用 CreateIndex

jestClient.execute(new CreateIndex.Builder("employees").build());

可以自定义索引设置:

Map<String, Object> settings = new HashMap<>();
settings.put("number_of_shards", 11);
settings.put("number_of_replicas", 2);
jestClient.execute(new CreateIndex.Builder("employees").settings(settings).build());

添加或删除别名使用 ModifyAliases

jestClient.execute(new ModifyAliases.Builder(
  new AddAliasMapping.Builder("employees", "e").build()).build());
jestClient.execute(new ModifyAliases.Builder(
  new RemoveAliasMapping.Builder("employees", "e").build()).build());

3.2. 创建文档

使用 Index 操作可以轻松地创建或更新文档。⚠️ Elasticsearch 中的文档本质就是 JSON 数据,Jest 支持多种方式传入 JSON 数据。

假设我们有如下 Employee 文档:

{
    "name": "Michael Pratt",
    "title": "Java Developer",
    "skills": ["java", "spring", "elasticsearch"],
    "yearsOfService": 2
}

我们可以使用字符串、Map 或 POJO 来表示该文档:

✅ 使用 Jackson 构建 JSON 字符串:

ObjectMapper mapper = new ObjectMapper();
JsonNode employeeJsonNode = mapper.createObjectNode()
  .put("name", "Michael Pratt")
  .put("title", "Java Developer")
  .put("yearsOfService", 2)
  .set("skills", mapper.createArrayNode()
    .add("java")
    .add("spring")
    .add("elasticsearch"));
jestClient.execute(new Index.Builder(employeeJsonNode.toString()).index("employees").build());

✅ 使用 Map:

Map<String, Object> employeeHashMap = new LinkedHashMap<>();
employeeHashMap.put("name", "Michael Pratt");
employeeHashMap.put("title", "Java Developer");
employeeHashMap.put("yearsOfService", 2);
employeeHashMap.put("skills", Arrays.asList("java", "spring", "elasticsearch"));
jestClient.execute(new Index.Builder(employeeHashMap).index("employees").build());

✅ 使用 POJO:

Employee employee = new Employee();
employee.setName("Michael Pratt");
employee.setTitle("Java Developer");
employee.setYearsOfService(2);
employee.setSkills(Arrays.asList("java", "spring", "elasticsearch"));
jestClient.execute(new Index.Builder(employee).index("employees").build());

3.3. 读取文档

有两种主要方式读取文档:

  1. 通过文档 ID 使用 Get 操作直接读取
Employee getResult = jestClient.execute(new Get.Builder("employees", "1").build())
    .getSourceAsObject(Employee.class);
  1. 使用 Search 操作进行搜索

⚠️ Jest 支持完整的 Elasticsearch 查询 DSL,查询同样是通过 JSON 文档表达:

String search = "{" +
  "  \"query\": {" +
  "    \"bool\": {" +
  "      \"must\": [" +
  "        { \"match\": { \"name\":   \"Michael Pratt\" }}" +
  "      ]" +
  "    }" +
  "  }" +
  "}";
jestClient.execute(new Search.Builder(search).build());

获取搜索结果时,可以使用 getSource 方法,也可以使用 Hit 类获取更多元信息:

List<SearchResult.Hit<Employee, Void>> searchResults = 
  jestClient.execute(new Search.Builder(search).build())
    .getHits(Employee.class);
searchResults.forEach(hit -> {
    System.out.println(String.format("Document %s has score %s", hit.id, hit.score));
});

3.4. 更新文档

使用 Update 操作更新文档:

employee.setYearOfService(3);
jestClient.execute(new Update.Builder(employee).index("employees").id("1").build());

支持与 Index 操作相同的 JSON 表达方式。

3.5. 删除文档

使用 Delete 操作删除文档:

jestClient.execute(new Delete.Builder("17")
  .index("employees")
  .build());

4. 批量操作

Jest 支持批量操作(Bulk),可以将多个操作合并为一次请求发送,节省时间和带宽:

jestClient.execute(new Bulk.Builder()
  .defaultIndex("employees")
  .addAction(new Index.Builder(employeeObject1).build())
  .addAction(new Index.Builder(employeeObject2).build())
  .addAction(new Delete.Builder("17").build())
  .build());

5. 异步操作

Jest 支持异步操作,可以使用非阻塞 I/O 执行上述所有操作。

调用异步方法需使用 executeAsync 并传入一个 JestResultHandler

jestClient.executeAsync(
  new Index.Builder(employeeObject1).build(),
  new JestResultHandler<JestResult>() {
      @Override public void completed(JestResult result) {
          // handle result
      }
      @Override public void failed(Exception ex) {
          // handle exception
      }
  });

6. 总结

本文简要介绍了 Jest,一个用于 Elasticsearch 的 RESTful Java 客户端。

虽然我们只展示了其部分功能,但很明显 Jest 是一个功能强大的客户端。✅ 其流畅的构建器类和 RESTful 接口使其易于上手,同时对 Elasticsearch 接口的完整支持也使其成为原生客户端的一个有力替代方案。

所有示例代码都可以在 GitHub 上找到。


原始标题:Jest - Elasticsearch Java Client