es--elasticsearch--篇四:完成各个功能的接口,实现完整的文本搜索案例

  • 先实现基本分页查询

  • 1)先新建了一个requestParams类,方便接收前端页面传过来的查询条件参数
  • 2)业务接口继承了mybatisplus的业务接口
  • 3)编写业务代码:
  • @Service
    public class HotelServiceImpl
            extends ServiceImpl<HotelMapper,Hotel>
            implements HotelService {
    
        @Autowired
        private RestHighLevelClient client;//如果容器当中没有注入,需要在启动类中注入
    
        @Override//ctrl + alt + b
        public PageResult search(RequestParams params) {
            try {
                //request
                SearchRequest request = new SearchRequest("hotel");
                //dsl
                String key = params.getKey();
                if(key==null || "".equals(key)){
                    //如果没有输如要搜索的内容,就全搜
                    request.source().query(QueryBuilders.matchAllQuery());
                }else{
                    request.source().query(QueryBuilders.matchQuery("all",key));
                }
                //分页
                int page = params.getPage();
                int size = params.getSize();
                request.source().from((page-1)*size).size(size);
                //发请求
                SearchResponse response = client.search(request, RequestOptions.DEFAULT);
                //处理结果
                PageResult pageResult = resHandler(response);
                return pageResult;
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
        //处理响应结果的方法
        public PageResult resHandler(SearchResponse response){
            SearchHits searchHits = response.getHits();
            //total
            long total = searchHits.getTotalHits().value;
            System.out.println("total"+total);
            //文档数组在hits>hits中
            SearchHit[] hits = searchHits.getHits();
            List<HotelDoc> hotelDocList = new ArrayList<>();
            for (SearchHit hit : hits) {
                String hotelJson = hit.getSourceAsString();
                HotelDoc hotelDoc = JSON.parseObject(hotelJson, HotelDoc.class);
    
                //高亮相关
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                if(highlightFields != null){
                    HighlightField highlightField = highlightFields.get("name");
                    if (highlightField != null){
                        String name = highlightField.getFragments()[0].string();
                        //替换为高亮的name
                        hotelDoc.setName(name);
                    }
                }
                //搜索的hoteldoc集合结果
                hotelDocList.add(hotelDoc);
            }
            return new PageResult(total,hotelDocList);
        }
    }
  • 4)编写接口:
  • 5)启动项目,postman测试接口
  •   项目启动报了如下错,根据提示,需要添加字符编码和useSSL=false

  •  解决办法:
  • spring.datasource.url=jdbc:mysql://192.168.8.171:3306/hotel?characterEncoding=utf8&useSSL=false
  • 启动之后,用postman测试接口是否能够正常查
  • 上面我们实现了基本查询,现在我们想要实现多条件过滤查询

  • 1)那么我们需要修改RequestParams类
  • 2)修改业务实现类,用布尔查询,增加四个查询条件
  • 我们将这些查询条件抽成一个方法,便于后续使用
  • //抽出来的布尔查询条件的方法
        private void buildBoolQuery(RequestParams params, SearchRequest request) {
            //dsl,构建一个复合查询
            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
            String key = params.getKey();
            if(key==null || "".equals(key)){
                //如果没有输如要搜索的内容,就全搜
                //request.source().query(QueryBuilders.matchAllQuery());
                boolQuery.must(QueryBuilders.matchAllQuery());
            }else{
                //request.source().query(QueryBuilders.matchQuery("all",key));
                boolQuery.must(QueryBuilders.matchQuery("all",key));
            }
            //围绕查询加条件
            //城市:
            if(params.getCity()!=null && !"".equals(params.getCity())){
                boolQuery.filter(QueryBuilders.termQuery("city", params.getCity()));
            }
            //品牌:
            if(params.getBrand()!=null && !"".equals(params.getBrand())){
                boolQuery.filter(QueryBuilders.termQuery("brand", params.getBrand()));
            }
            //星级:
            if(params.getStarName()!=null && !"".equals(params.getStarName())){
                boolQuery.filter(QueryBuilders.termQuery("starName", params.getStarName()));
            }
            //价格:
            if(params.getMinPrice()!=null && !"".equals(params.getMinPrice())&&
                    params.getMaxPrice()!=null && !"".equals(params.getMaxPrice())){
                boolQuery.filter(QueryBuilders.rangeQuery("price")
                        .gte(params.getMinPrice())
                        .lte(params.getMaxPrice()));
            }
            //设置条件
            request.source().query(boolQuery);
        }
  • 3)postman测试接口bool复合条件查询成功
  • 我们搜索附近的酒店,需要增加条件地理坐标

  • 1)修改请求参数类,增加坐标属性,提供get,set方法
  • 2)修改业务层实现类的方法,根据离指定地理坐标的距离升序排列(由近到远)
  •   //排序
                String location = params.getLocation();
                if(location!=null && !"".equals(location)){
                    request.source().sort(
                            SortBuilders.geoDistanceSort("location",new GeoPoint(location))//相对的地点坐标
                            .order(SortOrder.ASC) //排序方式:升序
                            .unit(DistanceUnit.KILOMETERS)  //单位:千米
                    );
                }
  • 3)但是我们现在虽然可以按距离排序,但是还看不到酒店距离目标地点的距离是多远
  • 因为我们的hoteldoc里面并没有距离这个属性,所以我们需要加
  • 将查出来的距离放到对象的属性中
  •  //得到距离
                Object[] sortValues = hit.getSortValues();
                //判断并取出数组当中的距离
                if(sortValues!=null && sortValues.length>0){
                    Object sortValue = sortValues[0];
                    //将距离放进对象中
                    hotelDoc.setDistance(sortValue);
                }
  • 4)postman测试成功:

  • 使用算分函数,给搜索出来的文档置顶(类似淘宝推广置顶)

  • 1)先去请求参数类当中增加“是否广告置顶”的属性:isAD
  • 2)然后在索引库当中选几个文档,将其设置为置顶广告
  • 这里要注意修改文档时选用哪一种方式:
    • 这里我们修改了数据库当中倒序排序的前四条记录 
    • POST /hotel/_update/2062643512
      {
        "doc":{
          "isAD":true
        }
      }
      POST /hotel/_update/2060618247
      {
        "doc":{
          "isAD":true
        }
      }
      POST /hotel/_update/2060510277
      {
        "doc":{
          "isAD":true
        }
      }
      POST /hotel/_update/2058250574
      {
        "doc":{
          "isAD":true
        }
      }
       
    • 2)有了广告置顶之后,我们现在就需要写算分控制来控制置顶效果了
    •  //算分控制
              FunctionScoreQueryBuilder functionScoreQuery =
                      QueryBuilders.functionScoreQuery(
                              //复合查询dsl其中的算分条件
                              boolQuery,
                              new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                                      new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                              //过滤条件
                                              QueryBuilders.termQuery("isAD",true),
                                              //算分方法
                                              ScoreFunctionBuilders.weightFactorFunction(100)
                                      )
                              }
                      );
      
              //设置条件
              request.source().query(functionScoreQuery);
    • 那么我们整个bool查询的条件如下:
    •    //抽出来的布尔查询条件的方法
          private void buildBoolQuery(RequestParams params, SearchRequest request) {
              //dsl,构建一个复合查询
              BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
              String key = params.getKey();
              if(key==null || "".equals(key)){
                  //如果没有输如要搜索的内容,就全搜
                  //request.source().query(QueryBuilders.matchAllQuery());
                  boolQuery.must(QueryBuilders.matchAllQuery());
              }else{
                  //request.source().query(QueryBuilders.matchQuery("all",key));
                  boolQuery.must(QueryBuilders.matchQuery("all",key));
              }
              //围绕查询加条件
              //城市:
              if(params.getCity()!=null && !"".equals(params.getCity())){
                  boolQuery.filter(QueryBuilders.termQuery("city", params.getCity()));
              }
              //品牌:
              if(params.getBrand()!=null && !"".equals(params.getBrand())){
                  boolQuery.filter(QueryBuilders.termQuery("brand", params.getBrand()));
              }
              //星级:
              if(params.getStarName()!=null && !"".equals(params.getStarName())){
                  boolQuery.filter(QueryBuilders.termQuery("starName", params.getStarName()));
              }
              //价格:
              if(params.getMinPrice()!=null && !"".equals(params.getMinPrice())&&
                      params.getMaxPrice()!=null && !"".equals(params.getMaxPrice())){
                  boolQuery.filter(QueryBuilders.rangeQuery("price")
                          .gte(params.getMinPrice())
                          .lte(params.getMaxPrice()));
              }
              //算分控制
              FunctionScoreQueryBuilder functionScoreQuery =
                      QueryBuilders.functionScoreQuery(
                              //复合查询dsl其中的算分条件
                              boolQuery,
                              new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                                      new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                              //过滤条件
                                              QueryBuilders.termQuery("isAD",true),
                                              //算分方法
                                              ScoreFunctionBuilders.weightFactorFunction(100)
                                      )
                              }
                      );
      
              //设置条件
              request.source().query(functionScoreQuery);
          }
    • 3)测试就可以看到我们设置的置顶文档显示在上面了

猜你喜欢

转载自blog.csdn.net/qq_60555957/article/details/127645584