个人名片:
博主:酒徒ᝰ.
个人简介:沉醉在酒中,借着一股酒劲,去拼搏一个未来。
本篇励志:三人行,必有我师焉。
本项目基于B站黑马程序员Java《SpringCloud微服务技术栈》,SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式
【SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式,系统详解springcloud微服务技术栈课程|黑马程序员Java微服务】 点击观看
目录
- 一、数据聚合
- 3. RestAPI实现聚合
一、数据聚合
3. RestAPI实现聚合
- API语法
es查询语句
GET /hotel/_search
{"size": 0,"aggs": {"brandAgg": {"terms": {"field": "brand","size": 20}}}
}
代码示例:
package cn.itcast.hotel;import org.apache.http.HttpHost;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;import java.io.IOException;
import java.util.List;@SpringBootTest
public class DSLSearch {private RestHighLevelClient client;@BeforeEachvoid setUp() {this.client = new RestHighLevelClient(RestClient.builder(HttpHost.create("192.168.179.128:9200")));}@AfterEachvoid tearDown() throws IOException {this.client.close();}@Testvoid testAggregation() throws IOException {SearchRequest request = new SearchRequest("hotel");request.source().size(0);request.source().aggregation(AggregationBuilders.terms("brandAgg").field("brand").size(20));SearchResponse response = client.search(request, RequestOptions.DEFAULT);handleResponse(response);}private void handleResponse(SearchResponse response) {Aggregations aggregations = response.getAggregations();Terms brandTerms = aggregations.get("brandAgg");List<? extends Terms.Bucket> buckets = brandTerms.getBuckets();for (Terms.Bucket bucket : buckets) {String brandName = bucket.getKeyAsString();long brandCount = bucket.getDocCount();System.out.println(brandName + " " + brandCount);}}
}
输出结果:
- 业务需求
需求:搜索页面的品牌、城市等信息不应该是在页面写死,而是通过聚合索引库中的酒店数据得来的:
分析:
目前,页面的城市列表、星级列表、品牌列表都是写死的,并不会随着搜索结果的变化而变化。但是用户搜索条件改变时,搜索结果会跟着变化。
例如:用户搜索“东方明珠”,那搜索的酒店肯定是在上海东方明珠附近,因此,城市只能是上海,此时城市列表中就不应该显示北京、深圳、杭州这些信息了。
也就是说,搜索结果中包含哪些城市,页面就应该列出哪些城市;搜索结果中包含哪些品牌,页面就应该列出哪些品牌。
如何得知搜索结果中包含哪些品牌?如何得知搜索结果中包含哪些城市?
使用聚合功能,利用Bucket聚合,对搜索结果中的文档基于品牌分组、基于城市分组,就能得知包含哪些品牌、哪些城市了。
因为是对搜索结果聚合,因此聚合是限定范围的聚合,也就是说聚合的限定条件跟搜索文档的条件一致。
查看浏览器可以发现,前端其实已经发出了这样的一个请求:
请求参数与搜索文档的参数完全一致。
分析:
filter地址,POST请求方式
参数:key, page. size, sortBy, city, starName, brand, minPrice, maxPrice,
返回值类型就是页面要展示的最终结果:
结果是一个Map结构:
key是字符串,城市、星级、品牌、价格
value是集合,例如多个城市的名称
- 业务实现
在
cn.itcast.hotel.controller
包的HotelController
中添加一个方法,遵循下面的要求:
请求方式:POST
请求路径:/hotel/filters
请求参数:RequestParams
,与搜索文档的参数一致
返回值类型:Map<String, List<String>>
代码:
@PostMapping("/filters")
public Map<String, List<String>> getFilter(@RequestBody RequestParam params) {return hotelService.getFilter(params);
}
这里调用了IHotelService中的getFilters方法,尚未实现。
在cn.itcast.hotel.service.IHotelService
中定义新方法:
Map<String, List<String>> getFilter(RequestParam params);
在cn.itcast.hotel.service.impl.HotelService
中实现该方法:
@Override
public Map<String, List<String>> getFilter(RequestParam params) {try {SearchRequest request = new SearchRequest("hotel");buildBasicQuery(params, request);request.source().size(0);buildAggregation(request);SearchResponse response = client.search(request, RequestOptions.DEFAULT);Map<String, List<String>> result = new HashMap<>();Aggregations aggregations = response.getAggregations();List<String> brandlist = getAggByName(aggregations, "brandAgg");result.put("brand", brandlist);List<String> starList = getAggByName(aggregations, "starAgg");result.put("starName", starList);List<String> cityList = getAggByName(aggregations, "cityAgg");result.put("city", cityList);return result;} catch (IOException e) {throw new RuntimeException(e);}
}private List<String> getAggByName(Aggregations aggregations, String aggName) {Terms brandTerm = aggregations.get(aggName);List<? extends Terms.Bucket> buckets = brandTerm.getBuckets();List<String> list = new ArrayList<>();for (Terms.Bucket bucket : buckets) {String key = bucket.getKeyAsString();list.add(key);}return list;
}private void buildAggregation(SearchRequest request) {request.source().aggregation(AggregationBuilders.terms("brandAgg").field("brand").size(100));request.source().aggregation(AggregationBuilders.terms("starAgg").field("starName").size(100));request.source().aggregation(AggregationBuilders.terms("cityAgg").field("city").size(100));
}
结果:
重启项目,刷新浏览器,如果城市中的杭州消失了,则业务实现成功。