当前位置: 首页 > news >正文

【Elasticsearch从入门到精通】第43篇:Elasticsearch搜索过程原理——分词、查询树与BM25评分

上一篇【第42篇】Elasticsearch倒排索引原理——Lucene的核心数据结构下一篇【第44篇】Elasticsearch分布式索引原理——分片路由与写入流程摘要搜索是Elasticsearch的核心能力而理解其背后的原理是写好查询、调优相关性的关键。本文从搜索引擎的底层出发逐层剖析一条查询从输入到返回结果的完整旅程首先介绍文本分析的三层处理架构字符过滤器→分词器→词元过滤器然后讲解查询语法分析如何将用户输入构建为查询树接着深入倒排索引的搜索流程Term查找→文档列表合并→得分计算最后重点解析TF-IDF的直觉理解与BM25算法的核心改进。文中通过完整的REST API代码示例和TF-IDF与BM25参数对比表格帮助读者建立起从理论到实践的完整知识体系。关键词Elasticsearch搜索原理、分词器、BM25评分、倒排索引、相关性排序。一、搜索引擎为什么需要索引在理解Elasticsearch的搜索原理之前我们先回答一个基础问题为什么需要搜索引擎传统的数据库查询如MySQL的LIKE查询属于顺序查找。当我们要在100万条文档中找到包含历史关键字的记录时数据库需要逐行遍历、逐字段扫描时间复杂度为 O(n)。这种查找方式在数据量较小的时候表现尚可但当文档数量达到百万、千万级别时性能会急剧下降。搜索引擎采用了完全不同的策略——索引查找。它的核心理念是顺序查找 文档1 → 扫描全文 → 没有 → 文档2 → 扫描全文 → 没有 → ... → 文档N → 找到了 索引查找 创建索引时文档 → 分词 → 建立 词→文档列表 映射 搜索时输入关键词 → 直接查找映射表 → 立即定位文档搜索引擎在写入数据时将非结构化内容中的信息提取出来重新组织成结构化数据这部分数据就叫作索引。这种用空间换时间的策略使得搜索过程从 O(n) 降低到 O(1) 级别是搜索引擎高性能的根本保证。二、文本分析分词器三层处理架构当一条文档被写入Elasticsearch时它首先要经历**文本分析Analysis**过程。这是搜索引擎的第一道工序也是最容易被忽视的一环——分词结果的好坏直接决定了后续搜索的召回率和准确率。Elasticsearch的分析器由三个核心组件按顺序组成形成三层流水线处理架构原始文本 The iPhone 14 Pro Max costs $1099! │ ▼ ┌─────────────────────────┐ │ 1. 字符过滤器 │ 过滤/替换无意义字符 │ (Character Filter) │ 如HTML标签清理、表情符号替换 └────────────┬────────────┘ │ The iPhone 14 Pro Max costs 1099! ▼ ┌─────────────────────────┐ │ 2. 分词器 │ 将文本切分为词元Term │ (Tokenizer) │ 如按空格/标点切分 └────────────┬────────────┘ │ [The, iPhone, 14, Pro, Max, costs, 1099] ▼ ┌─────────────────────────┐ │ 3. 词元过滤器 │ 对词元进行加工处理 │ (Token Filter) │ 如转小写、去除停用词、同义词扩展 └────────────┬────────────┘ │ [iphone, 14, pro, max, cost, 1099] ▼ 最终索引的Term列表2.1 字符过滤器Character Filter字符过滤器工作在原始文本层面在分词之前对字符流进行预处理。它主要完成三类任务HTML Strip去除HTML标签将biPhone/b处理为iPhoneMapping字符映射替换如将替换为and或将全角字符转为半角Pattern Replace基于正则表达式的替换如过滤掉敏感信息2.2 分词器Tokenizer分词器负责将字符流切分为独立的词元Term。这是分析器的核心组件一个分析器有且仅有一个分词器。Elasticsearch内置了多种分词器standard默认分词器按Unicode文本分段算法切分去除标点并转小写whitespace仅按空白字符切分不做任何其他处理keyword不分词将整个字段值作为一个Tokenpattern按正则表达式切分ik_smart/ik_max_wordIK分词器专门针对中文的分词模式2.3 词元过滤器Token Filter词元过滤器对切分后的Token进行二次加工可以有零个或多个。常见的Token Filter包括lowercase将所有Token转为小写stop移除停用词如的、“是”、“the”、“a”synonym同义词扩展如手机→移动电话,cellphonestemmer词干提取如running→runshingle生成N-gram词组2.4 使用_analyzeAPI 测试分词效果任何阶段的排查和调优都离不开_analyzeAPI。以下代码演示如何一步步测试分析效果// 1. 测试 standard 分析器的完整处理结果POST_analyze{analyzer:standard,text:The iPhone 14 Pro Max costs $1099!}// 返回结果省略部分字段{tokens:[{token:the,start_offset:0,end_offset:3,position:0},{token:iphone,start_offset:4,end_offset:10,position:1},{token:14,start_offset:11,end_offset:13,position:2},{token:pro,start_offset:14,end_offset:17,position:3},{token:max,start_offset:18,end_offset:21,position:4},{token:costs,start_offset:22,end_offset:27,position:5},{token:1099,start_offset:29,end_offset:33,position:6}]}// 2. 仅测试分词器跳过字符过滤器和词元过滤器POST_analyze{tokenizer:standard,text:The iPhone 14 Pro Max costs $1099!}// 3. 自定义分析器组合测试POST_analyze{char_filter:[html_strip],tokenizer:standard,filter:[lowercase,stop,stemmer],text:The biPhone/b 14 Pro Max costs $1099!}// 4. 测试中文分词需安装IK分词器POST_analyze{tokenizer:ik_max_word,text:中华人民共和国国歌}// 返回[中华人民共和国, 中华人民, 中华, 华人, 人民共和国, 人民, 共和国, 共和, 国, 国歌]最佳实践建议每个索引的Mapping都应该显式指定分析器而不是依赖默认值。分析器的选择直接影响搜索体验应当先在测试环境通过_analyzeAPI 验证分词效果再上线生产。三、查询语法分析构建查询树当用户在搜索框中输入 “iPhone 14” 并按下回车键后Elasticsearch并不会立即去倒排索引中查找Term而是先进行查询语法分析。3.1 查询语法分析的两大任务查询语法分析需要完成两个关键任务确定各个Term的搜索字段用户输入的关键词应该在哪个或哪些字段中搜索是title还是content如果是multi_match查询则需要确定权重分配。确定Term之间的逻辑组合关系多个关键词之间是 AND 关系同时匹配还是 OR 关系任一匹配用户是否指定了排序、分页等自定义条件3.2 查询树的构建过程Elasticsearch的查询语法分析会将用户输入构建为一棵查询树Query Tree。查询树的结构决定了后续倒排索引搜索时的文档列表合并策略。以搜索 “iPhone 14” 为例构建查询树的过程如下用户输入: iPhone 14 │ ▼ ┌─────────────────┐ │ 词法分析 │→ 切分为两个Term: iphone, 14 │ (分词器处理) │ └────────┬─────────┘ │ ▼ ┌─────────────────┐ │ 语法分析 │→ 确定搜索字段title, description │ (构建查询树) │→ 确定逻辑关系should (OR) └────────┬─────────┘ │ ▼ Boolean Query (查询树根节点) │ ┌────┴────┐ │ │ TermQuery TermQuery iphone 14 (title) (title) (desc) (desc)AND 关系的查询树match查询使用operator: “and”BooleanQuery (must) │ ┌────┴────┐ │ │ TermQuery TermQuery iphone 143.3 完整DSL示例// 默认OR关系查询GET/products/_search{query:{match:{title:{query:iPhone 14,operator:or}}}}// AND关系查询要求两个词同时出现GET/products/_search{query:{match:{title:{query:iPhone 14,operator:and}}}}// 多字段查询cross_fields类型将各字段视为一个大字段GET/products/_search{query:{multi_match:{query:iPhone 14,fields:[title^3,description,brand],type:cross_fields,operator:and}}}四、倒排索引搜索流程查询树构建完毕后就进入搜索引擎最核心的阶段——倒排索引搜索。这是一个三段式流水线Term查找 → 文档列表合并 → 得分计算。4.1 Term查找倒排索引的本质是一个词→文档ID列表的映射表。当查询树中的每个TermQuery被执行时Lucene直接去倒排索引中查找该Term对应的倒排列表Posting List倒排索引结构简化 Term → Posting List ───────────────────────────────────── iphone → [doc1, doc3, doc7, doc15, doc22, ...] 14 → [doc3, doc7, doc9, doc15, doc20, ...] pro → [doc1, doc15, doc18, doc27, ...] max → [doc1, doc15, doc22, doc30, ...]4.2 文档列表合并AND/OR 操作获取到各Term的Posting List后根据查询树的逻辑关系进行列表合并OR关系should—— 取并集Term iphone 的列表: [doc1, doc3, doc7, doc15, doc22] Term 14 的列表: [doc3, doc7, doc9, doc15, doc20] ↓ OR合并去重 结果列表: [doc1, doc3, doc7, doc9, doc15, doc20, doc22]AND关系must—— 取交集Term iphone 的列表: [doc1, doc3, doc7, doc15, doc22] Term 14 的列表: [doc3, doc7, doc9, doc15, doc20] ↓ AND双指针求交集 结果列表: [doc3, doc7, doc15]Lucene内部使用**跳表Skip List**数据结构优化AND/OR操作使得多列表合并的效率接近 O(min(len(A), len(B)))。4.3 得分计算文档列表合并完成后对结果集中的每个文档计算相关性得分Score。得分计算的完整公式将在下一节详细介绍这里先给出计算的三个核心维度词频TF查询词在文档中出现了多少次出现次数越多相关性越高。逆文档频率IDF查询词在整个索引中出现在了多少文档中出现越少越稀有相关性越高。字段长度归一化字段越长每个词的重要性越低。得分计算完成后按Score降序排序最终返回 Top N 结果给用户。五、TF-IDF的直觉理解在深入BM25算法之前先理解TF-IDF的直觉是必要的。TF-IDF虽然已经不再是Elasticsearch的默认评分算法从5.0开始默认使用BM25但它的设计思想影响了所有现代评分模型。5.1 TF词频词频越高越相关直觉如果搜索词Java在文档A中出现了100次在文档B中只出现了1次那么文档A显然与Java更相关。TF(t, d) 词t在文档d中出现的次数5.2 IDF逆文档频率文档越稀少越相关直觉如果搜索词Elasticsearch只出现在很少的文档中而搜索词的出现在几乎全部文档中那么匹配到Elasticsearch的文档比匹配到的的文档更有价值。IDF(t) log( 总文档数N / 包含词t的文档数n(t) )含义解读当 n(t) 很小时N/n(t) 很大log值大说明这个词很珍贵当 n(t) 接近N时N/n(t)≈1log值≈0说明这个词几乎没有区分度。5.3 TF-IDF的问题尽管TF-IDF在搜索领域影响深远但它有两个明显缺陷TF的线性增长词出现100次和出现101次对相关性的贡献应该是接近的但TF-IDF中二者的得分差距很大——这在直觉上不合理。不考虑字段长度一篇500字的文章中出现5次Java和一篇50000字的文章中出现5次Java其重要性显然是不同的。BM25正是针对这两个问题提出了改进方案。六、BM25算法深度解析BM25全称Best Matching 25是BM25F的信息检索排序函数。从Elasticsearch 5.0开始它取代了TF-IDF成为默认的相关性评分算法。6.1 BM25的核心公式BM25的得分公式如下score(D, Q) Σ IDF(qi) × ( f(qi, D) × (k1 1) ) / ( f(qi, D) k1 × (1 - b b × |D| / avgdl) ) 其中 D 文档Document Q 查询语句Query qi 查询Q分词后的第i个词元 f(qi, D) 词元qi在文档D中出现的频次 |D| 文档D的字段长度词元数 avgdl 索引中所有文档的平均字段长度 k1 可调参数控制词频的饱和度默认 1.2 b 可调参数控制字段长度归一化的程度默认 0.756.2 BM25对TF-IDF的两大改进改进一TF饱和度控制k1参数BM25对词频引入了非线性饱和度函数。随着词频增加得分增长逐渐变缓最终趋近于一个上限TF-IDF: score ∝ TF 线性增长永无止境 BM25: score ∝ TF / (TF k1) 非线性增长趋于饱和直观理解词频从0→1得分大幅提升从无到有的质变词频从1→2得分小幅提升词频从10→11得分几乎没有变化已经饱和k1的默认值为1.2。值越小饱和度越明显值越大越接近线性增长。改进二字段长度归一化b参数BM25通过b参数控制字段长度对得分的影响程度b 1完全归一化长文档的每个词权重被完全稀释b 0完全不归一化字段长度不影响得分b 0.75默认折中方案6.3 k1和b参数调优指南参数默认值含义调增方向适用场景k11.2控制词频对得分的影响增大→词频影响更线性短文档搜索标题/标签减小→词频饱和更快长文档搜索文章内容b0.75控制字段长度归一化程度增大→惩罚长文档更重短文段与长文本混合搜索减小→长度影响变小字段长度相对均匀的场景6.4 TF-IDF vs BM25 对比对比维度TF-IDFBM25词频增长模型线性增长无上限非线性增长趋于饱和字段长度影响不考虑通过b参数归一化可调参数无k1, b 两个调优参数评分合理性长文档易被埋没自动适配不同长度边界情况极端词频时不合理饱和机制保证稳定Elasticsearch支持已废弃不再默认5.0默认评分算法自定义参数DSLtype: tfidf通过similarity配置七、实战搜索iPhone 14的完整执行过程让我们通过一个具体案例完整追踪一条搜索请求从发出到返回结果的全过程。场景设定假设我们有一个商品索引shop包含以下文档// 索引MappingPUT/shop{mappings:{properties:{title:{type:text,analyzer:standard,similarity:BM25},description:{type:text,analyzer:standard},brand:{type:keyword},price:{type:double}}}}// 插入测试数据POST/shop/_bulk{index:{_id:1}}{title:iPhone 14 Pro Max 256GB,description:Apple最新旗舰手机 iPhone 14 Pro Max,brand:Apple,price:8999}{index:{_id:2}}{title:iPhone 14 Plus 128GB,description:大屏iPhone 14 Plus 超长续航,brand:Apple,price:6999}{index:{_id:3}}{title:iPhone 14 标准版,description:年度主打iPhone 14 超强性能,brand:Apple,price:5999}{index:{_id:4}}{title:华为Mate 60 Pro,description:华为旗舰拍照手机 卫星通信,brand:Huawei,price:6999}{index:{_id:5}}{title:小米14 Ultra,description:小米14旗舰 徕卡影像,brand:Xiaomi,price:5999}执行搜索GET/shop/_search{query:{multi_match:{query:iPhone 14,fields:[title^2,description],operator:and}},explain:true}完整执行过程追踪═══════════════════════════════════════════════════════ 搜索 iPhone 14 完整执行过程 ═══════════════════════════════════════════════════════ 第1步接收请求协调节点 ┌─────────────────────────────────────┐ │ 协调节点收到请求: iPhone 14 │ │ - operator: AND │ │ - 字段权重: title^2, description^1 │ └─────────────────────────────────────┘ 第2步查询分析Query Parsing ┌─────────────────────────────────────┐ │ 分词器处理查询字符串 iPhone 14 │ │ → 得到两个Term: iphone, 14 │ │ │ │ 语法分析构建查询树: │ │ BooleanQuery (must/AND) │ │ ├─ TermQuery(iphone) │ │ │ 在 title^2, description^1 │ │ └─ TermQuery(14) │ │ 在 title^2, description^1 │ └─────────────────────────────────────┘ 第3步反向索引查找Term Lookup ┌─────────────────────────────────────┐ │ 倒排索引查找: │ │ Term iphone → Posting List: │ │ title字段: [doc1, doc2, doc3] │ │ desc字段: [doc1, doc2, doc3] │ │ │ │ Term 14 → Posting List: │ │ title字段: [doc1, doc2, doc3, doc5] │ │ desc字段: [doc2] │ └─────────────────────────────────────┘ 第4步文档列表合并AND操作 ┌─────────────────────────────────────┐ │ 多字段合并后的文档列表: │ │ iphone 匹配: [doc1, doc2, doc3] │ │ 14 匹配: [doc1, doc2, doc3, │ │ doc5] │ │ AND 交集结果: [doc1, doc2, doc3] │ │ │ │ doc4: 未匹配 iphone → 淘汰 │ │ doc5: 14 只在title出现但desc没有 │ │ iphone → 淘汰AND关系 │ └─────────────────────────────────────┘ 第5步BM25评分计算 ┌─────────────────────────────────────┐ │ 对 doc1, doc2, doc3 逐一计算Score: │ │ │ │ doc1 (iPhone 14 Pro Max 256GB): │ │ IDF(iphone) × TF饱和 × 长度归一化 │ │ IDF(14) × TF饱和 × 长度归一化 │ │ 较高分两个词都密集出现在短标题中 │ │ │ │ doc2 (iPhone 14 Plus 128GB): │ │ 同理计算 高分 │ │ │ │ doc3 (iPhone 14 标准版): │ │ 同理计算 高分 │ └─────────────────────────────────────┘ 第6步排序与返回 ┌─────────────────────────────────────┐ │ 按Score降序排列: │ │ 1. doc1: score2.85 (标题两次命中权重) │ │ 2. doc2: score2.63 │ │ 3. doc3: score2.41 │ │ │ │ 返回Top N结果给客户端 │ └─────────────────────────────────────┘使用_explainAPI 查看评分细节// 查看特定文档的评分明细GET/shop/_explain/1{query:{multi_match:{query:iPhone 14,fields:[title^2,description],operator:and}}}// 该接口返回详细的计算过程包括每个Term的IDF值、TF值、// 字段长度、k1/b参数以及最终加权得分。八、总结与最佳实践核心要点回顾搜索引擎通过倒排索引实现O(1)级别的关键词查找将非结构化文本转化为结构化的 “Term→文档列表” 映射。文本分析是搜索质量的基础字符过滤器→分词器→词元过滤器的三层架构决定了哪些Term能被搜索到务必在写入前通过_analyzeAPI 验证分词效果。查询树结构决定了文档列表的合并策略AND关系取交集精确匹配OR关系取并集广泛召回根据业务场景合理选择。BM25通过TF饱和度k1和字段长度归一化b两大改进解决了TF-IDF的两个核心缺陷是更合理、更通用的评分算法。最佳实践清单实践建议详细说明显式指定analyzer每个text字段在Mapping中明确指定analyzer不依赖默认值写入前验证分词使用POST _analyze测试分词效果确保与预期一致搜索时使用相同分析器搜索时用search_analyzer指定确保搜索结果准确性合理选择AND/OR精确匹配用AND广泛召回用OR高精度场景用minimum_should_match调优BM25参数根据文档长度分布特征调整k1和b长短文本混合场景适当降低k1使用explain调试_explainAPI 可查看每篇文档的详细评分过程是调优的利器权重设置对multi_match使用^符号为不同字段设置权重如title^3上一篇【第42篇】Elasticsearch倒排索引原理——Lucene的核心数据结构下一篇【第44篇】Elasticsearch分布式索引原理——分片路由与写入流程
http://www.rkmt.cn/news/1393766.html

相关文章:

  • 联邦学习在网络威胁情报共享中的应用:FedScope系统设计与实践
  • 如何使用stremio-addons-list:新手必备的Stremio插件发现平台
  • Spring Modulith 事件驱动架构:模块间通信的最佳实践
  • 【收藏】2026年版:AI Coding崛起仅3年,程序员职场格局彻底改写!
  • Claude Code用户如何配置Taotoken解决密钥被封与Token不足困扰
  • 如何用Qwen-Agent构建企业级文档智能问答系统:终极实战指南
  • 为行为不一致的AI设计用户界面:从确定性交互到引导式协作
  • 三分钟完成taotoken的python sdk配置并调用首个聊天补全
  • ComfyUI-WD14-Tagger与Hugging Face模型库:深入理解12个预训练模型的特点与选择
  • FastAPI权限控制架构解析:基于声明式ACL的行级安全深度实践
  • 基于微信小程序实现外卖商城平台管理系统【附项目源码+论文说明】
  • 如何实现网易云音乐插件管理器自动化部署,优化客户端扩展生态
  • 探索智能歌词匹配:打造个性化音乐体验的完整方案
  • 键盘打字总出“鬼影字符“?这个开源神器让你1分钟告别按键连击烦恼![特殊字符]
  • 智能体:数字世界的“自驱者”
  • AI生成内容学术化重构全流程,从Prompt设计到语义熵压缩——教育部科技查新站认证的3级降重范式
  • Tiptap无头编辑器:现代Web内容创作的技术革命
  • 如何快速掌握Scikit-Fuzzy:面向开发者的终极模糊逻辑实战指南
  • GIS新手看过来:用Anaconda创建独立环境,手把手教你安装geemap玩转Google Earth Engine
  • 融合区块链与联邦学习的物联网分布式资源分配方法DRAM-BFL解析
  • LSTST:用语言支架让大模型理解时间序列分类
  • Git 文件状态管理:add、commit、status 和 diff
  • 主题页面滚动监听:everfu/hexo-theme-solitude实现导航栏动态样式变化
  • 10分钟学会使用Changchun_Ascend/bert-large-uncased:从安装到推理的完整指南
  • 学科竞赛管理(源码+论文)
  • HIMA Z6013 999601102电源模块
  • 第十六章:企业Agent应用案例
  • 基于跨模态语义嵌入的对抗样本检测:原理、实现与实战分析
  • Hima Z6018 999601802 印刷电路板
  • Python调用阿里云短信服务发送短信/验证码