使用Java对Elasticsearch执行相关操作时,需要保证Java引入的依赖需要与Elasticsearch版本对应。在Elasticsearch7.15版本后,RestHightLevelClient已废弃,需要使用Elasticsearch Java API Client替代。以下操作基于Elasticsearch7.17
一、引入依赖
在引入elasticsearch-java依赖时,需要检查elasticsearch-rest-client依赖版本是否与Elasticsearch版本一致,如果不是,需要排除该依赖,并引入正确的版本,否则会报方法找不到等异常
xml
<!-- elasticsearch -->
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>7.17.17</version>
<exclusions>
<exclusion>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>7.17.17</version>
</dependency>
<dependency>
<groupId>jakarta.json</groupId>
<artifactId>jakarta.json-api</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.6.3</version>
</dependency>
二、配置Java API Client
在application.yml文件中添加如下配置
yaml
elasticsearch:
host: localhost
port: 9200
使用Java代码配置Java API Client
java
@Data
@Configuration
@ConfigurationProperties(prefix = "elasticsearch")
public class ElasticsearchConfig {
private String host;
private int port;
/**
* 执行Elasticsearch操作时,以同步的方式等待响应
* @return
*/
@Bean
public ElasticsearchClient elasticsearchClient() {
RestClient restClient = RestClient.builder(new HttpHost(host, port)).build();
ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
return new ElasticsearchClient(transport);
}
/**
* 执行Elasticsearch操作时,以异步的方式等待响应
* @return
*/
@Bean
public ElasticsearchAsyncClient elasticsearchAsyncClient() {
RestClient restClient = RestClient.builder(new HttpHost(host, port)).build();
ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
return new ElasticsearchAsyncClient(transport);
}
}
三、使用Java API Clent执行Elasticsearch操作
1、索引相关操作
java
@Autowired
private ElasticsearchClient elasticsearchClient;
// 创建索引
@Test
void create_index_test() throws IOException {
CreateIndexRequest request = CreateIndexRequest.of(builder -> builder.index("user"));
CreateIndexResponse createIndexResponse = elasticsearchClient.indices().create(request);
log.info("acknowledged: {}", createIndexResponse.acknowledged());
log.info("index: {}", createIndexResponse.index());
log.info("shardsAcknowledged: {}", createIndexResponse.shardsAcknowledged());
}
// 获取索引信息
@Test
void get_index_test() throws IOException {
List<String> indexName = new ArrayList<>();
indexName.add("user");
GetIndexRequest request = GetIndexRequest.of(builder -> builder.index(indexName));
GetIndexResponse getIndexResponse = elasticsearchClient.indices().get(request);
Map<String, IndexState> result =
getIndexResponse.result();
result.entrySet().forEach(entry -> {
log.info("key: {}, value: {}", entry.getKey(), entry.getValue());
});
}
// 判断索引是否存在
@Test
void exist_index_test() throws IOException {
List<String> indexName = new ArrayList<>();
indexName.add("user");
ExistsRequest request = ExistsRequest.of(builder -> builder.index(indexName));
BooleanResponse response = elasticsearchClient.indices().exists(request);
log.info("exist: {}", response.value());
}
// 删除索引
@Test
void delete_index_test() throws IOException {
DeleteIndexRequest request = DeleteIndexRequest.of(builder -> builder.index("user"));
DeleteIndexResponse response = elasticsearchClient.indices().delete(request);
log.info("delete success: {}", response.acknowledged());
}
2、映射相关操作
java
@Autowired
private ElasticsearchClient elasticsearchClient;
// 设置索引映射,前提需要创建该索引
@Test
void mapping_setting_test() throws IOException {
Map<String, Property> properties = new HashMap<>();
Property name = Property.of(builder -> builder.text(TextProperty.of(pro -> pro.index(true))));
Property age = Property.of(builder -> builder.integer(IntegerNumberProperty.of(pro -> pro.index(true))));
properties.put("name", name);
properties.put("age", age);
PutMappingRequest request = PutMappingRequest.of(builder -> builder.index("user").properties(properties));
PutMappingResponse putMappingResponse = elasticsearchClient.indices().putMapping(request);
log.info("add mapping success: {}", putMappingResponse.acknowledged());
}
// 获取索引映射配置
@Test
void mapping_get_test() throws IOException {
GetMappingRequest request = GetMappingRequest.of(builder -> builder.index("user"));
GetMappingResponse response = elasticsearchClient.indices().getMapping(request);
Map<String, IndexMappingRecord> result = response.result();
log.info("user mapping message: {}", result);
}
3、文档相关操作
java
@Autowired
private ElasticsearchClient elasticsearchClient;
// 添加文档
@Test
void document_put_test() throws IOException {
User user = new User();
user.setName("zhangsang");
user.setAge(18);
CreateRequest request = CreateRequest.of(builder -> builder.index("user").id("123456").document(user));
CreateResponse createResponse = elasticsearchClient.create(request);
log.info("{}", createResponse.result().jsonValue());
}
// 获取文档
@Test
void document_get_test() throws IOException {
GetRequest request = GetRequest.of(builder -> builder.index("user").id("123456"));
GetResponse<User> userGetResponse = elasticsearchClient.get(request, User.class);
User user = userGetResponse.source();
log.info("query result: {}", user);
}
// 更新文档
@Test
void document_update_test() throws IOException {
User user = new User();
user.setAge(20);
UpdateRequest<Object, Object> request = UpdateRequest.of(builder ->
builder.index("user").id("123456").doc(user));
UpdateResponse<Object> response = elasticsearchClient.update(request, User.class);
log.info("update result: {}", response);
}
// 删除文档
@Test
void document_delete_test() throws IOException {
DeleteRequest request = DeleteRequest.of(builder -> builder.index("user").id("123456"));
DeleteResponse response = elasticsearchClient.delete(request);
log.info("delete result: {}", response);
}
// 判断文档是否存在
@Test
void document_exist_test() throws IOException {
ExistsRequest request = ExistsRequest.of(builder -> builder.index("user").id("123456"));
BooleanResponse response = elasticsearchClient.exists(request);
log.info("exist result: {}", response.value());
}
// 通过索引中的name字段获取文档
@Test
void document_query_byName_test() throws IOException {
SearchRequest request = SearchRequest.of(builder ->
builder.index("user")
.query(query -> query.match(match ->
match.field("name").query("zhansang"))));
SearchResponse<User> response = elasticsearchClient.search(request, User.class);
log.info("search result: {}", response);
}
四、Elasticsearch Java Client分析
使用Elasticsearch Java Client执行一些数据操作时,需要使用ElasticsearchClient 或ElasticsearchAsyncClient发送Http请求,如果熟悉使用Http请求执行Elasticsearch数据操作,那么很容易上手
上图为ElasticsearchClient的部分方法,观察以上方法,基本每一个同名的方法都会有两种参数,分别是xxxRequest和Function两种类型。
xxxRequest都是继承RequestBase类,如果需要查看有多少种请求类型,可以查看RquestBase的子类。以CreateRequest类为例简单分析使用ElasticsearchClient执行数据操作的基本思路。
在CreateRequest类中,只用一个构建方法。
java
private CreateRequest(CreateRequest.Builder<TDocument> builder) {
this.id = (String)ApiTypeHelper.requireNonNull(builder.id, this, "id");
this.index = (String)ApiTypeHelper.requireNonNull(builder.index, this, "index");
this.pipeline = builder.pipeline;
this.refresh = builder.refresh;
this.routing = builder.routing;
this.timeout = builder.timeout;
this.type = builder.type;
this.version = builder.version;
this.versionType = builder.versionType;
this.waitForActiveShards = builder.waitForActiveShards;
this.document = ApiTypeHelper.requireNonNull(builder.document, this, "document");
this.tDocumentSerializer = builder.tDocumentSerializer;
}
但是该构建方法为私有方法,不能使用该私有方法构造对象。通过仔细查找后,发现该类有一个静态方法可以构建该类对象,入参为 Function<CreateRequest.Builder<TDocument>, ObjectBuilder<CreateRequest<TDocument>>>
类型,也就可以说传入一个CreateRequest.Builder<TDocument>
类型并输出ObjectBuilder<CreateRequest<TDocument>>>
类型的lambda表达式
java
public static <TDocument> CreateRequest<TDocument> of(Function<CreateRequest.Builder<TDocument>, ObjectBuilder<CreateRequest<TDocument>>> fn) {
return (CreateRequest)((ObjectBuilder)fn.apply(new CreateRequest.Builder())).build();
}
观察lambda表达式的输入参数CreateRequest.Builder<TDocument>
,发现该类型是ObjectBuilder<CreateRequest<TDocument>>>
类的子类,并且该类型的一些方法返回值的类型都为CreateRequest.Builder<TDocument>
类型,因此lambda表达式的输出部分可以使用类似builder -> builder.a().b()
链式方法调用方式
分析完CreateRequest.of方法后,发现该方法的入参和调用ElasticsearchClient 的create方法中的Function参数类型一致,因此调用ElasticsearchClient的方法时,可以执行传xxxRequest对象,也可以直接传xxxRequest.of方法的参数
总结: 调用ElasticsearchClient执行数据操作时,需要结合使用Http请求方式的请求url及参数,构建xxxRquest对象作为输入参