一、 问题描述
- 需要搜索的内容为:【飞科剃须刀】
- 实际搜索,需要满足的条件:
a.检索关键词“飞科”,“剃须”、“刀”,都能搜出这篇文档。
b.干扰搜索“水果刀”,文档不要被检索出来。
c.单个字拆分“剃”、“须”太多干扰文档不要被检索出来。
d.待检索的词“科剃”、“科剃须”不在词典中,但只要在原文中出现,都要检索到。
e.检索关键词“飞科剃须”,能搜出这篇文档
f.检索速度要快,不影响性能。 - 分词测试:
- Standard分词器
GET /my_index/_analyze
{"text":"飞科剃须刀","analyzer":"standard"
}
分词结果:飞、科、剃、须、刀
- ik_max_word分词器
GET /my_index/_analyze
{"text":"飞科剃须刀","analyzer":"ik_max_word"
}
分词结果:飞、科、剃须刀、剃须、刀
- ik_smart分词器
GET /my_index/_analyze
{"text":"飞科剃须刀","analyzer":"ik_smart"
}
分词结果:飞、科、剃须刀
二、问题分析
- 针对要求a,可以采用全文检索(match、match_phrase)。
- match查询实现精确匹配
GET /my_index/_search
{"query":{"match":{"qss":{"query":"飞科" //分别尝试“剃须”、“刀”,都能检索到包含“飞科剃须刀”的文档}}}
}
- match_phrase查询实现完全匹配
文档同时满足下面两个条件才会被搜索到:
(1)分词后所有词项都要出现在该字段中
(2)字段中的词项顺序要一致
GET my_index/_search
{"query": {"match_phrase": {"qss": "飞科" //分别尝试“剃须”、“刀”,都能检索到包含“飞科剃须刀”的文档}}
}
- 针对要求b,match查询检索范围太大、干扰太多,排除;ik_smart分词器没有“剃须”词项,排除match查询后,与 match_phrase 结合使用,依然检索不到,故排除。
- 针对要求c,standard 分词器会出现非常多冗余数据,排除。
- 针对要求d,ik_max_word 有些词项不包含,排除。
- 针对要求e,match_phrase 查询不到,排除。
- 针对要求f,wildcard 模糊查询影响性能,排除。
三、为什么会检索不到?
- 分词原因/词典未收录原因。
在上文问题d中,由于分词器中不包含“科剃”等词项,所以检索不到。 - postition位置不一致。
在上文问题e中,由于分词器分词器对“飞科剃须”和“飞科剃须刀”分词的postition位置不一致,所以检索不到。
飞科剃须 | 飞科剃须刀 | ||
---|---|---|---|
token | position | token | position |
飞 | 0 | 飞 | 0 |
科 | 1 | 科 | 1 |
剃须 | 2 | 剃须刀 | 2 |
剃须 | 3 | ||
刀 | 4 |
四、解决方案
1. match_phrase_prefix结合slop的方案
GET my_index/_search
{"query": {"match_phrase_prefix": {"qss": {“query”:"飞科剃须",“slop”:1 //指定相邻词之间允许相隔多远。设置为0,则为完全匹配。}}}
}
能应对一部分业务。但是分析发现:slop设置不论多大、多小,都可能会引入噪音数据,导致结果不准确。
2. match_phrase结合match should关联匹配。
依然会引入噪音数据。
3. 终极方案:逐个字分词和ik分词结合的方式。
- mapping设置:
PUT mx_index{"mappings": {"_doc": {"properties": {"qss": {"type": "text","analyzer": "ik_max_word","fields": {"standard": {"type": "text","analyzer": "standard"},"keyword": {"type": "keyword","ignore_above": 256}}}}}}
}
- 搜索:
GET mx_index/_search
{"query": {"bool": {"should": [{"match_phrase": {"qss": "科剃"}},{"match_phrase": {"qss.standard": "科剃"}}]}}
}