原创 红尘旅人 2025-11-07 08:30 重庆
点击关注公众号,技术干货及时达!
LIKE '%关键词%'那样。”产品经理眨着期待的大眼睛,而我心里已经开始警铃大作。「“在ES里做前后通配符?这玩意搞不好会把集群搞崩啊!”」 我试图挣扎。“但是竞品都有这个功能了...” 产品经理使出了杀手锏。经过一番“友好协商”,我们达成共识:工期可以延长,但这个功能必须实现!这就是为什么最简单的ounter(lineounter(lineounter(line原始文本:"苹果手机真香"↓ 分词处理["苹果", "手机", "真", "香"]
match查询能够工作:「但是,这里藏着第一个坑!」「默认情况下,ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineGET /products/_search{"query": {"match": {"name": "苹果手机"}}}
match查询使用or操作符」,意味着:用户想要的是“苹果手机”,结果搜出来一堆不相干的东西,这体验能好吗?更精确的匹配方式match + operator "and" - 必须全部包含ounter(lineounter(lineounter(lineounter(lineounter(line// 搜索"苹果手机"可能返回:// - "苹果电脑"(只匹配"苹果")// - "华为手机"(只匹配"手机")// - "苹果手机"(完全匹配)// - "好吃苹果"(只匹配"苹果")
「效果」:必须同时包含"苹果"和"手机"两个词。「进步:」 排除了只包含一个词的无关结果。「新问题」:「顺序不固定!」“手机苹果”也会被匹配,这显然不符合正常语言习惯。match_phrase - 真正的词组匹配ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineGET /products/_search{"query": {"match": {"name": {"query": "苹果手机","operator": "and"}}}}
「完美!必须完整包含"苹果手机"这个词组,且顺序一致。」「但是...」 当测试用例显示:“用户只记得'果手'两个字,怎么搜不到'苹果手机'?”我意识到,「传统的分词搜索有其局限性」。ES 7.9之前的解决方案:n-gram分词器面对前后模糊匹配的需求,在「ES 7.9之前」,最成熟的方案就是「n-gram分词器」 + 「match_phrase」实现。什么是n-gram?简单说,就是把文本切成固定长度的片段:ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineGET /products/_search{"query": {"match_phrase": {"name": "苹果手机"}}}
配置n-gram分析器ounter(lineounter(lineounter(line原始文本:"苹果手机"2-gram分词:["苹果", "果手", "手机"]3-gram分词:["苹果手", "果手机"]
实现前后模糊匹配ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(linePUT /products{"settings": {"analysis": {"analyzer": {"ngram_analyzer": {"tokenizer": "ngram_tokenizer"}},"tokenizer": {"ngram_tokenizer": {"type": "ngram","min_gram": 2, // 最小2个字符"max_gram": 3 // 最大3个字符}}}},"mappings": {"properties": {"name": {"type": "text","analyzer": "ngram_analyzer","search_analyzer": "standard"}}}}
「效果:成功匹配到"苹果手机"!」「付出的代价:」✅ 支持任意位置的子串匹配❌ 「索引体积膨胀3倍以上」❌ 查询性能受影响❌ 需要精细调整n-gram参数危险的诱惑:7.9之前的wildcard查询在调研过程中,我发现ES其实一直都有ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineGET /products/_search{"query": {"match_phrase": {"name": "果手"}}}
wildcard查询,但文档里满是红色警告。揭开wildcard查询的真相「常见误解1:」 "7.9版本以下只能查keyword字段"「事实:」 wildcard可以作用于text字段,但「匹配的是分词后的term」,结果往往出乎意料,不尽人意。「常见误解2:」 "会进行全索引扫描"「事实:」 扫描的是字段「倒排索引中的所有term」,对每个term进行「正则匹配」。wildcard查询实战❝「说明」:当设置case_insensitive为true时,查询会忽略大小写。❞「性能灾难」:前导通配符ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line// 对keyword字段查询(相对可用)GET /products/_search{"query": {"wildcard": {"name": {"value": "*iPhone*","case_insensitive": true}}}}// 对text字段查询(强烈不推荐)GET /products/_search{"query": {"wildcard": {"name": {"value": "*iphone*"}}}}
*会导致遍历所有term,「CPU和内存瞬间飙升」,妥妥的集群杀手!新时代的解决方案:ES 7.9+的wildcard字段类型就在我纠结要不要接受n-gram的索引膨胀时,突然想起:我们不是准备采购ES 8.x吗?「ES 7.9引入的wildcard字段类型简直就是为此场景量身定制!」技术原理揭秘「智能n-gram索引」:底层使用优化的3字符n-gram「二进制doc value」:完整保存原始文档,保证匹配精度「专用查询引擎」:针对通配符场景深度优化实际配置和使用性能对比:数字说话在我们的测试环境中:ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(linePUT /products{"mappings": {"properties": {"name": {"type": "wildcard" // 专门为通配符优化的字段类型}}}}GET /products/_search{"query": {"wildcard": {"name": {"value": "*果手*" // 前后模糊匹配}}}}
方案
索引大小
平均查询延迟
集群影响
功能完整性
n-gram + match_phrase
原始大小 × 约3倍
50ms左右
中等
✅
旧版wildcard查询
原始大小
1000ms+
极高风险
✅
wildcard字段类型
原始大小 × 约1.4倍
25ms左右
很低
✅
「结果显而易见!」最终技术选型经过充分的测试和对比,我们最终拍板:「采购Elasticsearch 8.x集群」「对需要模糊匹配的字段使用wildcard类型」「传统搜索场景继续使用match_phrase等成熟方案」当演示结果出来时,产品和用户都很满意:“所以现在输入'果手'真的能找到'苹果手机'了?而且性能还不错?”“没错,这就是技术演进的力量!”我微笑着回答。(其实是工期足的力量☺️,工期足够长,资金足够多,什么都能做😊)总结:Elasticsearch模糊搜索方案对比ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line// 最终的映射设计PUT /products{"mappings": {"properties": {"name": {"type": "wildcard" // 用于前后模糊匹配},"description": {"type": "text" // 用于常规全文搜索},"category": {"type": "keyword" // 用于精确分类匹配}}}}
搜索方式
适用场景
优点
缺点
推荐指数
match常规全文搜索
简单易用
精度较低
⭐⭐⭐⭐
match + operator: "and"
多词必须匹配
提高相关性
顺序不固定
⭐⭐⭐
match_phrase精确词组匹配
顺序一致
不支持模糊
⭐⭐⭐⭐
n-gram + match_phrase
前后模糊匹配
功能完整
索引膨胀严重
⭐⭐⭐
旧版wildcard查询
通配符匹配
使用简单
性能极差
⭐
wildcard字段类型
前后模糊匹配
性能优秀
需要ES 7.9+
⭐⭐⭐⭐⭐
「技术心得:」从最初的match查询到最终的wildcard字段类型,这条演进之路告诉我们:「了解业务场景」:不同的搜索需求需要不同的技术方案「理解底层原理」:明白分词机制和查询原理才能做出正确选择「拥抱技术演进」:新版本往往用更优雅的方式解决老问题❝「友情提示:」 如果你的产品经理接下来要求实现“深度分页”,请温柔地提醒TA——就连淘宝搜索也只支持100页,这不是技术限制,而是用户体验的最优解!❞技术人的快乐,往往就藏在解决这些“模糊”需求的过程中。毕竟,让模糊的需求变得清晰,让不可能成为可能——这就是我们的职业乐趣所在!最后,不知道jym在使用ES搜索功能中还遇到过哪些有趣的技术挑战?欢迎大家在评论区分享你的“血泪史”,让我们一起在技术的道路上避坑前行!点击"阅读原文"了解详情~
