倒排索引

通常提到搜索我们想到的实现都是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,让关键词更加精准.