前言
最近在学习优化Elasticsearch中文搜索时发现它已经更新到了8.17版本了,并且推出了全新的JAVA Client,目前我们使用的还是7.X的版本,本着学习新技术的态度于是下载安装了全新的8.17版本,并且体验了一下全新的JAVA Client,短期内应该不会更新到8.17也不会替换原有的JAVA Client的,先记录一波,说不定后面会有用到的时候。
优化搜索文章:《如何让ElasticSearch完美实现数据库的Like查询》
Elasticsearch的官方全新的Java API 客户端主要有以下几个特点:
- 返回结果支持JAVA对象,之前查出结果还要手动转成需要的JAVA对象
- API支持同步和异步版本,异步可以有效的增加建立索引的吞吐量
- 使用构建器模式实现fluent链式编程
- 使用对象映射无缝集成应用程序类,例如Jackson或任何 JSON-B 实现。
安装
最简单方便的是使用Docker一键安装ElasticSearch+Kibana
curl -fsSL https://elastic.co/start-local | sh
脚本执行完成后会在控制台输出Kibana访问地址和对应的账号密码,打开后就安装完成了
安装IK分词器:
docker exec -it es-local-dev /bin/bash
/usr/share/elasticsearch/bin/elasticsearch-plugin install https://get.infini.cloud/elasticsearch/analysis-ik/8.17.0
ElasticSearch对应的IK分词词版本:release.infinilabs.com/analysis-ik…
安装完成后进入Kibana 页面:
全新的JAVA Client
引入依赖
目前最新的版本为8.17.0,内部使用jackson作JSON数据的反序列化,引用Jackson的版本为2.17.0
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>8.17.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.0</version>
</dependency>
创建Client
public static String serverUrl = "http://localhost:9200";
public static String apiKey = "TUJSSzhwTUJlOUNsckptS0poS***";
public static ElasticsearchClient esClient;
static {
RestClient restClient = RestClient
.builder(HttpHost.create(serverUrl))
.setDefaultHeaders(new Header[]{
new BasicHeader("Authorization", "ApiKey " + apiKey)
}).build();
ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
esClient = new ElasticsearchClient(transport);
}
创建ElasticsearchClient需要两个参数,一个是接口地址,另一个是认证ApiKey,其中ApiKey可以从Kibana后台Security-ApiKeys中创建
新建测试索引
我们创建一个test索引
PUT /test
{
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "ik_max_word"
},
"rank": {
"type": "long"
},
"pubTime": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
}
}
}
}
并创建对应的JAVA对象
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Doc {
private Long rank;
private Date pubTime;
private String title;
}
索引添加单文档
可以看出全新的Client在添加文档时我们不需要将JAVA转成JSON数据了,直接传入Doc对象
@Test
public void testIndex() throws IOException {
ElasticsearchClient esClient = ESClient.esClient;
for(Doc doc:docs){
IndexResponse response = esClient.index(j -> j
.index(indexName)
.id(doc.getId().toString())
.document(doc)
);
log.info("index {}", response.result().jsonValue());
}
}
索引批量添加文档
@Test
public void testBulkIndex() throws IOException {
BulkRequest.Builder br = new BulkRequest.Builder();
docs.forEach(doc->{
br.operations(op -> op
.index(idx -> idx
.index(indexName)
.id(doc.getId().toString())
.document(doc)
)
);
});
ElasticsearchClient esClient = ESClient.esClient;
BulkResponse result = esClient.bulk(br.build());
if (result.errors()) {
log.error("Bulk had errors");
for (BulkResponseItem item: result.items()) {
if (item.error() != null) {
log.error(item.error().reason());
}
}
}
}
通过ID查询文档
@Test
public void testQueryById() throws IOException {
ElasticsearchClient esClient = ESClient.esClient;
//使用对象接收
GetResponse<Doc> response = esClient.get(g -> g
.index(indexName)
.id("1"),
Doc.class
);
if (response.found()) {
Doc doc = response.source();
log.info("title :{} " , doc.getTitle());
} else {
log.info ("doc not found");
}
//使用Jackson接收
GetResponse<ObjectNode> responseJSON = esClient.get(g -> g
.index(indexName)
.id("1"),
ObjectNode.class
);
if (response.found()) {
ObjectNode json = responseJSON.source();
String title = json.get("title").asText();
log.info("title :{} " , title);
} else {
log.info ("doc not found");
}
}
使用match搜索文档
@Test
public void testSearch() throws IOException {
ElasticsearchClient esClient = ESClient.esClient;
String keyWord = "中国";
SearchResponse<Doc> response = esClient.search(s -> s
.index(indexName)
.query(q -> q
.match(t -> t
.field("title")
.query(keyWord)
)
),
Doc.class
);
List<Hit<Doc>> hits = response.hits().hits();
hits.forEach(hit -> {
Doc doc = hit.source();
log.info("{} {} {} ", doc.getTitle(), DateUtil.formatDate(doc.getPubTime()) ,doc.getRank());
});
}
聚合查询
@Test
public void testAggr() throws IOException {
ElasticsearchClient esClient = ESClient.esClient;
String keyWord = "中国";
Query query = MatchQuery.of(m -> m
.field("title")
.query(keyWord)
)._toQuery();
SearchResponse<Void> response = esClient.search(b -> b
.index(indexName)
.size(0)
.query(query)
.aggregations("yearHistogram", a -> a.dateHistogram(h -> h.calendarInterval(CalendarInterval.Year).field("pubTime"))
),
Void.class
);
List<DateHistogramBucket> buckets = response.aggregations()
.get("yearHistogram")
.dateHistogram()
.buckets().array();
for (DateHistogramBucket bucket: buckets) {
log.info("{} {} ", bucket.keyAsString(),bucket.docCount());
}
}
更多查询参数参考:www.elastic.co/guide/en/el…
总结
在本文中,我们探讨了Elasticsearch 8.17版本及其全新的Java API Client。新版本的Java Client具有多个显著特点,包括支持直接返回Java对象、提供同步和异步API、采用流式编程的构建器模式,以及与JSON-B实现的无缝集成。这些特性使得开发者在使用Elasticsearch时更加高效便捷。
我们还详细介绍了如何通过Docker快速安装Elasticsearch和Kibana,并安装IK分词器。接着,展示了如何引入依赖、创建Elasticsearch Client、以及如何进行索引的创建和文档的添加。通过示例代码,我们演示了如何通过ID查询文档、使用match进行搜索、以及进行聚合查询。
总的来说,Elasticsearch 8.17及其新的Java Client为开发者提供了更强大的功能和更简便的操作方式,值得在实际项目中进行尝试和应用。