Elasticsearch自动补全(拼音查询自动补全)
Elasticsearch自動補全
當用戶在搜索框輸入字符時,我們應該提示出與該字符有關的搜索項。
這種根據用戶輸入的字母,提示完整詞條的功能,就是自動補全了。
因為需要根據拼音字母來推斷,因此要用到拼音分詞功能。
1、拼音分詞器
要實現根據字母做補全,就必須對文檔按照拼音分詞。在GitHub上恰好有elasticsearch的拼音分詞插件。地址:https://github.com/medcl/elasticsearch-analysis-pinyin
安裝方式與IK分詞器一樣,分三步:
? ①解壓
? ②上傳到虛擬機中,elasticsearch的plugin目錄
? ③重啟elasticsearch
? ④測試
DSL測試如下
# 拼音分詞器 POST /_analyze {"text": "上海酒店還不錯","analyzer": "pinyin" }拼音分詞結果
{"tokens" : [{"token" : "shang","start_offset" : 0,"end_offset" : 0,"type" : "word","position" : 0},.......] }2、自定義分詞器
默認的拼音分詞器會將每個漢字單獨分為拼音,而我們希望的是每個詞條形成一組拼音,需要對拼音分詞器做個性化定制,形成自定義分詞器。
elasticsearch中分詞器(analyzer)的組成包含三部分:
- character filters:在tokenizer之前對文本進行處理。例如刪除字符、替換字符
- tokenizer:將文本按照一定的規則切割成詞條(term)。例如keyword,就是不分詞;還有ik_smart
- tokenizer filter:將tokenizer輸出的詞條做進一步處理。例如大小寫轉換、同義詞處理、拼音處理等
文檔分詞時會依次由這三部分來處理文檔:
聲明自定義分詞器的語法如下:
PUT /test {"settings": {"analysis": {"analyzer": { // 自定義分詞器"my_analyzer": { // 分詞器名稱"tokenizer": "ik_max_word", // 普通分詞器"filter": "py" // 拼音分詞器}},"filter": { // 自定義tokenizer filter"py": { // 過濾器名稱"type": "pinyin", // 過濾器類型,這里是 pinyin"keep_full_pinyin": false, // 單個字分詞"keep_joined_full_pinyin": true, // 全拼"keep_original": true, // 保留中文"limit_first_letter_length": 16,"remove_duplicated_term": true,"none_chinese_pinyin_tokenize": false}}}},"mappings": {"properties": {"name": {"type": "text","analyzer": "my_analyzer", // 指定分詞器(自定義)"search_analyzer": "ik_smart" // 指定搜索時不使用拼音}}} }自定義分詞器測試
1、指定文本進行拆分
# 測試自定義分詞器 POST /test/_analyze {"text": "上海酒店還不錯","analyzer": "my_analyzer" }測試結果
{"tokens" : [{"token" : "上海","start_offset" : 0,"end_offset" : 2,"type" : "CN_WORD","position" : 0},{"token" : "shanghai","start_offset" : 0,"end_offset" : 2,"type" : "CN_WORD","position" : 0}] }插入文檔測試
# 測試自定義分詞器 POST /test/_analyze {"text": "上海酒店還不錯","analyzer": "my_analyzer" }POST /test/_doc/1 {"id": 1,"name": "獅子" } POST /test/_doc/2 {"id": 2,"name": "虱子" }# 測試拼音搜索 GET /test/_search {"query": {"match": {"name": "shizi"}} }上面的測試查詢shizi因為插入的兩個文檔中都是同音的所以都會查詢到兩條數據
使用同音搜索結果測試
獅子與虱子是同音的搜索的時候也會出現虱子這就說明了自定義拼音分詞器有問題,使用中文搜索也會將同音的搜索出來
# 測試拼音搜索 GET /test/_search {"query": {"match": {"name": "掉入獅子籠咋辦"}} }解決方案
拼音分詞器適合在創建倒排序索引的時候使用,但不能在搜索時使用
因此字段在創建倒排序索引時應用my_analyzer分詞器,字段在搜索時應該使用ik_smart分詞器"search_analyzer": "ik_smart"
"mappings": {"properties": {"name": {"type": "text","analyzer": "my_analyzer", // 指定分詞器(自定義)"search_analyzer": "ik_smart" // 指定搜索時不使用拼音}}}指定之后,就不會出現出現同音的問題了
總結:
如何使用拼音分詞器?
-
①下載pinyin分詞器
-
②解壓并放到elasticsearch的plugin目錄
-
③重啟即可
如何自定義分詞器?
-
①創建索引庫時,在settings中配置,可以包含三部分
-
②character filter
-
③tokenizer
-
④filter
拼音分詞器注意事項?
- 為了避免搜索到同音字,搜索時不要使用拼音分詞器"search_analyzer": "ik_smart"
3、自動補全查詢
elasticsearch提供了Completion Suggester查詢來實現自動補全功能。這個查詢會匹配以用戶輸入內容開頭的詞條并返回。為了提高補全查詢的效率,對于文檔中字段的類型有一些約束:
-
參與補全查詢的字段必須是completion類型。
-
字段的內容一般是用來補全的多個詞條形成的數組。
比如,一個這樣的索引庫:
// 創建索引庫 PUT test {"mappings": {"properties": {"title":{"type": "completion"}}} }然后插入下面的數據:
// 示例數據 POST test/_doc {"title": ["Sony", "WH-1000XM3"] } POST test/_doc {"title": ["SK-II", "PITERA"] } POST test/_doc {"title": ["Nintendo", "switch"] }查詢的DSL語句如下:
// 自動補全查詢 GET /test/_search {"suggest": {"title_suggest": { // 自定義名稱"text": "s", // 關鍵字"completion": { // 補全類型"field": "title", // 補全查詢的字段"skip_duplicates": true, // 跳過重復的"size": 10 // 獲取前10條結果}}} }自動補全示例
# 自動補全的索引庫 PUT test2 {"mappings": {"properties": {"title":{"type": "completion"}}} } # 示例數據 POST test2/_doc {"title": ["Sony", "WH-1000XM3"] } POST test2/_doc {"title": ["SK-II", "PITERA"] } POST test2/_doc {"title": ["Nintendo", "switch"] }# 自動補全查詢 POST /test2/_search {"suggest": {"title_suggest": {"text": "s","completion": {"field": "title","skip_duplicates": true,"size": 10}}} }對關鍵字s的查詢結果
4、實現酒店搜索框自動補全
現在,我們的hotel索引庫還沒有設置拼音分詞器,需要修改索引庫中的配置。但是我們知道索引庫是無法修改的,只能刪除然后重新創建。
另外,我們需要添加一個字段,用來做自動補全,將brand、suggestion、city等都放進去,作為自動補全的提示。
因此,總結一下,我們需要做的事情包括:
修改hotel索引庫結構,設置自定義拼音分詞器
修改索引庫的name、all字段,使用自定義分詞器
索引庫添加一個新字段suggestion,類型為completion類型,使用自定義的分詞器
給HotelDoc類添加suggestion字段,內容包含brand、business
重新導入數據到hotel庫
4.1、修改酒店映射結構
# -------------酒店自動補全案例-----------# 查看索引庫的結果 GET /hotel/_mapping# 刪除原來的索引庫 DELETE /hotel# 修改酒店索引庫結構 PUT /hotel {"settings": {"analysis": {"analyzer": {"text_anlyzer": {"tokenizer": "ik_max_word","filter": "py"},"completion_analyzer": {"tokenizer": "keyword","filter": "py"}},"filter": {"py": {"type": "pinyin","keep_full_pinyin": false,"keep_joined_full_pinyin": true,"keep_original": true,"limit_first_letter_length": 16,"remove_duplicated_term": true,"none_chinese_pinyin_tokenize": false}}}},"mappings": {"properties": {"id":{"type": "keyword"},"name":{"type": "text","analyzer": "text_anlyzer","search_analyzer": "ik_smart","copy_to": "all"},"address":{"type": "keyword","index": false},"price":{"type": "integer"},"score":{"type": "integer"},"brand":{"type": "keyword","copy_to": "all"},"city":{"type": "keyword"},"starName":{"type": "keyword"},"business":{"type": "keyword","copy_to": "all"},"location":{"type": "geo_point"},"pic":{"type": "keyword","index": false},"all":{"type": "text","analyzer": "text_anlyzer","search_analyzer": "ik_smart"},"suggestion":{"type": "completion","analyzer": "completion_analyzer"}}} }4.2、修改接收數據的HotelDoc實體類
HotelDoc中要添加一個字段,用來做自動補全,內容可以是酒店品牌、城市、商圈等信息。按照自動補全字段的要求,最好是這些字段的數組。
因此我們在HotelDoc中添加一個suggestion字段,類型為List<String>,然后將brand、city、business等信息放到里面。
package cn.itcast.hotel.pojo;import lombok.Data; import lombok.NoArgsConstructor;import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List;@Data @NoArgsConstructor public class HotelDoc {...private List<String> suggestion;public HotelDoc(Hotel hotel) {// 組裝suggestionif(this.business.contains("/")){// business有多個值,需要切割String[] arr = this.business.split("/");// 添加元素this.suggestion = new ArrayList<>();this.suggestion.add(this.brand);Collections.addAll(this.suggestion, arr);}else {this.suggestion = Arrays.asList(this.brand, this.business);}} }4.3、運行自動導入數據
重新執行之前編寫的導入數據功能,可以看到新的酒店數據中包含了suggestion:
運行測試代碼
4.4、自動補全查詢的JavaAPI
之前我們學習了自動補全查詢的DSL,而沒有學習對應的JavaAPI,這里給出一個示例:
而自動補全的結果也比較特殊,解析的代碼如下:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-QMVTWXqV-1654397685637)(images/image-20220605102739780.png)]
4.5、控制器的編寫
/*** 前端頁面請求完成關鍵字查詢* @param key* @return*/ @GetMapping("/suggestion") public List<String> getSuggestion(@RequestParam("key") String key){return hotelService.getSuggestion(key); }4.5、業務層代碼實現
接口的編寫
List<String> getSuggestion(String key);實現類
/*** 實現前端關鍵字搜索* @param key* @return*/ @Override public List<String> getSuggestion(String key) {try {// 1、準備額requestSearchRequest request = new SearchRequest("hotel");// 2、準備DSLrequest.source().suggest(new SuggestBuilder().addSuggestion("suggestions",SuggestBuilders.completionSuggestion("suggestion").prefix(key).skipDuplicates(true).size(10)));// 3、發送請求SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 4.解析結果Suggest suggest = response.getSuggest();// 4.1.根據補全查詢名稱,獲取補全結果CompletionSuggestion suggestions = suggest.getSuggestion("suggestions");// 4.2.獲取optionsList<CompletionSuggestion.Entry.Option> options = suggestions.getOptions();// 4.3.遍歷List<String> list = new ArrayList<>(options.size());for (CompletionSuggestion.Entry.Option option : options) {String text = option.getText().toString();list.add(text);}return list;} catch (IOException e) {throw new RuntimeException(e);} }4.6、接口的測試
總結
以上是生活随笔為你收集整理的Elasticsearch自动补全(拼音查询自动补全)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 计算机网络普遍采用什么传输方式,网络传输
- 下一篇: [vue] vue能监听到数组变化的方法