前一篇綜整了三個主要的問題,並針對其中兩個—— significant-text aggregation 的效能和 suggestion 結果單一的問題做了一點點優化,這篇來探討一下 analyzer 設定不理想的問題:
先複習一下目前的設定:
{
"analysis": {
"analyzer": {
"index_analyzer": {
"type": "custom",
"tokenizer": "standard",
"filter": [
"lowercase",
"stop"
],
"char_filter": [
"pun_filter"
]
}
},
"char_filter": {
"pun_filter": {
"type": "mapping",
"mappings": [
"/=>",
".=>",
"ー=>"
]
}
}
}
}
先前有提到 ES 的 Text Analysis 會先經過 character filter(char_filter),再經過 tokenizer 分詞、最後經過 token filter(filter)過濾要被索引的 token。
在 char_filter 中,為了避免 standard tokenizer 把 tweet 中的網址非文字的符號當成分詞字元,因此先把 /
、 .
、 ー
移除(透過 mapping 替換成沒有字元),但這個做法蠻手工的,有可能有部分沒有處理到。
另外是可以從上一篇的結果發現,symptom 和 symptom 和 symptoms 會被視為不同的token 但這在 suggestion 中不是太有用。
這篇,我們就針對這兩個問題進行處理。
首先 char_filter 的部分,除了 mapping replacement filter 之外,ES 還有提供 pattern replacement ,透過 regex pattern 來決定哪些 character 會被取代掉,修改後的 analyzer 如下:
{
"analysis": {
"analyzer": {
"index_analyzer": {
"type": "custom",
"tokenizer": "standard",
"filter": [
"lowercase",
"stop"
],
"char_filter": [
"pun_filter"
]
}
},
"char_filter": {
"pun_filter": {
**"type": "pattern_replace",
"pattern": "([\\w\\s\\n]+)([\\S\\W]+?)(?=[_\\w\\s\\n]+)",
"replacement": "$1"**
}
}
}
}
這個 Pattern 主要是把 『文字或空白或換行』後接『非文字』的非文字部分去除掉,所以 http 內的符號都會一並被消除,非常的方便,副作用是其他的符號也會被消除(例如日期字串內的 -
或 /
)。
使用其中一筆資料丟入 _analyzer
api 示範如下:
GET _analyze
{
"char_filter": [
{
"type": "pattern_replace",
"pattern": "([\\w\\s\\n]+)([^\\w\\s\\n]+?)(?=[_\\w\\s\\n]+)",
"replacement": "$1"
}
],
"text":"""epal Daily Update on #COVID19.
31/3/2020 | Lockdown day 8ー
Sources:
1. https://t.co/s6hO80l4zc, https://t.co/wzozTS77hA
2. https://t.co/36Rp6NmAo1
3. https://t.co/ly03MoV1Vj
4. https://t.co/OxLSru8Nl4
5. https://t.co/m4aC1zRia8 https://t.co/REQiQceSco"""
}
得到的結果大致還蠻符合預期的:
{
"tokens": [
{
"token": """epal Daily Update on COVID19
3132020 Lockdown day 8
Sources
1 httpstcos6hO80l4zc httpstcowzozTS77hA
2 httpstco36Rp6NmAo1
3 httpstcoly03MoV1Vj
4 httpstcoOxLSru8Nl4
5 httpstcom4aC1zRia8 httpstcoREQiQceSco""",
"start_offset": 0,
"end_offset": 254,
"type": "word",
"position": 0
}
]
}
接下來是要解決重複的 suggestion word 的問題,ES 提供了一個叫做 stemmer 的 token filter,顧名思義是字根的轉換,例如複數型(-s)會被轉換成單數型,動詞過去式也會被轉換為現在式等等;示範如下:
GET _analyze
{
"tokenizer": "standard",
**"filter": {
"type": "stemmer",
"language": "light_english"**
},
"char_filter": [
{
"type": "pattern_replace",
"pattern": "([\\w\\s\\n]+)([^\\w\\s\\n]+?)(?=[\\w\\s\\n]+)",
"replacement": "$1"
}
],
"text": "docuemnts foxes using drunk"
}
取得的 token 如下:
{
"tokens": [
{
"token": "docuemnt",
"start_offset": 0,
"end_offset": 9,
"type": "<ALPHANUM>",
"position": 0
},
{
"token": "fox",
"start_offset": 10,
"end_offset": 15,
"type": "<ALPHANUM>",
"position": 1
},
{
"token": "use",
"start_offset": 16,
"end_offset": 21,
"type": "<ALPHANUM>",
"position": 2
},
{
"token": "drunk",
"start_offset": 22,
"end_offset": 27,
"type": "<ALPHANUM>",
"position": 3
}
]
}
雖然大部分的狀況都符合預期,但對於變形的過去式處理似乎沒有很理想。
把上述兩個修改都放入 analyzer settings 後如下:
{
"analysis": {
"analyzer": {
"index_analyzer": {
"type": "custom",
"tokenizer": "standard",
"filter": [
"lowercase",
"stop",
"stem_filter"
],
"char_filter": [
"pun_filter"
]
}
},
"char_filter": {
"pun_filter": {
"type": "pattern_replace",
"pattern": "([\\w\\s\\n]+)([^\\w\\s\\n]+?)(?=[\\w\\s\\n]+)",
"replacement": "$1"
}
},
"filter": {
"stem_filter": {
"type": "stemmer",
"language": "light_english"
}
}
}
}
修改後一樣測試一下:
輸入: cov
輸出:['covid19 percent', 'covid19 coronaviru', 'covid19 mild', 'covid 19']
輸入: covid
輸出:['covid19 percent', 'covid19 coronaviru', 'covid19 mild', 'covid_19 coronaviru', 'covid_19 evirahealth', 'covid_19 coronaupdate']
輸入: covid sym
輸出:['covid symptom mild', 'covid symptom 19', 'covid symptom showing', 'covid symply_tacha 19', 'covid symply_tacha everythintacha']
好的,看起來又更好了一點點,下一篇繼續。