基本概念
什么是分词?
分词就是将一个文本转化成为一系列的单词的过程,也叫文本分析,在 ElasticSearch 中称之为 Analysis。
默认是使用标准分词。
举例:我是中国人 --> 我/是/中国人
分词 api
指定分词器进行分词
分词测试
POST:127.0.0.1:9200/_analyze
1、英文分词
{
"analyzer":"standard",
"text":"hello world"
}
返回值:
{
"tokens": [
{
"token": "hello",
"start_offset": 0,
"end_offset": 5,
"type": "<ALPHANUM>",
"position": 0
},
{
"token": "world",
"start_offset": 6,
"end_offset": 11,
"type": "<ALPHANUM>",
"position": 1
}
]
}
2、中文分词
{
"analyzer": "standard",
"text": "我是中国人"
}
返回值:分为5个词、并不合理。
{
"tokens": [
{
"token": "我",
"start_offset": 0,
"end_offset": 1,
"type": "<IDEOGRAPHIC>",
"position": 0
},
{
"token": "是",
"start_offset": 1,
"end_offset": 2,
"type": "<IDEOGRAPHIC>",
"position": 1
},
{
"token": "中",
"start_offset": 2,
"end_offset": 3,
"type": "<IDEOGRAPHIC>",
"position": 2
},
{
"token": "国",
"start_offset": 3,
"end_offset": 4,
"type": "<IDEOGRAPHIC>",
"position": 3
},
{
"token": "人",
"start_offset": 4,
"end_offset": 5,
"type": "<IDEOGRAPHIC>",
"position": 4
}
]
}
3、指定索引,字段分词
POST:127.0.0.1:9200/test/_analyze
{
"analyzer": "standard",
"field": "hobby",
"text": "我是中国人"
}
中文分词
1、释义
中文分词的难点在于,在汉语中没有明显的词汇分界点,如在英语中,空格可以作为分隔符。如果分隔不正确就会造成歧义。
如:
我/爱/炒肉丝
我/爱/炒/肉丝
常用中文分词器, IK、jieba、 THULAC等,推荐使用IK分词器。
分词地址:
https://github.com/medcl/elasticsearch-analysis-ik
2、安装 ik 分词器
自动安装
./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.8.1/elasticsearch-analysis-ik-7.8.1.zip
手动安装
将下载到的 elasticsearch-analysis-ik-7.8.1. zip 解圧到 /elasticsearch/plugins/ik 目录下
1 mkdir es/plugins/ik
2 cp elasticsearch-analysis-ik-7.8.1.zip ./es/plugins/ik
3 unzip elasticsearch-analysis-ik-7.8.1.zip
4 ./bin/elasticsearch
3、测试是否安装成功
POST:127.0.0.1:9200/_analyze
{
"analyzer": "ik_max_word",
"text": "我是中国人"
}
返回结果:
{
"tokens": [
{
"token": "我",
"start_offset": 0,
"end_offset": 1,
"type": "CN_CHAR",
"position": 0
},
{
"token": "是",
"start_offset": 1,
"end_offset": 2,
"type": "CN_CHAR",
"position": 1
},
{
"token": "中国人",
"start_offset": 2,
"end_offset": 5,
"type": "CN_WORD",
"position": 2
},
{
"token": "中国",
"start_offset": 2,
"end_offset": 4,
"type": "CN_WORD",
"position": 3
},
{
"token": "国人",
"start_offset": 3,
"end_offset": 5,
"type": "CN_WORD",
"position": 4
}
]
}
全文搜索
1、全文搜索两个最重要的方面:
1、相关性( Relevance )它是评价查询与其结果间的相关程度,并根据这种相关程度对结果排名的一种能力,这种计算方式可以是TF/IDF方法、地理位置邻近、模糊相似,或其他的某些算法。
2、分词( Analysis )它是将文本块转换为有区别的、规范化的token的一个过程,目的是为了创建倒排索引以及查询倒排索引。
2、重置索引的分词
PUT:127.0.0.1:9200/study
{
"settings": {
"index": {
"number_of_shards": "2",
"number_of_replicas": "0"
}
},
"mappings": {
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "text"
},
"age": {
"type": "integer"
},
"mail": {
"type": "keyword"
},
"desc": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
3、数据添加
POST:127.0.0.1:9200/study/_doc/_bulk
{ "create": { "_index": "study","_type": "_doc", "_id": "1001" }}
{ "id": 1001,"name": "1001","age": 11,"sex": "女", "desc": "指尖轻触玻窗,嗤嗤的响声,惊动了脆弱的心脏,一阵阵的酸楚,像浪潮般袭来,若果这样酸酸的痛可以代替撕心裂肺,那就让他长久点,这样时间会把我忘记,这样便可躲在这里,让那些软弱手舞足蹈,让那些脆弱和不堪拼命娱乐,让那颗紧绷的心,少少松弦。" }
{ "create": { "_index": "study","_type": "_doc", "_id": "1002" }}
{ "id": 1002,"name": "1002","age": 12,"sex": "女", "desc": "曾过往,伊颜纯美无暇,如玉般璀璨,许多人像发现了财富,紧抱于怀,怜香般害怕失去。那时,遇见你的是洗礼过后的悔过者,只懂怜香,而不懂惜玉,再璀璨也掩盖不了他身上久积的灰尘,铸造不了你,也成就不了他,于是乎,迷糊坚固了戏剧化的情谊,疼只是简单的疼。" }
{ "create": { "_index": "study","_type": "_doc", "_id": "1003" }}
{ "id": 1003,"name": "1003","age": 13,"sex": "女", "desc": "岁月还远,徐徐的风吹着,却也有了几分萧瑟,春天,不仅有满天飘飞的花儿,还有到处弥散着花的幽香。随着秋韵渐渐浓郁起来,院子里的花便盛开了,整个院子里香气四溢,溢漫着甜丝丝的味儿。金灿的花儿一串串、一撮撮,重重叠叠簇涌着点缀在茂密的绿叶之间,温温暖暖象极了一个个孩子的笑脸,仿佛是给这温暖的春天注入了一道亮丽的风景。" }
{ "create": { "_index": "study","_type": "_doc", "_id": "1004" }}
{ "id": 1004,"name": "1004","age": 14,"sex": "女", "desc": "樱花有单樱和双樱,她们绽放时满树灿烂,清香扑鼻,单樱白的如雪如云,双樱色彩如火似霞。但是无论是单樱还是双樱,她们盛开的时间都不长,二十多天的光景,开的绚丽多彩、满树烂漫,落得星星瓣瓣,匆匆忙忙。" }
{ "create": { "_index": "study","_type": "_doc", "_id": "1005" }}
{ "id": 1005,"name": "1005","age": 15,"sex": "女", "desc": "真正的相爱,是人在千里,却梦魂相依;真正的相爱,是岁月流转,却不离不弃;真正的相爱,是彼此付出,却无怨无悔。" }
{ "create": { "_index": "study","_type": "_doc", "_id": "1006" }}
单词搜索
POST: 127.0.0.1:9200/study/_doc/_search
{
"query":{
"match": {
"desc": "时光"
}
}
}
过程说明
1.检查字段类型
描述字段 desc 是一个text类型(指定了IK分词器), 这意味着查询字符串本身也应该被分词。
2.分析查询字符串.
将查询的字符串“音乐传入IK分词器中,输出的结果是单个项音乐。因为只有一个单词项,所以match查询执行的是单个底层term查询。
3.查找匹配文档。
用term查询在倒排索引中查找“音乐”然后获取-组包含该项的文档。
4.为每个文档评分。
用term查询计算每个文档相关度评分. score ,这是种将词频( term frequency ,即词“时光"在相关文档的 desc 字段中出现的频率)和反向文档频率t inverse document frequency ,即词“时光"在所有文档的 desc 字段中出现的频率) , 以及字段的长度(即字段越短相关度越高)相结合的计算方式。
多词搜索
POST: 127.0.0.1:9200/study/_doc/_search
{
"query":{
"match": {
"desc": "时光 岁月"
}
}
}
1、operator
可以发现结果中,包含了 "时光"、 "岁月"的数据都已经被搜索到了。
可是,搜索的结果并不是我们的预期,因为我们想要的是即包含"时光",又包含"岁月"的用户,显然结果返回的是 或者 的关系。
在 ElasticSearch 中可以通过 operator 指定分词之间的逻辑关系, 默认是 or, 具体如下:
POST: 127.0.0.1:9200/study/_doc/_search
{
"query": {
"match": {
"desc": {
"query": "时光 岁月",
"operator": "and"
}
}
}
}
2、最小匹配度 minimum_should_match
前面我们测试了"OR"和“AND"搜索,这是两个极端,其实在实际场景中, 并不会选取这2个极端,更有可能是选取这种,或者说,只需要符合一定的相似度就可以查询到数据;
match 查询还支持 minimum_should_match 最小匹配参数,这个可以指定必须匹配的词项数用来表示一个文档是否相关。
我们可以将其设置为一个具体的数字,通常的做法是将其设置为一个百分数:如: 70% ;
POST: 127.0.0.1:9200/study/_doc/_search
{
"query": {
"match": {
"desc": {
"query": "时光 岁月",
"minimum_should_match": "50%"
}
}
}
}
相似度应该多少合适呢?需要在实际的需求中进行反复测试,才可以得到合理的值。
组合搜索
POST: 127.0.0.1:9200/study/_doc/_search
{
"query": {
"bool": {
"must": {
"match": {
"desc": "时光"
}
},
"must_not": {
"match": {
"desc": "日子"
}
},
"should": [
{
"match": {
"desc": "岁月"
}
}
]
}
}
}
上面搜索的意思是:
搜索结果中必须包含时光,不能包含日子,如果包含了岁月,那么他的相似度会更高。
评分的计算规则:
bool查询会为每一个文档计算相关度评分 _score, 在将所有匹配的 must 和 should 语句的分数 _score 求和。
最后除以 must 和 should 语句的总数。
must_not 语句不会影响评分;他的作用只是将不相关的文档排除。
默认情况下,should 中的内容不是必须匹配的,如果查询语句中没有 must,那么就会至少匹配其中一个。
当然也可以通过 minimum_should_match 参数进行控制,该值可以数字。也可以是百分比。
权重 boost
有些时候,我们可能需要对某些词增加权重来影响该条数据的评分。
搜索关键字为"时光 岁月", 如果包含了"日子" 权重为10,如果包含了"还远"权重为2。
POST: 127.0.0.1:9200/study/_doc/_search
{
"query": {
"bool": {
"must": {
"match": {
"desc": {
"query": "时光 岁月",
"operator": "and"
}
}
},
"should": [
{
"match": {
"desc": {
"query": "日子",
"boost": 10
}
}
},
{
"match": {
"desc": {
"query": "还远",
"boost": 2
}
}
}
]
}
}
}