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);
    }

 

效果展示:

 

posted @ 2024-04-20 17:10  一介桃白白  阅读(59)  评论(0编辑  收藏  举报