若是直接使用Elasticsearch的朋友在處理中文內容的搜索時,確定會遇到很尷尬的問題——中文詞語被分紅了一個一個的漢字,當用Kibana做圖的時候,按照term來分組,結果一個漢字被分紅了一組。html
這是由於使用了Elasticsearch中默認的標準分詞器,這個分詞器在處理中文的時候會把中文單詞切分紅一個一個的漢字,所以引入中文的分詞器就能解決這個問題。git
本篇文章按照下面的內容進行描述:github
分詞顧名思義,就是把一句話分紅一個一個的詞。這個概念在搜索中很重要,好比 This is a banana.
若是按照普通的空格來分詞,分紅this
,is
,a
,banana
,的出來的a
其實對咱們並無什麼用處。所以須要注意下面的問題:算法
a
,or
,and
這種都屬於停頓詞)Banana
與banana
)具體的算法能夠參考http://tartarus.org/~martin/PorterStemmer/,對照的詞語能夠參考這裏http://snowball.tartarus.org/algorithms/porter/diffs.txtapi
相比中文,就複雜的度了。由於中文不能單純的依靠空格,標點這種進行分詞。就好比中華人民共和國國民
,不能簡單的分紅一個詞,也不能粗暴的分紅中華人民共和國
和國民
,人民
、中華
這些也都算一個詞!app
所以常見的分詞算法就是拿一個標準的詞典,關鍵詞都在這個詞典裏面。而後按照幾種規則去查找有沒有關鍵詞,好比:curl
IK,elasticsearch-analysis-ik提供了兩種方式,ik_smart
就是最少切分,ik_max_word
則爲細粒度的切分(多是雙向,沒看過源碼)jvm
瞭解了分詞器的背景後,就能夠看一下如何在Elasticsearch重安裝分詞器了。elasticsearch
在github中下載相應的代碼,好比個人最新版本2.4.0就沒有對應的ik版本,不用擔憂,只須要修改pom.xml就能夠了:maven
<properties> <!-- 這裏的版本號,修改爲你對應的版本就好了。 不過最好不要跨度太大,相近的版本可能沒有問題,可是跨度太大的版本,這樣作就不保證好使了--> <elasticsearch.version>2.4.0</elasticsearch.version> <maven.compiler.target>1.7</maven.compiler.target> <elasticsearch.assembly.descriptor>${project.basedir}/src/main/assemblies/plugin.xml</elasticsearch.assembly.descriptor> <elasticsearch.plugin.name>analysis-ik</elasticsearch.plugin.name> <elasticsearch.plugin.classname>org.elasticsearch.plugin.analysis.ik.AnalysisIkPlugin</elasticsearch.plugin.classname> <elasticsearch.plugin.jvm>true</elasticsearch.plugin.jvm> <tests.rest.load_packaged>false</tests.rest.load_packaged> <skip.unit.tests>true</skip.unit.tests> <gpg.keyname>4E899B30</gpg.keyname> <gpg.useagent>true</gpg.useagent> </properties>
下載後,執行mvn package
,進行打包:
├─config ├─src └─target ├─archive-tmp ├─classes ├─generated-sources ├─maven-archiver ├─maven-status ├─releases │ └─elasticsearch-analysis-ik-1.9.5.zip └─surefire
編譯完成後,能夠在target/releases目錄下找到對應的zip包。
解壓zip包,複製到elasticsearch-root-path/plugins/ik下便可。
[root@hadoop-master ik]# ll total 1428 -rw-r--r-- 1 root root 263965 Sep 26 15:03 commons-codec-1.9.jar -rw-r--r-- 1 root root 61829 Sep 26 15:03 commons-logging-1.2.jar drwxr-xr-x 3 root root 4096 Sep 26 16:11 config -rw-r--r-- 1 root root 56023 Sep 26 15:03 elasticsearch-analysis-ik-1.9.5.jar -rw-r--r-- 1 root root 736658 Sep 26 15:03 httpclient-4.5.2.jar -rw-r--r-- 1 root root 326724 Sep 26 15:03 httpcore-4.4.4.jar -rw-r--r-- 1 root root 2666 Sep 26 15:03 plugin-descriptor.properties [root@hadoop-master ik]# pwd /usr/elk/elasticsearch-2.4.0/plugins/ik
拷貝後,重啓elasticsearch就能夠使用分詞器了。
這裏使用_analyze api對中文段落進行分詞,測試一下:
GET _analyze { "analyzer":"ik_max_word", "text":"中華人民共和國國歌" }
能夠看到ik儘量多的切分的單詞:
{ "tokens": [ { "token": "中華人民共和國", "start_offset": 0, "end_offset": 7, "type": "CN_WORD", "position": 0 }, { "token": "中華人民", "start_offset": 0, "end_offset": 4, "type": "CN_WORD", "position": 1 }, { "token": "中華", "start_offset": 0, "end_offset": 2, "type": "CN_WORD", "position": 2 }, { "token": "華人", "start_offset": 1, "end_offset": 3, "type": "CN_WORD", "position": 3 }, { "token": "人民共和國", "start_offset": 2, "end_offset": 7, "type": "CN_WORD", "position": 4 }, { "token": "人民", "start_offset": 2, "end_offset": 4, "type": "CN_WORD", "position": 5 }, { "token": "共和國", "start_offset": 4, "end_offset": 7, "type": "CN_WORD", "position": 6 }, { "token": "共和", "start_offset": 4, "end_offset": 6, "type": "CN_WORD", "position": 7 }, { "token": "國", "start_offset": 6, "end_offset": 7, "type": "CN_CHAR", "position": 8 }, { "token": "國歌", "start_offset": 7, "end_offset": 9, "type": "CN_WORD", "position": 9 } ] }
若是使用ik_smart,則會盡量少的返回詞語:
{ "tokens": [ { "token": "中華人民共和國", "start_offset": 0, "end_offset": 7, "type": "CN_WORD", "position": 0 }, { "token": "國歌", "start_offset": 7, "end_offset": 9, "type": "CN_WORD", "position": 1 } ] }
我這裏直接在elastic Sense中進行測試的(強烈推薦這個插件,很是好用,不過輸入中文的時候,有點BUG)
PUT test { }
若是你用的是curl,能夠執行curl -XPUT localhost:9200/test
POST test/test/_mapping { "test": { "_all": { "analyzer": "ik_max_word", "search_analyzer": "ik_max_word", "term_vector": "no", "store": "false" }, "properties": { "content": { "type": "string", "store": "no", "term_vector": "with_positions_offsets", "analyzer": "ik_max_word", "search_analyzer": "ik_max_word", "include_in_all": "true", "boost": 8 } } } }
上面的命令,是定義test索引下test類型的映射。其中定義了_all字段的分析方法,以及content屬性的分析方法。
這裏介紹下什麼是_all字段,其實_all字段是爲了在不知道搜索哪一個字段時,使用的。es會把全部的字段(除非你手動設置成false),都放在_all中,而後經過分詞器去解析。當你使用query_string的時候,默認就在這個_all字段上去作查詢,而不須要挨個字段遍歷,節省了時間。
properties
中定義了特定字段的分析方式。在上面的例子中,僅僅設置了content的分析方法。
POST test/test/1 { "test":"美國留給伊拉克的是個爛攤子嗎" } POST test/test/2 { "content":"公安部:各地校車將享最高路權嗎" } POST test/test/3 { "content":"中韓漁警衝突調查:韓警平均天天扣1艘中國漁船" } POST test/test/4 { "content":"中國駐洛杉磯領事館遭亞裔男子槍擊 嫌犯已自首" }
GET test/_search { "query" : { "term" : { "content" : "中國" }}, "highlight" : { "pre_tags" : ["<tag1>", "<tag2>"], "post_tags" : ["</tag1>", "</tag2>"], "fields" : { "content" : {} } } }
獲得返回結果:
{ "took": 4, "timed_out": false, "_shards": { "total": 5, "successful": 5, "failed": 0 }, "hits": { "total": 2, "max_score": 1.5, "hits": [ { "_index": "test", "_type": "test", "_id": "4", "_score": 1.5, "_source": { "content": "中國駐洛杉磯領事館遭亞裔男子槍擊 嫌犯已自首" }, "highlight": { "content": [ "<tag1>中國</tag1>駐洛杉磯領事館遭亞裔男子槍擊 嫌犯已自首" ] } }, { "_index": "test", "_type": "test", "_id": "3", "_score": 0.53699243, "_source": { "content": "中韓漁警衝突調查:韓警平均天天扣1艘中國漁船" }, "highlight": { "content": [ "中韓漁警衝突調查:韓警平均天天扣1艘<tag1>中國</tag1>漁船" ] } } ] } }
pinyin分詞器可讓用戶輸入拼音,就能查找到相關的關鍵詞。好比在某個商城搜索中,輸入shuihu
,就能匹配到水壺
。這樣的體驗仍是很是好的。
pinyin分詞器的安裝與IK是同樣的,這裏就省略掉了。下載的地址參考github.
這個分詞器在1.8版本中,提供了兩種分詞規則:
pinyin
,就是普通的把漢字轉換成拼音;pinyin_first_letter
,提取漢字的拼音首字母首先建立索引,並建立分詞器:
PUT medcl { "index" : { "analysis" : { "analyzer" : { "pinyin_analyzer" : { "tokenizer" : "my_pinyin", "filter" : "word_delimiter" } }, "tokenizer" : { "my_pinyin" : { "type" : "pinyin", "first_letter" : "none", "padding_char" : " " } } } } }
而後使用analyze api,進行測試
GET medcl/_analyze { "text":"劉德華", "analyzer":"pinyin_analyzer" }
能夠獲得結果:
{ "tokens": [ { "token": "liu", "start_offset": 0, "end_offset": 3, "type": "word", "position": 0 }, { "token": "de", "start_offset": 0, "end_offset": 3, "type": "word", "position": 1 }, { "token": "hua", "start_offset": 0, "end_offset": 3, "type": "word", "position": 2 } ] }
若是分詞器設置爲pinyin_first_letter,則分析的結果爲:
{ "tokens": [ { "token": "ldh", "start_offset": 0, "end_offset": 3, "type": "word", "position": 0 } ] }
POST medcl/_close { }
PUT medcl/_settings { "index" : { "analysis" : { "analyzer" : { "pinyin_analyzer" : { "tokenizer" : "my_pinyin", "filter" : ["word_delimiter","nGram"] } }, "tokenizer" : { "my_pinyin" : { "type" : "pinyin", "first_letter" : "prefix", "padding_char" : " " } } } } }
POST medcl/_open { }
POST medcl/folks/_mapping { "folks": { "properties": { "name": { "type": "multi_field", "fields": { "name": { "type": "string", "store": "no", "term_vector": "with_positions_offsets", "analyzer": "pinyin_analyzer", "boost": 10 }, "primitive": { "type": "string", "store": "yes", "analyzer": "keyword" } } } } } }
POST medcl/folks/1 { "name":"劉德華" }
GET medcl/folks/_search { "query": {"match": { "name": "l d hua" }} }
這裏搜liu de hua
,ldh
,l de hua
都能匹配到,仍是很強大滴。
{ "took": 7, "timed_out": false, "_shards": { "total": 5, "successful": 5, "failed": 0 }, "hits": { "total": 1, "max_score": 7.408082, "hits": [ { "_index": "medcl", "_type": "folks", "_id": "1", "_score": 7.408082, "_source": { "name": "劉德華" } } ] } }