第四章-分詞
下雨天留客天留我不留
本打算先介紹“簡單搜尋”,對ES的搜尋有一個直觀的感受。但在寫的過程中發現分詞無論如何都繞不過去。term
查詢,match
查詢都與分詞息息相關,索性先介紹分詞。
ES作為一個開源的搜尋引擎,其核心自然在於搜尋,而搜尋不同於我們在MySQL中的select
查詢語句,無論我們在百度搜尋一個關鍵字,或者在京東搜尋一個商品時,常常無法很準確的給出一個關鍵字,例如我們在百度希望搜尋“Java教程”,我們希望結果是“Java教程”、“Java”、“Java基礎教程”,甚至是“教程Java”。MySQL雖然能滿足前三種查詢結果,但卻無法滿足最後一種搜尋結果。
雖然我們很難做到對於百度或者京東的搜尋(這甚至需要了解Lucene和搜尋的底層原理),但我們能借助ES做出一款不錯的搜尋產品。
ES的搜尋中,分詞是非常重要的概念。掌握分詞原理,對待一個不甚滿意的搜尋結果我們能定位是哪裡出了問題,從而做出相應的調整。
ES中,只對字串進行分詞,在ElasticSearch2.x版本中,字串型別只有string
,ElasticSearch5.x版本後字串型別分為了text
和keyword
型別,需要明確的分詞只在text
型別。
ES的預設分詞器是standard
,對於英文搜尋它沒有問題,但對於中文搜尋它會將所有的中文字串挨個拆分,也就是它會將“中國”拆分為“中”和“國”兩個單詞,這帶來的問題會是搜尋關鍵字為“中國”時,將不會有任何結果,ES會將搜尋欄位進行拆分後搜尋。當然,你可以指定讓搜尋的欄位不進行分詞,例如設定為keyword
欄位。
分詞體驗
前面說到ES的預設分詞器是standard
,可直接通過API指定分詞器以及字串檢視分詞結果。
使用standard
進行英文分詞:
POST http://localhost:9200/_analyze
{
"analyzer":"standard",
"text":"hello world"
}
ES響應:
{
"tokens": [
{
"token": "hello",
"start_offset": 0,
"end_offset": 5,
"type": "<ALPHANUM>",
"position": 0
},
{
"token": "world",
"start_offset": 6,
"end_offset": 11,
"type": "<ALPHANUM>",
"position": 1
}
]
}
如果我們對“helloword”進行分詞,結果將只有“helloword”一個詞,standsard
對英文按照空格進行分詞。
使用standard
進行中文分詞:
POST http://localhost:9200/_analyze
{
"analyzer":"standard",
"text":"學生"
}
ES響應:
{
"tokens": [
{
"token": "學",
"start_offset": 0,
"end_offset": 1,
"type": "<IDEOGRAPHIC>",
"position": 0
},
{
"token": "生",
"start_offset": 1,
"end_offset": 2,
"type": "<IDEOGRAPHIC>",
"position": 1
}
]
}
“學生”顯然應該是一個詞,不應該被拆分。也就是說如果字串中是中文,預設的standard
不符合我們的需求。幸運地是, ES支援第三方分詞外掛。在ES中的中文分詞外掛使用最為廣泛的是ik外掛。
ik外掛
既然是外掛,就需要安裝。注意,版本5.0.0起,ik外掛已經不包含名為ik
的分詞器,只含ik_smart
和ik_max_word
,事實上後兩者使用得也最多。
ik外掛安裝
ik下載地址(直接下載編譯好了的zip檔案,需要和ES版本一致):https://github.com/medcl/elasticsearch-analysis-ik/releases/tag/v6.3.2。ik歷史版本下載頁面:https://github.com/medcl/elasticsearch-analysis-ik/releases。
下載完成後解壓elasticsearch-analysis-ik-6.3.2.zip
將解壓後的資料夾直接放入ES安裝目錄下的plugins
資料夾中,重啟ES。
使用ik外掛的ik_smart
分詞器:
POST http://localhost:9200/_analyze
{
"analyzer":"ik_smart",
"text":"學生"
}
ES響應:
{
"tokens": [
{
"token": "學生",
"start_offset": 0,
"end_offset": 2,
"type": "CN_WORD",
"position": 0
}
]
}
這才符合我們的預期。那麼ik外掛中的ik_smart
和ik_max_word
有什麼區別呢?簡單來講,ik_smart
會按照關鍵字的最粗粒度進行分詞,比如搜尋“北京大學”時,我們知道“北京大學”是一個特定的詞彙,它並不是指“北京的大學”,我們不希望搜尋出“四川大學”,“重慶大學”等其他學校,此時“北京大學”不會被分詞。而ik_max_word
則會按照最細粒度進行分詞,同樣搜尋“北京大學”時,我們也知道“北京”和“大學”都是一個詞彙,所以它將會被分詞為“北京大學”,“北京大”,“北京”,“大學”,顯然如果搜尋出現後三者相關結果,這會給我們帶來更多無用的資訊。
所以我們在進行搜尋時,常常指定ik_smart
為分詞器。
有時候一個詞並不在ik外掛的詞庫中,例如很多網路用語等。我們希望搜尋“小米手機”的時候,只出現“小米的手機”而不會出現“華為手機”、“OPPO手機”,但“小米手機”並不在ik詞庫中,此時我們可以將“小米手機”新增到ik外掛的自定義詞庫中。
“小米手機”使用ik_smart
的分詞結果:
{
"tokens": [
{
"token": "小米",
"start_offset": 0,
"end_offset": 2,
"type": "CN_WORD",
"position": 0
},
{
"token": "手機",
"start_offset": 2,
"end_offset": 4,
"type": "CN_WORD",
"position": 1
}
]
}
進入ik外掛安裝目錄elasticsearch-5.6.0/plugins/elasticsearch/config
,建立名為custom.dic
的自定義詞庫,向檔案中新增“小米手機”並儲存。仍然是此目錄,修改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">custom.dic</entry>
<!--使用者可以在這裡配置自己的擴充套件停止詞字典-->
<entry key="ext_stopwords"></entry>
<!--使用者可以在這裡配置遠端擴充套件字典 -->
<!-- <entry key="remote_ext_dict">words_location</entry> -->
<!--使用者可以在這裡配置遠端擴充套件停止詞字典-->
<!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>
重啟ES後,再次通過ik_smart
對“小米手機”進行分詞,發現“小米手機”不再被分詞。
建立對映指定分詞器
在建立對映時,我們可以指定欄位採用哪種分詞器,避免我們在每次搜尋時都指定。
建立word索引
PUT http://localhost:9200/word
建立analyzer_demo型別已經定義對映Mapping
PUT http://localhost:9200/word/analyzer_demo/_mapping { "properties":{ "name":{ "type":"text", "analyzer":"ik_smart" } } }
檢視word索引結構
GET http://localhost:9200/word
ES響應:
{ "word": { "aliases": {}, "mappings": { "analyzer_demo": { "properties": { "name": { "type": "text", "analyzer": "ik_smart" } } } }, "settings": { "index": { "creation_date": "1561304920088", "number_of_shards": "5", "number_of_replicas": "1", "uuid": "A2YO9GpzRrGAIm2Q6rCoWA", "version": { "created": "5060099" }, "provided_name": "word" } } } }
可以看到ES在對name欄位進行分詞時會採用ik_smart
分詞器。
關注公眾號:CoderBuff,回覆“es”獲取《ElasticSearch6.x實戰教程》完整版PDF,回覆“抽獎”參與《從Lucene到Elasticsearch:全文檢索實戰》圖書抽獎活動(7.17-7.21)。