《ElasticSearch6.x實戰教程》之分詞

第四章-分詞

下雨天留客天留我不留java

本打算先介紹「簡單搜索」,對ES的搜索有一個直觀的感覺。但在寫的過程當中發現分詞不管如何都繞不過去。term查詢,match查詢都與分詞息息相關,索性先介紹分詞。git

ES做爲一個開源的搜索引擎,其核心天然在於搜索,而搜索不一樣於咱們在MySQL中的select查詢語句,不管咱們在百度搜索一個關鍵字,或者在京東搜索一個商品時,經常沒法很準確的給出一個關鍵字,例如咱們在百度但願搜索「Java教程」,咱們但願結果是「Java教程」、「Java」、「Java基礎教程」,甚至是「教程Java」。MySQL雖然能知足前三種查詢結果,但卻沒法知足最後一種搜索結果。程序員

雖然咱們很難作到對於百度或者京東的搜索(這甚至須要瞭解Lucene和搜索的底層原理),但咱們能借助ES作出一款不錯的搜索產品。github

ES的搜索中,分詞是很是重要的概念。掌握分詞原理,對待一個不甚滿意的搜索結果咱們能定位是哪裏出了問題,從而作出相應的調整。json

ES中,只對字符串進行分詞,在ElasticSearch2.x版本中,字符串類型只有string,ElasticSearch5.x版本後字符串類型分爲了textkeyword類型,須要明確的分詞只在text類型。網絡

ES的默認分詞器是standard,對於英文搜索它沒有問題,但對於中文搜索它會將全部的中文字符串挨個拆分,也就是它會將「中國」拆分爲「中」和「國」兩個單詞,這帶來的問題會是搜索關鍵字爲「中國」時,將不會有任何結果,ES會將搜索字段進行拆分後搜索。固然,你能夠指定讓搜索的字段不進行分詞,例如設置爲keyword字段。app

分詞體驗

前面說到ES的默認分詞器是standard,可直接經過API指定分詞器以及字符串查看分詞結果。elasticsearch

使用standard進行英文分詞:ide

POST http://localhost:9200/_analyze
{
    "analyzer":"standard",
    "text":"hello world" 
}

ES響應:ui

{
    "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_smartik_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_smartik_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對「小米手機」進行分詞,發現「小米手機」再也不被分詞。

建立映射指定分詞器

在建立映射時,咱們能夠指定字段採用哪一種分詞器,避免咱們在每次搜索時都指定。

  1. 建立word索引 PUT http://localhost:9200/word

  2. 建立analyzer_demo類型已經定義映射Mapping

    PUT http://localhost:9200/word/analyzer_demo/_mapping
    {
     "properties":{
         "name":{
             "type":"text",
          "analyzer":"ik_smart"
         }
     }
    }
  3. 查看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。

這是一個能給程序員加buff的公衆號 (CoderBuff)
相關文章
相關標籤/搜索