前言
相信很多朋友剛開始在使用Elasticsearch的時候,一定都會遇到一個問題: 我的檔案內容清清楚楚的寫在那, 怎麼就是搜尋不到? 其中很大的可能就是分析器沒有正確配置唷!
舉個例子
搜尋英文進行式單詞
首先放入一筆資料, 內容是 "Set the shape to semi-transparent by calling set_trans(5)"
root@ubuntu-87:~# curl -XPOST http://localhost:9200/demo/demotype/1 -H 'Content-Type:application/json' -d '
{
"content": "Set the shape to semi-transparent by calling set_trans(5)"
}' | jq .
{
"_index": "demo",
"_type": "demotype",
"_id": "1",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}
然後我們試著搜尋 call; 結果是搜尋不到這筆資料.
root@ubuntu-87:~# curl -s -XPOST http://localhost:9200/demo/demotype/_search -H 'Content-Type:application/json' -d '
{
"query" : {
"match" : {
"content" : "call"
}
}
}' | jq .
{
"took": 6,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 0,
"max_score": null,
"hits": []
}
}
原因
在Elasticsearch可以搜尋文檔之前, 他必須先將字詞內容做拆分(分詞)和加工, 然後透過這些拆分好的詞建構倒排索引, 之後使用者才能在透過關鍵詞搜尋到我們想要的文檔.
而文檔內容要如何分詞或是加工, 依靠的就是分析器的配置規則囉!
也就是說,Elasticsearch認定的分詞是 calling 不是call, 所以搜尋不到.
分析器介紹
先附上文檔
英文: Elasticsearch: The Definitive Guide - Analysis and Analyzers
中文: Elasticsearch: 权威指南 - 分析与分析器
分析器是由: 字符過濾器(Character filters), 分詞器(Tokenizer), Token過濾器(Token filters) 組成的; 他們的工作流程如下
-
字符過濾器(Character filters): 預處理
首先,字符串按順序通過每個 字符過濾器 。他們的任務是在分詞前整理字符串。一個字符過濾器可以用來去掉HTML,或者將&
轉化成and
。 -
分詞器(Tokenizer): 主要分詞工作
其次,字符串被 分詞器 分為單個的詞條。一個簡單的分詞器遇到空格和標點的時候,可能會將文本拆分成詞條。 -
Token過濾器(Token filters): 後續加工
最後,詞條按順序通過每個 token 過濾器 。這個過程可能會改變詞條(例如,小寫化 Quick ),刪除詞條(例如, 像a
,and
,the
等無用詞),或者增加詞條(例如,像 jump 和 leap 這種同義詞)。
內置分析器效果預覽
Elasticsearch 內置了幾種分析器, 透過套用不同的分析器就可以讓這句文檔產生不同的索引效果
-
標準分析器 (Standard analyzer)
標準分析器是Elasticsearch默認使用的分析器。它是分析各種語言文本最常用的選擇。它根據 Unicode 聯盟 定義的 單詞邊界 劃分文本。刪除絕大部分標點。最後,將詞條小寫。他會產生
set, the, shape, to, semi, transparent, by, calling, set_trans, 5
-
簡單分析器 (Simple analyzer)
簡單分析器在任何不是字母的地方分隔文本,將詞條小寫。它會產生
set, the, shape, to, semi, transparent, by, calling, set, trans
-
空格分析器 (Whitespace analyzer)
空格分析器在空格的地方劃分文本。它會產生
Set, the, shape, to, semi-transparent, by, calling, set_trans(5)
-
語言分析器 (Language analyzers)
特定語言分析器可用於 很多語言。它們可以考慮指定語言的特點。例如, 英語 分析器附帶了一組英語無用詞(常用單詞,例如 and 或者 the ,它們對相關性沒有多少影響),它們會被刪除。 由於理解英語語法的規則,這個分詞器可以提取英語單詞的 詞幹 。
英語 分詞器會產生下面的詞條:
set, shape, semi, transpar, call, set_tran, 5
註意看 transparent
, calling
和 set_trans
已經變為詞根格式。
看到這邊大家應該了解到如果想要搜尋英文詞根的話; 必須套用語言分析器~
配置分析器
分析器的配置放在索引的mapping之下, 而更改過的mapping無法影響已存在的文檔; 所以我們另外配置一個demo1來做示範
- 新增索引:
curl -XPUT http://localhost:9200/demo1
- 配置映射:
curl -XPOST http://localhost:9200/demo1/demotype/_mapping -H 'Content-Type:application/json' -d'
{
"properties": {
"content": {
"type": "text",
"analyzer": "english"
}
}
}'
-
新增文檔:
curl -XPOST http://localhost:9200/demo1/demotype/1 -H 'Content-Type:application/json' -d '{"content":"Set the shape to semi-transparent by calling set_trans(5)"}'
-
嘗試搜尋:
root@ubuntu-87:~# curl -s -XPOST http://localhost:9200/demo1/demotype/_search -H 'Content-Type:application/json' -d '
{
"query" : {
"match" : {
"content" : "call"
}
}
}' | jq .
{
"took": 19,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 0.2876821,
"hits": [
{
"_index": "demo1",
"_type": "demotype",
"_id": "1",
"_score": 0.2876821,
"_source": {
"content": "Set the shape to semi-transparent by calling set_trans(5)"
}
}
]
}
}
查看分詞效果
僅管我們進行了這些配置, 但是只能透過搜尋來測試有沒有成功, 這樣子還是比較模糊的.
所以 Elasticsearch 提供了兩個 api 讓我們可以直接查看分詞效果: _termvectors
和 _analyze
, 而不管是哪一個都提供了以下資訊
- 剛檔案的分詞組成
start_offset
,end_offset
: 字詞出現的位置, 用於高亮搜索position
: 分詞出現的順序type
: 詞的類別
分詞效果-預設狀態
使用_analyze查看
root@ubuntu-87:~# curl 'http://localhost:9200/demo/_analyze?pretty=true' -H 'Content-Type: application/json' -d '{ "field": "content", "text":"Set the shape to semi-transparent by calling set_trans(5)"}'
{
"tokens" : [
{
"token" : "set",
"start_offset" : 0,
"end_offset" : 3,
"type" : "<ALPHANUM>",
"position" : 0
},
{
"token" : "the",
"start_offset" : 4,
"end_offset" : 7,
"type" : "<ALPHANUM>",
"position" : 1
},
{
"token" : "shape",
"start_offset" : 8,
"end_offset" : 13,
"type" : "<ALPHANUM>",
"position" : 2
},
{
"token" : "to",
"start_offset" : 14,
"end_offset" : 16,
"type" : "<ALPHANUM>",
"position" : 3
},
{
"token" : "semi",
"start_offset" : 17,
"end_offset" : 21,
"type" : "<ALPHANUM>",
"position" : 4
},
{
"token" : "transparent",
"start_offset" : 22,
"end_offset" : 33,
"type" : "<ALPHANUM>",
"position" : 5
},
{
"token" : "by",
"start_offset" : 34,
"end_offset" : 36,
"type" : "<ALPHANUM>",
"position" : 6
},
{
"token" : "calling",
"start_offset" : 37,
"end_offset" : 44,
"type" : "<ALPHANUM>",
"position" : 7
},
{
"token" : "set_trans",
"start_offset" : 45,
"end_offset" : 54,
"type" : "<ALPHANUM>",
"position" : 8
},
{
"token" : "5",
"start_offset" : 55,
"end_offset" : 56,
"type" : "<NUM>",
"position" : 9
}
]
}
使用_termvectors查看
root@ubuntu-87:~# curl -H "Content-Type:application/json" 'http://localhost:9200/demo/demotype/1/_termvectors?pretty=true' -d '{"fie
lds" : ["content"]}'
{
"_index" : "demo",
"_type" : "demotype",
"_id" : "1",
"_version" : 1,
"found" : true,
"took" : 0,
"term_vectors" : {
"content" : {
"field_statistics" : {
"sum_doc_freq" : 10,
"doc_count" : 1,
"sum_ttf" : 10
},
"terms" : {
"5" : {
"term_freq" : 1,
"tokens" : [
{
"position" : 9,
"start_offset" : 55,
"end_offset" : 56
}
]
},
"by" : {
"term_freq" : 1,
"tokens" : [
{
"position" : 6,
"start_offset" : 34,
"end_offset" : 36
}
]
},
"calling" : {
"term_freq" : 1,
"tokens" : [
{
"position" : 7,
"start_offset" : 37,
"end_offset" : 44
}
]
},
"semi" : {
"term_freq" : 1,
"tokens" : [
{
"position" : 4,
"start_offset" : 17,
"end_offset" : 21
}
]
},
"set" : {
"term_freq" : 1,
"tokens" : [
{
"position" : 0,
"start_offset" : 0,
"end_offset" : 3
}
]
},
"set_trans" : {
"term_freq" : 1,
"tokens" : [
{
"position" : 8,
"start_offset" : 45,
"end_offset" : 54
}
]
},
"shape" : {
"term_freq" : 1,
"tokens" : [
{
"position" : 2,
"start_offset" : 8,
"end_offset" : 13
}
]
},
"the" : {
"term_freq" : 1,
"tokens" : [
{
"position" : 1,
"start_offset" : 4,
"end_offset" : 7
}
]
},
"to" : {
"term_freq" : 1,
"tokens" : [
{
"position" : 3,
"start_offset" : 14,
"end_offset" : 16
}
]
},
"transparent" : {
"term_freq" : 1,
"tokens" : [
{
"position" : 5,
"start_offset" : 22,
"end_offset" : 33
}
]
}
}
}
}
}
分詞效果-經過配置後
使用_analyze查看
root@ubuntu-87:~# curl 'http://localhost:9200/demo1/_analyze?pretty=true' -H 'Content-Type: application/json' -d '{ "field": "content
", "text":"Set the shape to semi-transparent by calling set_trans(5)"}'
{
"tokens" : [
{
"token" : "set",
"start_offset" : 0,
"end_offset" : 3,
"type" : "<ALPHANUM>",
"position" : 0
},
{
"token" : "shape",
"start_offset" : 8,
"end_offset" : 13,
"type" : "<ALPHANUM>",
"position" : 2
},
{
"token" : "semi",
"start_offset" : 17,
"end_offset" : 21,
"type" : "<ALPHANUM>",
"position" : 4
},
{
"token" : "transpar",
"start_offset" : 22,
"end_offset" : 33,
"type" : "<ALPHANUM>",
"position" : 5
},
{
"token" : "call",
"start_offset" : 37,
"end_offset" : 44,
"type" : "<ALPHANUM>",
"position" : 7
},
{
"token" : "set_tran",
"start_offset" : 45,
"end_offset" : 54,
"type" : "<ALPHANUM>",
"position" : 8
},
{
"token" : "5",
"start_offset" : 55,
"end_offset" : 56,
"type" : "<NUM>",
"position" : 9
}
]
}
使用_termvectors查看
root@ubuntu-87:~# curl -H "Content-Type:application/json" 'http://localhost:9200/demo1/demotype/1/_termvectors?pretty=true' -d '{"fi
elds" : ["content"]}'
{
"_index" : "demo1",
"_type" : "demotype",
"_id" : "1",
"_version" : 1,
"found" : true,
"took" : 1,
"term_vectors" : {
"content" : {
"field_statistics" : {
"sum_doc_freq" : 7,
"doc_count" : 1,
"sum_ttf" : 7
},
"terms" : {
"5" : {
"term_freq" : 1,
"tokens" : [
{
"position" : 9,
"start_offset" : 55,
"end_offset" : 56
}
]
},
"call" : {
"term_freq" : 1,
"tokens" : [
{
"position" : 7,
"start_offset" : 37,
"end_offset" : 44
}
]
},
"semi" : {
"term_freq" : 1,
"tokens" : [
{
"position" : 4,
"start_offset" : 17,
"end_offset" : 21
}
]
},
"set" : {
"term_freq" : 1,
"tokens" : [
{
"position" : 0,
"start_offset" : 0,
"end_offset" : 3
}
]
},
"set_tran" : {
"term_freq" : 1,
"tokens" : [
{
"position" : 8,
"start_offset" : 45,
"end_offset" : 54
}
]
},
"shape" : {
"term_freq" : 1,
"tokens" : [
{
"position" : 2,
"start_offset" : 8,
"end_offset" : 13
}
]
},
"transpar" : {
"term_freq" : 1,
"tokens" : [
{
"position" : 5,
"start_offset" : 22,
"end_offset" : 33
}
]
}
}
}
}
}
尾聲
這樣基礎的分析器概念就說完囉, 之後會在新增中文分析器的利器ik分析器
~