文章目錄
1. Analysis 簡介
理解elasticsearch的ngram首先需要了解elasticsearch中的analysis。在此我們快速回顧一下基本原理:
當一個文檔被索引時,每個field都可能會創建一個倒排索引(如果mapping的時候沒有設置不索引該field)。倒排索引的過程就是將文檔通過analyzer分成一個一個的term,每一個term都指向包含這個term的文檔集合。當查詢query時,elasticsearch會根據搜索類型決定是否對query進行analyze,然后和倒排索引中的term進行相關性查詢,匹配相應的文檔
analyzer = CharFilters(0個或多個) + Tokenizer(恰好一個) + TokenFilters(0個或多個)
2. index analyzer VS search analyzer
如果mapping
中只設置了一個analyzer,那么這個analyzer
會同時用於索引文檔和搜索query
。當然索引文檔和對query進行analysis
也可以使用不同的analyzer
一個特殊的情況是有的query是需要被analyzed,有的並不需要。例如match query
會先用search analyzer
進行分析,然后去相應field
的倒排索引進行匹配。而term query
並不會對query內容進行分析,而是直接和相應field的倒排索引去匹配
3. Analyze API
Analyze API是一個有效的方式查看分析后的結果:
POST _analyze { "analyzer" : "standard", "text" : "hello, world" }
輸出的結果如下所示,即[hello, world]
4. Ngram
在機器學習和數據挖掘領域,ngram通常指的是n個詞的序列。不過在elasticsearch中,ngram代表的是n個字符的序列。可以把ngram理解成長度為n的滑動窗口,在文檔中滑動產生的序列
5. Ngram Tokenizer
POST _analyze { "tokenizer": "ngram", "text": "Quick Fox" }
ngram分詞器默認會產生最小長度為1,最大長度為2的N-grams序列。上述查詢語句的輸出是
[ Q, Qu, u, ui, i, ic, c, ck, k, "k ", " “, " F”, F, Fo, o, ox, x ]
也可以自定義ngram tokenizer的一些配置:
-
min_gram: 指定產生的最小長度的字符序列,默認為1
-
max_gram: 指定產生的最大長度的字符序列,默認為2
-
token_chars: 指定生成的token應該包含哪些字符.對沒有包含進的字符進行分割,默認為[],即保留所有字符
- letter - eg: a,b,字
- digit - eg: 3,7
- whitespace - eg: " ", “\n”
- punctuation - eg: !, "
- symbol - eg: $,√
定義min_gram
和max_gram
應該按照使用場景來定。使用ngram的一個常見場景就是自動補全。如果單個字符也進行自動補全,那么可能匹配的suggestion太多,導致沒有太大意義。
另一個需要考慮的便是性能,產生大量的ngram占用空間更大,搜索時花費的事件也更多。
假如現在需要將ngrams生成的token都轉換成小寫形式,可以使用如下形式
PUT my_index { "settings": { "analysis": { "tokenizer": { "ngram_tokenizer": { "type": "nGram", "min_gram": 4, "max_gram": 4, "token_chars": [ "letter", "digit" ] } }, "analyzer": { "ngram_tokenizer_analyzer": { "type": "custom", "tokenizer": "ngram_tokenizer", "filter": [ "lowercase" ] } } } } } POST my_index/_analyze { "analyzer": "ngram_tokenizer_analyzer", "text": "Hello, World!" } 生成的結果序列是[ello, hell, orld, worl]
6. Ngram Token Filter
上述的例子也可以使用Ngram Token Filter
,配上standard的分詞器和lower-case
的過濾器。
原文本被standard分詞器以whitespace和punctuation分割成token,然后通過lowercase過濾器轉換成小寫形式,最后通過ngram filter生成長度為4的字符序列
PUT /my_index { "settings": { "analysis": { "filter": { "ngram_filter": { "type": "nGram", "min_gram": 4, "max_gram": 4 } }, "analyzer": { "ngram_filter_analyzer": { "type": "custom", "tokenizer": "standard", "filter": [ "lowercase", "ngram_filter" ] } } } } } POST my_index/_analyze { "analyzer": "ngram_filter_analyzer", "text": "Hello, World!" }
上述mapping也會和ngram tokenizer產生同樣的效果,具體實際應用如何選擇應該視場景而定。假如想匹配的term中可以包含特殊字符,那么你應該使用ngram tokenizer。因為standard的tokenizer會過濾掉這些特殊字符。
7. Edge Ngram
Edge Ngram和ngram類似,只不過Edge Ngram產生的序列都是從代索引文檔的開頭延伸出去的。
POST _analyze { "tokenizer": "edge_ngram", "text": "Quick Fox" }
會產生[ Q, Qu ]
Edge Ngram也有着和ngram相同的配置
-
min_gram: 指定產生的最小長度的字符序列,默認為1
-
max_gram: 指定產生的最大長度的字符序列,默認為2
-
token_chars: 指定生成的token應該包含哪些字符.對沒有包含進的字符進行分割,默認為[],即保留所有字符
- letter - eg: a,b,字
- digit - eg: 3,7
- whitespace - eg: " ", “\n”
- punctuation - eg: !, "
- symbol - eg: $,√
對於如下的mapping
PUT my_index
{ "settings": { "analysis": { "analyzer": { "my_analyzer": { "tokenizer": "my_tokenizer" } }, "tokenizer": { "my_tokenizer": { "type": "edge_ngram", "min_gram": 2, "max_gram": 10, "token_chars": [ "letter", "digit" ] } } } } } POST my_index/_analyze { "analyzer": "my_analyzer", "text": "2 Quick Foxes." }
產生的token序列為[ Qu, Qui, Quic, Quick, Fo, Fox, Foxe, Foxes ]
參考連接:
https://njucz.github.io/2017/12/20/elasticsearch%20ngram,edgengram%E7%AC%94%E8%AE%B0/
https://blog.csdn.net/xixihahalelehehe/article/details/115302683