Elasticsearch 能实现自动补全检索的方案很多,可以简单归结为如下几种不同的方案:
Prefix
前缀匹配检索。MatchPhrase prefix
短语前缀匹配检索。ngram
分词间接解决前缀匹配检索。Search as your type
类型。Completion Suggest
自动补全。方案一、方案二的样例数据如下所示:
PUT worldcup_index
{
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "ik_max_word",
"fields": {
"keyword": {
"type": "keyword"
}
}
}
}
}
}
POST worldcup_index/_bulk
{"index":{"_id":1}}
{"title":"世界杯-拉莫斯帽子戏法 葡萄牙6-1晋级将战摩洛哥"}
{"index":{"_id":2}}
{"title":"世界杯2022赛程"}
{"index":{"_id":3}}
{"title":"世界杯8强全部出炉!C罗收获大礼,梅西被3大强队包围"}
{"index":{"_id":4}}
{"title":"FIFA世界杯的微博"}
{"index":{"_id":5}}
{"title":"世界杯赛事积分榜及排名"}
{"index":{"_id":6}}
{"title":"世杯界16强决赛对阵表"}
{"index":{"_id":7}}
{"title":"卡塔尔世界杯为什么在冬天"}
属于 term level 精准匹配的范畴。
POST worldcup_index/_search
{
"profile": true,
"query": {
"prefix": {
"title.keyword": "世界"
}
}
}
检索召回数据结果如下(仅截图最核心部分):
属于全文检索的范畴。
POST worldcup_index/_search
{
"query": {
"match_phrase_prefix": {
"title": {
"query": "世界"
}
}
}
}
检索召回数据结果如下(仅截图最核心部分):
实则为:MultiPhraseQuery 实现“世界”、“世”、“界”的组合检索。
有同学可能会问:“世界 世 界” 三个分词单元怎么来的?
看这里,和 analyzer 分词有关系,我们的字段 title 设置的是 text 类型,选择的分词器:ik_max_word 分词器。
之前咱们讲过,也是大家常见的问题,比如:手机号的自动补全检索问题。
可以看一下之前的视频:
这种传统的分词和咱们上面讲过的两种检索方式都不灵。怎么办?
需要自定义分词来实现。
PUT phone_index
{
"settings": {
"analysis": {
"analyzer": {
"autocomplete": {
"tokenizer": "autocomplete",
"filter": [
"lowercase"
]
}
},
"tokenizer": {
"autocomplete": {
"type": "edge_ngram",
"min_gram": 2,
"max_gram": 20
}
}
}
},
"mappings": {
"properties": {
"phone_number": {
"type": "text",
"analyzer": "autocomplete"
}
}
}
}
POST phone_index/_analyze
{
"field": "phone_number",
"analyzer": "autocomplete",
"text": [
"18366669999"
]
}
如上 analyze 分词结果为:
导入数据
POST phone_index/_bulk
{"index":{"_id":1}}
{"phone_number":"18366669999"}
{"index":{"_id":2}}
{"phone_number":"18311112222"}
{"index":{"_id":3}}
{"phone_number":"18333332222"}
{"index":{"_id":4}}
{"phone_number":"18255552222"}
执行检索
POST phone_index/_search
{
"profile": true,
"query": {
"match_phrase_prefix": {
"phone_number": "183"
}
}
}
search_as_your_type
字段类型(7.2 版本之后才有的功能)是一个类似文本的字段,经过优化以提供开箱即用的支持,用于完成自动补全的查询。
支持前缀完成(即匹配从输入开头开始的术语)和中缀完成(即匹配输入中任何位置的术语)的检索。
search_as_your_type 字段本质上支持四种不同的分词。
以“世界杯的微博”文档为例,各种不同类型的分词结果如下:
如下检索方式使用了:multi_match 和 bool_prefix 检索结合"title"、“title._2gram”、"title._3gram"、“title._index_prefix”多字段检索的方式。
这几个字段的最终检索结果基于 most_fields 方式求和得到总的评分。
实际业务层面根据需要选择字段即可。
POST world_cup_index_1207/_search
{
"query": {
"multi_match": {
"query": "世界",
"type": "bool_prefix",
"fields": [
"title",
"title._2gram",
"title._3gram",
"title._index_prefix"
]
}
}
}
对比可以看出,这种检索方式非常的灵活。
Suggesters
是 Elasticsearch 中的高级解决方案,可根据用户的输入的文本返回外观相似的短语。Suggesters
可以实现类似:检索时提示、用户搜索词联想或校验等功能。
相比于前四种实现方式,这种方式“根正苗红”,更加的适合实现企业级的自动补全检索。
创建索引及构造数据如下:
PUT worldcup_suggest_index
{
"mappings": {
"properties": {
"title": {
"type": "keyword"
},
"suggest": {
"type": "completion"
}
}
}
}
PUT worldcup_suggest_index/_bulk
{"index":{"_id":1}}
{"suggest":[{"input":["世界杯2022赛程"],"weight":3}]}
{"index":{"_id":2}}
{"suggest":[{"input":["世界杯赛事积分榜及排名"],"weight":2}]}
{"index":{"_id":3}}
{"suggest":[{"input":["FIFA世界杯的微博"],"weight":1}]}
{"index":{"_id":4}}
{"suggest":[{"input":["世界杯2022赛程"],"weight":1}]}
POST worldcup_suggest_index/_search
{
"suggest": {
"worldcup-suggest": {
"prefix": "世界",
"completion": {
"field": "suggest"
}
}
}
}
我们推荐结果有重复数据,如何去重呢?
这个问题业务层面也经常遇到。completion 借助参数:
"skip_duplicates" : true
实现推荐结果数据的去重。
关于Elasticsearch 8.X 能实现自动补全,本文提供了五种不同的方案。几种方案的对比概括如下:
解决企业级业务问题,远不止这几种方案。
你有没有遇到自动补全问题,用什么方案解决的?
欢迎留言讨论。
本文分享自 铭毅天下Elasticsearch 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!