掘金原文(個人技術文章優先在掘金髮布):https://juejin.cn/post/7559981310472470562
曾以為掌握了Elasticsearch的match查詢就征服了搜索世界——直到產品經理輕叩桌面,拋出一個看似簡單的要求:"我們需要像MySQL的LIKE '%關鍵詞%'那樣前後通配的模糊搜索。" 我嘴角微揚,意識到真正的技術探險才剛剛開始。
引子:一場關於“模糊”需求的拉鋸戰
“咱們這個搜索功能,用户反饋説經常只記得內容中間的幾個字,希望支持前後模糊匹配,就像MySQL裏LIKE '%關鍵詞%'那樣。”
產品經理眨着期待的大眼睛,而我心裏已經開始警鈴大作。
“在ES裏做前後通配符?這玩意搞不好會把集羣搞崩啊!” 我試圖掙扎。
“但是競品都有這個功能了...” 產品經理使出了殺手鐗。
經過一番“友好協商”,我們達成共識:工期可以延長,但這個功能必須實現!

送走產品經理,我盯着屏幕陷入沉思:在Elasticsearch裏做前後模糊匹配,這確實是個技術挑戰。不過話説回來,我們正準備新採購ES集羣,和主管評估後決定直接上8.x版本——等等,ES 7.9不是引入了專門的wildcard字段類型嗎?
最終方案:基於ES 8.x的wildcard類型字段 + wildcard查詢,完美實現前後模糊匹配!
從“分詞”這個基礎概念説起
要理解ES的模糊搜索,得先搞明白它最核心的概念——分詞。
分詞的奇妙世界
當你往ES裏存入“蘋果手機真香”時,背後發生了這樣的變化(使用不同分詞器,分出來的詞可能不一樣):
原始文本:"蘋果手機真香"
↓ 分詞處理
["蘋果", "手機", "真", "香"]
這就是為什麼最簡單的match查詢能夠工作:
GET /products/_search
{
"query": {
"match": {
"name": "蘋果手機"
}
}
}
但是,這裏藏着第一個坑!
默認情況下,match查詢使用or操作符,意味着:
// 搜索"蘋果手機"可能返回:
// - "蘋果電腦"(只匹配"蘋果")
// - "華為手機"(只匹配"手機")
// - "蘋果手機"(完全匹配)
// - "好吃蘋果"(只匹配"蘋果")
用户想要的是“蘋果手機”,結果搜出來一堆不相干的東西,這體驗能好嗎?
更精確的匹配方式
match + operator "and" - 必須全部包含
GET /products/_search
{
"query": {
"match": {
"name": {
"query": "蘋果手機",
"operator": "and"
}
}
}
}
效果:必須同時包含"蘋果"和"手機"兩個詞。
進步: 排除了只包含一個詞的無關結果。
新問題:順序不固定!“手機蘋果”也會被匹配,這顯然不符合正常語言習慣。
match_phrase - 真正的詞組匹配
GET /products/_search
{
"query": {
"match_phrase": {
"name": "蘋果手機"
}
}
}
完美!必須完整包含"蘋果手機"這個詞組,且順序一致。
但是... 當測試用例顯示:“用户只記得'果手'兩個字,怎麼搜不到'蘋果手機'?”
我意識到,傳統的分詞搜索有其侷限性。
ES 7.9之前的解決方案:n-gram分詞器
面對前後模糊匹配的需求,在ES 7.9之前,最成熟的方案就是n-gram分詞器 + match_phrase實現。
什麼是n-gram?
簡單説,就是把文本切成固定長度的片段:
原始文本:"蘋果手機"
2-gram分詞:["蘋果", "果手", "手機"]
3-gram分詞:["蘋果手", "果手機"]
配置n-gram分析器
PUT /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"
}
}
}
}
實現前後模糊匹配
GET /products/_search
{
"query": {
"match_phrase": {
"name": "果手"
}
}
}
效果:成功匹配到"蘋果手機"!
付出的代價:
- ✅ 支持任意位置的子串匹配
- ❌ 索引體積膨脹3倍以上
- ❌ 查詢性能受影響
- ❌ 需要精細調整n-gram參數
危險的誘惑:7.9之前的wildcard查詢
在調研過程中,我發現ES其實一直都有wildcard查詢,但文檔裏滿是紅色警告。
揭開wildcard查詢的真相
常見誤解1: "7.9版本以下只能查keyword字段"
事實: wildcard可以作用於text字段,但匹配的是分詞後的term,結果往往出乎意料,不盡人意。
常見誤解2: "會進行全索引掃描"
事實: 掃描的是字段倒排索引中的所有term,對每個term進行正則匹配。
wildcard查詢實戰
// 對keyword字段查詢(相對可用)
GET /products/_search
{
"query": {
"wildcard": {
"name": {
"value": "*iPhone*",
"case_insensitive": true
}
}
}
}
// 對text字段查詢(強烈不推薦)
GET /products/_search
{
"query": {
"wildcard": {
"name": {
"value": "*iphone*"
}
}
}
}
説明:當設置case_insensitive為true時,查詢會忽略大小寫。
性能災難:前導通配符*會導致遍歷所有term,CPU和內存瞬間飆升,妥妥的集羣殺手!
新時代的解決方案:ES 7.9+的wildcard字段類型
就在我糾結要不要接受n-gram的索引膨脹時,突然想起:我們不是準備採購ES 8.x嗎?
ES 7.9引入的wildcard字段類型簡直就是為此場景量身定製!
技術原理揭秘
- 智能n-gram索引:底層使用優化的3字符n-gram
- 二進制doc value:完整保存原始文檔,保證匹配精度
- 專用查詢引擎:針對通配符場景深度優化
實際配置和使用
PUT /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等成熟方案
// 最終的映射設計
PUT /products
{
"mappings": {
"properties": {
"name": {
"type": "wildcard" // 用於前後模糊匹配
},
"description": {
"type": "text" // 用於常規全文搜索
},
"category": {
"type": "keyword" // 用於精確分類匹配
}
}
}
}
當演示結果出來時,產品和用户都很滿意:“所以現在輸入'果手'真的能找到'蘋果手機'了?而且性能還不錯?”
“沒錯,這就是技術演進的力量!”我微笑着回答。
(其實是工期足的力量☺️,工期足夠長,資金足夠多,什麼都能做😊)
總結:Elasticsearch模糊搜索方案對比
| 搜索方式 | 適用場景 | 優點 | 缺點 | 推薦指數 |
|---|---|---|---|---|
match |
常規全文搜索 | 簡單易用 | 精度較低 | ⭐⭐⭐⭐ |
match + operator: "and" |
多詞必須匹配 | 提高相關性 | 順序不固定 | ⭐⭐⭐ |
match_phrase |
精確詞組匹配 | 順序一致 | 不支持模糊 | ⭐⭐⭐⭐ |
n-gram + match_phrase |
前後模糊匹配 | 功能完整 | 索引膨脹嚴重 | ⭐⭐⭐ |
舊版wildcard查詢 |
通配符匹配 | 使用簡單 | 性能極差 | ⭐ |
wildcard字段類型 |
前後模糊匹配 | 性能優秀 | 需要ES 7.9+ | ⭐⭐⭐⭐⭐ |
技術心得:
從最初的match查詢到最終的wildcard字段類型,這條演進之路告訴我們:
- 瞭解業務場景:不同的搜索需求需要不同的技術方案
- 理解底層原理:明白分詞機制和查詢原理才能做出正確選擇
- 擁抱技術演進:新版本往往用更優雅的方式解決老問題
友情提示: 如果你的產品經理接下來要求實現“深度分頁”,請温柔地提醒TA——就連淘寶搜索也只支持100頁,這不是技術限制,而是用户體驗的最優解!
技術人的快樂,往往就藏在解決這些“模糊”需求的過程中。畢竟,讓模糊的需求變得清晰,讓不可能成為可能——這就是我們的職業樂趣所在!