黑马旅游网第一天

学完linux,mybatis-plus、docker、mq、ElasticSearch后可进行黑马旅游网的学习,本小项目主要是为了对ElasticSearch、mq应用的练习

一:数据初始化

  1. 导入数据:

表名

CREATE TABLE `tb_hotel` (
  `id` bigint(20) NOT NULL COMMENT '酒店id',
  `name` varchar(255) NOT NULL COMMENT '酒店名称;例:7天酒店',
  `address` varchar(255) NOT NULL COMMENT '酒店地址;例:航头路',
  `price` int(10) NOT NULL COMMENT '酒店价格;例:329',
  `score` int(2) NOT NULL COMMENT '酒店评分;例:45,就是4.5分',
  `brand` varchar(32) NOT NULL COMMENT '酒店品牌;例:如家',
  `city` varchar(32) NOT NULL COMMENT '所在城市;例:上海',
  `star_name` varchar(16) DEFAULT NULL COMMENT '酒店星级,从低到高分别是:1星到5星,1钻到5钻',
  `business` varchar(255) DEFAULT NULL COMMENT '商圈;例:虹桥',
  `latitude` varchar(32) NOT NULL COMMENT '纬度;例:31.2497',
  `longitude` varchar(32) NOT NULL COMMENT '经度;例:120.3925',
  `pic` varchar(255) DEFAULT NULL COMMENT '酒店图片;例:/img/1.jpg',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  1. 项目导入
    在这里插入图片描述

  2. 修改数据库链接数据
    我这里的mysql是8.0.17版本
    yml文件

  3. 创建映射

PUT /hotel
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1
  },
  "mappings": {
    "properties": {
      "id": {
        "type": "keyword"
      },
      "name":{
        "type": "text",
        "analyzer": "ik_max_word",
        "copy_to": "all"
      },
      "address":{
        "type": "keyword",
        "index": false
      },
      "price":{
        "type": "integer"
      },
      "score":{
        "type": "integer"
      },
      "brand":{
        "type": "keyword",
        "copy_to": "all"
      },
      "city":{
        "type": "keyword",
        "copy_to": "all"
      },
      "starName":{
        "type": "keyword"
      },
      "business":{
        "type": "keyword"
      },
      "location":{
        "type": "geo_point"
      },
      "pic":{
        "type": "keyword",
        "index": false
      },
      "all":{
        "type": "text",
        "analyzer": "ik_max_word"
      }
    }
  }
}
几个特殊字段说明:

- location:地理坐标,里面包含精度、纬度
  - geo_point:有纬度(latitude)和经度(longitude)确定一个点。如:“32.8723,120.2981”
- all:一个组合字段,其目的是将多字段的值 利用copy_to合并,提供给用户搜索
  - copy_to:将此字段数据copy到一个目标字段中
  - 一个搜索条件,查询多个字段。
  - 自定义一个字段:all
  - all字段的取值 = name + city + brand

二:数据初始化

在这里插入图片描述

请求地址:http://localhost:8089/hotel/init
请求方式:get
请求参数:无
返回内容:批量查询mysql数据,保存到es服务器中
相应数据:导入的数据条数

后端代码展示
web层:

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

    @Autowired
    private HotelService hotelService;

    @GetMapping("/init")
    public Integer init() {
        return hotelService.init();
    }
}

serviceimpl层:

 @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Override
    public Integer init() throws IOException {
        List<Hotel> hotelList = hotelMapper.selectList(null);

        BulkRequest request = new BulkRequest();
        for (Hotel hotel : hotelList) {
            IndexRequest indexRequest = new IndexRequest("hotel");
            indexRequest.id(hotel.getId().toString());
            indexRequest.source(JSON.toJSONString(hotel),XContentType.JSON);
            request.add(indexRequest);
        }
        restHighLevelClient.bulk(request,RequestOptions.DEFAULT);
        return hotelList.size();
    }

结果展示:
初始化数据

三:酒店搜索和分页

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

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

后端代码展示:

web层:

 @PostMapping("/list")
    public PageResult list(@RequestBody RequestParams requestParams) throws IOException {
      return  hotelService.list(requestParams);
    }

这里为了以后方便使用,对解析数据和Builder进行了抽取
serviceImpl层:

  @Override
    public PageResult list(RequestParams requestParams) throws IOException {
        SearchRequest request = new SearchRequest("hotel");
        SearchSourceBuilder builder = buildBasicQuery(requestParams);
        request.source(builder);
        SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
        PageResult pageResult = handlerResponse(response);
        return pageResult;
    }
//使用bool是因为用户的搜索是多条件的,可进行多条件搜索。
private SearchSourceBuilder buildBasicQuery(RequestParams requestParams){
        SearchSourceBuilder builder = new SearchSourceBuilder();
        BoolQueryBuilder boolBuilder = QueryBuilders.boolQuery();
 if (StringUtils.isEmpty(requestParams.getKey())) {
            boolBuilder.must(QueryBuilders.matchAllQuery());
        }else {
            boolBuilder.must(QueryBuilders.matchQuery("all",requestParams.getKey()));
            boolBuilder.filter(QueryBuilders.termQuery("all",requestParams.getKey()));
        }
        builder.query(boolBuilder);
         builder.from((requestParams.getPage()-1)*requestParams.getSize());
        builder.size(requestParams.getSize());
}
/**
 * 抽取解析相应的数据
 *   解析数据(总数,数据列表,高亮的字段)
 */
private PageResult handlerResponse(SearchResponse response) {
    SearchHits searchHits = response.getHits();
    //获取总数
    long total = searchHits.getTotalHits().value;
    //获取所有文档数据
    SearchHit[] hits = searchHits.getHits();
    List<Hotel> hotels = new ArrayList<>();
    //循环遍历
    for (SearchHit hit : hits) {
        //获取source
        String json = hit.getSourceAsString();
        //解析成Hotel对象,并没有高亮的处理
        Hotel hotel = JSON.parseObject(json, Hotel.class);
        //添加到hotels中
        hotels.add(hotel);
    }
    return new PageResult(total,hotels);
}

四:酒店结果过滤

原图

请求体参数
后端代码展示:
在serviceImpl层,新增代码;
在这里插入图片描述

//完整代码
private SearchSourceBuilder buildBasicQuery(RequestParams requestParams){
        SearchSourceBuilder builder = new SearchSourceBuilder();
        BoolQueryBuilder boolBuilder = QueryBuilders.boolQuery();

        if (StringUtils.isEmpty(requestParams.getKey())) {
            boolBuilder.must(QueryBuilders.matchAllQuery());
        }else {
            boolBuilder.must(QueryBuilders.matchQuery("all",requestParams.getKey()));
            boolBuilder.filter(QueryBuilders.termQuery("all",requestParams.getKey()));
        }
        if (BeanUtil.isNotEmpty(requestParams.getCity())){
            boolBuilder.must(QueryBuilders.matchQuery("city",requestParams.getCity()));
        }
        if (BeanUtil.isNotEmpty(requestParams.getBrand())){
            boolBuilder.must(QueryBuilders.matchQuery("brand",requestParams.getBrand()));
        }
        if (BeanUtil.isNotEmpty(requestParams.getStarName())){
            boolBuilder.must(QueryBuilders.matchQuery("starName",requestParams.getStarName()));
        }
        if (requestParams.getMaxPrice() != null && requestParams.getMinPrice() != null){
            boolBuilder.must(QueryBuilders.rangeQuery("price").gte(requestParams.getMinPrice()).lte(requestParams.getMaxPrice()));
        }
        builder.query(boolBuilder);
        builder.from((requestParams.getPage()-1)*requestParams.getSize());
        builder.size(requestParams.getSize());
        return builder;
    }

五:高亮展示

在这里插入图片描述
后端代码展示:

在serviceImpl层,新增代码:
在这里插入图片描述

在这里插入图片描述


        HighlightBuilder highlighter = new HighlightBuilder();
        highlighter.preTags("<font color=red>");
        highlighter.postTags("</font>");
        highlighter.field("name");
        highlighter.requireFieldMatch(false);
        builder.highlighter(highlighter);
     for (SearchHit hit : hits) {
            String source = hit.getSourceAsString();
            Hotel hotel = JSON.parseObject(source, Hotel.class);

            Map<String, HighlightField> map = hit.getHighlightFields();
            HighlightField highlightField = map.get("name");
            if (highlightField != null){
                String name = highlightField.getFragments()[0].toString();
                hotel.setName(name);
            }
            hotels.add(hotel);
        }

六:酒店竞价排名

广告优先

画面
后端代码展示:
在这里插入图片描述


        FunctionScoreQueryBuilder scoreQuery = QueryBuilders.functionScoreQuery(boolBuilder, new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery("ad", 1),
                        ScoreFunctionBuilders.weightFactorFunction(10))});
        builder.query(scoreQuery);

serviceImpl完整代码展示:

package cn.itcast.hotel.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.itcast.hotel.domain.Hotel;
import cn.itcast.hotel.domain.PageResult;
import cn.itcast.hotel.domain.RequestParams;
import cn.itcast.hotel.mapper.HotelMapper;
import cn.itcast.hotel.service.HotelService;
import com.alibaba.fastjson.JSON;
import org.apache.lucene.search.QueryCache;
import org.apache.lucene.util.QueryBuilder;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.rescore.QueryRescorerBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
public class HotelServiceImpl implements HotelService {

    @Autowired
    private HotelMapper hotelMapper;

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Override
    public Integer init() throws IOException {
        List<Hotel> hotelList = hotelMapper.selectList(null);

        BulkRequest request = new BulkRequest();
        for (Hotel hotel : hotelList) {
            IndexRequest indexRequest = new IndexRequest("hotel");
            indexRequest.id(hotel.getId().toString());
            indexRequest.source(JSON.toJSONString(hotel),XContentType.JSON);
            request.add(indexRequest);
        }
        restHighLevelClient.bulk(request,RequestOptions.DEFAULT);
        return hotelList.size();
    }

    @Override
    public PageResult list(RequestParams requestParams) throws IOException {
        SearchRequest request = new SearchRequest("hotel");
        SearchSourceBuilder builder = buildBasicQuery(requestParams);
        request.source(builder);
        SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
        PageResult pageResult = handlerResponse(response);
        return pageResult;
    }
    private SearchSourceBuilder buildBasicQuery(RequestParams requestParams){
        SearchSourceBuilder builder = new SearchSourceBuilder();
        BoolQueryBuilder boolBuilder = QueryBuilders.boolQuery();

        if (StringUtils.isEmpty(requestParams.getKey())) {
            boolBuilder.must(QueryBuilders.matchAllQuery());
        }else {
            boolBuilder.must(QueryBuilders.matchQuery("all",requestParams.getKey()));
            boolBuilder.filter(QueryBuilders.termQuery("all",requestParams.getKey()));
        }
        if (BeanUtil.isNotEmpty(requestParams.getCity())){
            boolBuilder.must(QueryBuilders.matchQuery("city",requestParams.getCity()));
        }
        if (BeanUtil.isNotEmpty(requestParams.getBrand())){
            boolBuilder.must(QueryBuilders.matchQuery("brand",requestParams.getBrand()));
        }
        if (BeanUtil.isNotEmpty(requestParams.getStarName())){
            boolBuilder.must(QueryBuilders.matchQuery("starName",requestParams.getStarName()));
        }
        if (requestParams.getMaxPrice() != null && requestParams.getMinPrice() != null){
            boolBuilder.must(QueryBuilders.rangeQuery("price").gte(requestParams.getMinPrice()).lte(requestParams.getMaxPrice()));
        }
        //builder.query(boolBuilder);

        FunctionScoreQueryBuilder scoreQuery = QueryBuilders.functionScoreQuery(boolBuilder, new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery("ad", 1),
                        ScoreFunctionBuilders.weightFactorFunction(10))});
        builder.query(scoreQuery);

        builder.from((requestParams.getPage()-1)*requestParams.getSize());
        builder.size(requestParams.getSize());

        HighlightBuilder highlighter = new HighlightBuilder();
        highlighter.preTags("<font color=red>");
        highlighter.postTags("</font>");
        highlighter.field("name");
        highlighter.requireFieldMatch(false);
        builder.highlighter(highlighter);
        return builder;
    }
    /**
     * 抽取解析相应的数据
     *   解析数据(总数,数据列表,高亮的字段)
     */
    private PageResult handlerResponse(SearchResponse response) {

        SearchHit[] hits = response.getHits().getHits();
        ArrayList<Hotel> hotels = new ArrayList<>();
        for (SearchHit hit : hits) {
            String source = hit.getSourceAsString();
            Hotel hotel = JSON.parseObject(source, Hotel.class);

            Map<String, HighlightField> map = hit.getHighlightFields();
            HighlightField highlightField = map.get("name");
            if (highlightField != null){
                String name = highlightField.getFragments()[0].toString();
                hotel.setName(name);
            }
            hotels.add(hotel);
        }
        return new PageResult(response.getHits().getTotalHits().value, hotels);
    }
}

好了,第一天学习完毕

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

皇家小黄

创作不易!!!!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值