正排索引:文档ID到文档内容、单词的关联关系
倒排索引:单词到文档ID的关联关系
倒排索引查询流程:(以查询包含“搜索引擎”的文档为例)
倒排索引是搜索引擎的核心,主要包含两部分:
单词词典(Term Dictionary)
(一般由B+Tree实现)
倒排列表(Posting List)
: 记录单词对应的文档集合,由倒排索引项(Posting)
组成
倒排索引项(Posting)
主要包含如下信息:
文档ID
,用于获取原始信息单词频率(TF,Term Frequency
),记录该单词在该文档中的出现次数,用于后续相关性算分位置(Position)
,记录单词在文档中的分词位置(多个),用于做词语搜索(Phrase Query)偏移(Offset)
,记录单词在文档的开始和结束位置,用于做高亮显示 官方文档
分词是指将文本转换成一系列单词(term or token)的过程,也可以叫做文本分析,在es里面成为Analysis
.
分词器是es中专门处理分词的组件,英文为Analyzer
,它的组件如下:
Character Filters
: 针对原始文本进行处理,比如去除html特殊标记符Tokenizer
: 将原始文本按照一定规则切分为单词Token Filters
: 针对tokenizer处理的单词进行在加工,比如转小写、删除、或新增等处理es提供了一个测试分词的api接口,方便验证分词效果,endpoint是_analyze
直接指定analyzer进行测试:
POST _analyze
{
"analyzer": "standard", #analyzer:分词器
"text": "hello world" #text:测试文本
}
直接指定索引中的字段进行测试:
POST test_index/_analyze
{
"field": "username", #field:测试字段
"text": "hello world"
}
自定义分词器进行测试:
POST _analyze
{
"tokenizer": "standard",
"filter": ["lowercase"], #自定义analyzer
"text": "Hello World"
}
Standard
: 按词切分,支持多语言;小写处理
Simple:
按照非字母切分;小写处理
Whitespace:
按照空格切分
Stop:
Stop World指语气助词等修饰性的词语,比如the、an、的、这等等,相比simple多了stop word 处理
Keyword:
不分词,直接将输入作为一个单词输出
pattern:
通过正则表达式自定义分隔符;默认是\W+,即非字符的符号作为分隔符
Language:
提供了30+常见语言的分词器
测试:
POST _analyze
{
"analyzer": "standard",
"text": "The 2 QUICK Brown-Foxes jumped over the lazy dog's bone"
}
中文分词指的是将一个汉字序列切分成一个一个单独的词,在英文中,单词之间是以空格作为自然分界符,汉语中词没有一个形式上的分界符。
常用分词系统:
IK:
jieba:
基于自然语言处理的分词系统:
Hanlp:由一系列模型与算法组成的java工具包,目标是普及自然语言处理在生产环境中的应用
THULAC:THU Lexical Analyzer for Chinese,由清华大学自然语言处理与社会人文计算实验室研制推出的一套中文此法分析工具包,具有中文分词和词性标注功能
IK分词器的安装使用:
安装:
./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.0.0/elasticsearch-analysis-ik-6.0.0.zip
对应es版本,在集群所有节点上安装,然后重启服务
ik分词器基础知识:
两种analyzer,ik_smart
和 ik_max_word
,可以根据需求选择,但一般是选用ik_max_word
。
ik_max_word
:会将文本做最细粒度的拆分。例如,会将“中华人民共和国国歌”差分为“中华人民共和国,中华人民,中华,华人,人民共和国,人民,共和国,共和,国,国歌”,会穷尽各种可能的组合
ik_smart
:会做最粗粒度的拆分。例如,会将“中华人民共和国国歌”差分为“中华人民共和国,国歌”
使用ik分词:(需要在mapping中指定)
PUT myikindex
{
"mappings": {
"doc":{
"properties": {
"test":{
"analyzer": "ik_max_word",
"type": "text"
}
}
}
}
}
当自带的分词无法满足需求时,可以自定义分词,通过自定义Character Filters、Tokenizer
和Token Filters
实现。
(1)Character Filters:
在Tokenizer之前对原始文本进行处理,比如增加、删除或替换字符等,自带的如下:
HTML Strip
去除html标签和转换html实体Mapping
进行字符替换操作Pattern Replace
进行正则匹配替换会影响后续tokenizer解析的postion和offset信息
POST _analyze
{
"tokenizer": "keyword",
"char_filter": ["html_strip"],
"text": "I'm so happy!
"
}
(2)Tokenizer:
将原始文本按照一定规则切分为单词(term or token),自带的如下:
standard:
按照单词进行分割letter:
按照非字符类进行分割whitespace
: 按照空格进行分割UAX URL Email:
按照standard分割,但不会分割邮箱和urlNGram和Edge NGram:
连词分割Path Hierarchy:
按照文件路径进行切割POST _analyze
{
"tokenizer": "path_hierarchy",
"text": "/one/two/three"
}
(3)Token Filters:
针对tokenizer处理的单词进行在加工,比如转小写、删除、或新增等处理,自带的如下:
lowercase:
将所有term转换为小写stop:
删除stop wordsNGram和Edge NGram
连词分割Synonym
添加近义词的termPOST _analyze
{
"tokenizer": "standard",
"text": "a Hello,world!",
"filter": [
"stop",
"lowercase",
{
"type": "ngram",
"min_gram": 4,
"max_gram": 4
}
]
}
自定义分词需要在索引的配置中设定:
PUT test_index
{
"settings": {
"analysis": {
"char_filter": {},
"tokenizer": {},
"filter": {},
"analyzer": {}
}
}
}
# "analysis": 分词配置,可以自定义char_filter、tokenizer、filter、analyzer
#配置索引
PUT test_index
{
"settings": {
"analysis": {
"analyzer": {
"my_custom_abalyzer": {
"type": "custom",
"char_filter": ["html_strip"],
"tokenizer": "standard",
"filter": [
"lowercase",
"asciifolding"
]
}
}
}
}
}
#测试
POST test_index/_analyze
{
"analyzer": "my_custom_abalyzer",
"text": "Is this a box?"
}
#配置索引
PUT test_index3
{
"settings": {
"analysis": {
"analyzer": {
"my_custom_analyzer": {
"type": "custom",
"char_filter": ["emoticons"],
"tokenizer": "punctuation",
"filter": [
"lowercase",
"english_stop"
]
}
},
"char_filter": {
"emoticons": {
"type": "mapping",
"mappings": [
":) => _happy_",
":( => _sad_"
]
}
},
"tokenizer": {
"punctuation": {
"type": "pattern",
"pattern": "[.,!?]"
}
},
"filter": {
"english_stop": {
"type": "stop",
"stopwords": "_english_"
}
}
}
}
}
#验证
POST test_index3/_analyze
{
"analyzer": "my_custom_analyzer",
"text": "I'm a :) person,and you?"
}
分词会在如下两个时机使用:
Index Time
),会对相应的文档进行分词处理Search Time
),会对查询语句就行分词索引时分词是通过配置Index Mapping
中每个字段的analyzer
属性实现的,如下:
PUT test_index
{
"mappings": {
"doc": {
"properties": {
"title": {
"type": "text",
"analyzer": "whitespace"
}
}
}
}
}
# "analyzer"指定分词器
查询时分词的指定方式有如下几种:
search_analyzer
实现POST test_index/_search
{
"query":{
"match": {
"message": {
"query": "hello",
"analyzer": "standard"
}
}
}
}
PUT test_index
{
"mappings": {
"doc": {
"properties": {
"title": {
"type": "text",
"analyzer": "whitespace"
, "search_analyzer": "standard"
}
}
}
}
}
一般不需要特别指定查询时的分词器,直接使用索引时的分词器即可,否则会出现无法匹配的情况。