背景:
最近公司有个新的小程序项目,主页需要根据公司简称全局搜索并高亮显示,因为公司数据是存放在es中的,所以需要写个查询es的接口,就把之前的代码拷贝过来了,具体生成的DSL语句如下所示:
{
"from": 0,
"size": 10,
"query": {
"bool": {
"must": [
{
"term": {
"address": {
"value": "110000",
"boost": 1.0
}
}
},
//以下都是类似的模糊匹配条件,省略一部分
{
"function_score": {
"query": {
"bool": {
"should": [
{
"match_phrase": {
"name": {
"query": "D",
"slop": 0,
"zero_terms_query": "NONE",
"boost": 2.0
}
}
},
{
"match_phrase": {
"name.pinyin": {
"query": "D",
"slop": 0,
"zero_terms_query": "NONE",
"boost": 0.5
}
}
}
]
}
}
}
}
],
"adjust_pure_negative": true,
"boost": 1.0
}
},
"sort": [
{
"_score": {
"order": "desc"
}
}
],
"highlight": {
"require_field_match": false,
"fields": {
"name": {}
}
}
}
问题描述
前端在联调时传入的参数为D,查询公司简称包含D的公司,但是返回的结果中后几条数据没有高亮字段,如下所示
{
"_score": 28.505045,
"_source": {
"full_name": "酩香年华文化传播(北京)有限公司",
"name": "D老师德语网"
},
"highlight": {
"name": [
"<em>D</em>老师德语网"
]
}
},
{
"_score": 8.471922,
"_source": {
"full_name": "亚斯兰(北京)国际文化传播有限公司",
"name": "D-Agent"
}
}
原因分析
于是查看这个字段的分词情况,发现这个字段的分词器用的是ik_smart。以下是ik_smart对上述两条数据的分词效果。
GET test_analyzer_name/_analyze
{
"analyzer":"ik_max_word",
"text":"D老师德语网"
}
结果:
{
"tokens": [
{
"token": "d",
"start_offset": 0,
"end_offset": 1,
"type": "ENGLISH",
"position": 0
},
{
"token": "老师",
"start_offset": 1,
"end_offset": 3,
"type": "CN_WORD",
"position": 1
},
{
"token": "德语",
"start_offset": 3,
"end_offset": 5,
"type": "CN_WORD",
"position": 2
},
{
"token": "网",
"start_offset": 5,
"end_offset": 6,
"type": "CN_CHAR",
"position": 3
}
]
}
GET test_analyzer_name/_analyze
{
"analyzer":"ik_smart",
"text":"D-Agent"
}
结果:
{
"tokens": [
{
"token": "d-agent",
"start_offset": 0,
"end_offset": 7,
"type": "LETTER",
"position": 0
}
]
}
由此可知,ik_smart分词器对一些特别字符(D-Agent等)分词效果不佳,导致高亮显示时用分词器解析不出正确结果。
但是为什么会返回D-Agent这条数据呢,接着再看查询条件中使用到了拼音去分词,查看mapping中字段的配置,发现没有配置拼音的分词器,于是采用es默认的standard分词器,而它是能够把D分词出来的,并且由于在DSL语句中采用了相关性得分查询,还在query中使用到了should,满足一个查询条件就会被返回,所以结果如上述所示,只是这两条数据的得分相差比较大而已。
为什么高亮显示时采用的是ik_smart分词而不是pinyin的分词?
我的理解:对于pinyin、keyword我更愿意理解为是策略,因为我们在用keyword或者拼音查询时,会在字段名称后面加上这些后缀,例如name.pinyin,对于这种其实是让es走pinyin的策略分词,但实际上这个字段配置的分词器还是ik_smart,以上只是个人理解,仅供参考。
"name": {
"type": "text",
"fields": {
"completion": {
"type": "text"
},
"keyword": {
"type": "keyword"
},
"pinyin": {
"type": "text"
}
},
"analyzer": "ik_smart"
}
解决方法
把此字段的分词器由ik_smart改为ik_max_word,即可解决。顺便把拼音分词器加上,以支持拼音查询。
ik分词器分为ik_smart和ik_max_word
ik_max_word :会将文本做最细粒度的拆分,尽可能多的拆分出词语,分词结果可重复
ik_smart:会做最粗粒度的拆分;分词结果不可重复