es自带四种内置分析器,分别为标准分析器、简单分析器、空格分析器、语言分析器。
分析的过程:
包含三个过程:
- 字符过滤器(如过滤空格)
分词器(简单的进行分词)
Token 过滤器(通过设置的token过滤器,对词条进行处理,如大小写转换)
内科
内一科
内二科
普通内科
肿瘤内科
"hits": [
{
"_index": "studydemo",
"_type": "doc",
"_id": "TicbAWMB1wKYJm5vNAws",
"_score": 1.5603871,
"_source": {
"text": "肿瘤内科"
}
},
{
"_index": "studydemo",
"_type": "doc",
"_id": "SycaAWMB1wKYJm5v0gz9",
"_score": 0.5753642,
"_source": {
"text": "内一科"
}
},
{
"_index": "studydemo",
"_type": "doc",
"_id": "TScbAWMB1wKYJm5vEwyj",
"_score": 0.5753642,
"_source": {
"text": "普通内科"
}
},
{
"_index": "studydemo",
"_type": "doc",
"_id": "SicaAWMB1wKYJm5vvQzv",
"_score": 0.3971361,
"_source": {
"text": "内科"
}
},
{
"_index": "studydemo",
"_type": "doc",
"_id": "TCcaAWMB1wKYJm5v8gzZ",
"_score": 0.33706507,
"_source": {
"text": "内二科"
}
}
]
以上结果中有两处是我们不希望得到的:
“内一科”和“内二科”
最佳匹配结果“内科”得分很低
标准分析器(standard)会将中文拆分为单字,如:内科—–>内,科;内一科—–>内,一,科
同时,查询的时候并非直观的拿着“内科”(连在一起)进行匹配,而是首先会对搜索条件进行一下操作:
- 使用搜索的目标field配置的analyzer进行analysis 得到对应的词条
- 将第一步得到的词条逐个在文档内进行匹配
现在再看文档中的数据:
- “内科”其实保存的是“内”,“科”
- “内一科”其实保存的是“内”,“一”,“科”
- “肿瘤内科”其实保存的是“肿”,“瘤”,“内”,“科”
所以输入内科,进行搜索的是“内”和“科”的单子匹配,这是为什么会出现“内一科”这类结果的原因。
理解为什么“肿瘤内科”的得分为什么比内科高之前,需要了解es内部对于相关度的评分,“一个文档的相关度评分部分取决于每个查询词在文档中的 权重 。”
而影响权重的因素包含:
- 检索词频率: 检索词在该字段出现的频率?出现频率越高,相关性也越高。 字段中出现过 5 次要比只出现过 1 次的相关性高。
- 反向文档频率:每个检索词在索引中每个文档的对应字段中出现的频率?频率越高,相关性越低。检索词出现在多数文档中会比出现在少数文档中的权重更低。
- 字段长度准则:字段的长度是多少?长度越长,相关性越低。 检索词出现在一个短的 title 要比同样的词出现在一个长的 content 字段权重更大。
与此同时还需要理解一点:
{
"query": {
"bool": {
"should": [
{"match": { "text": "内科" }}
]
}
}
}
会被重写为:
{
"query": {
"bool": {
"should": [
{"match": { "text": "内" }},
{"match": { "text": "科" }}
]
}
}
}
只要一个文档与查询匹配,Lucene 就会为查询计算评分,然后合并每个匹配词的评分结果,
所以对于“肿瘤内科”,其评分等于“内”,“科”,“肿”,“瘤”四个单自结合以上的评分规则得到的分值,所以高于内科
具体评分规则参考相关度评分
索引时优化文档,使用“shingle”将目标field分解为单字和连字的倒排索引,如”肿瘤内科”分析为:
{
"tokens": [
{
"token": "肿",
"start_offset": 0,
"end_offset": 1,
"type": "" ,
"position": 0
},
{
"token": "肿 瘤",
"start_offset": 0,
"end_offset": 2,
"type": "shingle",
"position": 0,
"positionLength": 2
},
{
"token": "肿 瘤 内",
"start_offset": 0,
"end_offset": 3,
"type": "shingle",
"position": 0,
"positionLength": 3
},
{
"token": "肿 瘤 内 科",
"start_offset": 0,
"end_offset": 4,
"type": "shingle",
"position": 0,
"positionLength": 4
},
{
"token": "瘤",
"start_offset": 1,
"end_offset": 2,
"type": "" ,
"position": 1
},
{
"token": "瘤 内",
"start_offset": 1,
"end_offset": 3,
"type": "shingle",
"position": 1,
"positionLength": 2
},
{
"token": "瘤 内 科",
"start_offset": 1,
"end_offset": 4,
"type": "shingle",
"position": 1,
"positionLength": 3
},
{
"token": "内",
"start_offset": 2,
"end_offset": 3,
"type": "" ,
"position": 2
},
{
"token": "内 科",
"start_offset": 2,
"end_offset": 4,
"type": "shingle",
"position": 2,
"positionLength": 2
},
{
"token": "科",
"start_offset": 3,
"end_offset": 4,
"type": "" ,
"position": 3
}
]
}
此时输入上述任一字符,均可得到“肿瘤内科”,如搜索“瘤内科”。以上字符中的空格是分析器为了方便查阅,实质是上没有空格。
到这时,会发现输入“内科”,”内一科”将不会出现在搜索结果中,也就是说这时候搜索结果只会是包含“内科”(连字)的数据集,而不是包含“内“或”科”(单字)的结果集。但是新的问题,“肿瘤内科”的得分高于“内科”。
解决方案查询时使用正则提升权重