获取源码🚩
需要完整代码资料,请一键三连后评论区留下邮箱,安排发送!!!🤖
什么是High Level REST Client?
Elasticsearch 的 High Level REST Client 是一个用于与 Elasticsearch 进行交互的 Java 客户端库,它提供了比低级别的 REST 客户端更高级别的抽象。High Level REST Client 使用了 OkHttp 库作为底层的 HTTP 客户端,并且提供了自动重试、连接管理和错误处理等功能。它允许开发人员以面向对象的方式构建请求和解析响应,从而简化了与 Elasticsearch 的 REST API 的交互过程。
✔️优点:
- 易用性:High Level REST Client 提供了丰富的 API,使得常见的 Elasticsearch 操作变得简单直观。
- 自动重试和错误处理:它能够处理网络故障和重试失败的请求,减少了开发人员需要处理的异常情况。
- 封装了 JSON 处理:它自动处理了请求和响应的序列化和反序列化,使得开发人员无需直接处理 JSON 字符串。
- 连接管理:它提供了连接池管理,可以复用连接,提高性能。
❌缺点:
- 依赖较大:由于其提供了丰富的功能,因此其依赖库和整体大小相对较大。
- 性能开销:高层面的抽象可能会引入额外的性能开销,尤其是在高并发场景下。
High Level REST Client的核心
索引管理
CreateIndexRequest 和 CreateIndexResponse:用于创建一个新的索引。
GetIndexRequest 和 GetIndexResponse:用于获取索引的元数据。
DeleteIndexRequest 和 DeleteIndexResponse:用于删除一个索引。
文档操作
IndexRequest 和 IndexResponse:用于索引(或更新)一个文档。
UpdateRequest 和 UpdateResponse:用于更新已存在的文档。
GetRequest 和 GetResponse:用于检索一个文档。
DeleteRequest 和 DeleteResponse:用于删除一个文档。
搜索和聚合
SearchRequest 和 SearchResponse:用于执行搜索查询和聚合。
CountRequest 和 CountResponse:用于计算满足给定条件的文档数量。
ScrollRequest 和 ScrollResponse:用于滚动搜索结果,获取大量数据。
批量操作
BulkRequest 和 BulkResponse:用于执行批量操作,如批量索引、更新或删除多个文档。
request.source()等于DSL中query{}
Sprinboot集成High Level REST Client
创建索引
可以使用postman
或者谷歌浏览器插件Elasticvue
创建:
{
"article_doc": {
"aliases": {},
"mappings": {
"properties": {
"author": {
"type": "keyword"
},
"category": {
"type": "keyword"
},
"content": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart"
},
"createTime": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
},
"id": {
"type": "keyword"
},
"readCount": {
"type": "integer"
},
"title": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart"
}
}
},
"settings": {
"index": {
"routing": {
"allocation": {
"include": {
"_tier_preference": "data_content"
}
}
},
"number_of_shards": "1",
"provided_name": "article_doc",
"creation_date": "1721725035763",
"number_of_replicas": "1",
"uuid": "r55CXBCLSxO8aFWPtL1Niw",
"version": {
"created": "7130299"
}
}
}
}
}
添加pom依赖
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.14.0</version>
</dependency>
<dependency>
<groupId> org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.14.0</version>
</dependency>
编写配置类
@Configuration
public class ElasticsearchConfig {
@Value("${elasticsearch.host}")
private String host;
@Value("${elasticsearch.port}")
private int port;
/*@Value("${elasticsearch.username}")
private String userName;
@Value("${elasticsearch.password}")
private String password;*/
/**
* 如果@Bean没有指定bean的名称,那么这个bean的名称就是方法名
*/
@Bean
public RestHighLevelClient restHighLevelClient() {
return new RestHighLevelClient(
RestClient.builder(
new HttpHost(host, port, "http")
)
);
}
}
编写模型
@Data
@TableName("tb_article")
public class Article {
private String id;
private String author;
private String category;
private String title;
private String content;
private Integer readCount;
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
}
编写接口服务类
获取索引结构接口
http://localhost:9000/articleDoc/getMapping
数据同步接口
/**
* 批量从数据库导入
* @throws IOException
*/
public void importFromDb() throws IOException {
List<Article> articleList = articleService.list();
BulkRequest bulkRequest = new BulkRequest();
for (Article doc : articleList) {
String data = JSON.toJSONString(doc);
IndexRequest indexRequest = new IndexRequest(Constant.ARTICLE_INDEX);
indexRequest.id(doc.getId()).source(data, XContentType.JSON);
bulkRequest.add(indexRequest);
}
BulkResponse response = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
log.info("批量导入状态:{}, 耗时{}", response.status(), response.getTook().getSeconds());
}
精确查询
public List<Article> searchByAuthor(String author) {
try {
// 构建查询条件(注意:termQuery 支持多种格式查询,如 boolean、int、double、string 等,这里使用的是 string 的查询)
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.termQuery("author", author));
SearchRequest searchRequest = new SearchRequest(Constant.ARTICLE_INDEX);
searchRequest.source(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
// 根据状态和数据条数验证是否返回了数据
List<Article> articleList = new ArrayList<>();
if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits) {
Article doc = JSON.parseObject(hit.getSourceAsString(), Article.class);
articleList.add(doc);
}
}
return articleList;
} catch (IOException e) {
log.error("精确查询异常:", e);
return null;
}
}
全文查询
- 查全部,
java MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
太简单,不再演示。
/**
* 全文查询
*/
public List<Article> matchQuery(String keyword) {
try {
// 构建查询条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.multiMatchQuery(keyword,"title","content"));
// 创建查询请求对象,将查询对象配置到其中
SearchRequest searchRequest = new SearchRequest(Constant.ARTICLE_INDEX);
searchRequest.source(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
return getArticleListFromSearchResponse(searchResponse);
} catch (IOException e) {
log.error("全文查询异常", e);
return null;
}
}
分页查询(带排序)
/**
* 分页查询
* @param page
*/
public void pageSortQuery(Page<Article> page) {
try {
// 构建查询条件
MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
// 创建查询源构造器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(matchAllQueryBuilder);
// 设置分页
searchSourceBuilder.from((int) ((page.getCurrent()-1)*page.getSize()));
searchSourceBuilder.size((int) page.getSize());
// 按照阅读量排序
searchSourceBuilder.sort("readCount", SortOrder.DESC);
// 创建查询请求对象,将查询对象配置到其中
SearchRequest searchRequest = new SearchRequest(Constant.ARTICLE_INDEX);
searchRequest.source(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
// 根据状态和数据条数验证是否返回了数据
page.setTotal(getHitTotal(searchResponse));
page.setRecords(getArticleListFromSearchResponse(searchResponse));
} catch (IOException e) {
log.error("分页查询失败", e);
}
}
高亮查询✨
public List<Article> highlightQuery(String keyword) {
try {
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("title", keyword);
HighlightBuilder highlightBuilder = new HighlightBuilder().field("title").preTags("<font color='red'>").postTags("</font>");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(matchQueryBuilder);
searchSourceBuilder.highlighter(highlightBuilder);
searchSourceBuilder.size(100);
SearchRequest searchRequest = new SearchRequest(Constant.ARTICLE_INDEX);
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
List<Article> articleList = new ArrayList<>();
if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits) {
Article doc = JSON.parseObject(hit.getSourceAsString(), Article.class);
// 获取高亮的数据
HighlightField highlightField = hit.getHighlightFields().get("title");
System.out.println("高亮名称:" + highlightField.getFragments()[0].string());
// 替换掉原来的数据
Text[] fragments = highlightField.getFragments();
if (fragments != null && fragments.length > 0) {
StringBuilder title = new StringBuilder();
for (Text fragment : fragments) {
title.append(fragment);
}
doc.setTitle(title.toString());
}
articleList.add(doc);
}
}
return articleList;
} catch (Exception e) {
log.error("高亮查询失败", e);
return null;
}
}
范围查询
/**
* 范围查询
* 时间文档表达式:https://www.elastic.co/guide/en/elasticsearch/reference/current/common-options.html#date-math
* @return
*/
public List<Article> rangeQuery() {
try {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//searchSourceBuilder.query(QueryBuilders.rangeQuery("price").gte(10000));
searchSourceBuilder.query(QueryBuilders.rangeQuery("createTime")
.gte("now-2d").includeLower(true).includeUpper(true));
SearchRequest searchRequest = new SearchRequest(Constant.ARTICLE_INDEX);
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
return getArticleListFromSearchResponse(searchResponse);
} catch (Exception e) {
log.error("范围查询失败", e);
return null;
}
}
布尔查询(BooleanQuery)
bool 查询可以用来合并多个条件查询结果的布尔逻辑,它包含一下操作符:
must:多个查询条件必须完全匹配,相当于关系型数据库中的 and。
should:至少有一个查询条件匹配,相当于关系型数据库中的 or。
must_not: 多个查询条件的相反匹配,相当于关系型数据库中的 not。
filter:过滤满足条件的数据。
range:条件筛选范围。
gt:大于,相当于关系型数据库中的 >。
gte:大于等于,相当于关系型数据库中的 >=。
lt:小于,相当于关系型数据库中的 <。
lte:小于等于,相当于关系型数据库中的 <=。
public List<Article> highlightQuery(String keyword) {
try {
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("title", keyword);
HighlightBuilder highlightBuilder = new HighlightBuilder().field("title").preTags("<font color='red'>").postTags("</font>");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(matchQueryBuilder);
searchSourceBuilder.highlighter(highlightBuilder);
searchSourceBuilder.size(100);
SearchRequest searchRequest = new SearchRequest(Constant.ARTICLE_INDEX);
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
List<Article> articleList = new ArrayList<>();
if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits) {
Article doc = JSON.parseObject(hit.getSourceAsString(), Article.class);
// 获取高亮的数据
HighlightField highlightField = hit.getHighlightFields().get("title");
System.out.println("高亮名称:" + highlightField.getFragments()[0].string());
// 替换掉原来的数据
Text[] fragments = highlightField.getFragments();
if (fragments != null && fragments.length > 0) {
StringBuilder title = new StringBuilder();
for (Text fragment : fragments) {
title.append(fragment);
}
doc.setTitle(title.toString());
}
articleList.add(doc);
}
}
return articleList;
} catch (Exception e) {
log.error("高亮查询失败", e);
return null;
}
}
聚合查询
在使用Elasticsearch时,更多会用到聚合操作,它类似SQL中的groupby操作。ES的聚合查询是先查出结果,然后对结果使用聚合函数做处理,常用的操作有:avg:求平均、max:最大值、min:最小值、sum:求和等。
在ES中聚合分为指标聚合和分桶聚合:
Metric 指标聚合:指标聚合对一个数据集求最大、最小、和、平均值等
Bucket 分桶聚合:除了有上面的聚合函数外,还可以对查询出的数据进行分组group by,再在组上进行游标聚合。
- Metric 指标聚合
/**
* 指标查询
*/
public int metricQuery() {
try {
MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(matchAllQueryBuilder);
AggregationBuilder mostPopular = AggregationBuilders.max("mostPopular").field("readCount");
searchSourceBuilder.aggregation(mostPopular);
SearchRequest searchRequest = new SearchRequest(Constant.ARTICLE_INDEX);
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
// log.info("查询结果:{}", JSON.toJSONString(searchResponse));
Aggregations aggregations = searchResponse.getAggregations();
ParsedMax max = aggregations.get("mostPopular");
return (int) max.getValue();
} catch (Exception e) {
log.error("指标查询失败", e);
return -1;
}
}
- Bucket 分桶聚合
根据文章分类,统计文章数量。
/**
* 分桶聚合
* @return
*/
public Map<String,Object> bucketQuery() {
try {
// 构建查询条件
MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
// 创建查询源构造器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(matchAllQueryBuilder);
// 根据商品分类进行分组查询
TermsAggregationBuilder categoryGroup = AggregationBuilders.terms("categoryGroup").field("category");
searchSourceBuilder.aggregation(categoryGroup);
// 创建查询请求对象,将查询对象配置到其中
SearchRequest searchRequest = new SearchRequest(Constant.ARTICLE_INDEX);
searchRequest.source(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
Aggregations aggregations = searchResponse.getAggregations();
ParsedStringTerms agg = aggregations.get("categoryGroup");
Map<String,Object> resultMap = new HashMap<>();
for (Terms.Bucket bucket : agg.getBuckets()) {
resultMap.put(bucket.getKeyAsString(), bucket.getDocCount());
}
return resultMap;
} catch (Exception e) {
log.error("分桶聚合查询失败", e);
return null;
}
}
为什么8.0版本要弃用?
在 Elasticsearch 8.0 版本中,High Level REST Client 被标记为弃用,主要是因为以下几个原因:
- 维护成本:High Level REST Client 的维护成本较高,因为它需要随着 Elasticsearch 的发展而持续更新,以保持与 REST API 的兼容性。
- 性能考量:为了提高性能和减少内存消耗,Elasticsearch 开发团队决定移除一些高成本的组件,High Level REST Client 因其封装层次较多而成为目标之一。
- 替代方案:Elasticsearch 推出了新的 Java API Client,这是一个更轻量级、更高效的选择,它直接使用了 Elasticsearch 的内部协议,从而提供了更好的性能和更低的延迟。
新的 Java API Client 能够更好地利用 Elasticsearch 的内部机制,减少序列化和反序列化的开销,同时也提供了更细粒度的控制和更高的灵活性。因此,Elasticsearch 推荐用户迁移到新的 Java API Client 上。
所以后续我会再整理个新的Java API Client 用法。与时俱进。
点赞收藏加评论
用到的时候再来看,收获更大。