ElasticSearch-查詢

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

前言 前面我已經搭建好了ElasticSearch服務,並完成了MySQL到ElasticSearch的數據遷移; 使用ElasticSearch的初衷就是為了大數據搜索,本文將介紹ElaticSearch中各種查詢方法; 一、精確查詢(termQuery) termQuery不會對查詢條件進行分詞 ...


前言

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

使用ElasticSearch的初衷就是為了大數據搜索,本文將介紹ElaticSearch中各種查詢方法;

一、精確查詢(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

 

五、多域(欄位)查詢

當用戶在搜索框輸入查詢條件時,為了給用戶展示更多的數據,該條件不應該僅僅作用於某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查詢

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

2.JavaAPI查詢

 

 

 

七、範圍查詢(range)

 

八、多條件查詢

 

九、糾錯查詢(fuzzy)

 

十、高亮展示搜索結果

當用戶在搜索框輸入搜索條件後,對於查詢結果的展示,應將搜索條件以特殊的樣式展示,這種查詢就稱為高亮結果查詢;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

參考

 


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

-Advertisement-
Play Games
更多相關文章
  • 本章將和大家分享在ASP.NET Core中如何使用UseMiddleware擴展方法註冊自定義中間件及其實現原理。 ...
  • AT24C系列是常見的EEPROM存儲晶元, 常用於保存參數及掉電記憶的數據. 訪問AT24C時I2C匯流排的頻率不能太高, AT24C系列的I2C匯流排最高頻率是400KHz(2.7V), 在1.8V時頻率會降到100KHz, STC8H系列的主頻基本上從24MHz起步, 甚至直接運行在36.864... ...
  • Background NGINX 是一個通用且流行的應用程式。也是最流行的 Web 伺服器,它可用於提供靜態文件內容,但也通常與其他服務一起用作分散式系統中的組件,在其中它用作反向代理、負載均衡 或 API 網關。 分散式追蹤 distributed tracing 是一種可用於分析與監控應用程式的 ...
  • pr 2022不僅可以幫助用戶對各種視頻進行剪輯、旋轉、分割、合併、字幕添加、背景音樂等基礎的處理,還能幫助用戶進行視頻顏色校正、顏色分級、穩定鏡頭、調整層、更改片段的持續時間和速度、效果預設等操作,功能十分的全面強大。 詳情:Premiere Pro 2022 for Mac(pr 2022) 新 ...
  • 一. linux常用命令 查看linux系統版本 方式一: lsb_release -a 如果顯示未找到命令使用命令安裝:yum install -y redhat-lsb 方式二:cat /etc/redhat-release (適用於RedHat、CentOS) 方式三:cat /etc/iss ...
  • Lightroom Classic 2022是一款桌面照片編輯和管理軟體,照片後期處理軟體,數位攝影師必備工具,主要面向數位攝影師、圖形設計等專業人士和高端用戶,以及所有喜好拍照、需要拍照的人群,支持各種RAW圖像相機配置,HDR全景照片,主要用於數位相片導入整理、編輯處理、後期列印等製作。 詳情: ...
  • Red Giant Magic Bullet Suite for Mac是電影製作人不可或缺的一套調色降噪插件,可以進行色彩校正、修飾和電影效果,它能夠為您製作出和好萊塢一樣的效果,為電影製作人提供專業的色彩校正。 詳情:Red Giant Magic Bullet Suite for Mac(紅巨 ...
  • LVM: LVM: Logical Volume Manager,可以實現動態的擴容和縮容。邏輯捲是一種邏輯上的管理方式,把一塊或多塊硬碟或分區邏輯的組合在一起,命令成一個捲組(VG),捲組的空間來自所有硬碟空間的總和。(組成邏輯捲的硬碟或分區大小可以不一樣) VG: 多個磁碟或者分區組合在一起的( ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...