倒排索引
通常提到搜索我们想到的实现都是es,原因是它比MySQL高效得多.
在MySQL中想检索包含特定关键词的数据,比如最近疫情爆发了,有关部门想找出家在某某街道的同学们,就可以写这样一条SQL:
select * from school where address like '%某某街道%'
很显然,像B+树这样的结构需要全表扫描,无法应付数据量较大的场景.
而es通过倒排索引这种结构高效地解决了这类问题.
倒排索引原理就像它的名字一样,即把正常索引的映射关系反转
例如下面是正常索引和它对应的倒排索引
id | code |
---|---|
1 | b,c,e |
2 | a,c,e |
3 | c,b,d |
code | id |
---|---|
a | 2 |
b | 1,3 |
c | 1,2,3 |
d | 3 |
e | 1,2 |
在正常索引中,如果想找出code包含b的数据得遍历全部数据逐条检查,而在倒排索引中只需找到b所在行即可.
仔细观察,在上面的例子中不仅使用了倒排索引,还把字符串"b,c,e"切分成了三个单独的字母,因此才能建立三条独立的映射.
可以说,分词是搜索技术的核心,是最关键的实现.
分词器原理
对于初学者来讲分词并没有那么直观.
例如某个文档的内容是Eating an apple a day keeps doctor away
,然后用eat
这个关键词去搜索却没能搜索出预期结果.这是典型从MySQL转过来的"like思维".
在es中分词是指把全文本转换成一系列单词(term)的过程,也叫做文本分析(analysis).这个过程通过分词器(analyzer)完成.
Analyzer组件
分词器包含下面三个组件,工作顺序自上而下
- character filters(0个或多个) : 对文本进行预处理,比如过滤html标签,标点符号等.
- tokenizers(恰好1个) : 将文本切分成一个个单词
- token filters(0个或多个) : 将切分后的单词进行加工,例如大小写转换,同义词转换等.
这个过程可以想象成跟做菜一样,比如做红烧肉,先要对食材预处理,洗干净猪肉,拔掉上面的猪毛(character filters),然后把猪肉切成一块块(tokenizers),最后倒入锅中烹煮,上色调味(token filters)
Analyzer类型
- Standard Analyzer - 默认分词器,按词切分,小写处理
- Simple Analyzer - 按照非字母切分(符号被过滤), 小写处理
- Stop Analyzer - 小写处理,停用词过滤(the,a,is)
- Whitespace Analyzer - 按照空格切分,不转小写
- Keyword Analyzer - 不分词,直接将输入当作输出
- Patter Analyzer - 正则表达式,默认\W+(非字符分割)
- Language - 提供了30多种常见语言的分词器
- Customer Analyzer 自定义分词器(例如ik)
下面是自定义Analyzer的例子
PUT /test_idx
{
"settings": {
"analysis": {
"filter": {
"pinyin_full_filter": {
"type": "pinyin"
}
},
"analyzer": {
"ik_pinyin_analyzer": {
"type": "custom",
"tokenizer": "standard",
"filter": [
"pinyin_full_filter"
]
}
}
}
}
}
在创建mapping时可以指定两个分词器
POST /test_idx/_mapping
{
"properties": {
"name": {
"type": "text",
"analyzer": "standard",
"search_analyzer": "keyword"
}
}
}
- analyzer: 写时分词,写入文档时对文档进行分词.文档写入后,分词结果不会随着分词器的更新而更新,只能手动重建索引
- search_analyzer: 读时分词,对输入关键词进行分词.当这两个阶段分出来的词相同时视为搜索命中
中文分词
中文一般使用ik分词器.ik中有两种策略
- ik_smart : 做最粗粒度的拆分
- ik_max_word : 做最细粒度的拆分
不难推导出ik_smart是ik_max_word的子集,ik_smart中不可能包含不存在于ik_max_word的单词.曾经我在腾讯云上就观察到违背这个结论的现象,提工单过去,然后发现果然是他们的BUG.
一般search_analyzer使用ik_max_word,让文档能匹配更多的关键词,而analyzer使用ik_smart,让关键词更加精准.