SpringCloud(七.7)ES(elasticsearch)-- 实战练习
demo地址:链接:https://pan.baidu.com/s/16c1mMcQv7bF3Fcz2X_PE7A 提取码:msvy
库表 tb_hotel.sql : 链接:https://pan.baidu.com/s/1wVdh-fZoyeNbLUkyQYCD5g 提取码:3t4y
练习目标一:实现酒店搜索功能,完成关键字搜索和分页功能
如图,点击搜索按钮,我们发现它调用的是 hotel/list 接口,且有四个必填入参分别是 key(搜索关键字)、page(当前页码)、size(每页条数)、sortBy(排序字段)
其次根据页面发现还会有其它非必填入参,分别是 city(城市)、starName(星级)、brand(品牌)、minPrice(最低价格)、maxPrice(最高价格)、location(地理位置)
1、添加入参实体和返回实体
新增入参实体
@Data public class RequestParams { private String key; private Integer page; private Integer size; private String sortBy; private String city; private String starName; private String brand; private Integer minPrice; private Integer maxPrice; private String location; }
返回实体(通过页面我们不难发现,只需返回总条数和酒店数据集即可。)
@Data public class PageResult { private Long total; private List<HotelDoc> hotels; public PageResult() { } public PageResult(Long total, List<HotelDoc> hotels) { this.total = total; this.hotels = hotels; } }
2、编写Controller
@RestController @RequestMapping("hotel") public class HotelController { @Autowired private IHotelService hotelService; @PostMapping("list") public PageResult search(@RequestBody RequestParams params) { return hotelService.search(params); } }
3、在Controller中Alt+Enter生成IHotelService 中的search方法
4、在IHotelService中Alt+Enter生成HotelService中的search实现方法
5、接下来就和上一章 SpringCloud(七.6)ES(elasticsearch)-- RestClient查询文档和结果处理 练习的步骤一致了
- 准备Request
- 准备DSL
- 发送请求
- 解析响应
但是有个前提,需要准备RestClient,在单元测试中是new出来的。在这里我们应该通过@bean注入到spring容器中。然后在HotleService中就可以@Autowired注入使用了。
@Bean public RestHighLevelClient restHighLevelClient(){ return new RestHighLevelClient(RestClient.builder( HttpHost.create("http://192.168.223.129:9200") )); }
现在万事具备就可以编写代码实现功能了。
@Slf4j @Service public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService { @Autowired private RestHighLevelClient restHighLevelClient; @Override public PageResult search(RequestParams params) { try { // 1.准备Request SearchRequest request = new SearchRequest("hotel"); // 2.准备请求参数 // 2.1.query buildBasicQuery(params, request); // 2.2.分页 int page = params.getPage(); int size = params.getSize(); request.source().from((page - 1) * size).size(size); // 2.3.距离排序 String location = params.getLocation(); if (StringUtils.isNotBlank(location)) { request.source().sort(SortBuilders .geoDistanceSort("location", new GeoPoint(location)) .order(SortOrder.ASC) .unit(DistanceUnit.KILOMETERS) ); } // 3.发送请求 SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT); // 4.解析响应 return handleResponse(response); } catch (IOException e) { throw new RuntimeException("搜索数据失败", e); } } private void buildBasicQuery(RequestParams params, SearchRequest request) { // 1.准备Boolean查询 BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); // 1.1.关键字搜索,match查询,放到must中 String key = params.getKey(); if (StringUtils.isNotBlank(key)) { // 不为空,根据关键字查询 boolQuery.must(QueryBuilders.matchQuery("all", key)); } else { // 为空,查询所有 boolQuery.must(QueryBuilders.matchAllQuery()); } // 1.2.品牌 String brand = params.getBrand(); if (StringUtils.isNotBlank(brand)) { boolQuery.filter(QueryBuilders.termQuery("brand", brand)); } // 1.3.城市 String city = params.getCity(); if (StringUtils.isNotBlank(city)) { boolQuery.filter(QueryBuilders.termQuery("city", city)); } // 1.4.星级 String starName = params.getStarName(); if (StringUtils.isNotBlank(starName)) { boolQuery.filter(QueryBuilders.termQuery("starName", starName)); } // 1.5.价格范围 Integer minPrice = params.getMinPrice(); Integer maxPrice = params.getMaxPrice(); if (minPrice != null && maxPrice != null) { maxPrice = maxPrice == 0 ? Integer.MAX_VALUE : maxPrice; boolQuery.filter(QueryBuilders.rangeQuery("price").gte(minPrice).lte(maxPrice)); }// 2.设置查询条件 request.source().query(boolQuery); } private PageResult handleResponse(SearchResponse response) { SearchHits searchHits = response.getHits(); // 4.1.总条数 long total = searchHits.getTotalHits().value; // 4.2.获取文档数组 SearchHit[] hits = searchHits.getHits(); // 4.3.遍历 List<HotelDoc> hotels = new ArrayList<>(hits.length); for (SearchHit hit : hits) { // 4.4.获取source String json = hit.getSourceAsString(); // 4.5.反序列化,非高亮的 HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class); // 4.6.处理高亮结果 // 1)获取高亮map Map<String, HighlightField> map = hit.getHighlightFields(); if (map != null && !map.isEmpty()) { // 2)根据字段名,获取高亮结果 HighlightField highlightField = map.get("name"); if (highlightField != null) { // 3)获取高亮结果字符串数组中的第1个元素 String hName = highlightField.getFragments()[0].toString(); // 4)把高亮结果放到HotelDoc中 hotelDoc.setName(hName); } } // 4.8.排序信息 Object[] sortValues = hit.getSortValues(); if (sortValues.length > 0) { hotelDoc.setDistance(sortValues[0]); } // 4.9.放入集合 hotels.add(hotelDoc); } return new PageResult(total, hotels); } }
效果展示:
1、给HotelDoc类添加isAD字段,Boolean类型。
2、在kibana中挑几个酒店编写DSL语句给这几个文档添加isAD字段,值为true
#给几个文档添加isAD字段,值为true POST /hotel/_update/38812 { "doc": { "isAD":true } } POST /hotel/_update/45845 { "doc": { "isAD":true } } POST /hotel/_update/46829 { "doc": { "isAD":true } }
3、修改search搜索方法,添加function score功能,给isAD值为true的酒店添加权重。(只是变动query部分)
private void buildBasicQuery(RequestParams params, SearchRequest request) { // 1.准备Boolean查询 BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); // 1.1.关键字搜索,match查询,放到must中 String key = params.getKey(); if (StringUtils.isNotBlank(key)) { // 不为空,根据关键字查询 boolQuery.must(QueryBuilders.matchQuery("all", key)); } else { // 为空,查询所有 boolQuery.must(QueryBuilders.matchAllQuery()); } // 1.2.品牌 String brand = params.getBrand(); if (StringUtils.isNotBlank(brand)) { boolQuery.filter(QueryBuilders.termQuery("brand", brand)); } // 1.3.城市 String city = params.getCity(); if (StringUtils.isNotBlank(city)) { boolQuery.filter(QueryBuilders.termQuery("city", city)); } // 1.4.星级 String starName = params.getStarName(); if (StringUtils.isNotBlank(starName)) { boolQuery.filter(QueryBuilders.termQuery("starName", starName)); } // 1.5.价格范围 Integer minPrice = params.getMinPrice(); Integer maxPrice = params.getMaxPrice(); if (minPrice != null && maxPrice != null) { maxPrice = maxPrice == 0 ? Integer.MAX_VALUE : maxPrice; boolQuery.filter(QueryBuilders.rangeQuery("price").gte(minPrice).lte(maxPrice)); } // 2.算分函数查询 FunctionScoreQueryBuilder functionScoreQuery = QueryBuilders.functionScoreQuery( boolQuery, // 原始查询,boolQuery new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{ // function数组 new FunctionScoreQueryBuilder.FilterFunctionBuilder( QueryBuilders.termQuery("isAD", true), // 过滤条件 ScoreFunctionBuilders.weightFactorFunction(10) // 算分函数 ) } ); // 3.设置查询条件 request.source().query(functionScoreQuery); }
效果展示: