Elastic Stack系列--ElasticSearch高级
1.全文检索
1.1.倒排索引
倒排索引源于实际应用中需要根据属性的值来查找记录.这种索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址.由于不是由记录来确定属性值.而是由属性值来确定记录的位置,因而称为倒排索引(inverted index).带有倒排索引的文件我们称为倒排索引文件,简称倒排文件(inverted file).
正排索引:
转化成倒排索引:
说明:
- "单词ID"一栏记录了每个单词的单词编号;
- 第二栏是对应的单词;
- 第三栏即每个单词对应的倒排列表;
- 比如单词"谷歌",其单词编号为1,倒排列表为(1,2,3,4,5),说明文档集中每个文档都包含了这个单词.
而事实上,索引系统还可以记录除此之外的更多信息,在单词对应的倒排列表中不仅记录了文档编号,还记载了单词频率信息(TF),即这个单词在某个文档中的出现次数,之所以要记录这个信息,是因为词频信息在搜索结果排序时,计算查询和文档相似度是很重要的一个计算因子,所以将其记录在倒排列表中,以方便后续排序时进行分值计算.
倒排索引还可以记载更多的信息,除了记录文档编号和单词频率信息外,额外记载了两类信息,即每个单词对应的"文档频率信息",以及在倒排列表中记录单词某个文档出现的位置信息.
1.2.全文搜索
全文搜索两个最重要的方面是:
- **相关性(Relevance)**它是评价查询与其结果见的相关程度,并根据这种相关程度对结果排名的一种能力,这种计算方式可以是TF/DF方法,地理位置邻近,模糊相似,或其他的某些算法.
- **分析(Analysis)**它是将文本块转换为有区别的,规范化的token的一个过程,目的是为了创建倒排索引以及查询倒排索引.
1.2.1.构造数据
PUT http://172.16.124.131:9200/fechin01
#请求体
{
"settings":{
"index":{
"number_of_shards":"1",
"number_of_replicas":"0"
}
},
"mappings":{
"person":{
"properties":{
"name":{
"type":"text"
},
"age":{
"type":"integer"
},
"mail":{
"type":"keyword"
},
"hobby":{
"type":"text",
"analyzer":"ik_max_word"
}
}
}
}
}
之后插入数据:
POST http://172.16.124.131:9200/fechin01/_bulk
#请求体
{
"index":{
"_index":"fechin","_type":"person"}}
{
"name":"张三","age":20,"mail":"111@qq.com","hobby":"羽毛球,乒乓球,足球"}
{
"index":{
"_index":"fechin","_type":"person"}}
{
"name":"李四","age":21,"mail":"222@qq.com","hobby":"羽毛球,乒乓球,足球,篮球"}
{
"index":{
"_index":"fechin","_type":"person"}}
{
"name":"王五","age":22,"mail":"333@qq.com","hobby":"羽毛球,篮球,游泳,听音乐"}
{
"index":{
"_index":"fechin","_type":"person"}}
{
"name":"赵六","age":23,"mail":"444@qq.com","hobby":"跑步,游泳,篮球"}
{
"index":{
"_index":"fechin","_type":"person"}}
{
"name":"孙七","age":24,"mail":"555@qq.com","hobby":"听音乐,看电影,羽毛球"}
1.2.2.单词搜索
POST http://172.16.124.131:9200/fechin01/_search
#请求体
{
"query":{
"match":{
"hobby":"音乐"
}
},
"highlight":{
"fields":{
"hobby":{
}
}
}
}
过程说明:
- 检查字段类型:爱好hobby字段是一个text类型(指定了IK分词器),这意味着查询字符串本身也应该被分词;
- 分析查询字符串:将查询的字符串"音乐"传入IK分词器中,输出的结果是单个项音乐,因为只有一个单词项,所以match查询执行的是单个底层term查询;
- 查找匹配文档:用term查询在倒排索引中查找"音乐",然后获取一组包含该项的文档,本例的结果是文档:3,5;
- 为每个文档评分:用term查询计算每个文档相关度评分_score,这是种将词频(term frequency,即词"音乐"在相关文档的hobby字段中出现的频率)和反向文档频率(inverse document frequency,即词"音乐"在所有文档的hobby字段中出现的频率),以及字段的长度(即字段越短相关度越高)相结合的计算方式.
1.2.3.多词搜索
POST http://172.16.124.131:9200/fechin01/person01/_search
#请求体
{
"query":{
"match":{
"hobby":"音乐 篮球"
}
},
"highlight":{
"fields":{
"hobby":{
}
}
}
}
#响应体
{
"took": 22,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 4,
"max_score": 1.3192271,
"hits": [
{
"_index": "fechin01",
"_type": "person01",
"_id": "D6nHznAByqluhSOWWW9g",
"_score": 1.3192271,
"_source": {
"name": "王五",
"age": 22,
"mail": "333@qq.com",
"hobby": "羽毛球,篮球,游泳,听音乐"
},
"highlight": {
"hobby": [
"羽毛球,<em>篮球</em>,游泳,听<em>音乐</em>"
]
}
},
{
"_index": "fechin01",
"_type": "person01",
"_id": "EanHznAByqluhSOWWW9g",
"_score": 0.81652206,
"_source": {
"name": "孙七",
"age": 24,
"mail": "555@qq.com",
"hobby": "听音乐,看电影,羽毛球"
},
"highlight": {
"hobby": [
"听<em>音乐</em>,看电影,羽毛球"
]
}
},
{
"_index": "fechin01",
"_type": "person01",
"_id": "EKnHznAByqluhSOWWW9g",
"_score": 0.6987338,
"_source": {
"name": "赵六",
"age": 23,
"mail": "444@qq.com",
"hobby": "跑步,游泳,篮球"
},
"highlight": {
"hobby": [
"跑步,游泳,<em>篮球</em>"
]
}
},
{
"_index": "fechin01",
"_type": "person01",
"_id": "DqnHznAByqluhSOWWW9g",
"_score": 0.50270504,
"_source": {
"name": "李四",
"age": 21,
"mail": "222@qq.com",
"hobby": "羽毛球,乒乓球,足球,篮球"
},
"highlight": {
"hobby": [
"羽毛球,乒乓球,足球,<em>篮球</em>"
]
}
}
]
}
}
由上述结果可以得到查出来的是既包含"音乐"也包含"篮球"的用户,结果返回的是"或"的关系.在ElasticSearch中,可以指定词之间的逻辑关系.
POST http://172.16.124.131:9200/fechin01/person01/_search
#请求体
{
"query":{
"match":{
"hobby":{
"query":"音乐 篮球",
"operator":"and"
}
}
},
"highlight":{
"fields":{
"hobby":{
}
}
}
}
#响应体
{
"took": 40,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 1.3192271,
"hits": [
{
"_index": "fechin01",
"_type": "person01",
"_id": "D6nHznAByqluhSOWWW9g",
"_score": 1.3192271,
"_source": {
"name": "王五",
"age": 22,
"mail": "333@qq.com",
"hobby": "羽毛球,篮球,游泳,听音乐"
},
"highlight": {
"hobby": [
"羽毛球,<em>篮球</em>,游泳,听<em>音乐</em>"
]
}
}
]
}
}
我们可以看到结果是符合预期的.前面我们测试了"OR"和"AND"搜索,这是两个极端,在实际场景中,并不会选取这两个极端,我们只需要符合一定的相似度就可以查询到数据,在ElasticSearch中也支持这样的查询,通过minimum_should_match来指定匹配度,如70%.
POST http://172.16.124.131:9200/fechin01/person01/_search
#请求体
{
"query":{
"match":{
"hobby":{
"query":"音乐 篮球",
"minimum_should_match":"80%"
}
}
},
"highlight":{
"fields":{
"hobby":{
}
}
}
}
相似度多少合适,需要在实际的需求中进行反复测试,才可得到合理的值
1.2.4.组合搜索
POST http://172.16.124.131:9200/fechin01/person01/_search
#请求体
{
"query":{
"bool":{
"must":{
"match":{
"hobby":"篮球"
}
},
"must_not":{
"match":{
"hobby":"音乐"
}
},
"should":{
"match":{
"hobby":"游泳"
}
}
}
},
"highlight":{
"fields":{
"hobby":{
}
}
}
}
#响应体
{
"took": 22,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 1.8336569,
"hits": [
{
"_index": "fechin01",
"_type": "person01",
"_id": "EKnHznAByqluhSOWWW9g",
"_score": 1.8336569,
"_source": {
"name": "赵六",
"age": 23,
"mail": "444@qq.com",
"hobby": "跑步,游泳,篮球"
},
"highlight": {
"hobby": [
"跑步,<em>游泳</em>,<em>篮球</em>"
]
}
},
{
"_index": "fechin01",
"_type": "person01",
"_id": "DqnHznAByqluhSOWWW9g",
"_score": 0.50270504,
"_source": {
"name": "李四",
"age": 21,
"mail": "222@qq.com",
"hobby": "羽毛球,乒乓球,足球,篮球"
},
"highlight": {
"hobby": [
"羽毛球,乒乓球,足球,<em>篮球</em>"
]
}
}
]
}
}
上面搜索的结果的意思是:搜索结果中必须包含篮球,不能包含音乐,如果包含游泳,那么相似度会更高.
评分的计算规则:
- bool查询会为每个文档计算相关度评分_score,再讲所有匹配的must和should语句的分数_score求和,最后除以must和should语句的总数;
- must_not语句不会影响评分,它的作用只是将不相关的文档排除;
默认情况下,should中的内容不是必须匹配的,如果查询语句中没有must,那么就会至少匹配其中一个,当然,也可以通过minimum_should_match
参数进行控制,该值可以是数字也可以是百分比.
POST http://172.16.124.131:9200/fechin01/person01/_search
#请求体
{
"query":{
"bool":{
"should":[
{
"match":{
"hobby":"游泳"
}
},
{
"match":{
"hobby":"篮球"
}
},
{
"match":{
"hobby":"音乐"
}
}
],
"minimum_should_match":2
}
},
"highlight":{
"fields":{
"hobby":{
}
}
}
}
#响应体
{
"took": 25,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 4,
"max_score": 2.135749,
"hits": [
{
"_index": "fechin01",
"_type": "person01",
"_id": "D6nHznAByqluhSOWWW9g",
"_score": 2.135749,
"_source": {
"name": "王五",
"age": 22,
"mail": "333@qq.com",
"hobby": "羽毛球,篮球,游泳,听音乐"
},
"highlight": {
"hobby": [
"羽毛球,<em>篮球</em>,<em>游泳</em>,听<em>音乐</em>"
]
}
},
{
"_index": "fechin01",
"_type": "person01",
"_id": "EKnHznAByqluhSOWWW9g",
"_score": 1.8336569,
"_source": {
"name": "赵六",
"age": 23,
"mail":