springboot-es(elasticsearch)搜索项目

news2024/11/15 8:59:43

目标界面

html页面

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>途牛旅游</title>

  <link rel="stylesheet" href="https://a.amap.com/jsapi_demos/static/demo-center/css/demo-center.css"/>
  <link rel="stylesheet" href="./css/index.css"/>
  <link rel="stylesheet" href="./css/banner.css"/>
</head>

<body>
<div id="app" @click="showOps=false">
  <div style="width: 70%;text-align: center;margin: auto;">
    <div class="banner">
      <div class="logo">
        <span>
          <div class="logo-ch">途牛旅游</div>
          <div class="logo-en">www.apesource.com</div>
        </span>
      </div>
      <div >
        <input type="checkbox" id="toggle-button" v-model="isTest">
        <!--label中的for跟input的id绑定。作用是在点击label时选中input或者取消选中input-->
        <label for="toggle-button" class="button-label">
          <span class="circle"></span>
          <span class="text on">测试</span>
          <span class="text off">正式</span>
        </label>
      </div>
    </div>
    <!--关键字搜索-->
    <div class="search-bar">
      <div class="input-box">
        <input title="输入关键字搜索酒店" v-model="params.key" type="text" @click.stop="" @focus="showOps=true"
               @keyup="handleKeyUp($event)">
        <button @click="handleSearch">搜索</button>
      </div>
      <div id="complete-box" v-show="showOps && ops.length > 0">
        <div v-for="(item, i) in ops" :key="i" @click="handleSearch" @mouseover="opsIndex = i"
             :style='{"background-color": opsIndex===i ? "#EEE" : "#fff"}'>{{item}}
        </div>
      </div>
    </div>
    <!-- 已选择过滤项 -->
    <div class="selected-ops">
      <div class="open">全部结果:</div>
      <div class="selected-op" v-for="(v, k) in params.filters" :key="k" @click="deleteFilter(k)">
        {{filterNames[k]}}:<span>{{v}} <span class='close'>×</span></span>
      </div>
    </div>
    <!--过滤项-->
    <div class="filter-list">
      <div v-for="(v, k) in remainFilter" :key="k">
        <div class="filter-box">
          <div class="f-key"><strong>{{filterNames[k]}}</strong></div>
          <div class="column-divider"></div>
          <div class="f-items">
            <div class="f-item" @click="clickFilter(k, o)" v-for="(o, j) in v" :key="j"><a href="javascript:void(0)">
              {{o}}</a></div>
          </div>
        </div>
        <div class="row-divider"></div>
      </div>
      <div class="filter-box">
        <div class="f-key"><strong>价格</strong></div>
        <div class="column-divider"></div>
        <div class="f-items">
          <div class="f-item" @click="clickFilter('price','0-100')"><a href="javascript:void(0)">100元以下</a></div>
          <div class="f-item" @click="clickFilter('price','100-300')"><a href="javascript:void(0)">100-300元</a></div>
          <div class="f-item" @click="clickFilter('price','300-600')"><a href="javascript:void(0)">300-600元</a></div>
          <div class="f-item" @click="clickFilter('price','600-1500')"><a href="javascript:void(0)">600-1500元</a></div>
          <div class="f-item" @click="clickFilter('price','1500-0')"><a href="javascript:void(0)">1500元以上</a></div>
        </div>
      </div>
    </div>
    <!-- 排序 -->
    <div class="top-ban">
      <!--排序条件-->
      <div class="sort-items">
        <div class="sort-item" v-for="(item, i) in sortItems" :key="i">
          <a :class="{selected: params.sortBy===item.key}" @click="params.sortBy=item.key"
             href="javascript:void(0)">{{item.text}}</a>
          |
        </div>
      </div>

      <!--分页条-->
      <div class="top-pagination">
        <span>共 <i style="color: #222;">{{total}}</i> 家酒店</span>
        <span><i style="color: red;">{{params.page}}</i>/{{totalPage}}</span>
        <a class="btn-arrow" href="#" style="display: inline-block" @click="prePage">&lt;</a>
        <a class="btn-arrow" href="#" style="display: inline-block" @click="nextPage">&gt;</a>
      </div>
    </div>
    <div class="row-divider" style="margin-bottom: 5px; width: 100%"></div>
    <!--酒店列表-->
    <div style="display: flex; justify-content: space-between;">

      <div class="hotel-list">
        <div class="hotel-box" v-for="(hotel,i) in hotels" :key="i"
             style="display: flex;justify-content: space-between;" @mouseover="handleMarkerFocus(hotel)">
          <div class="ad-mark" v-if="hotel.isAD">
            <img src="./img/ad.png" width="25"/>
          </div>
          <div style="width: 0;"></div>
          <div style="width: 25%"><img width="100%" :src="hotel.pic"></div>
          <div class="hotel-info">
            <div class="hotel-name" v-html="hotel.name">
            </div>
            <div class="star-name"> {{hotel.starName}}</div>
            <div class="address">
              位于 <span style="color: #BC8516;">{{hotel.business}}</span> 周边,{{hotel.address}}
            </div>
            <div class="order"> 1分钟前有人预订了该酒店</div>
            <div v-if="hotel.distance" class="address">距离您 {{hotel.distance.toFixed(2)}} km</div>
          </div>
          <div style="text-align: left; width: 15%;">
            <div>
              <b style="color: #f60;">¥</b> <span id='hotel-price'>{{hotel.price}}</span> <span
                style="font-size: 0.2em; color: #999;">起</span>
            </div>
            <div class='btn'>立即预定</div>
            <div>
              <span class="hotel-score">{{hotel.score / 10}}分</span> /<span>5分</span>
            </div>
          </div>
        </div>
      </div>
      <div :class='{"map-box": true, "fixed-map": isFixed}' :style="{left: ml + 'px'}">
        <div class="map-head">地图预览</div>
        <div class="amap" id="container"></div>
        <div class="map-geo" @click="getGeoLoc">
          <img src="https://a.amap.com/jsapi/static/image/plugin/waite.png" v-show="loading">
        </div>
      </div>
    </div>
  </div>
</div>

<script src="./js/vue.js"></script>
<script src="./js/axios.min.js"></script>
<script src="./js/amap.min.js"></script>
<script type="text/javascript" src="https://webapi.amap.com/maps?v=2.0&key=ddd292c88aa1bad9c04891a47724f40a"></script>


<script>
  // 设置后台服务地址
  axios.defaults.baseURL = "http://localhost:8089";
  axios.defaults.timeout = 3000;

  let app = new Vue({
    el: "#app",
    data: {
      isTest: false,
      filterNames: {
        brand: "品牌",
        city: "城市",
        starName: "星级",
        price: "价格",
      },
      filterList: {},// 过滤项的假数据
      sortItems: [
        {
          key: "default",
          text: "默认",
        },
        {
          key: "score",
          text: "评价",
        },
        {
          key: "price",
          text: "价格",
        }
      ],// 排序字段的假数据
      hotels: [],// 酒店数据
      total: 0, // 总条数
      totalPage: 0, // 总页数
      params: {
        key: "", // 搜索关键字
        page: 1, // 当前页码
        size: 5, // 每页大小
        sortBy: "default",// 排序字段
        filters: {}, // 过滤字段
      },
      map: {},// 地图对象
      loc: "", // 地图标记
      ml: 0, // 控制地图位置
      geolocation:{}, // 定位系统
      loading: false, // 是否在定位
      currentHotel: {},
      ops: [],
      showOps: false,
      opsIndex: -1,
      isFixed: false,
      testFilterData: {
        "city": ["上海", "北京", "深圳", "杭州"],
        "starName": ["四星","五星","二钻","三钻","四钻","五钻"],
        "brand": ["7天酒店",  "如家","速8", "皇冠假日","华美达","万怡","喜来登","万豪","和颐","希尔顿"],
      },
    },
    watch: {
      "params.sortBy"() {
        // 每当page改变,当前代码就会执行。搜索一下
        this.search(this.loc);
      },
      "params.page"() {
        // 每当page改变,当前代码就会执行。搜索一下
        this.search(this.loc);
      },
      "params.filters": {
        deep: true,
        handler() {
          this.search();
          // 获取过滤项
          this.getFilter();
        }
      },
      opsIndex() {
        if (this.opsIndex !== -1) {
          this.params.key = this.ops[this.opsIndex]
        }
      }
    },
    created() {
      // 页面加载时,先搜索一下
      this.search();
      // 获取过滤项
      this.getFilter();
    },
    mounted() {
      //初始化地图
      this.map = new AMap.Map('container', {
        resizeEnable: true, //是否监控地图容器尺寸变化
        zoom: 11, //初始地图级别
        center: [120.0, 32.0], //初始地图中心点
      });
      // 初始化定位系统
      AMap.plugin('AMap.Geolocation', () => {
        this.geolocation = new AMap.Geolocation({
          position: 'RT',    //定位按钮的停靠位置
          buttonOffset: new AMap.Pixel(10, 20),//定位按钮与设置的停靠位置的偏移量,默认:Pixel(10, 20)
          zoomToAccuracy: true,   //定位成功后是否自动调整地图视野到定位点
        });

        // AMap.Event.addListener(geolocation, 'complete', result => {
        //   if (this.params.page !== 1) {
        //     this.params.page = 1;
        //     this.loc = result.position.lat + ", " + result.position.lng;
        //   } else {
        //     this.searchByMap(result.position.lat + ", " + result.position.lng);
        //   }
        //
        // });//返回定位信息
        // AMap.Event.addListener(geolocation, 'error', err => console.log(err));      //返回定位出错信息
      });

      let oDiv = document.querySelector(".map-box"),
        H = 0,
        Y = oDiv;
      while (Y) {
        H += Y.offsetTop;
        Y = Y.offsetParent;
      }
      window.addEventListener('scroll', () => this.handleScroll(H, oDiv));
    },
    methods: {
      handleScroll(h, o) {
        let s = document.body.scrollTop || document.documentElement.scrollTop
        this.isFixed = s > h;
        this.ml = o.offsetLeft;
      },
      handleKeyUp(e) {
        if ((e.keyCode >= 48 && e.keyCode <= 57) || (e.keyCode >= 65 && e.keyCode <= 90) || e.keyCode === 8) {
          // 用户输入的字符,需要自动补全
          this.getSuggestion();
        } else if (e.keyCode === 13) {
          // 用户按回车,需要搜索
          this.search();
          this.getFilter();
        } else if (e.keyCode === 38) {
          if (this.opsIndex > 0) {
            this.opsIndex--;
          } else {
            this.opsIndex = this.ops.length - 1;
          }
        } else if (e.keyCode === 40) {
          this.opsIndex = (this.opsIndex + 1) % this.ops.length;
        } else if (e.keyCode === 27) {
          this.show = false
        }
      },
      handleSearch(){
        this.search();
        this.getFilter();
      },
      getSuggestion() { // 查询自动补全
        if (!this.params.key) {
          // key没有值,不去搜索了
          this.ops = [];
          return;
        }
        axios.get("/hotel/suggestion?key=" + this.params.key)
          .then(resp => {
            this.ops = resp.data;
          })
          .catch(e => {
            if(this.isTest){
              this.ops = ["万豪", "如家", "喜来登"]
            }else{
              this.ops = []
            }
            console.log(e);
          })
      },
      getFilter() {
        if(this.isTest){
          this.filterList = this.testFilterData;
          return;
        }
        // 准备参数
        const {filters: {price: ps, ... fs}, ...params} = this.params;
        for( _k in fs){
          params[_k] = fs[_k];
        }
        // 处理价格
        if(ps){
          let pArr = ps.split("-");
          params.minPrice = parseInt(pArr[0]);
          let max = parseInt(pArr[1]);
          params.maxPrice = max === 0 ? 999999 : max;
        }
        axios.post("/hotel/filters", params)
          .then(resp => {
            this.filterList = resp.data;
          })
          .catch(err => {
            console.log(err);
            this.filterList = this.testFilterData;
          })
      },
      searchByMap(location) {
        // 准备参数
        const {filters: {price: ps, ... fs}, ...params} = this.params;
        for( _k in fs){
          params[_k] = fs[_k];
        }
        // 处理价格
        if(ps){
          let pArr = ps.split("-");
          params.minPrice = parseInt(pArr[0]);
          let max = parseInt(pArr[1]);
          params.maxPrice = max === 0 ? 999999 : max;
        }

        // 准备地址
        if (location) {
          params.location = location;
          this.loc = location;
        }

        axios.post("/hotel/list", params)
          .then(resp => {
            if(!resp.data.hotels){
              this.hotels = resp.data;
              this.total = 271;
              this.totalPage = 28;
            }else{
              this.hotels = resp.data.hotels;
              this.total = resp.data.total;
              this.totalPage = Math.ceil((this.total + 5 - 1) / 5);
            }
            if (location) {
              this.setMapCenter(location);
            } else {
              this.setMapCenter(this.hotels[0].location);
            }
            this.initMarker();
          })
          .catch(err => {
            console.log(err)
            this.hotels = [{"id":60223,"name":"上海希尔顿酒店","address":"静安华山路250号","price":2688,"score":37,"brand":"希尔顿","city":"上海","starName":"五星级","business":"静安寺地区","location":"31.219306, 121.445427","pic":"https://m.tuniucdn.com/filebroker/cdn/res/92/10/9210e74442aceceaf6e196d61fc3b6b1_w200_h200_c1_t0.jpg"},{"id":60922,"name":"上海虹桥祥源希尔顿酒店","address":"红松东路1116号","price":1108,"score":45,"brand":"希尔顿","city":"上海","starName":"五钻","business":"虹桥地区","location":"31.18746, 121.395312","pic":"https://m.tuniucdn.com/fb3/s1/2n9c/tQRqDTFkHnHzMZiDKjcGV81ekvc_w200_h200_c1_t0.jpg"},{"id":309208,"name":"北京王府井希尔顿酒店","address":"王府井东街8号","price":1679,"score":46,"brand":"希尔顿","city":"北京","starName":"五钻","business":"天安门/王府井地区","location":"39.914539, 116.413392","pic":"https://m.tuniucdn.com/fb2/t1/G6/M00/52/10/Cii-TF3ePt2IX9UEAALb6VYBSmoAAGKMgGsuW8AAtwB147_w200_h200_c1_t0.jpg"},{"id":395434,"name":"北京希尔顿酒店","address":"东三环北路东方路1号","price":350,"score":45,"brand":"希尔顿","city":"北京","starName":"五星级","business":"燕莎/朝阳公园商业区","location":"39.952703, 116.462387","pic":"https://m.tuniucdn.com/fb3/s1/2n9c/3fwNbKGhk6XCrkdVyxwhC5uGpLVy_w200_h200_c1_t0.jpg"},{"id":395702,"name":"北京首都机场希尔顿酒店","address":"首都机场3号航站楼三经路1号","price":222,"score":46,"brand":"希尔顿","city":"北京","starName":"五钻","business":"首都机场/新国展地区","location":"40.048969, 116.619566","pic":"https://m.tuniucdn.com/fb2/t1/G6/M00/52/10/Cii-U13ePtuIMRSjAAFZ58NGQrMAAGKMgADZ1QAAVn_167_w200_h200_c1_t0.jpg"},{"id":615175,"name":"千岛湖滨江希尔顿度假酒店","address":"环湖北路600号","price":1265,"score":47,"brand":"希尔顿","city":"杭州","starName":"五钻","business":"千岛湖镇","location":"29.603634, 119.077596","pic":"https://m.tuniucdn.com/fb3/s1/2n9c/6qzYeUrrXsH5H3cd9bMXLz8MJtT_w200_h200_c1_t0.jpg"},{"id":2351601,"name":"深圳蛇口希尔顿南海酒店","address":"望海路1177号","price":509,"score":47,"brand":"希尔顿","city":"深圳","starName":"五钻","business":"深圳湾口岸/蛇口","location":"22.479373, 113.916013","pic":"https://m.tuniucdn.com/fb2/t1/G6/M00/45/EA/Cii-TF3ZpXOIfa6fAAJjiUOiuYgAAFrtgDtgpQAAmOh799_w200_h200_c1_t0.jpg"},{"id":368701368,"name":"深圳大中华希尔顿酒店","address":"福田深南大道1003号","price":1666,"score":46,"brand":"希尔顿","city":"深圳","starName":"五钻","business":"会展中心/CBD","location":"22.539313, 114.069763","pic":"https://m.tuniucdn.com/fb3/s1/2n9c/4EnHseZ73LXdFJY7DSdJ8xqAcjXe_w200_h200_c1_t0.jpg"},{"id":2048042240,"name":"北京大兴希尔顿酒店","address":"高米店南里18号楼","price":1283,"score":48,"brand":"希尔顿","city":"北京","starName":"五钻","business":"大兴北京新机场地区","location":"39.76875, 116.339199","pic":"https://m.tuniucdn.com/fb3/s1/2n9c/3B32F8zSU2CJCWzs1hoH2o4WcquR_w200_h200_c1_t0.jpg"},{"id":2056105938,"name":"北京通州北投希尔顿酒店","address":"新华东街289号2号楼","price":1068,"score":48,"brand":"希尔顿","city":"北京","starName":"五钻","business":"果园环岛/通州区","location":"39.908805, 116.659748","pic":"https://m.tuniucdn.com/fb3/s1/2n9c/NGKdpec3tZJNUUNWJ5pd67Cp5AY_w200_h200_c1_t0.png"}]
            this.total = 271;
            this.totalPage = 28;
          })
      },
      search(location) {
        // 发送ajax
        this.searchByMap(location);

      },
      prePage() {
        if (this.params.page > 1) {
          this.params.page--
        }
      },
      nextPage() {
        if (this.params.page < this.totalPage) {
          this.params.page++
        }
      },
      clickPrice(min, max) {
        this.params.minPrice = min;
        this.params.maxPrice = max;
      },
      clickFilter(key, option) {
        this.loc = "";
        const {...obj} = this.params.filters;
        obj[key] = option;
        this.params.filters = obj;
      },
      deleteFilter(k) {
        this.loc = "";
        const {...obj} = this.params.filters;
        delete obj[k];
        this.params.filters = obj;
      },
      location(loc) {
        let arr = loc.split(", ");
        let json = '[' + arr[1] + ', ' + arr[0] + ']'
        return JSON.parse(json);
      },
      initMarker() {
        if (!this.loc) {
          this.map.clearMap();
        }
        this.markHotels();
      },
      setMapCenter(location){
        this.map.setCenter(this.location(location));
      },
      markHotels(){
        this.hotels.forEach((h, i) => {
          // 将创建的点标记添加到已有的地图实例:
          let marker = new AMap.Marker({
            position: this.location(h.location),   // 经纬度构成的一维数组[116.39, 39.9]
            title: h.name,
            offset: new AMap.Pixel(0, 0),
            anchor:'bottom-center'
          });
          marker.vid = h.id;
          this.map.add(marker);
        });
      },
      handleMarkerFocus(h) {
        this.map.setCenter(this.location(h.location));
        let old = this.currentHotel;
        let oldMarker = this.map.getAllOverlays("marker").find(m => m.vid === old.id);
        if (oldMarker) {
          this.updateMarker(oldMarker, "//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png", "", this.location(old.location), old.id);
        }

        this.currentHotel = h;
        let marker = this.map.getAllOverlays("marker").find(m => m.vid === h.id);
        this.updateMarker(marker, "//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-red.png", h.name, this.location(h.location), h.id);
      },
      updateMarker(marker, icon, text, position, id) {
        // 自定义点标记内容
        var markerContent = document.createElement("div");

        // 点标记中的图标
        var markerImg = document.createElement("img");
        markerImg.className = "markerlnglat";
        markerImg.src = icon;
        markerImg.setAttribute('width', '25px');
        markerImg.setAttribute('height', '34px');
        markerContent.appendChild(markerImg);

        // 点标记中的文本
        if (text) {
          var markerSpan = document.createElement("span");
          markerSpan.className = 'marker';
          markerSpan.innerHTML = text;
          markerContent.appendChild(markerSpan);
        }

        marker.setContent(markerContent);
        marker.setPosition(position);
        marker.vid = id;
      },
      getGeoLoc(){
        this.loading = true;
        this.geolocation.getCurrentPosition((status,result) => {
          this.loading = false;
          if(status === 'complete'){
            console.log("successs")
            // https://a.amap.com/jsapi/static/image/plugin/point.png
            this.addMaker('//a.amap.com/jsapi/static/image/plugin/point.png', result.position.lng,result.position.lat);
            if (this.params.page !== 1) {
              this.params.page = 1;
              this.loc = result.position.lat + ", " + result.position.lng;
            } else {
              this.searchByMap(result.position.lat + ", " + result.position.lng);
            }
          }else{
            console.log("err", status)
          }
        });
      },
      addMaker(iconUrl, lng, lat){
        // 创建 AMap.Icon 实例:
        let icon = new AMap.Icon({
          size: new AMap.Size(25, 25),    // 图标尺寸
          image: iconUrl,  // Icon的图像
          imageOffset: new AMap.Pixel(0, 0),  // 图像相对展示区域的偏移量,适于雪碧图等
          imageSize: new AMap.Size(25, 25)   // 根据所设置的大小拉伸或压缩图片
        });

        // 将 Icon 实例添加到 marker 上:
        let marker = new AMap.Marker({
          position: new AMap.LngLat(lng, lat),
          offset: new AMap.Pixel(0, 0),
          icon: icon, // 添加 Icon 实例
          title: '北京',
          zoom: 13,
          anchor:"center"
        });
        this.map.add(marker);
      }
    },
    computed: {
      remainFilter() {
        let keys = Object.keys(this.params.filters);
        let obj = {};
        Object.keys(this.filterList).forEach(key => {
          if (!keys.includes(key) && this.filterList[key].length > 1) {
            obj[key] = this.filterList[key];
          }
        })
        return obj;
      }
    }
  })
</script>
</body>

</html>

请求es (josn数据)

public class HotelConstants {
    public static final String MAPPING_TEMPLATE = "{\n" +
            "  \"mappings\": {\n" +
            "    \"properties\": {\n" +
            "      \"id\": {\n" +
            "        \"type\": \"keyword\"\n" +
            "      },\n" +
            "      \"name\":{\n" +
            "        \"type\": \"text\",\n" +
            "        \"analyzer\": \"ik_max_word\",\n" +
            "        \"copy_to\": \"all\"\n" +
            "      },\n" +
            "      \"address\":{\n" +
            "        \"type\": \"keyword\",\n" +
            "        \"index\": false\n" +
            "      },\n" +
            "      \"price\":{\n" +
            "        \"type\": \"integer\"\n" +
            "      },\n" +
            "      \"score\":{\n" +
            "        \"type\": \"integer\"\n" +
            "      },\n" +
            "      \"brand\":{\n" +
            "        \"type\": \"keyword\",\n" +
            "        \"copy_to\": \"all\"\n" +
            "      },\n" +
            "      \"city\":{\n" +
            "        \"type\": \"keyword\",\n" +
            "        \"copy_to\": \"all\"\n" +
            "      },\n" +
            "      \"starName\":{\n" +
            "        \"type\": \"keyword\"\n" +
            "      },\n" +
            "      \"business\":{\n" +
            "        \"type\": \"keyword\"\n" +
            "      },\n" +
            "      \"location\":{\n" +
            "        \"type\": \"geo_point\"\n" +
            "      },\n" +
            "      \"pic\":{\n" +
            "        \"type\": \"keyword\",\n" +
            "        \"index\": false\n" +
            "      },\n" +
            "      \"all\":{\n" +
            "        \"type\": \"text\",\n" +
            "        \"analyzer\": \"ik_max_word\"\n" +
            "      }\n" +
            "    }\n" +
            "  }\n" +
            "}";
}

实体类

Hotel(sql)
@Data
@TableName("tb_hotel")
public class Hotel {
    @TableId(type = IdType.INPUT)
    private Long id;
    private String name;
    private String address;
    private Integer price;
    private Integer score;
    private String brand;
    private String city;
    private String starName;
    private String business;
    private String longitude;
    private String latitude;
    private String pic;
}
HotelDoc(es)
@Data
@NoArgsConstructor
public class HotelDoc {
    private Long id;
    private String name;
    private String address;
    private Integer price;
    private Integer score;
    private String brand;
    private String city;
    private String starName;
    private String business;
    private String location;
    private String pic;

    public HotelDoc(Hotel hotel) {
        this.id = hotel.getId();
        this.name = hotel.getName();
        this.address = hotel.getAddress();
        this.price = hotel.getPrice();
        this.score = hotel.getScore();
        this.brand = hotel.getBrand();
        this.city = hotel.getCity();
        this.starName = hotel.getStarName();
        this.business = hotel.getBusiness();
        this.location = hotel.getLatitude() + ", " + hotel.getLongitude();
        this.pic = hotel.getPic();
    }
}
PageResult(返回数据给前端)
@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;
    }
}
RequestParams(接收前端数据)
@Data
public class RequestParams {
    private String key;//搜索关键字
    private Integer page;//当前页
    private Integer size;//每页记录数
    private String sortBy;//排序字段

    /*****组合查询服务属性*****/
    private String brand;//品牌
    private String city;//城市
    private String starName;//星级
    private Integer minPrice;
    private Integer maxPrice;

}

控制层

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

    @Autowired
    private IHotelService hotelService;

    @PostMapping("list")
    public PageResult search(@RequestBody RequestParams params) {
        return hotelService.search(params);
    }
}

业务层

接口
public interface IHotelService extends IService<Hotel> {

    PageResult search(RequestParams params);

}
实现类
@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("hotels");
//            // 2.准备请求参数
//            // 2.1.查询条件
//            String key = params.getKey();
//            if (key == null || "".equals(key)) {
//                request.source().query(QueryBuilders.matchAllQuery());
//            } else {
//                request.source().query(QueryBuilders.matchQuery("name",key));
//            }
//            // 2.2.分页
//            int page = params.getPage();
//            int size = params.getSize();
//            request.source().from((page - 1) * size).size(size);
//            // 3.发送请求
//            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
//            // 4.解析响应
//            return handleResponse(response);
//        } catch (IOException e) {
//            throw new RuntimeException("搜索数据失败", e);
//        }
//    }

    /***********************条件查询***************************/
    @Override
    public PageResult search(RequestParams params) {
        try {
            // 1.准备Request
            SearchRequest request = new SearchRequest("hotels");
            // 1.准备Boolean查询
            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

            // 1.1.关键字搜索,match查询,放到must中
            String key = params.getKey();
            if (key == null || "".equals(key)) {
                // 为空,查询所有
                boolQuery.must(QueryBuilders.matchAllQuery());
            } else {
                // 不为空,根据关键字查询
                boolQuery.must(QueryBuilders.matchQuery("name", key));
            }

            // 1.2.品牌
            String brand = params.getBrand();
            if (brand != null) {
                boolQuery.filter(QueryBuilders.termQuery("brand", brand));
            }
            // 1.3.城市
            String city = params.getCity();
            if (city!=null) {
                boolQuery.filter(QueryBuilders.termQuery("city", city));
            }
            // 1.4.星级
            String starName = params.getStarName();
            if (starName!=null) {
                boolQuery.filter(QueryBuilders.termQuery("starName", starName));
            }
            // 1.5.价格范围
            Integer minPrice = params.getMinPrice();
            Integer maxPrice = params.getMaxPrice();
            if (minPrice != null && maxPrice != null) {
                boolQuery.filter(QueryBuilders.rangeQuery("price").gte(minPrice).lte(maxPrice));
            }
            // 3.设置查询条件
            request.source().query(boolQuery);
            // 2.2.分页
            int page = params.getPage();
            int size = params.getSize();
            request.source().from((page - 1) * size).size(size);
            // 3.发送请求
            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
            // 4.解析响应
            PageResult pageResult = handleResponse(response);
            System.out.println(pageResult);
            return pageResult;
        } catch (IOException e) {
            throw new RuntimeException("搜索数据失败", e);
        }
    }




    //处理结果集
    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.9.放入集合
            hotels.add(hotelDoc);
            System.out.println(hotelDoc);
        }
        return new PageResult(total, hotels);
    }

}

启动器

@MapperScan("com.apesource.hotel.mapper")
@SpringBootApplication
public class HotelDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(HotelDemoApplication.class, args);
    }

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

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2104975.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

windows安装php7.4

windows安装php7.4 1.通过官网下载所需的php版本 首先从PHP官网&#xff08;https://www.php.net/downloads.php&#xff09;或者Windows下的PHP官网&#xff08;http://windows.php.net/download/&#xff09;下载Windows版本的PHP安装包。下载后解压到一个路径下。 2.配…

2024/9/4 Canlink配置介绍与常见故障排查

双击一个站进去配置&#xff0c;如果双击PLC则是PLC往外面发数据&#xff0c;双击伺服&#xff0c;则是伺服往外发数据。 例如我想读伺服的功能吗&#xff1f; 点击伺服的配置 将0b00的地址数据发给PLC&#xff08;D100&#xff09; ,寄存器长度是一个 然后下载程序即可

使用docker安装jenkins,然后使用jenkins本地发版和远程发版

使用docker安装jenkins&#xff0c;然后使用jenkins本地发版和远程发版 1、安装docker 1.安装必要的一些系统工具 sudo yum install docker-ce 2.添加软件源信息 sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo 3.更新…

电子行业最全【芯片标签二维码】知识剖析

电子行业最全【芯片标签二维码】知识剖析 本文为辰逸绅士小编原创&#xff0c;未经许可请勿私下复制转载 长 文 预 警 目录 ★01--------前言 ★02--------关于电子元器件协会ECIA ★03--------关于矩阵二维码 3.1--------矩阵二维码 构成 3.2--------矩阵二维码 种类 3.…

【数学分析笔记】第3章第1节 函数极限(3)

3. 函数极限与连续函数 3.1 函数极限 3.1.1 函数极限的性质 【局部有界性】若 lim ⁡ x → x 0 f ( x ) A \lim\limits_{x\to x_{0}}f(x)A x→x0​lim​f(x)A&#xff0c;则 ∃ δ > 0 , ∀ x ( 0 < ∣ x − x 0 ∣ < δ ) : m ≤ f ( x ) ≤ M \exists \delta>…

BUUCTF Crypto wp--RSA1

第一步 查看下载文件 我们发现出现了dp dq&#xff0c;属于dp、dq泄露攻击 上述方程本来是用于在加密中进行快速解密的&#xff0c;但是如果二者发生泄露&#xff0c;就有可能进行对密文的解密。 当我们知道了 dp、 dq、p、q、c,在不知道e的情况下&#xff0c;也可以求解明文。…

社交达人秘籍:巧妙维护你的人脉关系!

在这个人脉为王的时代&#xff0c;微信不仅连接了亲朋好友&#xff0c;更成为了拓展职业网络、深化人际关系的重要平台。如何巧妙地在微信上维护并优化你的人脉关系&#xff0c;成为了每位社交达人必修的功课。今天&#xff0c;就让我们一起探索那些让社交关系更加稳固与活跃的…

【DSP】无法在线仿真无法进入main()函数

DSP无法在线仿真无法进入main()函数 1.问题描述 ​ 接手前人的DSP代码&#xff0c;硬件平台是DSP C6701&#xff0c;软件IDE是CCS 12.3.0。仿真器版本是XDS 560V2。 ​ 在进行在线仿真时&#xff0c;经常出现“伪在线”的情况。简单来说&#xff0c;正常的在线过程&#xff…

使用Python的Elasticsearch客户端 elasticsearch-py 来完成删除现有索引、重新创建索引并测试分词的示例代码

以下是一个使用Python的Elasticsearch客户端 elasticsearch-py 来完成删除现有索引、重新创建索引并测试分词的示例代码 一、安装依赖 pip install elasticsearch二、运行效果 三、程序代码 from elasticsearch import Elasticsearch, NotFoundError# 连接到Elasticsearch es…

基本滤波器响应(低通+高通+带通+带阻)+滤波器的响应特性(阻尼系数+截止频率下降率)

2024-9-4&#xff0c;星期三&#xff0c;20:40&#xff0c;天气&#xff1a;晴&#xff0c;心情&#xff1a;多云。又是上班的一天&#xff0c;至于心情为什么多云&#xff0c;是因为女朋友换季感冒了&#xff0c;我有上班回不去&#xff0c;难受&#xff0c;赶紧到周五吧&…

多模态:DetCLIPv3解析

文章目录 前言一、介绍二、方法2. Open vocabulary detector2.1 encoder2.2 decoder 3. Object captioner 三、数据工程1. Auto-annotation data pipeline 四、训练策略五、实验总结 前言 目前多模态目标检测逐渐成为检测领域的主要发力方向&#xff0c;从最初的检测大模型gro…

2024年最新版Ajax+Axios 学习【包含原理、Promise、报文、接口等...】

基础知识 AJAX概念 AJAX概念&#xff1a;是浏览器与服务器进行数据通信的技术。 认识URL 定义&#xff1a;统一资源定位符&#xff0c;简称网址&#xff0c;用于访问网络上的资源。 组成&#xff1a; http协议&#xff1a;超文本传输协议&#xff0c;规定浏览器和服务器之…

C++系统教程001

1. 安装 Dev-C编程软件 2. 熟悉 Dev-C的界面 3. cout 输出语句的使用 4. 学会 C程序的编译运 一、认识编译器 我们平时所说的程序&#xff0c;一般指双击后就可以直接运行的程序&#xff0c;这样的程序又称为可执行程序。Windows系统下&#xff0c;可执行程序的后缀一般为.ex…

【C++】手动实现栈的封装

完成了以下功能的封装 目录 代码实现&#xff1a; 输出结果如下&#xff1a; 代码实现&#xff1a; #include <iostream> #include <cstring>using namespace std;class Static { private:int *arr; //动态分配栈int top; //指向栈顶元素i…

Docker中的容器内部无法使用vi命令怎么办?

不知道你是否遇到过,在修改容器内部的配置的时候,有时候会提示vi命令不可用。尝试去安装vi插件,好像也不是很容易,有什么办法可以帮助我们修改这个配置文件呢? 解决办法 这时候,我们就需要用到docker cp 命令了,它可以帮助我们把容器内部的文件复制到宿主机上,也可以将…

深入探究 RocketMQ:分布式消息中间件的卓越之选》

《深入探究 RocketMQ&#xff1a;分布式消息中间件的卓越之选》 一、引言 在当今复杂的网络通讯环境中&#xff0c;传统的 Http 请求同步方式存在诸多弊端。当客户端与服务器进行通讯时&#xff0c;客户端必须等待服务端完成处理后返回结果才能继续执行&#xff0c;这种同步调…

万字详解 Redis

1 Redis 是什么 1.1 定义 Redis是一种开源的、基于内存的数据结构存储系统&#xff0c;可以用作数据库、缓存、消息队列等。它支持多种数据结构&#xff0c;如字符串&#xff08;String&#xff09;、哈希&#xff08;Hash&#xff09;、列表&#xff08;List&#xff09;、集…

黑马点评2——商户查询缓存(P37店铺类型查询业务添加缓存练习题答案)redis缓存、更新、穿透、雪崩、击穿、工具封装

文章目录 什么是缓存&#xff1f;添加Redis缓存店铺类型查询业务添加缓存练习题 缓存更新策略给查询商铺的缓存添加超时剔除和主动更新的策略 缓存穿透缓存空对象布隆过滤 缓存雪崩解决方案 缓存击穿解决方案基于互斥锁方式解决缓存击穿问题基于逻辑过期的方式解决缓存击穿问题…

DrissionPage设置启动浏览器为edge

1.查看浏览器启动路径 在浏览器地址栏输入下面地址&#xff0c;拿到可执行文件的路径 。 edge://version/ 2.替换路径 打开DrissionPage._configs. chromium_options.py文件&#xff0c;找到def browser_path(self)这个函数&#xff0c;将返回内容替换为edge的启动路径&#x…

xacro->urdf->pdf

在ROS 2系统中&#xff0c;要将xacro文件转换为PDF文件&#xff0c;可以按照以下步骤操作&#xff1a; 步骤1&#xff1a;将Xacro文件转换为URDF文件 首先&#xff0c;需要将xacro文件转换为urdf文件。可以使用ROS 2提供的xacro工具来完成这个转换。 ros2 run xacro xacro p…