elasticsearch7.1.1学习之分词

正排索引

文档id到文档内容、单词的关联关系

倒排索引

单词到文档id的关联关系

单词词典

记录所有文档的单词,一般比较大

记录单词到倒排列表的关联信息

一般为B+Tree

倒排列表

记录了单词对应的文档集合,由倒排索引项组成

倒排索引项主要组成部分:

1.文档id

用于获取原始信息

2.单词频率

记录该单词在文档出现的次数,用于后续相关性算分

3.位置:

记录单词在文档的分词位置,用于词语搜索,高亮显示

4.偏移:

记录单词在文档的开始和结束的位置

大概的查询过程

用户输入关键词通过倒排索引先从单词字典中的b+tree结构定位到某个子节点,该子节点对应到倒排列表中的某个位置,通过倒排列表的对应位置的信息获取对应的文档id,然后根据文档id查询到完整的内容并返回结果

在es中,每个字段都会有自己的倒排索引

分词:

分词是指将文本转换成一系列的单词(term or token)的过程,也可以叫文本分析,在es里面称为Analysis,为什么要使用分词呢?可以简单的理解为提高搜索的准确度,分词器越合适,形成的倒排索引在查询时得到的结果也越准确

分词器是es中专门处理分词的组件,组成部分如下

Character Filter:针对原始文本进行处理,去除html标记符

Tokenizer:将原始文本按一定的规则切分为单词

Token Filters:针对tokenizer处理的单词进行再加工,比如转小写,删除或新增某些字符

es中的测试分词api接口为_analyze

1.可以直接指定analyzer进行测试

2.可以直接指定索引中的字段进行测试

3.可以自定义分词器进行测试

下面先用自带的标准分词器进行分词测试

token为分词结果,offset为描述位置的信息

 

elasticsearch内置分词器,通过analyzer指定

standard

默认分词器 ,小写处理,去掉stop word(要配置),按词切分,支持多语言,

simple

按照非字母切分,小写处理

whitespace

按空格切分

stop

比simple多了stopword处理,去除一些不必要的字段

keyword

不分词,直接将输入作为一个单词输出

parttern

通过正则表达自定义分隔符,默认是\W+,非字词符号作为分隔符

language

提供多种常见语言分词器

另外:

中文分词

ki分词器

下载zip包,解压到plugin的ik目录下,重启es

Character Filter

在Tokenizer之前对原始文本进行处理

htmlscript去除html标签和转换html实体

mapping进行字符替换操作

pattern relace进行正则匹配

会影响后续tokenizer解析的postion和offset信息

Tokenizers

standard tokenizer

根据某种算法切分字符串,max_token_length限定切分后的每个字符长度,默认255

letter tokenizer

遇到非字母字符时进行切分

lowercase tokenizer

letter tokenizer+lowercase token filters

whitespace tokenizer

根据空白格切分,也有最长字符限定配置

uax url email tokenizer

与standard相似,但保留了url或者邮箱地址,也有最长字符限定配置

classic tokenizer

对英文分词有很好的支持,也有最长字符限定配置

thai tokenizer

泰文分词器

NGram tokenizer

类似于滑动窗口一直遍历该字符串并截取一定长度的字符,默认最小长度(min_gram)为,最大长度(max_gram)为2,token_chars(用于确定什么类型的字符会被包含在截取后的字符,可配置的有 letter、digit等等类型,默认为包含全部类型)

Edge NGram Tokenizer

和NGram相似,只不过从头开始切分,可以用作类似按头匹配的模糊查询(like a%)的效果

keyword tokenizer

什么都不做,输入当输出,可以和token filter配合使用

pattern tokenizer

根据java的正则表达式切分

char group tokenizer

可配置项:tokenize on chars,里面的词语作为划分的依据

simple pattern tokenizer

simple pattern spilt tokenizer

和正则差不多

Path Hierararchy tokenizer

路径相关,例子

Token Filters

对于tokenizer输出的单词(term)进行增、删、修改等操作

这里由于太多了,只举几个例子

1.stop删除stop words

2.NGram和Edge NGram连词分割

3.Synonym添加近义词的term

 

自定义分词需要在索引的配置中设定

格式如下,type为custom区别于其他analyzer,my_custom_analyzer为自定义的分词名,char_filter为对原始文本进行处理,然后交由tokenizer进行处理,最后由filter一步步处理

PUT my_index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_custom_analyzer": {
          "type":      "custom", 
          "tokenizer": "standard",
          "char_filter": [
            "html_strip"
          ],
          "filter": [
            "lowercase",
            "asciifolding"
          ]
        }
      }
    }
  }
}

这里再用client的方式写一个简单的例子做对比,比如为博客的内容建立一个索引,内容建立一个中文分词加过滤html和敏感词的例子,标题则用标准的tokenizer,其实ik的话stop词典可以通过线上接口或本地文件进行配置,这里仅仅配置了线上添加的词库简单例子,下面是这个索引的配置

{
	"settings": {
		"number_of_shards":3,
		"number_of_replicas":"1",
		"analysis": {
			"analyzer": {
				"filterhtml": {
					"type": "custom",
					"tokenizer": "standard",
					"char_filter": ["html_strip"],
					"filter": ["lowercase"]
				},
				"ikhtml": {
					"type": "custom",
					"tokenizer": "ik_max_word",
					"char_filter": ["html_strip", "emoji_filter"],
					"filter": ["my_stop"]
				}
			},
			"char_filter": {
				"emoji_filter": {
					"type": "mapping",
					"mappings": ["(づ ̄ 3 ̄)づ=>亲亲", "o(* ̄▽ ̄*)ブ=>开心"]
				}
			},
			"filter": {
				"my_stop": {
					"type": "stop",
					"stopwords": ["尼玛", "扑街"]
				}
			}
		}
	},
	"mappings": {
		"properties": {
			"content": {
				"type": "text",
				"analyzer": "ikhtml",
				"search_analyzer": "ik_smart"
			},
			"title": {
				"type": "text",
				"analyzer": "filterhtml"
			},
			"author": {
				"type": "keyword"
			},
			"date": {
				"type": "date"
			}
		}
	}
}

首先先按ik文档指示,首先要把ik插件放到es的plugin目录下,把ik的IKAnalyzer.cfg文件里的远程更新接口添加上去,然后进行更新词典的服务端代码的编写,

@Controller
public class KeyWordController {
    
    @GetMapping(value = "/keyword.txt")
    public ResponseEntity<String> getCustomDict() throws FileNotFoundException {
            // 读取字典文件
            HttpHeaders httpHeaders = new HttpHeaders();

            File file = new File("D:\\workhb\\demo\\src\\main\\resources\\keyword.dic");
            StringBuilder builder = new StringBuilder();
            Scanner sc=new Scanner(file);
            while(sc.hasNext()){
                builder.append(sc.next()+"\n");
            }
            httpHeaders.set("Last-Modified", String.valueOf(builder.length()));
            httpHeaders.set("ETag", String.valueOf(builder.length()));
            // 返回数据
            return new ResponseEntity(builder.toString(), httpHeaders, HttpStatus.OK);

    }
    @GetMapping(value = "/stopword.txt")
    public ResponseEntity<String> stopword() throws FileNotFoundException {
        // 读取字典文件
        HttpHeaders httpHeaders = new HttpHeaders();

        File file = new File("D:\\workhb\\demo\\src\\main\\resources\\stopword.dic");
        StringBuilder builder = new StringBuilder();
        Scanner sc=new Scanner(file);
        while(sc.hasNext()){
            builder.append(sc.next()+"\n");
        }
        httpHeaders.set("Last-Modified", String.valueOf(builder.length()));
        httpHeaders.set("ETag", String.valueOf(builder.length()));
        // 返回数据
        return new ResponseEntity(builder.toString(), httpHeaders, HttpStatus.OK);

    }



}

然后在配置文件上添加远程访问的地址,这里我用了花生壳的内网穿透映射到本机来改

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
	<comment>IK Analyzer 扩展配置</comment>
	<!--用户可以在这里配置自己的扩展字典 -->
	<entry key="ext_dict"></entry>
	 <!--用户可以在这里配置自己的扩展停止词字典-->
	<entry key="ext_stopwords"></entry>
	<!--用户可以在这里配置远程扩展字典 -->
	<entry key="remote_ext_dict">http://1n9178z595.goho.co/keyword.txt</entry>
	<!--用户可以在这里配置远程扩展停止词字典-->
	 <entry key="remote_ext_stopwords">http://1n9178z595.goho.co/stopword.txt</entry>
</properties>

接下来看一下效果

post请求后localhost:9200/article/_analyze

{
	"field":"content",
	"text":"青青草原的羊羊子孙你们好棒,不要说扑街和尼玛(づ ̄ 3 ̄)づ"
	
}

就被解析如下,这代表了分词后的结果,然后出现过滤了之前定义的一些stopword和把表情符号变为自定义的符号

{
    "tokens": [
       
        {
            "token": "青青草原",
            "start_offset": 0,
            "end_offset": 4,
            "type": "CN_WORD",
            "position": 1
        },
        {
            "token": "青青草",
            "start_offset": 0,
            "end_offset": 3,
            "type": "CN_WORD",
            "position": 2
        },
        {
            "token": "青青",
            "start_offset": 0,
            "end_offset": 2,
            "type": "CN_WORD",
            "position": 3
        },
        {
            "token": "青草",
            "start_offset": 1,
            "end_offset": 3,
            "type": "CN_WORD",
            "position": 4
        },
        {
            "token": "草原",
            "start_offset": 2,
            "end_offset": 4,
            "type": "CN_WORD",
            "position": 5
        },
        {
            "token": "的",
            "start_offset": 4,
            "end_offset": 5,
            "type": "CN_CHAR",
            "position": 6
        },
        {
            "token": "羊",
            "start_offset": 5,
            "end_offset": 6,
            "type": "CN_CHAR",
            "position": 7
        },
        {
            "token": "羊",
            "start_offset": 6,
            "end_offset": 7,
            "type": "CN_CHAR",
            "position": 8
        },
        {
            "token": "子孙",
            "start_offset": 7,
            "end_offset": 9,
            "type": "CN_WORD",
            "position": 9
        },
        {
            "token": "你们",
            "start_offset": 9,
            "end_offset": 11,
            "type": "CN_WORD",
            "position": 10
        },
        {
            "token": "好棒",
            "start_offset": 11,
            "end_offset": 13,
            "type": "CN_WORD",
            "position": 11
        },
        {
            "token": "不要",
            "start_offset": 14,
            "end_offset": 16,
            "type": "CN_WORD",
            "position": 12
        },
        {
            "token": "要说",
            "start_offset": 15,
            "end_offset": 17,
            "type": "CN_WORD",
            "position": 13
        },
        {
            "token": "和",
            "start_offset": 19,
            "end_offset": 20,
            "type": "CN_CHAR",
            "position": 15
        },
        {
            "token": "亲亲",
            "start_offset": 22,
            "end_offset": 30,
            "type": "CN_WORD",
            "position": 17
        }
    ]
}

这时候访问另一个接口,添加词组,然后再次解析:

发现在原有基础上出现这个词组,在搜索的时候,不同的分词器可能会导致不同的得分情况,分词的结果与否会影响搜索匹配与否

client api中的是验证分词的方法,用来验证分词结果,而创建索引时使用分词的话,则按照文档定义好需要的json数据

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值