Springboot项目中使用Elasticsearch的RestClient
上一篇介绍了Elasticsearch的入门《5000字详说Elasticsearch入门(一)》,本篇介绍Springboot如何集成使用Elasticsearch。分为3步:配置properties文件、引入pom依赖、配置RestHighLevelClient
类。
1、选择ES的Client API
我们知道Elasticsearch是一款Restful API风格的分布式搜索引擎。ES Client有两种连接方式:TransportClient 和 RestClient。TransportClient通过TCP方式访问ES,RestClient方式通过Http方式访问ES。ES在7.0中已经弃用TransportClient,在8.0中完全删除它,所以建议使用RestClient的方式。
RestClient方式有多种实现,比如:ES自带的RestHighLevelClient
、Springboot实现的ElasticsearchRestTemplate
。笔者建议使用RestHighLevelClient
,原因有3个:
- 使用
RestHighLevelClient
比较灵活,可以直接使用ES的DSL语法,实现复杂查询,同时没有与其他部件绑定,所以版本可以自由选择。 - 由于
ElasticsearchRestTemplate
是spring-boot-starter-data-elasticsearch
封装的工具类,虽然使用上稍微方便一些,但是失去了灵活性,出现问题时也不易排查。而且ElasticsearchRestTemplate
本身与spring-boot-starter-data-elasticsearch
紧密依赖。如果想升级ElasticsearchRestTemplate
,那就必须连带升级项目的Springboot版本,这个风险就比较高了,一般项目的Springboot版本不会轻易升级。 - 还有些第三方的插件,将SQL转成ES的DSL,笔者也不建议使用。它们即不灵活,更新速度也慢。这里也再次吐槽下,ES的DSL设计确实有点反人类,不过也不用死记,可以通过归类记忆核心内容,其余使用细节直接查阅官方文档就好。
整理来说,用ES提供的RestHighLevelClient
是最稳定最方便的。由于使用RestHighLevelClient
,多少会涉及到ES的DSL语法,详见官网:https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-match-query-phrase.html。
下文中的示例中使用RestHighLevelClient
时,也会附带上对应的DSL语法。
2、配置properties文件
# ES集群机器
elasticsearch.hosts=10.20.1.29,10.20.0.91,10.20.0.93
# ES提供服务的端口号
elasticsearch.port=9200
3、引入pom依赖
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.10.2</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>7.10.2</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.10.2</version>
</dependency>
4、配置RestHighLevelClient
@Data
@Configuration
@ConfigurationProperties(prefix = "elasticsearch")
public class ElasticSearchConfig {
private String hosts;
private Integer port;
@Bean
public RestHighLevelClient restHighLevelClient() {
HttpHost[] httpHosts = Arrays.stream(hosts.split(","))
.filter(e -> !StringUtils.isEmpty(e))
.map(e -> new HttpHost(e, port, "http"))
.toArray(HttpHost[]::new);
return new RestHighLevelClient(
RestClient.builder(
httpHosts
)
);
}
}
至此Springboot与Elasticsearch的集成已经结束,接下来就是使用了。
5、使用RestClient API
下文演示常规场景下的RestClient API的使用方式和对应的DSL语法,涉及到的相关完整代码见如下地址:
5.1、创建索引,指定Mapping
通过代码里调用RestClient的API也可以创建索引,但是笔者建议统一通过如下方式建立,字段和类型非常清晰可控,方便统一管理。比如创建一个goods
索引:
PUT /goods
{
"mappings": {
"properties": {
"brandName": {
"type": "keyword"
},
"categoryName": {
"type": "keyword"
},
"createTime": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss"
},
"id": {
"type": "keyword"
},
"price": {
"type": "double"
},
"saleNum": {
"type": "integer"
},
"status": {
"type": "integer"
},
"stock": {
"type": "integer"
},
"title": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart"
}
}
}
}
5.2、创建实例类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Goods {
/**
* 商品编号
*/
private Long id;
/**
* 商品标题
*/
private String title;
/**
* 商品价格
*/
private BigDecimal price;
/**
* 商品库存
*/
private Integer stock;
/**
* 商品销售数量
*/
private Integer saleNum;
/**
* 商品分类
*/
private String categoryName;
/**
* 商品品牌
*/
private String brandName;
/**
* 上下架状态
*/
private Integer status;
/**
* 商品创建时间
*/
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
}
5.3、使用API操作数据
下面介绍主流场景下API的使用,每个场景会配上对应的DSL语法。更多语法详见官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-match-query-phrase.html。
5.3.1、增加文档
// 创建索引请求对象
IndexRequest indexRequest = new IndexRequest("goods").id(goods.getId() + "").source(data, XContentType.JSON);
// 执行增加文档
IndexResponse response = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
DSL:
PUT goods/_doc/3
{
"id": 3,
"brandName": "华为",
"categoryName": "手机",
"createTime": "2023-10-23 19:12:56",
"price": 5999,
"saleNum": 1001,
"status": 1,
"stock": 900,
"title": "华为自研芯片Meta60测试机"
}
5.3.2、更新文档
// 创建索引请求对象
UpdateRequest updateRequest = new UpdateRequest("goods", "4");
// 设置更新文档内容
updateRequest.doc(data, XContentType.JSON);
// 执行更新文档
UpdateResponse response = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
DSL:
POST goods/_update/3
{
"doc": {
"title":"华为自研芯片Meta60测试机111"
}
}
5.3.3、删除文档
// 创建删除请求对象
DeleteRequest deleteRequest = new DeleteRequest("goods", "3");
// 执行删除文档
DeleteResponse response = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
DSL:
DELETE goods/_doc/3
5.3.4、Match匹配查询
// 构建查询条件 (查询全部)
MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
// 创建查询源构造器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(matchAllQueryBuilder);
// 设置分页
searchSourceBuilder.from(0); // 从第几条开始,不包含它
searchSourceBuilder.size(3); // 要取多少条数据
// 设置排序
searchSourceBuilder.sort("price", SortOrder.ASC);
// 设置源字段过虑,第一个参数结果集包括哪些字段,第二个参数表示结果集不包括哪些字段;查询的文档只包含哪些指定的字段
searchSourceBuilder.fetchSource(new String[]{"id", "title", "categoryName"}, new String[]{});
// 创建查询请求对象,将查询对象配置到其中
SearchRequest searchRequest = new SearchRequest("goods");
searchRequest.source(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
// 根据状态和数据条数验证是否返回了数据
if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits) {
// 将 JSON 转换成对象
Goods goods = JSON.parseObject(hit.getSourceAsString(), Goods.class);
// 输出查询信息
log.info(goods.toString());
}
}
DSL:
POST goods/_search
{
"query": {
"match_all": {
}
}
}
POST goods/_search
{
"query": {
"match": {
"title": "移动多余"
}
}
}
5.3.5、MatchPhrase查询
短语匹配会把查询文本看做短语,有顺序要求。
// 构建查询条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// title是 舒适轻巧 时,可以查到,改成 轻巧舒适 时,则查不到,因为短语匹配,会把查询文本看做短语,有顺序要求
searchSourceBuilder.query(QueryBuilders.matchPhraseQuery("title", "轻巧舒适"));
// 创建查询请求对象,将查询对象配置到其中
SearchRequest searchRequest = new SearchRequest("goods");
searchRequest.source(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
// 根据状态和数据条数验证是否返回了数据
if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits) {
// 将 JSON 转换成对象
Goods goods = JSON.parseObject(hit.getSourceAsString(), Goods.class);
// 输出查询信息
log.info(goods.toString());
}
}
DSL:
POST goods/_search
{
"query": {
"match_phrase": {
"title": "轻巧舒适"
}
}
}
5.3.6、Term查询
Term查询时不对查询文本做分词。
// 构建查询条件(注意:termQuery 支持多种格式查询,如 boolean、int、double、string 等,这里使用的是 string 的查询)
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 表示检索title字段值为 李宁的跑鞋 的文档,如果是match则可以查到,term查询不到,因为term查询时不对查询文本做分词
searchSourceBuilder.query(QueryBuilders.termQuery("title", "李宁的跑鞋"));
// 创建查询请求对象,将查询对象配置到其中
SearchRequest searchRequest = new SearchRequest("goods");
searchRequest.source(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
// 根据状态和数据条数验证是否返回了数据
if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits) {
// 将 JSON 转换成对象
Goods goods = JSON.parseObject(hit.getSourceAsString(), Goods.class);
// 输出查询信息
log.info("=======" + goods.toString());
}
}
DSL:
POST goods/_search
{
"query": {
"term": {
"title": {
"value": "李宁的跑鞋"
}
}
}
}
5.3.7、设置排序
// 设置排序
searchSourceBuilder.sort("price", SortOrder.ASC);
DSL:
POST goods/_search
{
"query": {
"match_all": {
}
},
"from": 6,
"size": 2,
"sort": [
{
"price": {
"order": "asc"
}
}
]
}
5.3.8、通配符查询
// 构建查询条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 查询所有以 “鞋” 结尾的商品信息
searchSourceBuilder.query(QueryBuilders.wildcardQuery("title", "*鞋"));
// 创建查询请求对象,将查询对象配置到其中
SearchRequest searchRequest = new SearchRequest("goods");
searchRequest.source(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
// 根据状态和数据条数验证是否返回了数据
if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits) {
// 将 JSON 转换成对象
Goods goods = JSON.parseObject(hit.getSourceAsString(), Goods.class);
// 输出查询信息
log.info(goods.toString());
}
}
DSL:
POST goods/_search
{
"query": {
"wildcard": {
"title": {
"value": "*鞋"
}
}
}
}
5.3.9、范围查询
// 构建查询条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.rangeQuery("price").gte(1000));
DSL:
POST goods/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"title": "华为"
}
}
],
"filter": [
{
"range": {
"price": {
"gte": 5000,
"lte": 10000
}
}
}
]
}
}
}
5.3.10、Scroll滚动查询
滚动查询可以用来解决深度分页查询问题,每次都要记住上一次的scrollId。
// 假设用户想获取第5页数据,其中每页1条
int pageNo = 3;
int pageSize = 1;
// 定义请求对象
SearchRequest searchRequest = new SearchRequest("goods");
// 构建查询条件
SearchSourceBuilder builder = new SearchSourceBuilder();
searchRequest.source(builder.query(QueryBuilders.matchAllQuery()).sort("price", SortOrder.DESC).size(pageSize));
String scrollId = null;
// 3、发送请求到ES
SearchResponse scrollResponse = null;
// 设置游标id存活时间
Scroll scroll = new Scroll(TimeValue.timeValueMinutes(2));
// 记录所有游标id
List<String> scrollIds = new ArrayList<>();
for (int i = 0; i < pageNo; i++) {
try {
// 首次检索
if (i == 0) {
//记录游标id
searchRequest.scroll(scroll);
// 首次查询需要指定索引名称和查询条件
SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
// 下一次搜索要用到该游标id
scrollId = response.getScrollId();
}
// 非首次检索
else {
// 不需要在使用其他条件,也不需要指定索引名称,只需要使用执行游标id存活时间和上次游标id即可,毕竟信息都在上次游标id里面呢
SearchScrollRequest searchScrollRequest = new SearchScrollRequest(scrollId);
searchScrollRequest.scroll(scroll);
scrollResponse = restHighLevelClient.scroll(searchScrollRequest, RequestOptions.DEFAULT);
// 下一次搜索要用到该游标id
scrollId = scrollResponse.getScrollId();
}
// 记录所有游标id
scrollIds.add(scrollId);
} catch (Exception e) {
e.printStackTrace();
}
}
// 查询完毕,清除游标id
ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
clearScrollRequest.scrollIds(scrollIds);
try {
restHighLevelClient.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);
} catch (IOException e) {
System.out.println("清除滚动查询游标id失败");
e.printStackTrace();
}
// 4、处理响应结果
System.out.println("滚动查询返回数据:");
assert scrollResponse != null;
SearchHits hits = scrollResponse.getHits();
for (SearchHit hit : hits) {
// 将 JSON 转换成对象
Goods goods = JSON.parseObject(hit.getSourceAsString(), Goods.class);
// 输出查询信息
log.info(goods.toString());
}
DSL:
# 第一次使用 scroll API
POST goods/_search?scroll=2m
{
"query": {
"match_all": {}
},
"size": 2
}
# 进行翻页
POST /_search/scroll
{
"scroll" : "2m",
"scroll_id" : "FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFkxBWkYwOGw2U1dPSF94aHZTelFkaWcAAAAAAAADHhZoU05ERFl3WFIycXM3M3JKMmRQVkJB"
}
5.3.11、组合查询
组合查询一般用到bool
。
// 创建 Bool 查询构建器
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
// 构建查询条件
boolQueryBuilder
.must(QueryBuilders.matchQuery("title", "李宁"))
.filter()
.add(QueryBuilders.rangeQuery("createTime").format("yyyyMMdd")
.gte("20231023").lte("20231025"));
// 构建查询源构建器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(boolQueryBuilder);
searchSourceBuilder.size(100);
// 创建查询请求对象,将查询对象配置到其中
SearchRequest searchRequest = new SearchRequest("goods");
searchRequest.source(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
// 根据状态和数据条数验证是否返回了数据
if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits) {
// 将 JSON 转换成对象
Goods goods = JSON.parseObject(hit.getSourceAsString(), Goods.class);
// 输出查询信息
log.info(goods.toString());
}
}
DSL:
POST goods/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"title": "李宁"
}
}
],
"filter": [
{
"range": {
"price": {
"gte": 5000,
"lte": 10000
}
}
}
]
}
}
}
5.3.12、高亮查询
//查询条件(词条查询:对应ES query里的match)
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("title", "华为手机");
//设置高亮三要素
// field: 你的高亮字段
// preTags :前缀
// postTags:后缀
HighlightBuilder highlightBuilder = new HighlightBuilder()
.field("title")
.preTags("<font color='blue'>")
.postTags("</font>");
// 构建查询源构建器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(matchQueryBuilder);
searchSourceBuilder.highlighter(highlightBuilder);
searchSourceBuilder.size(100);
// 创建查询请求对象,将查询对象配置到其中
SearchRequest searchRequest = new SearchRequest("goods");
searchRequest.source(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
// 根据状态和数据条数验证是否返回了数据
if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits) {
// 将 JSON 转换成对象
Goods goods = JSON.parseObject(hit.getSourceAsString(), Goods.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) {
//System.out.println(fragment);
title.append(fragment);
}
goods.setTitle(title.toString());
}
// 输出查询信息
log.info(goods.toString());
}
}
DSL:
POST goods/_search
{
"query": {
"match": {
"title": "跑鞋"
}
},
"highlight": {
"fields": {
"body": {
"pre_tags": [
"<font color='red'>"
],
"post_tags": [
"</font>"
]
},
"title": {}
}
}
}
5.3.13、聚合查询
// 构建查询条件
MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
// 创建查询源构造器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(matchAllQueryBuilder);
// 获取最贵的商品
AggregationBuilder maxPrice = AggregationBuilders.max("maxPrice").field("price");
searchSourceBuilder.aggregation(maxPrice);
// 获取最便宜的商品
AggregationBuilder minPrice = AggregationBuilders.min("minPrice").field("price");
searchSourceBuilder.aggregation(minPrice);
// 创建查询请求对象,将查询对象配置到其中
SearchRequest searchRequest = new SearchRequest("goods");
searchRequest.source(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
Aggregations aggregations = searchResponse.getAggregations();
ParsedMax max = aggregations.get("maxPrice");
log.info("最贵的价格:" + max.getValue());
ParsedMin min = aggregations.get("minPrice");
log.info("最便宜的价格:" + min.getValue());
DSL:
POST goods/_search
{
"aggs": {
"max_price": {
"max": {
"field": "price"
}
}
}
}
5.3.14、分组查询
// 构建查询条件
MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
// 创建查询源构造器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(matchAllQueryBuilder);
// 根据商品分类进行分组查询
TermsAggregationBuilder aggBrandName = AggregationBuilders
.terms("brandNameName")
.field("brandName");
searchSourceBuilder.aggregation(aggBrandName);
// 创建查询请求对象,将查询对象配置到其中
SearchRequest searchRequest = new SearchRequest("goods");
searchRequest.source(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
Aggregations aggregations = searchResponse.getAggregations();
ParsedStringTerms aggBrandName1 = aggregations.get("brandNameName");
for (Terms.Bucket bucket : aggBrandName1.getBuckets()) {
// 分组名 ==== 数量
System.out.println(bucket.getKeyAsString() + "====" + bucket.getDocCount());
}
DSL:
POST goods/_search
{
"aggs": {
"brandNameName": {
"terms": {
"field": "brandName"
}
}
}
}
5.3.15、子查询
// 构建查询条件
MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
// 创建查询源构造器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(matchAllQueryBuilder);
// 根据商品分类进行分组查询,并且获取分类商品中的平均价格
TermsAggregationBuilder subAggregation = AggregationBuilders
.terms("brandNameName")
.field("brandName")
.subAggregation(AggregationBuilders.avg("avgPrice").field("price"));
searchSourceBuilder.aggregation(subAggregation);
// 创建查询请求对象,将查询对象配置到其中
SearchRequest searchRequest = new SearchRequest("goods");
searchRequest.source(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
Aggregations aggregations = searchResponse.getAggregations();
ParsedStringTerms aggBrandName1 = aggregations.get("brandNameName");
for (Terms.Bucket bucket : aggBrandName1.getBuckets()) {
// 获取聚合后的品牌的平均价格,注意返回值不是Aggregation对象,而是指定的ParsedAvg对象
ParsedAvg avgPrice = bucket.getAggregations().get("avgPrice");
// 分组名 ==== 平均价格
System.out.println(bucket.getKeyAsString() + "====" + avgPrice.getValueAsString());
}
DSL:
POST goods/_search
{
"aggs": {
"brandNameName": {
"terms": {
"field": "brandName"
},
"aggs": {
"avgPrice": {
"avg": {
"field": "price"
}
}
}
}
}
}
5.3.16、子查询下的子查询
子查询下的子查询有点绕。
// 构建查询条件
MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
// 创建查询源构造器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(matchAllQueryBuilder);
// 注意这里聚合写的位置不要写错,很容易搞混,错一个括号就不对了
TermsAggregationBuilder subAggregation = AggregationBuilders
.terms("categoryNameAgg").field("categoryName")
.subAggregation(AggregationBuilders.avg("categoryNameAvgPrice").field("price"))
.subAggregation(AggregationBuilders.terms("brandNameAgg").field("brandName")
.subAggregation(AggregationBuilders.avg("brandNameAvgPrice").field("price")));
searchSourceBuilder.aggregation(subAggregation);
// 创建查询请求对象,将查询对象配置到其中
SearchRequest searchRequest = new SearchRequest("goods");
searchRequest.source(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
//获取总记录数
System.out.println("totalHits = " + searchResponse.getHits().getTotalHits().value);
// 获取聚合信息
Aggregations aggregations = searchResponse.getAggregations();
ParsedStringTerms categoryNameAgg = aggregations.get("categoryNameAgg");
//获取值返回
for (Terms.Bucket bucket : categoryNameAgg.getBuckets()) {
// 获取聚合后的分类名称
String categoryName = bucket.getKeyAsString();
// 获取聚合命中的文档数量
long docCount = bucket.getDocCount();
// 获取聚合后的分类的平均价格,注意返回值不是Aggregation对象,而是指定的ParsedAvg对象
ParsedAvg avgPrice = bucket.getAggregations().get("categoryNameAvgPrice");
System.out.println(categoryName + "======平均价:" + avgPrice.getValue() + "======数量:" + docCount);
ParsedStringTerms brandNameAgg = bucket.getAggregations().get("brandNameAgg");
for (Terms.Bucket brandeNameAggBucket : brandNameAgg.getBuckets()) {
// 获取聚合后的品牌名称
String brandName = brandeNameAggBucket.getKeyAsString();
// 获取聚合后的品牌的平均价格,注意返回值不是Aggregation对象,而是指定的ParsedAvg对象
ParsedAvg brandNameAvgPrice = brandeNameAggBucket.getAggregations().get("brandNameAvgPrice");
System.out.println(" " + brandName + "======" + brandNameAvgPrice.getValue());
}
}
DSL:
POST goods/_search
{
"aggs": {
"categoryNameName": {
"terms": {
"field": "categoryName"
},
"aggs": {
"avgPrice": {
"avg": {
"field": "price"
}
},
"brandNameAgg": {
"terms": {
"field": "brandName"
},
"aggs": {
"brandNameAvg": {
"avg": {
"field": "price"
}
}
}
}
}
}
}
}
RestClient API使用的参考文献:https://huaweicloud.csdn.net/637f7baddacf622b8df85bf5.html
6、总结
本文主要介绍了Springboot与Elasticsearch集成和使用,重点内容如下:
- 选择RestHighLevelClient作为ES的RestClient。
- 核心步骤:增加properties配置、引入pom、配置conf类。
- 调用API、使用DSL语法。
本篇完结!感谢你的阅读,欢迎点赞 关注 收藏 私信!!!
原文链接:http://www.mangod.top/articles/2023/10/25/1698206210944.html、https://mp.weixin.qq.com/s/5SMObCipKYdCCsHa0FSVUg