全文搜索两个最重要的方面是
- 相关性 它是评价查询与其结果间的相关程度,并根据这种相关程度对结果排名的一种能力,这种计算方式可以是TF/IDF方法,地理位置临近,模糊相似,或其他的某些算法
- 分词它是将文本块转换为有区别的,规范化的token的一个过程,目的是为了创建倒排索引以及查询倒排索引
构造数据
- 创建索引 使用ik分词器指定要分词的字段
# 创建指定ik分词器的索引
PUT /itcast
{
"settings":{
"index":{
"number_of_shards":"2",
"number_of_replicas":"0"
}
},
"mappings":{
"person":{
"properties":{
"name":{
"type":"text"
},
"age":{
"type":"integer"
},
"mail":{
"type":"keyword"
},
"hobby":{
"type":"text",
# 给hobby字段指定使用ik分词器
"analyzer":"ik_max_word"
}
}
}
}
}
- 使用_bulk命令批量构造数据
POST /itcast/_bulk
# 注意 这个url中没有指定文档类型
# 新创建的索引 第一次添加数据并且不指定_id使用index进行添加
{"index":{"_index":"itcast","_type":"person"}}
{"name":"张三","age": 20,"mail": "111@qq.com","hobby":"羽毛球,乒乓球,足球"}
{"index":{"_index":"itcast","_type":"person"}}
{"name":"李四","age": 21,"mail": "222@qq.com","hobby":"羽毛球,乒乓球,足球,篮球"}
{"index":{"_index":"itcast","_type":"person"}}
{"name":"王五","age": 22,"mail": "333@qq.com","hobby":"羽毛球,篮球,游泳,听音乐"}
{"index":{"_index":"itcast","_type":"person"}}
{"name":"赵六","age": 23,"mail": "444@qq.com","hobby":"跑步,游泳"}
{"index":{"_index":"itcast","_type":"person"}}
{"name":"孙七","age": 24,"mail": "555@qq.com","hobby":"听音乐,看电影"}
# 别忘了最下面的回车
单词搜索
# 使用match查询 查询非结构化数据
POST /itcast/person/_search
{
"query":{
"match":{
"hobby":"音乐"
}
},
"highlight":{
"fields":{
"hobby":{}
}
}
}
过程说明:
- 检查字段类型
爱好hobby字段是一个text类型(指定了IK分词器),这意味着查询字符串本身也应该被分词 - 分析查询字符串
将查询的字符串"音乐"传入IK分词器中,输出的结果是单个项"音乐",因为只有一个单词项,所以match查询执行的是单个底层term查询 - 查找匹配文档
用term查询在倒排索引中查找"音乐"然后获取一组包含该项的文档,本例的结果是文档3,5 - 为每个文档评分
用term查询计算每个文档相关度评分_score,这是种将词频("音乐"这个词在相关文档的hobby字段中出现的频率)和反向文档频率("音乐"这个词在所有文档中hobby字段中出现的频率),以及字段的长度(即字段越短相关度越高)相结合的计算方式
多词搜索
POST /itcast/person/_search
{
"query":{
"match":{
# 多个词中间用空格隔开
"hobby":"音乐 篮球"
}
},
"highlight":{
"fields":{
"hobby":{}
}
}
}
# 这个搜索 搜索出来的是包含音乐或者包含篮球的,但是不是我们想要的结果,我们想要的是即包含音乐又包含篮球的
可以看到,包含了"音乐","篮球"的数据都已经被搜索到了
但是,搜索的结果不符合我们的预期,因为我们想搜索的是即包含"音乐"又包含"篮球"的用户,显然结果返回的是"或"的关系
在Elasticsearch中,可以指定词之间的逻辑关系,如下:
POST /itcast/person/_search
{
"query":{
"match":{
"hobby":{
"query":"音乐 篮球",
"operator":"and"
}
}
},
"highlight":{
"fields":{
"hobby":{}
}
}
}
前面我们测试了"OR"和"AND"搜索,这是两个极端,其实在实际场景中,并不会选取这两个极端,更有可能是选取这种,或者说,只要符合一定的相似度就可以查询到数据,在ElasticSearch中也支持这样的查询,通过minimum_should_match来指定匹配度,如:70% 如果是and 就设置100% 如果是or就设置50%
POST /itcast/person/_search
{
"query":{
"match":{
"hobby":{
"query":"游泳 羽毛球",
#设置匹配度
"minimum_should_match":"80%"
}
}
},
"highlight":{
"fields":{
"hobby":{}
}
}
}
相似度设置多少合适,需要在实际的需求中进行反复测试,才可以得到合理的值
组合搜索
在搜索时,也可以使用过滤器中讲过的bool组合查询
POST /itcast/person/_search
{
"query":{
"bool":{
"must":{
"match":{
"hobby":"篮球"
}
},
"must_not":{
"match":{
"hobby":"音乐"
}
},
"should":[
{
"match":{
"hobby":"游泳"
}
}
]
}
},
"highlight":{
"fields":{
"hobby":{}
}
}
}
# 必须包含篮球,必须不包含音乐 ,如果包含游泳那么它的相似度更高
评分的计算规则
bool查询会为每个文档计算相关度评分_score,再将所有匹配的must和should语句的分数_score求和,最后除以must和should语句的总数
must_not语句不会影响评分,它的作用只是将不想管的文档排除
默认情况下,should中的内容不是必须匹配的,如果查询语句中没有must,那么就会至少匹配其中一个.当然,也可以通过minimum_should_match参数进行控制,该值可以是数字(最少匹配几个)也可以是百分比
示例:
POST /itcast/person/_search
{
"query":{
"bool":{
"should":[
{
"match":{
"hobby":"游泳"
}
},
{
"match":{
"hobby":"篮球"
}
},
{
"match":{
"hobby":"音乐"
}
}
],
#表示最少匹配两个
"minimum_should_match":2
}
},
"highlight":{
"fields":{
"hobby":{}
}
}
}
minimum_should_match为2 表示should的三个词中,至少要满足两个,而且 上一条bool查询语句中没有must 表示should中的3个词最少要匹配一个,但是已经配置minumum_should_match为2了就把这个默认的覆盖了
权重
有些时候,我们可能需要对某些词增加权重来影响该条数据的得分,
搜索关键字为"游泳 篮球"and关系,如果结果中包含了"音乐"权重为10,包含了"跑步"权重为2
{
"query":{
"bool":{
"must":{
"match":{
"hobby":{
"query":"游泳 篮球",
"operator":"and"
}
}
},
"should":[
{
"match":{
"hobby":{
"query":"音乐",
# 设置权重为10
"boost":10
}
}
},
{
"match":{
"hobby":{
"query":"跑步",
#设置权重为2
"boost":2
}
}
}
]
}
},
"highlight":{
"fields":{
"hobby":{}
}
}
}