Elasticsearch 篇之倒排索引与分词
书的目录与索引
正排索引
- 文档Id到文档内容、单词的关联关系
文档Id | 文档内容 |
---|---|
1 | elasticsearch是最流行的搜索引擎 |
2 | php是世界上最好的语言 |
3 | 搜索引擎是如何诞生的 |
倒排索引
- 单词到文档id的关联关系
单词 | 文档ID列表 |
---|---|
elasticsearch | 1 |
流行 | 2 |
搜索引擎 | 1,3 |
php | 2 |
是 | 1,2,3 |
正排与倒排索引简介
查询包含"搜素引擎"的文档
- 通过倒排索引获得"搜索引擎"对应的文档Id有1和3
- 通过正排索引查询1和3的完整内容
- 返回用户最终结果
倒排索引详解
倒排索引组成
- 单词词典(Term Dictionary)是倒排索引的重要组成
- 记录所有文档的单词,一般都比较大
- 记录单词到倒排列表的关联信息
- 单词字典的实现一般是用B+ Tree,示例如下图:
- 倒排列表(Posting List)记录了单词对应的文档集合,由倒排索引项(Posting)组成
- 倒排索引项(Posting)主要包含如下信息:
- 文档Id,用于获取原始信息
- 单词频率(TF,Term Frequency):记录改单词在该文档中出现次数,用于后继相关性算分
- 位置(Position):记录单词在文档中的分词位置(多个),用于做词语搜索(Phrase Query)
- 偏移(Offser):记录单词在文档的开始和结束位置,用于做高亮显示
- 以"搜索引擎"为例
- 倒排索引与倒排列表整合在一起的结构如下:
- es存储的是一个json格式的文档,其中包含多个字段,每个字段会有自己的倒排索引。
- 倒排索引项(Posting)主要包含如下信息:
分词介绍
分词是指将文本转换成一系列单词(term or token)的过程,也可以叫做文本分析,在es里面成为Analysis,如下图所示:
- 文本:elasticsearch是最流行的搜索引擎
- 分词结果:elasticsearch 流行 搜索引擎
分词器是es中专门处理分词的组件,英文为Analysis,它的组成如下:
- Character Filters
- 针对原始文本进行处理,比如去除html特殊标记符
- Tokenizer
- 将原始文本按照一定规则切分为单词
- Token Filters
- 针对tokenizer处理的单词进行再加工,比如转小写,删除或新增等处理
analyze_api
es提供了一个测试分词的api接口,方便验证分词效果,endpoint是_analyze
- 可以直接指定analyze进行测试
POST _analyze
{
"analyzer": "standard",
"text": "hello word"
}
POST _analyze
{
"analyzer": "standard",
"filter": ["lowercase"],
"text": "Hello Word"
}
- 可以直接指定索引中的字段进行测试
- 可以自定义分词器进行测试
自带分词器
es自带如下的分词器
- Standard
- 默认分词器
- 其组成如图,特性为:
- 按词切分,支持多语言
- 小写处理
- Simple
- 其组成如图,特性为:
- 按照非字母切分
- 小写处理
- 其组成如图,特性为:
POST _analyze
{
"analyzer": "simple",
"filter": ["lowercase"],
"text": "Hello Word 1"
}
- Whitespace
- 其组成如图,特性为:
- 按照空格切分
- 按照空格切分
- 其组成如图,特性为:
POST _analyze
{
"analyzer": "whitespace",
"text": "Hello Word 1 啊"
}
- Stop
- Stop Word指语气助词等修饰性的词语,比如the、an、的、这等
- 其组成如图,特性为:
- 相比Simple多了Stop Word处理
- Stop Word指语气助词等修饰性的词语,比如the、an、的、这等
POST _analyze
{
"analyzer": "stop",
"text": "Hello or and Word 1 啊"
}
- Keyword
- 其组成如图,特性为:
- 不分词,直接将输入作为一个单词输出
- 不分词,直接将输入作为一个单词输出
- 其组成如图,特性为:
POST _analyze
{
"analyzer": "keyword",
"text": "Hello or and Word 1 啊"
}
- Pattern
- 其组成如图,特性为:
- 通过正则表达式自定义分隔符
- 默认是\W+,即非字词的符号作为分隔符
POST _analyze
{
"analyzer": "pattern",
"text": "Hello-Word day's"
}
- Language
- 提供了30+常见语言的分词器
中文分词
- 中文分词指的是将一个汉字序列切分成一个一个单独的词。在英文中,单词之间是以空格作为自然分界符,汉字中词没有一个形式上的分界符。
- 上下文不用,分词结果迥异,比如交叉歧义问题,比如下面两种分词都合理
- 乒乓球拍/卖/完了
- 乒乓球/拍卖/完了
- 常见分词系统
- IK
- 实现中英文单词的切分,支持ik_smart、ik_maxword模式
- 可自定义词库,支持热更新分词词典
- https://github.com/medcl/elasticsearch-analysis-ik
- jieba
- python中最流行的分词系统,支持分词和词性标注
- 支持繁体分词、自定义词典、并行分词等
- https://github.com/sing1ee/elasticsearch-jieba-plugin
- IK
自定义分词
当自带的分词无法满足需求的时候,就需要自定义分词。
自定义分词就是通过Character Filters、Tokenizer和Token Filter实现。
自定义分词之CharacterFilter
在Tokenizer之前对原始文本进行处理,比如增加、删除或替换的字符等。
es自带的如下:
- html_strip:去除html标签和转换html字体
- Mapping:进行字符串替换
- Pattern Replace:进行正则匹配替换
- 由于它是第一步,所以它的结果会影响Tokenizer的position和offset的结果
POST _analyze
{
"tokenizer": "keyword",
"char_filter": ["html_strip"],
"text": "<p>I'm so <b>happy</b>!</p>"
}
自定义分词之Tokenizer
将原始文本按照一定规则切分为单词(term or token)
自带的如下:
- standard:按照单词切割
- letter:按照非字符切割
- whitespace:按照空格切割
- UAX URL Eamil 按照standard切割,但不会分割邮箱和url
- NGram和Edge NGram:连词分割(例如搜索时根据输入提示相关内容)
- path_hierarchy:按照文件路径进行分割
POST _analyze
{
"tokenizer": "path_hierarchy",
"text": "/one/two/three"
}
自定义分词之 TokenFilter
对于tokenizer输出的单词(term)进行增加、删除、修改等操作
自带的如下:
- lowercase:将所有term转化为小写
- stop:删除stop word
- NGram和Edge NGram:连词分割
- Synonym:添加近义词的term
POST _analyze
{
"text": "a Hello,word",
"tokenizer": "standard",
"filter": [
"stop",
"lowercase",
{
"type": "ngram",
"min_gram": 4,
"max_gram": 4
}
]
}
自定义分词
PUT test_index
{
"settings": {
"analysis": {
"analyzer": {
"my_custom_analyzer": {
"type": "custom",
"tokenizer": "standard",
"char_filter": ["html_strip"],
"filter": ["lowercase","asciifolding"]
}
}
}
}
}
POST test_index/_analyze
{
"analyzer": "my_custom_analyzer",
"text": "Is this <b>a box</b>"
}
分词使用说明
分词一般会在以下的情况下使用:
- 创建或更新文档的时候,会对相应的文档进行分词处理。
- 查询时,会对查询语句进行分词
明确字段是否需要分词,不需要分词的字段就将 type 设置为 keyword,可以节省空间和提高写性能。