wildcard 检索可以定义为:支持通配符的模糊检索。
类似 Mysql 中的 like 模糊匹配,如下所示:
Elasticsearch 中的 wildcard 使用方式如下:
通配符运算符是匹配一个或多个字符的占位符。
通配符支持两种:
全局认知非常重要,检索核心类型大致(非严谨、精确)分为:精准匹配检索(Term-level queries)和基于分词的全文匹配检索(Full text queries)。
全文匹配检索细分如下:
精准匹配检索细分如下:
也就是:wildcard 是和Term、Terms检索平级的检索。
适用于:召回率要求高的业务场景。
基于分词的全文检索,可能会导致明明存在,但是检索不到。可能的原因如下:
举个例子一看就明白了:
前置说明:
PUT test-004
{
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "ik_max_word",
"fields": {
"keyword": {
"type": "keyword"
}
}
}
}
}
}
POST test-004/_bulk
{"index":{"_id":1}}
{"title":"英文官网承认刘强东一度被捕的原因是涉嫌XX"}
{"index":{"_id":2}}
{"title":"别提了朋友哥哥刘强东窗事发了"}
{"index":{"_id":3}}
{"title":"刘强东施效颦,没想到竟然收获了流量"}
{"index":{"_id":4}}
{"title":"刘强东是谁?我不认识"}
POST test-004/_search
{
"query": {
"match_phrase": {
"title": "刘强东"
}
}
}
用的短语检索 match_phrase,搜索结果如下:
原因说明,analyzer API 能说明一切。
POST test-004/_analyze
{
"text": [
"京东英文官网承认刘强东一度被捕的原因是涉嫌XX"
],
"analyzer": "ik_max_word"
}
分词结果如下:
面对如上召回情况,部分不追求精准率只追求召回率的业务场景,可能会需要文档_id = 1、2、3、4 全部都要召回。
这时候,如果不改变分词的情况下,可能的解决方案之一就是:wildcard 检索实现。
POST test-004/_search
{
"query": {
"wildcard": {
"title.keyword": "*刘强东*"
}
}
}
如上的方式,文档1、2、3、4全部召回。
相当于在原有DSL的基础上,只改动检索方式和字段名称就搞定了产品经理的提高召回率的需求。
貌似,可以交差大吉了。实则,有非常大的隐患。
官方文档是这么说的:
中文含义是:避免以*或?开头的模式。这会增加查找匹配项所需的迭代次数并降低搜索性能。
wildcard 到底有多慢?如下示例可见一斑:
wildcard 检索字段指定的字符数多了以后,会报错如下:
在 wood 大叔 2017年的文章中,曾经指出如下的核心点:
用户输入的字符串长度没有做限制,导致首尾通配符中间可能是很长的一个字符串。后果就是对应的wildcard Query执行非常慢,非常消耗CPU。
为了加速通配符和正则表达式的匹配速度,Lucene4.0开始会将输入的字符串模式构建成一个DFA (Deterministic Finite Automaton),带有通配符的pattern构造出来的DFA可能会很复杂,开销很大。
源码及细节推荐阅读:
https://elasticsearch.cn/article/171
https://elasticsearch.cn/article/186
如下,采用原汁原味的技术群交流内容,更具有说服力。
更能警示大家:慎用 Wildcard!
注意是:不同100个字组合,一直搜。
根因:bool 组合了近 100 组+ wildcard 不同关键词的检索。
在寻求解决方案的时候,我们要先问一下:为什么大家喜欢用 wildcard 实现模糊检索?
得到的答复往往是:顺手,类似Mysql like 查询,短、平、快的达到了产品经理的要求,满足了项目需求。
但,这忽略了性能问题以及可能带来的灾难后果。
所以,解决方案应该从根源上入手,以寻求彻底解决。
更细粒度分词,更有利于数据的召回!
PUT test-005
{
"settings": {
"index.max_ngram_diff": 10,
"analysis": {
"analyzer": {
"my_analyzer": {
"tokenizer": "my_tokenizer"
}
},
"tokenizer": {
"my_tokenizer": {
"type": "ngram",
"min_gram": 3,
"max_gram": 10,
"token_chars": [
"letter",
"digit"
]
}
}
}
},
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "my_analyzer",
"fields": {
"keyword": {
"type": "keyword"
}
}
}
}
}
}
POST test-005/_bulk
{"index":{"_id":1}}
{"title":"英文官网承认刘强东一度被捕的原因是涉嫌性侵"}
{"index":{"_id":2}}
{"title":"别提了朋友哥哥刘强东窗事发了"}
{"index":{"_id":3}}
{"title":"刘强东施效颦,没想到竟然收获了流量"}
{"index":{"_id":4}}
{"title":"刘强东是谁?我不认识"}
POST test-005/_search
{
"query": {
"match_phrase": {
"title": "刘强东"
}
}
}
Ngram 实现推荐:
Elasticsearch能检索出来,但不能正确高亮怎么办?
wildcard 类型出现的目的:一方面避免了某些场景下分词查询不准确的问题,另一方面也解决了通配符和正则检索的效率问题。
注意:新上的数据类型 wildcard,而非 wildcard 检索。
使用方法参见:
https://www.elastic.co/guide/en/elasticsearch/reference/master/keyword.html#wildcard-field-type。
特殊业务场景需要禁止:wildcard 检索。
实现如下:
PUT _cluster/settings
{
"transient": {
"search.allow_expensive_queries": false
}
}
需要强调的是:
"search.allow_expensive_queries" 是 7.7+ 版本才有的功能,早期版本会报错。
由于技术惯性,我们习惯于相同或者相通技术的技术迁移,比如:mysql like 查询迁移到 Elasticsearch 中的 wildcard 模糊检索。但迁移的时候一定要注意:不同技术点的实现差异,同时要多关注技术点不能可能导致的性能问题。
即便 2017年 wood 大叔就发了两篇文章让大家警惕 wildcard 模糊检索可能带来的性能问题。但四年后的今天,仍然很多公司的实战业务中还未考虑性能及后果的前提下,乐此不疲的用着 wildcard 检索!
所以,本文算是 wood 大叔的 wildcard 警示文章接力,希望更多人看到。
参考:
https://t.zsxq.com/Y3zv7Eq
https://t.zsxq.com/bm62zZf
本文分享自 铭毅天下Elasticsearch 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有