Elasticsearch 6.x 倒排索引与分词

栏目: 后端 · 发布时间: 7年前

内容简介:示例: 对以下三个文档去除停用词后构造倒排索引查询包含“搜索引擎”的文档单词词典的实现一般用B+树,B+树构造的可视化过程网址:
  • 正排索引:文档id到单词的关联关系
  • 倒排索引:单词到文档id的关联关系

示例: 对以下三个文档去除停用词后构造倒排索引

Elasticsearch 6.x 倒排索引与分词

倒排索引-查询过程

查询包含“搜索引擎”的文档

  1. 通过倒排索引获得“搜索引擎”对应的文档id列表,有1,3
  2. 通过正排索引查询1和3的完整内容
  3. 返回最终结果

倒排索引-组成

  • 单词词典(Term Dictionary)
  • 倒排列表(Posting List)

单词词典(Term Dictionary)

单词词典的实现一般用B+树,B+树构造的可视化过程网址: B+ Tree Visualization

Elasticsearch 6.x 倒排索引与分词

倒排列表(Posting List)

  • 倒排列表记录了单词对应的文档集合,有倒排索引项(Posting)组成
  • 倒排索引项主要包含如下信息:
    1. 文档id用于获取原始信息
    2. 单词频率(TF,Term Frequency),记录该单词在该文档中出现的次数,用于后续相关性算分
    3. 位置(Posting),记录单词在文档中的分词位置(多个),用于做词语搜索(Phrase Query)
    4. 偏移(Offset),记录单词在文档的开始和结束位置,用于高亮显示
Elasticsearch 6.x 倒排索引与分词

B+树 内部结点存索引,叶子结点存数据 ,这里的 单词词典就是B+树索引,倒排列表就是数据,整合在一起后如下所示

note: B+树索引中文和英文怎么比较大小呢?unicode比较还是拼音呢?

Elasticsearch 6.x 倒排索引与分词

ES存储的是一个JSON格式的文档,其中包含多个字段,每个字段会有自己的倒排索引

分词

分词是将文本转换成一系列单词(Term or Token)的过程,也可以叫文本分析,在ES里面称为Analysis

Elasticsearch 6.x 倒排索引与分词

分词器

分词器是ES中专门处理分词的组件,英文为Analyzer,它的组成如下:

  • Character Filters:针对原始文本进行处理,比如去除html标签
  • Tokenizer:将原始文本按照一定规则切分为单词
  • Token Filters:针对Tokenizer处理的单词进行再加工,比如转小写、删除或增新等处理

分词器调用顺序

Elasticsearch 6.x 倒排索引与分词

Analyze API

ES提供了一个可以测试分词的API接口,方便验证分词效果,endpoint是_analyze

  • 可以直接指定analyzer进行测试
Elasticsearch 6.x 倒排索引与分词
  • 可以直接指定索引中的字段进行测试
POST test_index/doc
{
  "username": "whirly",
  "age":22
}

POST test_index/_analyze
{
  "field": "username",
  "text": ["hello world"]
}
复制代码
  • 可以自定义分词器进行测试
POST _analyze
{
  "tokenizer": "standard",
  "filter": ["lowercase"],
  "text": ["Hello World"]
}

复制代码

预定义的分词器

ES自带的分词器有如下:

  • Standard Analyzer
    • 默认分词器
    • 按词切分,支持多语言
    • 小写处理
  • Simple Analyzer
    • 按照非字母切分
    • 小写处理
  • Whitespace Analyzer
    • 空白字符作为分隔符
  • Stop Analyzer
    • 相比Simple Analyzer多了去除请用词处理
    • 停用词指语气助词等修饰性词语,如the, an, 的, 这等
  • Keyword Analyzer
    • 不分词,直接将输入作为一个单词输出
  • Pattern Analyzer
    • 通过正则表达式自定义分隔符
    • 默认是\W+,即非字词的符号作为分隔符
  • Language Analyzer
    • 提供了30+种常见语言的分词器

示例:停用词分词器

POST _analyze
{
  "analyzer": "stop",
  "text": ["The 2 QUICK Brown Foxes jumped over the lazy dog's bone."]
}

复制代码

结果

{
  "tokens": [
    {
      "token": "quick",
      "start_offset": 6,
      "end_offset": 11,
      "type": "word",
      "position": 1
    },
    {
      "token": "brown",
      "start_offset": 12,
      "end_offset": 17,
      "type": "word",
      "position": 2
    },
    {
      "token": "foxes",
      "start_offset": 18,
      "end_offset": 23,
      "type": "word",
      "position": 3
    },
    {
      "token": "jumped",
      "start_offset": 24,
      "end_offset": 30,
      "type": "word",
      "position": 4
    },
    {
      "token": "over",
      "start_offset": 31,
      "end_offset": 35,
      "type": "word",
      "position": 5
    },
    {
      "token": "lazy",
      "start_offset": 40,
      "end_offset": 44,
      "type": "word",
      "position": 7
    },
    {
      "token": "dog",
      "start_offset": 45,
      "end_offset": 48,
      "type": "word",
      "position": 8
    },
    {
      "token": "s",
      "start_offset": 49,
      "end_offset": 50,
      "type": "word",
      "position": 9
    },
    {
      "token": "bone",
      "start_offset": 51,
      "end_offset": 55,
      "type": "word",
      "position": 10
    }
  ]
}
复制代码

中文分词

  • 难点
    • 中文分词指的是将一个汉字序列切分为一个一个的单独的词。在英文中,单词之间以空格作为自然分界词,汉语中词没有一个形式上的分界符
    • 上下文不同,分词结果迥异,比如交叉歧义问题
  • 常见分词系统
    • IK :实现中英文单词的切分,可自定义词库,支持热更新分词词典
    • jieba :支持分词和词性标注,支持繁体分词,自定义词典,并行分词等
    • Hanlp :由一系列模型与算法组成的 Java 工具包,目标是普及自然语言处理在生产环境中的应用
    • THUAC :中文分词和词性标注

安装ik中文分词插件

# 在Elasticsearch安装目录下执行命令,然后重启es
bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.3.0/elasticsearch-analysis-ik-6.3.0.zip

# 如果由于网络慢,安装失败,可以先下载好zip压缩包,将下面命令改为实际的路径,执行,然后重启es
bin/elasticsearch-plugin install file:///path/to/elasticsearch-analysis-ik-6.3.0.zip
复制代码
  • ik测试 - ik_smart
POST _analyze
{
  "analyzer": "ik_smart",
  "text": ["公安部:各地校车将享最高路权"]
}

# 结果
{
  "tokens": [
    {
      "token": "公安部",
      "start_offset": 0,
      "end_offset": 3,
      "type": "CN_WORD",
      "position": 0
    },
    {
      "token": "各地",
      "start_offset": 4,
      "end_offset": 6,
      "type": "CN_WORD",
      "position": 1
    },
    {
      "token": "校车",
      "start_offset": 6,
      "end_offset": 8,
      "type": "CN_WORD",
      "position": 2
    },
    {
      "token": "将",
      "start_offset": 8,
      "end_offset": 9,
      "type": "CN_CHAR",
      "position": 3
    },
    {
      "token": "享",
      "start_offset": 9,
      "end_offset": 10,
      "type": "CN_CHAR",
      "position": 4
    },
    {
      "token": "最高",
      "start_offset": 10,
      "end_offset": 12,
      "type": "CN_WORD",
      "position": 5
    },
    {
      "token": "路",
      "start_offset": 12,
      "end_offset": 13,
      "type": "CN_CHAR",
      "position": 6
    },
    {
      "token": "权",
      "start_offset": 13,
      "end_offset": 14,
      "type": "CN_CHAR",
      "position": 7
    }
  ]
}
复制代码
  • ik测试 - ik_max_word
POST _analyze
{
  "analyzer": "ik_max_word",
  "text": ["公安部:各地校车将享最高路权"]
}

# 结果
{
  "tokens": [
    {
      "token": "公安部",
      "start_offset": 0,
      "end_offset": 3,
      "type": "CN_WORD",
      "position": 0
    },
    {
      "token": "公安",
      "start_offset": 0,
      "end_offset": 2,
      "type": "CN_WORD",
      "position": 1
    },
    {
      "token": "部",
      "start_offset": 2,
      "end_offset": 3,
      "type": "CN_CHAR",
      "position": 2
    },
    {
      "token": "各地",
      "start_offset": 4,
      "end_offset": 6,
      "type": "CN_WORD",
      "position": 3
    },
    {
      "token": "校车",
      "start_offset": 6,
      "end_offset": 8,
      "type": "CN_WORD",
      "position": 4
    },
    {
      "token": "将",
      "start_offset": 8,
      "end_offset": 9,
      "type": "CN_CHAR",
      "position": 5
    },
    {
      "token": "享",
      "start_offset": 9,
      "end_offset": 10,
      "type": "CN_CHAR",
      "position": 6
    },
    {
      "token": "最高",
      "start_offset": 10,
      "end_offset": 12,
      "type": "CN_WORD",
      "position": 7
    },
    {
      "token": "路",
      "start_offset": 12,
      "end_offset": 13,
      "type": "CN_CHAR",
      "position": 8
    },
    {
      "token": "权",
      "start_offset": 13,
      "end_offset": 14,
      "type": "CN_CHAR",
      "position": 9
    }
  ]
}
复制代码
  • ik两种分词模式ik_max_word 和 ik_smart 什么区别?
    • ik_max_word: 会将文本做最细粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,中华人民,中华,华人,人民共和国,人民,人,民,共和国,共和,和,国国,国歌”,会穷尽各种可能的组合;

    • ik_smart: 会做最粗粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,国歌”。

自定义分词

当自带的分词无法满足需求时,可以自定义分词,通过定义Character Filters、Tokenizer和Token Filters实现

Character Filters

  • 在Tokenizer之前对原始文本进行处理,比如增加、删除或替换字符等
  • 自带的如下:
    • HTML Strip Character Filter:去除HTML标签和转换HTML实体
    • Mapping Character Filter:进行字符替换操作
    • Pattern Replace Character Filter:进行正则匹配替换
  • 会影响后续tokenizer解析的position和offset信息

Character Filters测试

POST _analyze
{
  "tokenizer": "keyword",
  "char_filter": ["html_strip"],
  "text": ["<p>I'm so <b>happy</b>!</p>"]
}

# 结果
{
  "tokens": [
    {
      "token": """

I'm so happy!

""",
      "start_offset": 0,
      "end_offset": 32,
      "type": "word",
      "position": 0
    }
  ]
}
复制代码

Tokenizers

  • 将原始文本按照一定规则切分为单词(term or token)
  • 自带的如下:
    • standard 按照单词进行分割
    • letter 按照非字符类进行分割
    • whitespace 按照空格进行分割
    • UAX URL Email 按照standard进行分割,但不会分割邮箱和URL
    • Ngram 和 Edge NGram 连词分割
    • Path Hierarchy 按照文件路径进行分割

Tokenizers 测试

POST _analyze
{
  "tokenizer": "path_hierarchy",
  "text": ["/path/to/file"]
}

# 结果
{
  "tokens": [
    {
      "token": "/path",
      "start_offset": 0,
      "end_offset": 5,
      "type": "word",
      "position": 0
    },
    {
      "token": "/path/to",
      "start_offset": 0,
      "end_offset": 8,
      "type": "word",
      "position": 0
    },
    {
      "token": "/path/to/file",
      "start_offset": 0,
      "end_offset": 13,
      "type": "word",
      "position": 0
    }
  ]
}
复制代码

Token Filters

  • 对于tokenizer输出的单词(term)进行增加、删除、修改等操作
  • 自带的如下:
    • lowercase 将所有term转为小写
    • stop 删除停用词
    • Ngram 和 Edge NGram 连词分割
    • Synonym 添加近义词的term

Token Filters测试

POST _analyze
{
  "text": [
    "a Hello World!"
  ],
  "tokenizer": "standard",
  "filter": [
    "stop",
    "lowercase",
    {
      "type": "ngram",
      "min_gram": 4,
      "max_gram": 4
    }
  ]
}

# 结果
{
  "tokens": [
    {
      "token": "hell",
      "start_offset": 2,
      "end_offset": 7,
      "type": "<ALPHANUM>",
      "position": 1
    },
    {
      "token": "ello",
      "start_offset": 2,
      "end_offset": 7,
      "type": "<ALPHANUM>",
      "position": 1
    },
    {
      "token": "worl",
      "start_offset": 8,
      "end_offset": 13,
      "type": "<ALPHANUM>",
      "position": 2
    },
    {
      "token": "orld",
      "start_offset": 8,
      "end_offset": 13,
      "type": "<ALPHANUM>",
      "position": 2
    }
  ]
}
复制代码

自定义分词

自定义分词需要在索引配置中设定 char_filter、tokenizer、filter、analyzer等

自定义分词示例:

  • 分词器名称:my_custom\
  • 过滤器将token转为大写
PUT test_index_1
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_custom_analyzer": {
          "type":      "custom",
          "tokenizer": "standard",
          "char_filter": [
            "html_strip"
          ],
          "filter": [
            "uppercase",
            "asciifolding"
          ]
        }
      }
    }
  }
}
复制代码

自定义分词器测试

POST test_index_1/_analyze
{
  "analyzer": "my_custom_analyzer",
  "text": ["<p>I'm so <b>happy</b>!</p>"]
}

# 结果
{
  "tokens": [
    {
      "token": "I'M",
      "start_offset": 3,
      "end_offset": 11,
      "type": "<ALPHANUM>",
      "position": 0
    },
    {
      "token": "SO",
      "start_offset": 12,
      "end_offset": 14,
      "type": "<ALPHANUM>",
      "position": 1
    },
    {
      "token": "HAPPY",
      "start_offset": 18,
      "end_offset": 27,
      "type": "<ALPHANUM>",
      "position": 2
    }
  ]
}
复制代码

分词使用说明

分词会在如下两个时机使用:

  • 创建或更新文档时(Index Time),会对相应的文档进行分词处理
  • 查询时(Search Time),会对查询语句进行分词
    • 查询时通过analyzer指定分词器
    • 通过index mapping设置search_analyzer实现
    • 一般不需要特别指定查询时分词器,直接使用索引分词器即可,否则会出现无法匹配的情况

分词使用建议

  • 明确字段是否需要分词,不需要分词的字段就将type设置为keyword,可以节省空间和提高写性能
  • 善用_analyze API,查看文档的分词结果

更多内容请访问我的个人网站:laijianfeng.org

参考文档:

  1. elasticsearch 官方文档
  2. 慕课网 Elastic Stack从入门到实践

欢迎关注我的微信公众号

Elasticsearch 6.x 倒排索引与分词

以上所述就是小编给大家介绍的《Elasticsearch 6.x 倒排索引与分词》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Web Anatomy

Web Anatomy

Robert Hoekman Jr.、Jared Spool / New Riders / 2009-12-11 / USD 39.99

At the start of every web design project, the ongoing struggles reappear. We want to design highly usable and self-evident applications, but we also want to devise innovative, compelling, and exciting......一起来看看 《Web Anatomy》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具