ElasticSearch查詢(一)

来源:https://www.cnblogs.com/sss4/archive/2022/07/03/16435492.html
-Advertisement-
Play Games

前言 前面我已經搭建好了ElasticSearch服務,並完成了MySQL到ElasticSearch的數據遷移; 使用ES專門做搜索功能,打破MySQL搜索瓶頸; ElasticSearch的應用場景 資料庫欄位太多,查詢太慢,索引沒有辦法再做優化; 資料庫1個count查詢就拖死全表; MySQ ...


前言

前面我已經搭建好了ElasticSearch服務,並完成了MySQL到ElasticSearch的數據遷移;

使用ES專門做搜索功能,打破MySQL搜索瓶頸;

ElasticSearch的應用場景

  • 資料庫欄位太多,查詢太慢,索引沒有辦法再做優化;
  • 資料庫1個count查詢就拖死全表;
  • MySQL的limit翻到幾十幾百萬頁後實在是太慢;
  • 資料庫like實在太慢,每次like整個伺服器cpu記憶體飆高,拖慢整個線上服務;
  • 想要對外/內提供db里的數據的全文檢索服務;
  • 提供日誌(程式運行)查詢功能;

本文將使用ElasticSearch的倒排索引取代MySQL的索引,進行大數據查詢,提升查詢效率;

一、精確查詢(termQuery)

termQuery不會對查詢條件進行分詞 但這並不以為著查詢的欄位沒有進行分詞存儲

1.Kibana查詢

term精確查詢並不會對查詢條件進行分詞,類似於MySQL中 select * from table where 欄位='xx值';

GET hotel/_search
{
  "query": {
    "term": {
      "brand": {
        "value": "萬豪"
      }
    }
  },
  "from": 0,
  "size": 20
}

2.JavaAPI查詢

將以上在Kibana輸入的DSM轉換成Java代碼;

 //按照品牌精確查詢
    @Override
    public Map<String, Object> brandTermQuery(int current, int size, Map<String, Object> searchParam) {
        //按品牌精確查詢實現
        //1.獲取前端參數
        String brand = (String) searchParam.get("brand");
        //響應前端的Map
        Map<String, Object> resultMap = new HashMap<>();
        //2.構建查詢條件
        //查詢請求
        SearchRequest hotelSearchRequest = new SearchRequest("hotel");
        //請求體
        SearchSourceBuilder hotelSearchSourceBuilder = new SearchSourceBuilder();
        //如果查詢條件為空就查詢所有
        if (StringUtils.hasText(brand)) {
            //請求體-查詢部分
            TermQueryBuilder hotelTermQueryBuilder = QueryBuilders.termQuery("brand", brand);
            hotelSearchSourceBuilder.query(hotelTermQueryBuilder);
        }
        //請求體-分頁部分
        hotelSearchSourceBuilder.from((current - 1) * size);
        hotelSearchSourceBuilder.size(size);
        //查詢請求-封裝請求體
        hotelSearchRequest.source(hotelSearchSourceBuilder);

        //3.去查詢
        try {
            SearchResponse hotelSearchResponse = restHighLevelClient.search(hotelSearchRequest, RequestOptions.DEFAULT);
            //4.處理查詢結果集
            SearchHits hotelSearchResponseHits = hotelSearchResponse.getHits();
            //獲取命中總條目
            Long totalHotelHits = hotelSearchResponseHits.getTotalHits().value;
            //獲取命中的每1個條
            SearchHit[] hoteHits = hotelSearchResponseHits.getHits();
            //前端
            ArrayList<HotelEntity> hotelEntitieList = new ArrayList<>();
            if (hoteHits != null || hoteHits.length > 0) {
                for (SearchHit hoteHit : hoteHits) {
                    String sourceAsString = hoteHit.getSourceAsString();
                    //字元串轉換成Java對象
                    HotelEntity hotelEntity = JSON.parseObject(sourceAsString, HotelEntity.class);
                    hotelEntitieList.add(hotelEntity);
                }
            }
            //前端展示
            resultMap.put("list", hotelEntitieList);
            resultMap.put("totalResultSize", totalHotelHits);
            //設置分頁相關
            resultMap.put("current", current);
            resultMap.put("totalPage", (totalHotelHits + size - 1) / size);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        return resultMap;
    }
HotelServiceImpl.java

 

二、中文分詞器

如果設置了欄位的type為keyword,就可以對該欄位使用term精確查詢;

如果設置了欄位的type為text,當添加文檔時,對於該域的值會進行分詞,形成若幹term(詞條)存儲在倒排索引中。

當用戶進行term查詢時,ES會將當前查詢條件當做1個term(詞條),和當前倒排索引中term(詞條)進行匹配?

匹配成功則會查詢到數據,如果倒排索引中不存在該term(詞條)則查詢不到數據。

那我們如何對text類型的欄位進行term查詢呢?

這就需要利用中文分詞器對文檔中的內容進行中文分詞, 重構ES的倒排索引的結構,把整個文檔分詞成為若幹中文term(詞條)

1.ElasticSearch內置分詞器

在ElasticSearch預設內置了多種分詞器:

  • Standard Analyzer - 預設分詞器,按英文空格切分
  •  Simple Analyzer - 按照非字母切分(符號被過濾)
  •  Stop Analyzer - 小寫處理,停用詞過濾(the,a,is)
  •  Whitespace Analyzer - 按照空格切分,不轉小寫
  • Keyword Analyzer - 不分詞,直接將輸入當作輸出
  • Patter Analyzer - 正則表達式,預設\W+(非字元分割)

 

2.預設分詞無法對中文分詞

看看ES是預設使用Standard Analyzer分詞器對文檔內容進行分詞;

GET _analyze
{
"text": "北京市東城區萬豪酒店" }

此時可以發現Standard Analyzer分詞器把每1個漢字形成了一個詞,這顯然無法滿足漢語搜索習慣;

 

3.安裝IK分詞器

IK分詞器是國人開發的1款智能中文分詞器,基於Java語言開發、具有60萬字/秒的高速處理能力。

並且用戶可以按需求,設置停用詞與擴展詞。

3.1.上傳IK安裝包

#因為啟動es時候 已經做好的目錄掛載
容器內部:/usr/share/elasticsearch/plugins 
宿主機:/mydata/elasticsearch/plugins

所以只需要將文件複製到/mydata/elasticsearch/plugins 目錄下即可

3.2.重啟容器

docker restart elasticsearch

 3.3.測試

GET /_analyze
{
  "analyzer": "ik_max_word",
  "text": "北京市東城區萬豪酒店"
}

 

4.使用IK分詞器

4.1.分詞模式

IK分詞器有兩種分詞模式it_max_word和ik_smart模式
  • ik_max_word:細粒度分詞,存儲的時候分詞可以細粒度一些;
  • ik_smart:粗粒度分詞,搜索的時候可以對搜索條件分詞粗粒度一些;

4.2.配置自定義詞庫

IK分詞器雖然非常智能。但是中華語言博大精深,其並不能完全識別所有的詞。

如在文檔存儲時,無法對專業術語、店名、網路詞語等等。所以IK提供了擴展詞庫,用戶可以按需求添加自定義分詞數據。

4.2.1.定義擴展詞

4.2.2.定義停用詞

4.3.修改IK分詞器的配置文件

vim IKAnalyzer.cfg.xml
#修改配置文件 註意這個地方 不要把搞亂碼了!!! 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
    <comment>IK Analyzer 擴展配置</comment>
    <!--用戶可以在這裡配置自己的擴展字典 -->
    <entry key="ext_dict">my.dic</entry>
     <!--用戶可以在這裡配置自己的擴展停止詞字典-->
    <entry key="ext_stopwords">extra_stopword.dic</entry>
    <!--用戶可以在這裡配置遠程擴展字典 -->
    <entry key="remote_ext_dict">http://106.75.109.43:28888/remote.dic</entry>
    <!--用戶可以在這裡配置遠程擴展停止詞字典-->
    <!-- <entry key="remote_ext_stopwords">http://ip地址:埠號/詞典文件</entry> -->
</properties>

 

4.4.重建索引指定分詞器

把數據從原索引(表)中遷移到目標索引(表);

如果是線上環境,在重建索引時,一定要選擇非同步構建和平滑構建; 

PUT hotel_2
{
  "mappings": {
    "properties": {
      "name":{
        "type": "text",
        "analyzer": "ik_max_word",
        "search_analyzer": "ik_smart"
      },
      "address":{
        "type": "text",
        "analyzer": "ik_max_word",
        "search_analyzer": "ik_smart"
      },
      "brand":{
        "type": "keyword"
      },
      "type":{
        "type": "keyword"
      },
       "price":{
        "type": "integer"
      },
      "specs":{
        "type": "keyword"
      },
       "salesVolume":{
        "type": "integer"
      },
      "area":{
        "type": "text",
        "analyzer": "ik_max_word",
        "search_analyzer": "ik_smart"
      },
      "imageUrl":{
        "type": "text"
      },
      "synopsis":{
        "type": "text",
        "analyzer": "ik_max_word",
        "search_analyzer": "ik_smart"
      },
      "createTime":{
        "type": "date",
        "format": "yyyy-MM-dd"
      },
      "isAd":{
        "type":"integer"
      }
    }
  }
}


#重建索引 非同步構建和平滑構建 
POST _reindex?wait_for_completion=false&requests_per_second=2000
{
  "source": {
    "index": "原始索引名字"
  },
  "dest": {
    "index": "目標索引名字"
  }
}

#查看任務完成情況
GET _tasks/任務id

#重建別名關聯關係
#斷開原來的關係
POST _aliases
{
  "actions": [
    {
      "remove": {
        "index": "hotel_1",
        "alias": "hotel"
      }
    }
  ]
}
#刪除原來的索引表
DELETE hotel_1

#新建hotel_2的關係
POST _aliases
{
  "actions": [
    {
      "add": {
        "index": "hotel_2",
        "alias": "hotel"
      }
    }
  ]
}
dms

 

4.5.測試文檔是否被分詞

此時文檔在存儲時已經被中文分詞器進行了中文分詞並存儲,我們就可以使用termQuery精確查詢進行分詞結果測試了;

由於termQuery精確查詢,不會對查詢條件進行分詞,所依我根據分詞結果進行查詢,如果分詞成功,就會查詢到text欄位的結果;

 

三、分詞查詢(mathQuery)

上述的term精確查詢必須要根據分詞之後的結果進行精確查詢;

可是用戶不知道你的文檔是怎麼分詞的,所以我們需要對用戶的查詢條件也進行分詞;

1.Kibana分詞查詢

GET hotel/_search
{
  "query": {
    "match": {
     "name":"北京市東城區瑞麟灣"
    }
  }
}

matchQuery會對查詢條件進行分詞,並拿分詞後的結果,去ES中進行逐一匹配,預設取結果並集。

2.JavaAPI分詞查詢

 //根據酒店名稱匹配查詢
    @Override
    public Map<String, Object> nameMatchQuery(Integer current, Integer size, Map<String, Object> searchParam) {
        //設置查詢
        SearchRequest searchRequest = new SearchRequest("hotel");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        Map<String, Object> map = new HashMap<>();
        //獲取name參數
        String name = (String) searchParam.get("name");
        if (StringUtils.hasText(name)) {
            //組裝查詢對象
            MatchQueryBuilder nameMatchQueryBuilder = QueryBuilders.matchQuery("name", name);
            searchSourceBuilder.query(nameMatchQueryBuilder);
        }
        //設置分頁
        searchSourceBuilder.from((current - 1) * size);
        searchSourceBuilder.size(size);

        searchRequest.source(searchSourceBuilder);

        //處理查詢結果
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            SearchHits hits = searchResponse.getHits();

            long totalHits = hits.getTotalHits().value;

            SearchHit[] searchHits = hits.getHits();

            List<HotelEntity> list = new ArrayList<>();

            for (SearchHit searchHit : searchHits) {
                String sourceAsString = searchHit.getSourceAsString();
                list.add(JSON.parseObject(sourceAsString, HotelEntity.class));
            }
            map.put("list", list);
            map.put("totalResultSize", totalHits);
            map.put("current", current);
            //設置總頁數
            map.put("totalPage", (totalHits + size - 1) / size);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return map;
    }
HotelServiceImpl.java

 

四、模糊搜索(wildcardQuery)

當在搜索框進行搜索時,展示出所有品牌以美開頭的酒店。

wildcardQuery:會對查詢條件進行分詞,還可以使用通配符 ?(任意單個字元) 和 * (0個或多個字元)

1.Kibana分詞查詢

GET hotel/_search
{
  "query": {
    "wildcard": {
      "brand": {
        "value": "美*"
      }
    }
  }
}

2.JavaAPI分詞查詢

 //根據酒店品牌模糊查詢
    @Override
    public Map<String, Object> nameWildcardQuery(Integer current, Integer size, Map<String, Object> searchParam) {
        //設置查詢
        SearchRequest searchRequest = new SearchRequest("hotel");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //根據酒店名稱模糊查詢
        //1.獲取前端參數
        String name = (String) searchParam.get("name");
        //2.組裝查詢對象
        if (StringUtils.hasText(name)) {
            WildcardQueryBuilder brandWildcardQuery = QueryBuilders.wildcardQuery("brand", name+"*");
            searchSourceBuilder.query(brandWildcardQuery);
        }

        //設置分頁
        searchSourceBuilder.from((current - 1) * size);
        searchSourceBuilder.size(size);

        searchRequest.source(searchSourceBuilder);
        Map<String, Object> map = new HashMap<>();

        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            SearchHits hits = searchResponse.getHits();
            long totalHits = hits.getTotalHits().value;
            SearchHit[] searchHits = hits.getHits();
            List<HotelEntity> list = new ArrayList<>();
            for (SearchHit searchHit : searchHits) {
                String sourceAsString = searchHit.getSourceAsString();
                list.add(JSON.parseObject(sourceAsString, HotelEntity.class));
            }
            map.put("list", list);
            map.put("totalResultSize", totalHits);
            map.put("current", current);
            //設置總頁數
            map.put("totalPage", (totalHits + size - 1) / size);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return map;
    }
HotelServiceImpl.java

 

五、多域(欄位)查詢(query_string

當用戶在搜索框輸入查詢條件時,為了給用戶展示更多的數據,該條件不應該僅僅作用於某1個域(欄位),而要讓其作用於多個域進行搜索,從而搜索出更多的查詢結果。

類似於MySQL數據中的 select * from table 欄位1=條件  or 欄位2=條件.....;

簡而言之就是使用1個條件去多個欄位中查詢;

當前需要將用戶的搜索條件,作用於:酒店名稱(name)、酒店描述(synopsis)、酒店地區(area)、酒店地址(address);

1.Kibana查詢

GET hotel/_search
{
  "query": {
    "query_string": {
      "fields": ["name","brand","address","synopsis"],
      "query": "萬豪 OR 北京 OR 上海"
    }
  }
}

2.JavaAPI查詢

    //根據name,synopsis,area,address進行多域(欄位)查詢
    @Override
    public Map<String, Object> searchQueryStringQuery(Integer current, Integer size, Map<String, Object> searchParam) {
        //設置查詢
        SearchRequest searchRequest = new SearchRequest("hotel");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        Map<String, Object> map = new HashMap<>();
        //根據name,synopsis,area,address進行多域查詢
        String condition = (String) searchParam.get("condition");
        //組裝查詢對象
        if (StringUtils.hasText(condition)) {
            QueryStringQueryBuilder queryStringQueryBuilder = QueryBuilders.queryStringQuery(condition)
                    .field("name")
                    .field("address")
                    .field("synopsis")
                    .field("area")
                    .defaultOperator(Operator.OR);
            searchSourceBuilder.query(queryStringQueryBuilder);
        }

        //設置分頁
        searchSourceBuilder.from((current - 1) * size);
        searchSourceBuilder.size(size);

        searchRequest.source(searchSourceBuilder);
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            SearchHits hits = searchResponse.getHits();

            long totalHits = hits.getTotalHits().value;

            SearchHit[] searchHits = hits.getHits();

            List<HotelEntity> list = new ArrayList<>();

            for (SearchHit searchHit : searchHits) {
                String sourceAsString = searchHit.getSourceAsString();
                list.add(JSON.parseObject(sourceAsString, HotelEntity.class));
            }


            map.put("list", list);
            map.put("totalResultSize", totalHits);
            map.put("current", current);
            //設置總頁數
            map.put("totalPage", (totalHits + size - 1) / size);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return map;
    }
HotelServiceImpl

 

六、排序查詢(sort)

當用戶進行搜索時,有時會關註該商品的銷量、評論數等信息,對某些域(欄位)進行進行排序,搜索出銷量最高或評論數最多的商品

1.Kibana查詢

使用match_all查詢所有數據,對price和salesVolume域(欄位)進行排序;

#排序查詢:支持多欄位排序
GET hotel/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "price": {
        "order": "desc"
      }
    },
     {
      "salesVolume": {
        "order": "asc"
      }
    }
  ]
}

2.JavaAPI查詢

 //根據銷量排序查詢
    @Override
    public Map<String, Object> salesSortQuery(Integer current, Integer size, Map<String, Object> searchParam) {
        //設置查詢
        SearchRequest searchRequest = new SearchRequest("hotel");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //設置按銷量排序
        String sortWay = (String) searchParam.get("sortWay");
        //組裝sort部分
        if (StringUtils.hasText(sortWay)) {
            if ("asc".equals(sortWay)) {
                searchSourceBuilder.sort("price", SortOrder.ASC).sort("salesVolume", SortOrder.ASC);
            } else {
                searchSourceBuilder.sort("price", SortOrder.DESC).sort("salesVolume", SortOrder.DESC);
            }
        }

        //設置分頁
        searchSourceBuilder.from((current - 1) * size);
        searchSourceBuilder.size(size);

        searchRequest.source(searchSourceBuilder);
        Map<String, Object> map = new HashMap<>();
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            SearchHits hits = searchResponse.getHits();

            long totalHits = hits.getTotalHits().value;

            SearchHit[] searchHits = hits.getHits();

            List<HotelEntity> list = new ArrayList<>();

            for (SearchHit searchHit : searchHits) {
                String sourceAsString = searchHit.getSourceAsString();
                list.add(JSON.parseObject(sourceAsString, HotelEntity.class));
            }


            map.put("list", list);
            map.put("totalResultSize", totalHits);
            map.put("current", current);
            //設置總頁數
            map.put("totalPage", (totalHits + size - 1) / size);
            map.put("sortWay", searchParam.get("sortWay"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return map;
    }
HotelServiceImpl.java

 

七、範圍查詢(range)

當用戶要搜索商品時,有時會對某一個特定的價格區間進行查詢,搜索出符合心理預期價格的商品;

1.Kibana查詢

區間範圍查詢:查詢價格在100-500元範圍之間的商品;

GET hotel/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 100,
        "lte": 500
      }
    }
  }
}

2.JavaAPI查詢

 //處理前端參數
    public Long transferToLong(Object param){
        if(param==null || "".equals(param)){
            return null;
        }else{
           return Long.parseLong((String)param);
        }
    }
    //根據價格範圍查詢
    @Override
    public Map<String, Object> priceRangeQuery(Integer current, Integer size, Map<String, Object> searchParam) {

        //設置查詢
        SearchRequest searchRequest = new SearchRequest("hotel");

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //根據價格範圍查詢
        //1.獲取參數
        Long maxPrice = transferToLong(searchParam.get("maxPrice"));
        Long minPrice = transferToLong(searchParam.get("minPrice"));
        //2.組裝搜索條件
        if (maxPrice != null || maxPrice != null) {
            RangeQueryBuilder priceRangeQueryBuilder = QueryBuilders.rangeQuery("price");
            if (maxPrice != null) {
                priceRangeQueryBuilder.lte(maxPrice);
            }
            if (minPrice != null) {
                priceRangeQueryBuilder.gte(maxPrice);
            }
        }
        //設置分頁
        searchSourceBuilder.from((current - 1) * size);
        searchSourceBuilder.size(size);

        searchRequest.source(searchSourceBuilder);

        //處理查詢結果
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            SearchHits hits = searchResponse.getHits();

            long totalHits = hits.getTotalHits().value;

            SearchHit[] searchHits = hits.getHits();

            List<HotelEntity> list = new ArrayList<>();

            for (SearchHit searchHit : searchHits) {
                String sourceAsString = searchHit.getSourceAsString();
                list.add(JSON.parseObject(sourceAsString, HotelEntity.class));
            }

            Map<String, Object> map = new HashMap<>();
            map.put("list", list);
            map.put("totalResultSize", totalHits);
            map.put("current", current);
            //設置總頁數
            map.put("totalPage", (totalHits + size - 1) / size);

            map.put("minPrice", searchParam.get("minPrice"));
            map.put("maxPrice", searchParam.get("maxPrice"));

            return map;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
HotelServiceImpl.java

 

八、多條件查詢(boolQuery

多欄位查詢類似於沒MySQL中的以下SQL

select * from table where 欄位1='條件1' or 欄位2='條件2' ;

當用戶進行搜索時,不會僅僅只輸入一個搜索條件,有時會傳遞多個條件,將符合多條件的商品搜索出來。

boolQuery可以對多個查詢條件連接。

連接方式有

  • must(and):       條件必須成立
  • must_not(not): 條件必須不成立
  • should(or):       1個條件可以成立即可
  • filter:                條件必須成立,性能比must高

1.Kibana查詢

查詢北京市的萬豪價格區間在500-2000,最好是五星級;

GET /hotel/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "brand": {
              "value": "萬豪"
            }
          }
        },
        {
          "term": {
            "area": {
              "value": "北京市"
            }
          }
        }
      ],
      "should": [
        {
          "term": {
            "specs": {
              "value": "五星級"
            }
          }
        }
      ], 
      "filter": [
        {
          "range": {
            "price": {
              "gte": 500,
              "lte": 2000
            }
          }
        }
      ]
    }
  }
}

2.JavaAPI查詢

//多條件查詢
//搜索框多域、品牌精確、城市精確、星級精確、價格範圍、銷量排序
@Override
public Map<String, Object> searchBoolQuery(Integer current, Integer size, Map<String, Object> searchParam) {

    //設置查詢
    SearchRequest searchRequest = new SearchRequest("hotel");

    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();


    //todo 多條件查詢 :多域、品牌精確、城市精確、星級精確、價格範圍、銷量排序
    //設置查詢方式
    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

    //多域
    if (StringUtils.hasText(searchParam.get("condition"))) {
        QueryBuilder queryBuilder = QueryBuilders.queryStringQuery(searchParam.get("condition").toString())
            .field("name")
            .field("synopsis")
            .field("area")
            .field("address")
            .defaultOperator(Operator.OR);
        boolQueryBuilder.must(queryBuilder);
    }

    //品牌精確
    if (StringUtils.hasText(searchParam.get("brand"))) {
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("brand", searchParam.get("brand"));
        boolQueryBuilder.filter(termQueryBuilder);
    }

    //城市精確
    if (StringUtils.hasText(searchParam.get("area"))) {
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("area", searchParam.get("area"));
        boolQueryBuilder.filter(termQueryBuilder);
    }

    //星級精確
    if (StringUtils.hasText(searchParam.get("specs"))) {
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("specs", searchParam.get("specs"));
        boolQueryBuilder.filter(termQueryBuilder);
    }

    //價格範圍
    //1.獲取參數
    Long maxPrice = transferToLong(searchParam.get("maxPrice"));
    Long minPrice = transferToLong(searchParam.get("minPrice"));
    //2.根據情況組裝條件
    if (maxPrice!=null||minPrice!=null){
        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("price");

        //gte great than and equal 大於等於
        //lte less than and equal 小於等於

        if (maxPrice!=null){
            rangeQueryBuilder.lte(maxPrice);
        }
        if (minPrice!=null){
            rangeQueryBuilder.gte(minPrice);
        }

        boolQueryBuilder.must(rangeQueryBuilder);
    }

    //銷量排序
    if (StringUtils.hasText(searchParam.get("sortWay"))) {
        if ("desc".equalsIgnoreCase(searchParam.get("sortWay").toString())) {
            searchSourceBuilder.sort("salesVolume", SortOrder.DESC);
        } else {
            searchSourceBuilder.sort("salesVolume", SortOrder.ASC);
        }
    }

    searchSourceBuilder.query(boolQueryBuilder);


    //設置分頁
    searchSourceBuilder.from((current - 1) * size);
    searchSourceBuilder.size(size);

    searchRequest.source(searchSourceBuilder);

    //處理查詢結果
    try {
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        SearchHits hits = searchResponse.getHits();

        long totalHits = hits.getTotalHits().value;

        SearchHit[] searchHits = hits.getHits();

        List<HotelEntity> list = new ArrayList<>();

        for (SearchHit searchHit : searchHits) {
            String sourceAsString = searchHit.getSourceAsString();
            list.add(JSON.parseObject(sourceAsString, HotelEntity.class));
        }

        Map<String, Object> map = new HashMap<>();
        map.put("list", list);
        map.put("totalResultSize", totalHits);
        map.put("current", current);
        //設置總頁數
        map.put("totalPage", (totalHits + size - 1) / size);

        map.put("brand", searchParam.get("brand"));
        map.put("area", searchParam.get("area"));
        map.put("specs", searchParam.get("specs"	   

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Background NGINX 是一個通用且流行的應用程式。也是最流行的 Web 伺服器,它可用於提供靜態文件內容,但也通常與其他服務一起用作分散式系統中的組件,在其中它用作反向代理、負載均衡 或 API 網關。 分散式追蹤 distributed tracing 是一種可用於分析與監控應用程式的 ...
  • 1 存儲引擎 1、簡單描述一個Mysql的內部結構? MySQL的基本架構示意圖: 大體來說,MySQL可以分為server層和存儲引擎層兩部分。 ① server層包括連接器、查詢緩存、分析器、優化器、執行器等,涵蓋MySQL的大多數核心服務功能 ② 存儲引擎層:存儲引擎層負責數據的存儲和提取。其 ...
  • ###@Spark分區器(Partitioner) ####HashPartitioner(預設的分區器) HashPartitioner分區原理是對於給定的key,計算其hashCode,並除以分區的個數取餘,如果餘數小於0,則餘數+分區的個數,最後返回的值就是這個key所屬的分區ID,當key為 ...
  • 一鍵直達直播間 一、直播介紹 上兩期渡劫同學為大家分享了ChunJun數據還原的DDL模塊,想必大家對這一模塊有了比較深入的瞭解,本期無倦同學將會為大家分享ChunJun同步Hive事務表的相關內容,直播將從Hive事務表的結構及原理、ChunJun讀寫Hive事務表實戰、源碼解析及ChunJun文 ...
  • 一. 下載mysql 8.0.29軟體包 下載點我 二. 解壓,初始化安裝 1,打開下載後文件所在目錄,使用解壓軟體解壓,打開文件夾!(如圖,文件路徑不要出現中文!) 2,創建my.ini文件,創建前先開啟文件尾碼名顯示防止文件格式錯誤! 3,右鍵空白處,新建>文本文檔>選中文件>重命名>全選>文件 ...
  • 前言 上文介紹了ES的各種查詢; 本文介紹如何在ES進行MySQL中的分組和聚合查詢 實現用戶輸入拼音自動補全功能 實現MySQL和ES之間的數據自動同步; 一、分組聚合 在ES中對於聚合查詢,主要分為2大類:指標(Metric)聚合 與 桶(Bucket)聚合。 指標聚合:max、min、sum等 ...
  • PostgreSQL upgrade 以升級 PostgreSQL 9.1 至 PostgreSQL 11 (跨越 9.2、9.3、9.4、9.5、9.6、10 六個大版本) 為例,本文將分享一下過去一年升級數十套 PostgreSQL 生產集群的實際經驗。 此步驟同樣適用於 PostgreSQL ...
  • SIGMOD 數據管理國際會議是資料庫領域具有最高學術地位的國際性會議,位列資料庫方向頂級會議之首。近日,騰訊雲資料庫團隊的最新研究成果入選 SIGMOD 2022 Research Full Paper(研究類長文),入選論文題目為“HUNTER: An Online Cloud Database ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...