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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...