如何在Elasticsearch中安裝中文分詞器(IK+pinyin)

若是直接使用Elasticsearch的朋友在處理中文內容的搜索時,確定會遇到很尷尬的問題——中文詞語被分紅了一個一個的漢字,當用Kibana做圖的時候,按照term來分組,結果一個漢字被分紅了一組。html

這是由於使用了Elasticsearch中默認的標準分詞器,這個分詞器在處理中文的時候會把中文單詞切分紅一個一個的漢字,所以引入中文的分詞器就能解決這個問題。git

本篇文章按照下面的內容進行描述:github

  • 分詞器的做用
  • 安裝IK
  • 簡單的測試
  • 模擬測試
  • 安裝elasticsearch-analysis-pinyin
  • 簡單的測試
  • 模擬測試

分詞器的做用

分詞顧名思義,就是把一句話分紅一個一個的詞。這個概念在搜索中很重要,好比 This is a banana. 若是按照普通的空格來分詞,分紅this,is,a,banana,的出來的a其實對咱們並無什麼用處。所以須要注意下面的問題:算法

  • 1 區分停頓詞(a,or,and這種都屬於停頓詞)
  • 2 大小寫轉換(Bananabanana)
  • 3 時態的轉換....

具體的算法能夠參考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

安裝IK

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的分析方法。

  • type,字段的類型爲string,只有string類型才涉及到分詞,像是數字之類的是不須要分詞的。
  • store,定義字段的存儲方式,no表明不單獨存儲,查詢的時候會從_source中解析。當你頻繁的針對某個字段查詢時,能夠考慮設置成true。
  • term_vector,定義了詞的存儲方式,with_position_offsets,意思是存儲詞語的偏移位置,在結果高亮的時候有用。
  • analyzer,定義了索引時的分詞方法
  • search_analyzer,定義了搜索時的分詞方法
  • include_in_all,定義了是否包含在_all字段中
  • boost,是跟計算分值相關的。

設置完成後,添加一個文檔

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>漁船"
          ]
        }
      }
    ]
  }
}

安裝elasticsearch-analysis-pinyin分詞器

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": "劉德華"
        }
      }
    ]
  }
}

參考

相關文章
相關標籤/搜索