目标:

我们实现六部分功能:

  • 酒店搜索和分页
  • 酒店结果过滤
  • 我周边的酒店
  • 酒店竞价排名
  • 给黑马旅游添加排序功能
  • 给黑马旅游添加搜索关键字高亮效果

    一.酒店搜索和分页

    image.png
    请求信息: ```java

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

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

      定义实体类

1)请求参数

  1. package cn.itcast.hotel.pojo;
  2. import lombok.Data;
  3. @Data
  4. public class RequestParams {
  5. private String key;
  6. private Integer page;
  7. private Integer size;
  8. private String sortBy;
  9. }

2)返回值

package cn.itcast.hotel.pojo;

import lombok.Data;

import java.util.List;

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

定义controller

@RestController
@RequestMapping("/hotel")
public class HotelController {

    @Autowired
    private IHotelService hotelService;
    // 搜索酒店数据
    @PostMapping("/list")
    public PageResult search(@RequestBody RequestParams params){
        return hotelService.search(params);
    }
}

实现业务

1)定义方法

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

2) 注册bean

@Bean
public RestHighLevelClient client(){
    return  new RestHighLevelClient(RestClient.builder(
        HttpHost.create("http://192.168.150.101:9200")
    ));
}

3)实现search方法

@Override
public 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)) {
            request.source().query(QueryBuilders.matchAllQuery());
        } else {
            request.source().query(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<HotelDoc> 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);
}

二.酒店结果过滤

1.需求分析

image.png
包含的过滤条件有:

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

我们需要做两件事情:

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

    2.修改实体类

    @Data
    public 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;
    }
    

    3.修改搜索业务

    image.png

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

    三.我周边的酒店

    1.需求分析

    ```java 我们要做的事情就是基于这个location坐标,然后按照距离对周围酒店排序。实现思路如下:

  • 修改RequestParams参数,接收location字段

  • 修改search方法业务逻辑,如果location有值,添加根据geo_distance排序的功能
<a name="sIFpo"></a>
### 2.修改实体类
```java
package cn.itcast.hotel.pojo;

import lombok.Data;

@Data
public 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;
    // 我当前的地理坐标
    private String location;
}

3.功能实现

 //TODO:距离排序功能
            String location = params.getLocation();
            if (StringUtils.isNotBlank(location)) {
                //SortBuilders.geoDistanceSort构建距离排序   new GeoPoint(location) 当前自己的坐标
                source.sort(SortBuilders.geoDistanceSort("location", new GeoPoint(location))
                        //升序排序
                        .order(SortOrder.ASC)
                        //单位是km
                        .unit(DistanceUnit.KILOMETERS)
                );
            }

4.排序距离显示

1)修改HotelDoc类,添加距离字段

 // 排序时的 距离值
    private Object distance;

2)修改解析响应结果

 //TODO:获取距离排序值
            Object[] sortValues = hit.getSortValues();
            if (sortValues.length > 0) {
                hotelDoc.setDistance(sortValues[0]);
            }
            list.add(hotelDoc);
        }

四.酒店竞价排名

1.需求分析

image.png
image.png
image.png

2.修改HotelDoc实体

    //是否广告
    private Boolean isAD;

3.添加广告标记

挑几个酒店,添加isAD字段,设置为true:

POST /hotel/_update/1902197537
{
    "doc": {
        "isAD": true
    }
}
POST /hotel/_update/2056126831
{
    "doc": {
        "isAD": true
    }
}

4.添加算分函数查询

    //TODO: 算分控制
        FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(
                boolQuery,  //原始的查询条件
                new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                        //算分函数
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                //过滤条件
                                QueryBuilders.termQuery("isAD", true),
                                //计算得分的方法  weight常量分值
                                ScoreFunctionBuilders.weightFactorFunction(10)
                        )
                }
                //加权模式
        ).boostMode(CombineFunction.MULTIPLY); //默认相乘
        source.query(functionScoreQueryBuilder);

五.给黑马旅游添加排序功能

1.需求分析

image.png
image.png

2.功能实现

 //TODO:排序功能
            if (StringUtils.isNotBlank(params.getSortBy())) {
                //根据score字段  降序
                FieldSortBuilder sortBuilder = SortBuilders.fieldSort("score");
                sortBuilder.order(SortOrder.DESC);

                //根据price排序,升序
                FieldSortBuilder priceBuilder = SortBuilders.fieldSort("price");
                priceBuilder.order(SortOrder.ASC);

                source.sort(sortBuilder);
                source.sort(priceBuilder);
            }

六.给黑马旅游添加搜索关键字效果

1.需求分析

image.png

2.代码实现

  //TODO:高亮
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("name"); //那个字段高亮
            highlightBuilder.preTags("<em>"); //前置标签
            highlightBuilder.postTags("</em>"); //后置标签
            highlightBuilder.requireFieldMatch(false); //关闭默认设置  默认高亮处理不允许搜索字段和高亮字段不一致
            source.highlighter(highlightBuilder);

3.解析响应结果

 //TODO:单独获取高亮处理结果
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            //判断
            if (!CollectionUtils.isEmpty(highlightFields)) {
                // 文档name字段的高亮处理结果
                HighlightField highlightField = highlightFields.get("name");
                if (highlightField != null) {
                    //拼接高亮字段
                    String highlightResultStr = StringUtils.join(highlightField.getFragments());
                    //用高亮处理结果替换之前的name字段内容
                    hotelDoc.setName(highlightResultStr);
                }
            }