全文檢索與常規關系型數據庫SQL查詢的顯著區別,就是全文檢索具備對大段文本進行分析的能力,它可以通過文本分析把大段的文本切分為細粒度的分詞。
elasticsearch在兩種情況下會用到文本分析:
- 原始數據寫入索引時,如果索引的某個字段類型是text,則會將分析之后的內容寫入索引
- 對text類型字段的索引數據做全文檢索時,搜索內容也會經過文本分析。
1 文本分析的原理
elasticsearch規定,一個完整的文本分析過程需要經過>=0個字符過濾器,一個分詞器,>=0分詞過濾器的處理過程。
文本分析的順序是先進行字符過濾器的處理,然后是分詞器的處理,最后分詞過濾器的處理。
字符過濾器
用于對原始文本做簡單字符過濾和轉換,例如,elasticsearch內置的HTML strip字符過濾器可以方便地刪除文本中的HTML標簽。
分詞器
把原始的文本按照一定的規則切分成一個個單詞,對于中文文本而言分詞的效果和中文分詞器的類型有關。
分詞器還會保留每個關鍵詞在原始文本中出現的位置數據。
elasticsearch的內置的分詞器有幾十種,通常針對不同語言的文本需要使用不同的分詞器。
分詞過濾器
用于對分詞器切詞后的單詞做進一步的過濾和轉換,例如:停用詞分詞過濾器(stop token filter)可以把分詞器切分出來的冠詞a,介詞of等無實際意義的單詞直接丟棄,避免影響搜索結果。
2 內置的分析器分析文本
elasticsearch內置了8種分析器,可以直接使用
標準分析器
是文本分析默認的分析器,標準分析器的內部包含一個標準分詞器(standard tokenizer)和一個小寫分詞過濾器(lower case token ilter),可以使用_analyze端點測試分析器默認的分詞效果,如:
POST _analyze
{"analyzer":"standard","text":"The 2025頭條新聞has spread out。"
}
{"tokens" : [{"token" : "the","start_offset" : 0,"end_offset" : 3,"type" : "<ALPHANUM>","position" : 0},{"token" : "2025","start_offset" : 4,"end_offset" : 8,"type" : "<NUM>","position" : 1},{"token" : "頭","start_offset" : 8,"end_offset" : 9,"type" : "<IDEOGRAPHIC>","position" : 2},{"token" : "條","start_offset" : 9,"end_offset" : 10,"type" : "<IDEOGRAPHIC>","position" : 3},{"token" : "新","start_offset" : 10,"end_offset" : 11,"type" : "<IDEOGRAPHIC>","position" : 4},{"token" : "聞","start_offset" : 11,"end_offset" : 12,"type" : "<IDEOGRAPHIC>","position" : 5},{"token" : "has","start_offset" : 12,"end_offset" : 15,"type" : "<ALPHANUM>","position" : 6},{"token" : "spread","start_offset" : 16,"end_offset" : 22,"type" : "<ALPHANUM>","position" : 7},{"token" : "out","start_offset" : 23,"end_offset" : 26,"type" : "<ALPHANUM>","position" : 8}]
}
可以看到,標準分析器默認的切詞效果可以實現將英語的大寫轉為小寫字母,這是因為該分析器內置了一個將大寫字母轉為小寫字母的小寫分詞過濾器;
另外,按照空格來切分英語單詞,數字作為一個整體不會被切分,中文文本會被切分成一個個漢字,標點符合則是被自動去除。
標準分析器可以配置停用詞分詞過濾器去除沒有實際意義的虛詞,還可以通過設置切詞粒度來配置分詞的長度。
例如:把標準分析器設置到索引中。
PUT standard-text
{"settings": {"analysis": {"analyzer": {"my_standard_analyzer":{"type":"standard","max_token_length":3,"stopwords":"_english_"}}}},"mappings": {"properties": {"content":{"type": "text","analyzer": "my_standard_analyzer"}}}
}
{"acknowledged" : true,"shards_acknowledged" : true,"index" : "standard-text"
}
在上面的索引standard-text中,配置了一個名為my_standard_analyzer的分析器。type為standard表明它使用了內置的標準分析器;
max_token_length=3,表示切詞的最大長度設置為3,這樣數字和英文長度都會被切分為不超過3個字符的形式;
stopwords為_english_表示該分析器會自動過濾英文的停用詞。
該索引只有一個文本字段content,映射中指定了該字段的分析器為my_standard_analyzer,表示建索引時該字段的文本數據使用my_standard_analyzer做文本分析。接下來看看新的切詞效果:
POST standard-text/_analyze
{"analyzer":"my_standard_analyzer","text":"The 2025頭條新聞has spread out。"
}
{"tokens" : [{"token" : "202","start_offset" : 4,"end_offset" : 7,"type" : "<NUM>","position" : 1},{"token" : "5","start_offset" : 7,"end_offset" : 8,"type" : "<NUM>","position" : 2},{"token" : "頭","start_offset" : 8,"end_offset" : 9,"type" : "<IDEOGRAPHIC>","position" : 3},{"token" : "條","start_offset" : 9,"end_offset" : 10,"type" : "<IDEOGRAPHIC>","position" : 4},{"token" : "新","start_offset" : 10,"end_offset" : 11,"type" : "<IDEOGRAPHIC>","position" : 5},{"token" : "聞","start_offset" : 11,"end_offset" : 12,"type" : "<IDEOGRAPHIC>","position" : 6},{"token" : "has","start_offset" : 12,"end_offset" : 15,"type" : "<ALPHANUM>","position" : 7},{"token" : "spr","start_offset" : 16,"end_offset" : 19,"type" : "<ALPHANUM>","position" : 8},{"token" : "ead","start_offset" : 19,"end_offset" : 22,"type" : "<ALPHANUM>","position" : 9},{"token" : "out","start_offset" : 23,"end_offset" : 26,"type" : "<ALPHANUM>","position" : 10}]
}
可以明顯看出該結果跟使用默認的標準分析器standard的有兩點不同,一是分詞the作為停用詞被過濾掉了,二是每個分詞的長度都不超過3.
簡單分析器
簡單分析器(simple analyzer)只由一個小寫分詞器(lowercase tokenizer)組成,它的功能是將文本在任意非字母字符(如數字,空格,連字符)處進行切分,丟棄非字母字符,并將大寫字母改為小寫字母.
例如:
POST _analyze
{"analyzer": "simple","text":"The 2025頭條新聞 hasn`t spread out。"
}
{"tokens" : [{"token" : "the","start_offset" : 0,"end_offset" : 3,"type" : "word","position" : 0},{"token" : "頭條新聞","start_offset" : 8,"end_offset" : 12,"type" : "word","position" : 1},{"token" : "hasn","start_offset" : 13,"end_offset" : 17,"type" : "word","position" : 2},{"token" : "t","start_offset" : 18,"end_offset" : 19,"type" : "word","position" : 3},{"token" : "spread","start_offset" : 20,"end_offset" : 26,"type" : "word","position" : 4},{"token" : "out","start_offset" : 27,"end_offset" : 30,"type" : "word","position" : 5}]
}
可以看出,簡單分析器丟棄了所有的非字母字符并從非字母字符處切詞,大寫會轉為小寫.
空格分析器
空格分析器(whitespace analyzer)的結構也非常簡單,只由一個空格分詞器(whitespace tokenizer)構成,它會在所有出現空格的地方切詞,而每個分詞本身的內容不變。
例如:
POST _analyze
{"analyzer": "whitespace","text":"The 2025頭條新聞 hasn`t spread out。"
}
{"tokens" : [{"token" : "The","start_offset" : 0,"end_offset" : 3,"type" : "word","position" : 0},{"token" : "2025頭條新聞","start_offset" : 4,"end_offset" : 12,"type" : "word","position" : 1},{"token" : "hasn`t","start_offset" : 13,"end_offset" : 19,"type" : "word","position" : 2},{"token" : "spread","start_offset" : 20,"end_offset" : 26,"type" : "word","position" : 3},{"token" : "out。","start_offset" : 27,"end_offset" : 31,"type" : "word","position" : 4}]
}
3 使用IK分詞器分析文本
由于中文字符是方塊字,默認的標準分詞器把中文文本切成孤立的漢字不能正確的反映中文文本的語義。IK分詞器是比較受歡迎的中文分詞器。
安裝ik分詞器
1.進入IK分詞器的GitHub頁面,下載與elasticsearch配套的分詞器安裝包,也要選擇對應的版本。
2.進入elasticsearch的安裝目錄,找到plugins文件夾,在里面新建一個名為ik的文件夾,把下載的安裝包解壓后放進ik文件夾中。
3.重啟elasticsearch服務.
使用ik分詞器
IK分詞器提供了兩種分析器供開發人員使用:ik_smart 和 ik_max_word
POST _analyze
{"analyzer": "ik_smart","text":"諾貝爾獎是著名的世界級別大獎"
}
{"tokens" : [{"token" : "諾貝爾獎","start_offset" : 0,"end_offset" : 4,"type" : "CN_WORD","position" : 0},{"token" : "是","start_offset" : 4,"end_offset" : 5,"type" : "CN_CHAR","position" : 1},{"token" : "著名","start_offset" : 5,"end_offset" : 7,"type" : "CN_WORD","position" : 2},{"token" : "的","start_offset" : 7,"end_offset" : 8,"type" : "CN_CHAR","position" : 3},{"token" : "世界","start_offset" : 8,"end_offset" : 10,"type" : "CN_WORD","position" : 4},{"token" : "級別","start_offset" : 10,"end_offset" : 12,"type" : "CN_WORD","position" : 5},{"token" : "大獎","start_offset" : 12,"end_offset" : 14,"type" : "CN_WORD","position" : 6}]
}
可以看出,ik_smart將文本切分成了7個分詞.
如果使用ik_max_word,則會切分成更多的分詞。
POST _analyze
{"analyzer": "ik_max_word","text":"諾貝爾獎是著名的世界級別大獎"
}
{"tokens" : [{"token" : "諾貝爾獎","start_offset" : 0,"end_offset" : 4,"type" : "CN_WORD","position" : 0},{"token" : "諾貝爾","start_offset" : 0,"end_offset" : 3,"type" : "CN_WORD","position" : 1},{"token" : "貝爾","start_offset" : 1,"end_offset" : 3,"type" : "CN_WORD","position" : 2},{"token" : "獎","start_offset" : 3,"end_offset" : 4,"type" : "CN_CHAR","position" : 3},{"token" : "是","start_offset" : 4,"end_offset" : 5,"type" : "CN_CHAR","position" : 4},{"token" : "著名","start_offset" : 5,"end_offset" : 7,"type" : "CN_WORD","position" : 5},{"token" : "的","start_offset" : 7,"end_offset" : 8,"type" : "CN_CHAR","position" : 6},{"token" : "世界級","start_offset" : 8,"end_offset" : 11,"type" : "CN_WORD","position" : 7},{"token" : "世界","start_offset" : 8,"end_offset" : 10,"type" : "CN_WORD","position" : 8},{"token" : "級別","start_offset" : 10,"end_offset" : 12,"type" : "CN_WORD","position" : 9},{"token" : "大獎","start_offset" : 12,"end_offset" : 14,"type" : "CN_WORD","position" : 10}]
}
從結果中看,切分了11個分詞,這種模式下,切出來的分詞會盡可能多,更便于被搜索到.
通常,索引時的文本分析使用ik_max_word更加合適,而全文檢索的文本分析使用ik_smart較為多見。值得注意的是,每個分詞結果用偏移量保存每個分詞在原始文本中出現的位置,這類位置信息在全文檢索結果高亮展示時會被用到。
下面的請求將新建一個索引ik-text并使用IK分詞器進行文本分析.
PUT ik-text
{"settings": {"analysis": {"analyzer": {"default":{"type":"ik_max_word"},"default_search":{"type":"ik_smart"}}}},"mappings": {"properties": {"content":{"type": "text"},"abstract":{"type": "text","analyzer": "ik_smart","search_analyzer": "ik_max_word"}}}
}
{"acknowledged" : true,"shards_acknowledged" : true,"index" : "ik-text"
}
在索引ik-text中,指定text類型的字段默認是索引分析器是ik_max_word,而默認的全文檢索分析器是ik_smart。content字段正式采用的這種分析方式,而abstract字段則是在映射中顯示地指定了索引時的分析器為ik_smart,搜索時的分析器為ik_max_word,這會覆蓋掉索引默認的分析器配置。
注意:IK分詞器雖然能夠識別很多中文詞,但是無法自動發現新詞,如果想擴展自己的詞典,則需要把擴展的內容配置到IK分詞器目錄下的IK Analyzer.cfg.xml文件中。即便如此,由于IK分詞器切詞的粒度不夠細,在面對姓名,數字,英文單詞的檢索時常常力不從心.
4 自定義文本分析器
字符過濾器
字符過濾器是文本分析的第一個環節,在開始分詞之前,字符過濾器可以丟棄一些沒有意義的字符。
1. HTML strip字符過濾器
用于去除文本中的HTML標簽,還可以用于解析類似于&的轉義字符串。
POST _analyze
{"char_filter": ["html_strip"],"tokenizer": {"type":"keyword"},"filter": [],"text": ["Tom & Jerrey < <b>world</b>" ]
}
{"tokens" : [{"token" : "Tom & Jerrey < world","start_offset" : 0,"end_offset" : 34,"type" : "word","position" : 0}]
}
可以看到,分析后的文本中,HTML標簽被去掉了,轉義字符串在輸出結果中也轉義成功。
2. 映射字符過濾器
映射字符過濾器會根據提供的字符映射,把文本中出現的字符轉換為映射的另一種字符,例如:
POST _analyze
{"char_filter": [{"type":"mapping","mappings":["& => and"]} ],"tokenizer": {"type":"keyword"},"filter": [],"text": ["Tom & Jerrey"]
}
{"tokens" : [{"token" : "Tom and Jerrey","start_offset" : 0,"end_offset" : 12,"type" : "word","position" : 0}]
}
此時,&已經轉換為and.
3. 模式替換字符過濾器
根據指定的正則表達式把匹配的文本轉換成指定的字符串。例如:
POST _analyze
{"char_filter": [{"type":"pattern_replace","pattern":"runoo+b","replacement":"tomcat"} ],"tokenizer": {"type": "keyword"},"filter": [],"text":["runooobbbb"]
}
{"tokens" : [{"token" : "tomcatbbb","start_offset" : 0,"end_offset" : 10,"type" : "word","position" : 0}]
}
5. 分詞器
分詞器(tokenizer)是文本分析的靈魂,它是文本分析過程中不可缺少的一部分,因為它直接決定按照怎樣的算法來切分。
分詞器會把原始文本切分為一個個分詞(token),通常分詞器會保存每個分詞的以下三種信息:
1.文本分析后每個分詞的相對順序,主要用于短語搜索和單詞鄰近搜索。
2.字符偏移量,記錄分詞在原始文本中出現的位置。
3.分詞類型,記錄分詞的種類,例如單詞,數字等.
分詞器按照切分方式大致分為3種類型:
1.面向單詞的分詞器會把文本切分成獨立的單詞。
2.部分單詞分詞器會把每個單詞按照某種規則切分成小的字符串碎片。
3.特定結構的分詞器主要針對特定格式的文本,比如:郵箱,郵政編碼,路徑等
1.面向單詞的分詞器
elasticsearch 7.9.1內置的面向單詞的分詞器,如標準分詞器,小寫分詞器,空格分詞器等。
2.部分單詞分詞器
這種分詞器會把完整的單詞按照某種規則切分成一些字符串碎片,搜索時其可用于部分匹配,這種分詞器主要有兩種:
- N元語法分詞器
- 側邊N元語法分詞器
對于N元語法分詞器,效果如下:
POST _analyze
{"char_filter": [],"tokenizer": {"type":"ngram","min_gram":2,"max_gram":3,"token_chars":["letter" ]},"filter": [],"text": ["tom cat8" ]
}
{"tokens" : [{"token" : "to","start_offset" : 0,"end_offset" : 2,"type" : "word","position" : 0},{"token" : "tom","start_offset" : 0,"end_offset" : 3,"type" : "word","position" : 1},{"token" : "om","start_offset" : 1,"end_offset" : 3,"type" : "word","position" : 2},{"token" : "ca","start_offset" : 4,"end_offset" : 6,"type" : "word","position" : 3},{"token" : "cat","start_offset" : 4,"end_offset" : 7,"type" : "word","position" : 4},{"token" : "at","start_offset" : 5,"end_offset" : 7,"type" : "word","position" : 5}]
}
在這個ngram分詞器的配置中,設置了每個分詞的最小長度為2,最大長度為3,表示ngram分詞器會通過長度為2和3的滑動窗口切分文本,得到的字串作為分詞。
token_chars配置了只保留字母作為分詞,其余的字符(例如空格和數字)則會被過濾掉。
對于側邊N元語法分詞器與N元語法分詞器的區別在于,其在分詞時總是從第一個字母開始,會保留一定長度的前綴字符,例如:
POST _analyze
{"char_filter": [],"tokenizer": {"type":"edge_ngram","min_gram":2,"max_gram":5},"filter": [],"text": ["tom cat8" ]
}
{"tokens" : [{"token" : "to","start_offset" : 0,"end_offset" : 2,"type" : "word","position" : 0},{"token" : "tom","start_offset" : 0,"end_offset" : 3,"type" : "word","position" : 1},{"token" : "tom ","start_offset" : 0,"end_offset" : 4,"type" : "word","position" : 2},{"token" : "tom c","start_offset" : 0,"end_offset" : 5,"type" : "word","position" : 3}]
}
這里設置了每個分詞的長度最小為2,最大為5,這種分詞器很適合做前綴搜索。
3.特定結構的分詞器
如下分詞器可以對正則表達式,分隔符或者路徑等進行切分。