倒排索引、分词、同义词
倒排索引
- 正排索引:文檔ID =>文檔內容和單詞
- 倒排索引:詞條 =>文檔ID
- 倒排索引組成:
- 詞條字典(Term Dictionary),記錄所有的詞條與倒排列表的映射關系。這個字典很大,通過B+樹或哈希拉鏈法實現,以滿足高性能的插入與查詢。
- 倒排列表(Posting List),由倒排索引項組成,包含如下信息:
- 文檔ID
- 詞頻(TF):該單詞在文檔中出現的次數,用于相關性評分
- 位置(Position):單詞在文檔中分詞的位置(也就是第幾個分詞),用于語句搜索(phrase query)
- 偏移(Offset):記錄單詞的開始結束位置,實現高亮顯示(從第幾個字符開始,到第幾個字符結束)
- ES的倒排索引:
- ES文檔的每個字段,默認都有自己的倒排索引
- 可以通過mappings,指定某些字段不做索引,以節省存儲空間,但會導致該字段無法被搜索。
分詞
文本分析(Analysis),就是把全文轉換成一系列詞條(term / token)的過程(也叫分詞)。文本分析是通過分詞器(Analyzer)實現的。
https://www.elastic.co/guide/en/elasticsearch/reference/7.2/analysis.html
分詞器有兩個作用:
- 數據寫入時轉換詞條
- 分析查詢語句
ES內置了多種分詞器:https://www.elastic.co/guide/en/elasticsearch/reference/7.2/analysis-analyzers.html
在mapping中,每個text字段都可以指定專門的分詞器:
PUT {index} {"mappings": {"properties": {"{field}": {"type": "text","analyzer": "{standard}" // 指定分詞器}}} }說明:已經存在的索引執行上述操作會報錯。可以在創建索引時指定。standard分詞器是默認的分詞器。
通常情況下,創建(index)和查詢(search)時,應該使用同樣的分詞器。執行全文檢索時,比如match查詢,會在mapping中為每個字段查找分詞器。在查詢具體字段時使用何種分詞器,遵循如下查找順序:
- 查詢條件有指定analyzer
- mapping中的search_anylyzer參數
- mapping中的analyzer參數
- 索引設置中的default_search
- 索引設置中的default
- standard分詞器
分詞器由以下三部分組合而成:
-
Character Filters:處理原始文本,比如去除html標簽。一個分詞器可以沒有或擁有多個char filter。
-
Tokenizer:接收上一步處理后的文本流,按照規則切分出詞條并輸出,比如:
- whitespace 按空格切分詞條
- keyword 不做分詞,直接作為關鍵詞
- path_hierarchy, 按路徑切分,保證搜索任意一級目錄都可以搜索到結果
更多類別參考官網,每個分詞器必須有一個tokenizer。
-
Token Filters:接收詞條流,進行再加工,可能會添加、刪除、或者變更詞條。比如:
- lowercase 將所有的詞條轉為小寫
- stop將停頓詞(比如,the, in, on)從詞條流中移除
- synonym往詞條流中引入同義詞
- multiplexer 一個單詞觸發多個詞條(應用多個過濾器,每個過濾器觸發一個),重復的詞條會被去除。建議:將同義詞過濾器追加到每個multiplexer過濾器列表后面,不要放在multiplexer之后,以避免異常。建議具體見官網底部的note
更過token filter類別可以參考官網,一個分詞器可以沒有或擁有多個token filter
通過組合以上三部分,我們也可以自定義分詞器。
###analyze接口
analyze接口可以用來執行文本分析處理,并查看分詞結果,比如:
GET _analyze {"analyzer": "standard","text": "hello world" }使用standard分詞器對文本進行分詞,返回結果如下:
{"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}] }自定義分詞
也可以自己定義分詞器,其實就是組合tokenizer, token filter 和 char filer,比如:
示例1:自定義token filter
POST _analyze {"tokenizer": "standard","filter": [ "lowercase", "asciifolding" ],"text": "Is this déja vu?" }示例2:自定義char_filter
POST _analyze {"tokenizer": "standard","char_filter": [{"type": "mapping","mappings": ["- => _"] // 將中劃線替換為下劃線, 甚至可以將emoji表情替換為單詞}],"text": "123-456, hello-world" }// 結果如下: {"tokens" : [{"token" : "123_456","start_offset" : 0,"end_offset" : 7,"type" : "<NUM>","position" : 0},{"token" : "hello_world","start_offset" : 9,"end_offset" : 20,"type" : "<ALPHANUM>","position" : 1}] }也可以為某個索引(集)自定義分詞器:
put forum {"settings": {"analysis": {"analyzer": {"my_analyzer": { // 自定義分詞器名字"type": "custom","tokenizer": "standard","filter": ["lowercase","asciifolding"]}}}},"mappings": {"properties": {"title": {"type": "text","analyzer": "my_analyzer" // 使用自定義分詞器}}} }測試分詞效果:
GET forum/_analyze {"analyzer": "my_analyzer","text": "Mr déjà" }分詞結果如下:
{"tokens" : [{"token" : "mr","start_offset" : 12,"end_offset" : 14,"type" : "<ALPHANUM>","position" : 3},{"token" : "deja","start_offset" : 15,"end_offset" : 19,"type" : "<ALPHANUM>","position" : 4}] }mapping自定義分詞器完全版:
{'settings': {'analysis': {'analyzer': { # 自定義analyzer: 可以使用下面自定義的tokenizer, char_filter,token_filter 等內容'my_analyzer': {'type': 'custom','char_filter': 'my_char_flt','tokenizer': 'my_tokenizer','filter': ['my_flt'], # token_filter}},'char_filter': { # 自定義 char_filter'my_char_flt': {}},'tokenizer': { # 自定義 tokenizer'my_tokenizer': {}},'filter': { # 自定義 token_filter'my_flt': {'type': 'synonym','synonyms_path': 'xxxxx'}},# "search_analyzer": {} # 也可以定義搜索分詞器, 同analyzer},},'mappings': {'properties': {'content': {'type': 'text','analyzer': 'my_analyzer', # 使用自定義的分詞器'search_analyzer': 'ik_smart',}}} }中文分詞
ES內置的中文分詞不好用,可以通過插件安裝,比如非常流行的ik分詞:
.\elasticsearch-plugin.bat install https://github.com/medcl/elasticsearch-ana lysis-ik/releases/download/v7.2.0/elasticsearch-analysis-ik-7.2.0.zip裝好后重啟ES,查看插件列表:
GET _cat/plugins返回剛剛安裝的ik插件
DESKTOP-L1E59GR analysis-ik 7.2.0ik支持一些自定義配置,比如擴展詞典和熱詞更新。配置文件默認位置在es安裝目錄的configik目錄下。具體配置可以參考:https://github.com/medcl/elasticsearch-analysis-ik
安裝完成后,我們測試下效果:
GET _analyze {"analyzer": "ik_smart","text": "創新空間的不朽傳奇" }分詞結果:
{"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},{"token" : "的","start_offset" : 4,"end_offset" : 5,"type" : "CN_CHAR","position" : 2},{"token" : "不朽","start_offset" : 5,"end_offset" : 7,"type" : "CN_WORD","position" : 3},{"token" : "傳奇","start_offset" : 7,"end_offset" : 9,"type" : "CN_WORD","position" : 4}] }試試官網的例子,定義news索引的mapping:
PUT news {"mappings": {"properties": {"content": {"type": "text","analyzer": "ik_max_word", "search_analyzer": "ik_smart"}}} }按照文檔說明,索引一些doc進去:
PUT news/_doc/4 {"content": "中國駐洛杉磯領事館遭亞裔男子槍擊 嫌犯已自首" }進行高亮查詢:
POST news/_search {"query": {"match": {"content": "中國"}},"highlight": {"pre_tags": "<bold>","post_tags": "</bold>","fields": {"content": {}}} }返回結果如下:
{"took" : 2,"timed_out" : false,"_shards" : {"total" : 1,"successful" : 1,"skipped" : 0,"failed" : 0},"hits" : {"total" : {"value" : 2,"relation" : "eq"},"max_score" : 0.642793,"hits" : [{"_index" : "news","_type" : "_doc","_id" : "3","_score" : 0.642793,"_source" : {"content" : "中韓漁警沖突調查:韓警平均每天扣1艘中國漁船"},"highlight" : {"content" : ["中韓漁警沖突調查:韓警平均每天扣1艘<bold>中國</bold>漁船"]}},{"_index" : "news","_type" : "_doc","_id" : "4","_score" : 0.642793,"_source" : {"content" : "中國駐洛杉磯領事館遭亞裔男子槍擊 嫌犯已自首"},"highlight" : {"content" : ["<bold>中國</bold>駐洛杉磯領事館遭亞裔男子槍擊 嫌犯已自首"]}}]} }測試下來后,在索引階段,建議使用ik_max_word分詞器,能更靈活的切割詞條。
除了IK分詞,還可以使用THULAC分詞器,號稱準確率目前最高。不過目前只更新到6.4,不知道7.2能否使用。
搜索時指定分詞器
GET post001/_search {"query": {"match": {"content": {"query": "考研","analyzer": "standard"}}},"highlight": {"fields": {"content": {}}}}同義詞圖過濾器 Synonym Graph Token Filter
synonym_graph詞條過濾器(token filter)用于處理同義詞。
注意:該過濾器僅適用于作為搜索分詞器的一部分。如果想在索引期間應用同義詞,需要使用標準的同義詞過濾器
同義詞可以使用配置文件指定,如下:
PUT /{index} {"settings": {"index" : {"analysis" : {"analyzer" : {"search_synonyms" : { // 自定義分詞器名字"tokenizer" : "whitespace","filter" : ["graph_synonyms"] // 使用自定義filter}},"filter" : {"graph_synonyms" : {"type" : "synonym_graph", // 指定類型為同義詞圖"synonyms_path" : "analysis/synonym.txt" // 指定同義詞詞庫位置}}}}} }說明:同義詞詞庫的位置是相對于config目錄的相對地址。另外以下參數也是可用的:
- expand 默認為true
- leninet 默認為false,是否忽略同義詞配置的解析異常,記住,如果設置為true,只要那些無法被解析到同義詞規則會被忽略。如下示例:
說明:bar在自定義的my_stop的filter中被剔除,但是在synonym_graph的filter中,foo => baz仍然被添加成功。但如果添加的是"foo, baz => bar", 那么什么也不會被添加到同義詞列表。這時因為映射到目標單詞"bar"本身作為停用詞已被剔除。相似的,如果映射是"bar, foo, baz"并且expand設置為false,那么不會添加任何同義詞映射,因為當expand為false時,目標映射是第一個單詞,也就是"bar"。但是如果expand=true,那么映射將會添加為"foo, baz => foo, baz",即所有詞相互映射,除了停用詞。
同義詞配置文件如下:
# 空行和#號開頭的都是注釋# 近義詞以 => 映射,這種映射會忽略模式中的 expand 參數 # 匹配到"=>"左側的詞條后,被替換為"=>"右側的詞條。 i-pod, i pod => ipod, sea biscuit, sea biscit => seabiscuit# 近義詞以逗號分隔,映射行為由模式中的expand參數決定。如此同樣的近義詞文件,可以用于不同的策略 ipod, i-pod, i pod foozball , foosball universe , cosmos lol, laughing out loud# 當expand==true, "ipod, i-pod, i pod" 等價于: ipod, i-pod, i pod => ipod, i-pod, i pod # 當expand==false, "ipod, i-pod, i pod" 僅映射第一個單詞, 等價于: ipod, i-pod, i pod => ipod# 多個同義詞映射將會合并 foo => foo bar foo => baz # 等價于: foo => foo bar, baz雖然可以直接在filter中使用synonyms定義同義詞,比如:
PUT /test_index {"settings": {"index" : {"analysis" : {"filter" : {"synonym" : {"type" : "synonym_graph","synonyms" : [ // 直接定義"lol, laughing out loud","universe, cosmos"]}}}}} }但是對于大型的同義詞集合,還是推薦使用synonyms_path參數在文件中定義。
測試
定義mapping
PUT article {"settings": {"index": {"analysis": {"analyzer": {"my_analyzer": {"tokenizer": "ik_max_word","filter": ["my_synonyms"]}},"filter": {"my_synonyms": {"type": "synonym_graph","synonyms_path": "analysis/synonyms.txt"}},"search_analyzer": "ik_smart"}}} }在ES的config/analysis目錄下新建synonyms.txt文件,內容如下:
北京大學, 北大索引一篇文檔:
PUT article/_doc/1 {"content": "北京大學今年考研一飛沖天" }試著搜索:
GET article/_search {"query": {"match": {"content": {"query": "北大"}}} }搜索結果:
{"took" : 0,"timed_out" : false,"_shards" : {"total" : 1,"successful" : 1,"skipped" : 0,"failed" : 0},"hits" : {"total" : {"value" : 1,"relation" : "eq"},"max_score" : 0.5753642,"hits" : [{"_index" : "article","_type" : "_doc","_id" : "1","_score" : 0.5753642,"_source" : {"content" : "北京大學今年考研一飛沖天"}}]} }同義詞過濾器 Synonym Token Filter
同義詞過濾器配置示例:
PUT /test_index {"settings": {"index" : {"analysis" : {"analyzer" : {"my-synonym" : { // 自定義分詞器名字"tokenizer" : "whitespace","filter" : ["my_synonym_fltr"] // 指定過濾器}},"filter" : {"my_synonym_fltr" : { // 自定義同義詞過濾器名字"type" : "synonym", // type 為synonym,"synonyms_path" : "analysis/synonym.txt"}}}}} }其實同義詞過濾器和以上面的同義詞圖過濾器配置用法都是一樣的。區別如下:
- 前者type=synonym,后者type=synonym_graph
- 前者可以在文檔索引期間使用,后者只能作為搜索分詞的一部分。
另外在同義詞圖中的測試,將type更改后,測試仍然適用。
總結
以上是生活随笔為你收集整理的倒排索引、分词、同义词的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 去线程化与智能调度
- 下一篇: EDM邮件群发十大技巧提升邮件群发效果