知识问答

Spring Boot 整合 分布式搜索引擎 Elastic Search 实现 搜索、分页与结果过滤

文章目录

  • ⛄引言
  • 一、酒店搜索和分页
    • ⛅需求分析
    • ⚡源码编写
    • 二、酒店结果过滤
      • ⌚需求分析
      • ⏰修改搜索业务
      • ✅效果图
      • ⛵小���

        ⛄引言

        本文参考黑马 分布式Elastic search

        Elasticsearch是一款非常强大的开源搜索引擎,具备非常多强大功能,可以帮助我们从海量数据中快速找到需要的内容

        一、酒店搜索和分页

        ⛅需求分析

        实现黑马旅游的酒店搜索功能,完成关键字搜索和分页

        在项目首页,有一个很大的搜索框、也有分页按钮

        点击搜索按钮,可以看到浏览器控制台 网络发出了请求

        请求参数如下:

        {    "key": "",    "page": 1,    "size": 5,    "sortBy": "default"}

        由此可以知道,我们这个请求的信息如下:

        • 请求方式:POST
        • 请求路径:/hotel/list
        • 请求参数:JSON对象,包含4个字段:
          • key:搜索关键字
          • page:页码
          • size:每页大小
          • sortBy:排序,目前暂不实现
          • 返回值:分页查询,需要返回分页结果PageResult,包含两个属性:
            • total:总条数
            • List:当前页的数据

              因此,我们实现业务的流程如下:

              • 步骤一:定义实体类,接收请求参数的JSON对象
              • 步骤二:编写controller,接收页面的请求
              • 步骤三:编写业务实现,利用RestHighLevelClient实现搜索、分页

                ⚡源码编写

                定义实体类

                实体类有两个,一个是前端的请求参数实体,一个是服务端应该返回的响应结果实体。

                import lombok.Data;@Datapublic class RequestParams {    private String key;    private Integer page;    private Integer size;    private String sortBy;}

                定义返回值对象,由于要实现分页,所以需要定义。

                import lombok.Data;import java.util.List;@Datapublic class PageResult {    private Long total;    private List hotels;    public PageResult() {    }    public PageResult(Long total, List hotels) {        this.total = total;        this.hotels = hotels;    }}

                定义Controller 控制层

                定义一个HotelController,声明查询接口,满足下列要求:

                • 请求方式:Post
                • 请求路径:/hotel/list
                • 请求参数:对象,类型为RequestParam
                • 返回值:PageResult,包含两个属性
                  • Long total:总条数
                  • List hotels:酒店数据
                    @RestController@RequestMapping("/hotel")public class HotelController {    @Autowired    private IHotelService hotelService;// 搜索酒店数据    @PostMapping("/list")    public PageResult search(@RequestBody RequestParams params){        return hotelService.search(params);    }}

                    实现搜索业务

                    我们在controller调用了IHotelService,并没有实现该方法,因此下面我们就在IHotelService中定义方法,并且去实现业务逻辑。

                    1)在cn.itcast.hotel.service中的IHotelService接口中定义一个方法:

                    /** * 根据关键字搜索酒店信息 * @param params 请求参数对象,包含用户输入的关键字  * @return 酒店文档列表 */PageResult search(RequestParams params);

                    2)实现搜索业务,肯定离不开RestHighLevelClient,我们需要把它注册到Spring中作为一个Bean。在项目中的HotelDemoApplication中声明这个Bean:

                    @Beanpublic RestHighLevelClient client(){    return  new RestHighLevelClient(RestClient.builder(        HttpHost.create("http://IP地址:9200")    ));}

                    3)在service.impl中的HotelService中实现search方法:

                    @Overridepublic PageResult search(RequestParams params) {    try {        // 1.准备Request        SearchRequest request = new SearchRequest("hotel");        // 2.准备DSL        // 2.1.query        String key = params.getKey();        if (key == null || "".equals(key)) {            boolQuery.must(QueryBuilders.matchAllQuery());        } else {            boolQuery.must(QueryBuilders.matchQuery("all", key));        }        // 2.2.分页        int page = params.getPage();        int size = params.getSize();        request.source().from((page - 1) * size).size(size);        // 3.发送请求        SearchResponse response = client.search(request, RequestOptions.DEFAULT);        // 4.解析响应        return handleResponse(response);    } catch (IOException e) {        throw new RuntimeException(e);    }}// 结果解析private PageResult handleResponse(SearchResponse response) {    // 4.解析响应    SearchHits searchHits = response.getHits();    // 4.1.获取总条数    long total = searchHits.getTotalHits().value;    // 4.2.文档数组    SearchHit[] hits = searchHits.getHits();    // 4.3.遍历    List hotels = new ArrayList();    for (SearchHit hit : hits) {        // 获取文档source        String json = hit.getSourceAsString();        // 反序列化        HotelDoc hotelDoc = JSON.parSEObject(json, HotelDoc.class);// 放入***        hotels.add(hotelDoc);    }    // 4.4.封装返回    return new PageResult(total, hotels);}

                    二、酒店结果过滤

                    ⌚需求分析

                    需求: 添加品牌、城市、星级、价格等过滤功能

                    在页面搜索框下,会有几个过滤条件:

                    请求参数如下:

                    包含的过滤条件有:

                    • brand:品牌值
                    • city:城市
                    • minPrice~maxPrice:价格范围
                    • starName:星级

                      我们需要做两件事情:

                      • 修改请求参数的对象RequestParams,接收上述参数
                      • 修改业务逻辑,在搜索条件之外,添加一些过滤条件

                        修改实体类

                        @Datapublic class RequestParams {    private String key;    private Integer page;    private Integer size;    private String sortBy;    // 下面是新增的过滤条件参数    private String city;    private String brand;    private String starName;    private Integer minPrice;    private Integer maxPrice;}

                        ⏰修改搜索业务

                        在HotelService的search方法中,只有一个地方需要修改:requet.source().query( … )其中的查询条件。

                        在之前的业务中,只有match查询,根据关键字搜索,现在要添加条件过滤,包括:

                        • 品牌过滤:是keyword类型,用term查询
                        • 星级过滤:是keyword类型,用term查询
                        • 价格过滤:是数值类型,用range查询
                        • 城市过滤:是keyword类型,用term查询

                          多个查询条件组合,肯定是boolean查询来组合:

                          • 关键字搜索放到must中,参与算分
                          • 其它过滤条件放到filter中,不参与算分

                            因为条件构建的逻辑比较复杂,这里先封装为一个函数:

                            buildBasicQuery 函数如下:

                            private void buildBasicQuery(RequestParams params, SearchRequest request) {    // 1.构建BooleanQuery    BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();    // 2.关键字搜索    String key = params.getKey();    if (key == null || "".equals(key)) {        boolQuery.must(QueryBuilders.matchAllQuery());    } else {        boolQuery.must(QueryBuilders.matchQuery("all", key));    }    // 3.城市条件    if (params.getCity() != null && !params.getCity().equals("")) {        boolQuery.filter(QueryBuilders.termQuery("city", params.getCity()));    }    // 4.品牌条件    if (params.getBrand() != null && !params.getBrand().equals("")) {        boolQuery.filter(QueryBuilders.termQuery("brand", params.getBrand()));    }    // 5.星级条件    if (params.getStarName() != null && !params.getStarName().equals("")) {        boolQuery.filter(QueryBuilders.termQuery("starName", params.getStarName()));    }// 6.价格    if (params.getMinPrice() != null && params.getMaxPrice() != null) {        boolQuery.filter(QueryBuilders                         .rangeQuery("price")                         .gte(params.getMinPrice())                         .lte(params.getMaxPrice())                        );    }// 7.放入source    request.source().query(boolQuery);}

                            再次重启运行即可。

                            ✅效果图

                            ⛵小结

                            如果这篇【文章】有帮助到你,希望可以给【Bug 终结者】点个赞,创作不易,如果有对【后端技术】、【前端领域】感兴趣的小可爱,也欢迎关注❤️❤️❤️ 【Bug 终结者】❤️❤️❤️,我将会给你带来巨大的【收获与惊喜】!