把 Elasticsearch 全文检索讲明白:倒排索引、IK 分词器和 BM25

Elasticsearch 全文检索的核心并没有想象中复杂。文本先经过分词建立倒排索引,查询时再通过关键词快速找到文档,最后使用 BM25 计算相关性并排序。中文场景下,再加上 IK 分词器。把这三件事串起来,ES 的检索主线就通了。

之前做 RAG 的时候,我们用 Milvus 做过向量检索。

向量检索有一个很明显的特点:

它更关心"意思像不像"。

比如用户问:

复制代码
怎么让 AI 记住以前聊过的内容?

知识库里可能根本没有这句话,而是:

复制代码
可以使用向量数据库存储大模型的长期记忆。

两个句子的字面内容差别很大,但语义比较接近,所以向量检索仍然可能把它找出来。

但有些东西并不适合只靠语义。

比如:

复制代码
Elasticsearch
IK 分词器
订单号
错误码
产品型号

这时候我们真正关心的往往就是:

这个关键词到底有没有出现。

这就是 Elasticsearch 擅长的场景:全文检索。


Elasticsearch 为什么搜文本这么快?

先看一个最简单的例子。

现在有三篇文档:

复制代码
doc1:正在学习 Elasticsearch

doc2:Elasticsearch 可以做全文检索

doc3:Milvus 可以做向量检索

现在搜索:

复制代码
Elasticsearch

最直接的办法当然是:

复制代码
检查 doc1 有没有 Elasticsearch
检查 doc2 有没有 Elasticsearch
检查 doc3 有没有 Elasticsearch

三篇文章完全没问题。

但如果不是三篇,而是:

yaml 复制代码
1000 万篇文章

每次查询都把所有文本重新扫描一遍,就很难接受了。

Elasticsearch 的思路不是"查询的时候再找",而是:

提前把以后搜索需要的信息整理好。

这个东西就是倒排索引。


什么是倒排索引?

还是刚才三篇文档。

先把里面的文本拆成一个个词。

比如:

复制代码
doc1:
学习
Elasticsearch

doc2:
Elasticsearch
全文检索

doc3:
Milvus
向量检索

然后建立这样的关系:

复制代码
Elasticsearch → doc1、doc2

全文检索 → doc2

Milvus → doc3

向量检索 → doc3

这就是倒排索引

它名字听起来有点唬人,其实理解起来非常简单。

正常情况下,我们习惯从文档出发:

复制代码
文档 → 里面有哪些词

倒排索引把这个关系反过来了:

复制代码
词 → 出现在哪些文档

所以叫"倒排"。

现在再搜索:

复制代码
Elasticsearch

ES 不需要重新扫描所有文章。

直接查:

复制代码
Elasticsearch → doc1、doc2

就知道这个词出现在哪些文档里。

如果用 JavaScript 粗略理解,大概就是:

ini 复制代码
const invertedIndex = {
  Elasticsearch: ["doc1", "doc2"],
  Milvus: ["doc3"],
  全文检索: ["doc2"],
  向量检索: ["doc3"],
};

console.log(invertedIndex["Elasticsearch"]);

输出:

css 复制代码
["doc1", "doc2"]

当然,真正的 Elasticsearch 底层远比这个复杂。

但理解 ES 时,先记住这一层就够了:

倒排索引本质上维护的是"词条 → 文档"的关系。

小结

所以 Elasticsearch 适合全文检索,核心原因并不是"它搜索算法更快",而是它提前为文本建立了倒排索引。

数据写入时,文本会被拆成一个个词条,并建立:

复制代码
词条 → 文档

这样的映射关系。

查询时就不需要再扫描所有原始文档,而是直接通过词条去倒排索引中找到对应文档。

可以把这一节压缩成一句话:

倒排索引解决的是:如何根据一个关键词,快速找到包含它的文档。

理解到这里,马上就会出现下一个问题:

既然倒排索引是围绕"词条"建立的,那一句中文到底应该怎么切成词?


中文到底怎么分词?

倒排索引依赖一个很重要的前提:

先把文本拆成词。

比如:

复制代码
Elasticsearch RAG 混合检索知识库

英文还比较好办。

中文就麻烦了。

如果直接变成:

复制代码
混
合
检
索
知
识
库

虽然也能建立索引,但这些单字的意义很弱。

我们真正想要的可能是:

复制代码
混合
检索
知识库
知识

所以对于 Elasticsearch 来说:

怎么分词,会直接影响后面建立出来的倒排索引。

这也是为什么中文场景经常会使用 IK 分词器。


IK 分词器是干什么的?

先把一句话记住:

IK 不负责搜索,它负责告诉 Elasticsearch 中文应该怎么切。

常见的两个模式是:

复制代码
ik_max_word

ik_smart

它们一般承担不同的任务。


ik_max_word:建索引的时候尽量多切

比如:

复制代码
知识库

ik_max_word 会倾向于尽可能拆出更多有意义的词。

可以粗略理解成:

复制代码
知识库
知识
......

为什么数据入库的时候要切得这么细?

因为现在我们还不知道用户以后会搜什么。

所以建立索引时,希望:

复制代码
能拆的尽量拆
↓
倒排索引里的词条更多
↓
用户未来搜索时更容易命中

也就是尽量提高:

召回率。

所谓召回率,可以先简单理解成:

本来应该被搜出来的内容,我到底搜到了多少。

所以:

复制代码
ik_max_word
→ 建索引的时候尽量多切
→ 提高未来被搜索到的机会

ik_smart:查询的时候别切得太碎

用户真正搜索的时候,情况又不一样。

假设用户输入了一句话。

如果继续疯狂拆成大量小词,那么确实可能搜出来很多文档。

问题是:

复制代码
搜出来很多

不等于:

复制代码
搜出来的都相关

拆得太碎,很容易产生很多无意义的匹配。

所以查询时一般使用:

复制代码
ik_smart

让查询词尽量保持比较合理的语义单位。

于是可以这样记:

复制代码
ik_max_word
→ 建索引
→ 尽可能多切
→ 提高"能不能搜到"

ik_smart
→ 查询
→ 尽量合理地切
→ 提高"搜到的是不是更相关"

映射到 Elasticsearch 的配置里就是:

json 复制代码
{
  "title": {
    "type": "text",
    "analyzer": "ik_max_word",
    "search_analyzer": "ik_smart"
  }
}

其中:

复制代码
analyzer
→ 数据写进去的时候怎么分词

search_analyzer
→ 用户查询的时候怎么分词

这个区别还是很值得记住的。


文档已经找到了,谁排第一?

到这里,搜索流程已经能跑起来了。

比如用户搜索:

复制代码
Elasticsearch 检索

通过倒排索引找到了:

复制代码
doc1
doc2
doc3
doc4
......

但现在又出现一个新问题:

这么多文档,到底谁应该排在前面?

总不能随机返回。

这就是 BM25 负责的事情。

可以先记:

复制代码
倒排索引
→ 负责找到候选文档

BM25
→ 负责判断谁更相关

BM25 本质上就是一个:

相关性打分算法。

它里面的数学公式可以以后再看。

刚开始理解它,只需要抓住三个思想。


第一:词出现得多,不代表可以无限加分

假设用户搜索:

复制代码
Elasticsearch

文章 A:

复制代码
Elasticsearch 是一个全文检索引擎。

文章 B:

复制代码
Elasticsearch Elasticsearch Elasticsearch
Elasticsearch Elasticsearch Elasticsearch......

如果简单按照:

复制代码
出现一次 +1 分

那就意味着,只要不停重复关键词,就能把自己的排名刷上去。

显然不合理。

所以 BM25 有:

词频饱和。

关键词多出现几次,确实能说明文章可能更加相关。

但是出现次数越来越多以后,额外带来的收益会越来越小。

大概可以理解成:

复制代码
出现 1 次 → 很有价值

出现 2 次 → 继续加分

出现 5 次 → 还会增加

出现 100 次 → 不会跟着暴涨

所以:

关键词出现次数越多,相关性通常会上升,但不会无限线性上涨。

这就避免了简单堆砌关键词。


第二:长文章不能天然占便宜

再看两个文档。

css 复制代码
文档 A:
100 个字
Elasticsearch 出现 3 次
css 复制代码
文档 B:
10000 个字
Elasticsearch 出现 3 次

都是出现三次。

但直觉上显然不完全一样。

A 一共才 100 个字,出现了三次 Elasticsearch。

说明它很可能就在集中讨论 Elasticsearch。

而 B 有一万字,出现三次可能只是顺带提了一嘴。

所以 BM25 还会考虑:

文档长度。

也就是做一定的长度归一化。

目的很简单:

不让长文章因为内容多、更容易碰巧出现关键词,就天然获得更高排名。

所以相同词频下,关键词在短文档中更加集中,通常会体现出更高的相关性。


第三:稀有词比常见词更有价值

比如:

复制代码
的
是
可以

这种词几乎到处都有。

如果一个文档命中了"的",其实并不能说明什么。

因为几乎所有文章都有。

但是:

复制代码
IK 分词器

这种词就不一样。

它比较少见。

一旦一篇文章里出现了它,就很可能和用户当前搜索的话题有关。

所以 BM25 还会考虑:

一个词到底有多稀有。

可以这样理解:

复制代码
常见词
→ 到处都有
→ 区分能力很弱
→ 权重低

稀有词
→ 很少出现
→ 区分能力强
→ 权重高

所以:

BM25 会给更有区分度的稀有词更高的权重。


把整条 Elasticsearch 检索流程串起来

到这里,ES 的核心流程其实已经很清楚了。

先看数据写入阶段:

复制代码
文章
↓
IK 分词
↓
ik_max_word
↓
拆成多个词条
↓
建立倒排索引

词条 → 文档

然后是查询阶段:

复制代码
用户输入
↓
ik_smart 分词
↓
得到查询词
↓
查询倒排索引
↓
找到候选文档
↓
BM25 计算相关性
↓
按照分数排序
↓
返回结果

所以 Elasticsearch 全文检索最核心的三个东西,其实可以压缩成三句话:

复制代码
IK 分词器
→ 解决"词怎么切"

倒排索引
→ 解决"文档怎么快速找到"

BM25
→ 解决"找到以后谁排前面"

这三件事一旦串起来,Elasticsearch 的核心基本就通了。


再回头看 Milvus 和 Elasticsearch

现在再看之前学过的 Milvus,就会发现它和 Elasticsearch 其实不是一回事。

Milvus 更偏向:

复制代码
用户问题
↓
Embedding
↓
向量
↓
计算向量相似度
↓
找到语义相近的内容

关注的是:

意思像不像。

而 Elasticsearch 更偏向:

复制代码
用户问题
↓
分词
↓
关键词
↓
倒排索引
↓
BM25 排序

关注的是:

关键词有没有准确命中。

所以两者并不冲突。

在 RAG 里完全可以同时使用:

markdown 复制代码
             用户问题
                ↓
        ┌───────┴───────┐
        ↓               ↓
 Elasticsearch        Milvus
 关键词检索            语义检索
        ↓               ↓
        └───────┬───────┘
                ↓
             合并结果
                ↓
               重排
                ↓
               LLM

一边解决关键词、专业术语、精确实体的问题。

另一边解决自然语言表达不同但语义相近的问题。

这也就是后面做混合检索的基础。


最后

第一次接触 Elasticsearch,会看到很多东西:

复制代码
Index
Document
Mapping
Analyzer
Query DSL
IK
BM25
......

很容易学着学着就散掉。

其实全文检索这一块,先抓住一条主线就够了:

文本先经过 IK 分词器切成词,通过倒排索引快速找到包含这些词的文档,再使用 BM25 计算相关性并排序。

或者再短一点:

IK 负责切词,倒排索引负责找文档,BM25 负责排顺序。

这句话记住以后,再回头看 Elasticsearch 的各种 API,很多东西就都有位置了。

相关推荐
远翔调光芯片^138287988721 小时前
ECP5702能芯科技PD取电芯片在市场上的优势有哪些?
开发语言·人工智能·单片机·嵌入式硬件·智能家居
东风破_1 小时前
从 RAG 到 Agentic RAG:第三步,本地知识不够就去网络搜索
人工智能
桃西西呀1 小时前
别被"秒回"骗了:推理模型背后那只"吞金兽",吃的是你看不见的预算
人工智能·llm·ai编程
龙亘川1 小时前
AI + 人社新范式:智慧人社系统如何为民生治理数字化难题提供帮助
人工智能·智慧城市·数据可视化·政务
水如烟1 小时前
孤能子视角:蓝星文明篇·市——交换机制的运行化:从偶发交换到日常运行的制度化
人工智能
技灵AI2 小时前
Wan 3.0 API怎么做多参考商品视频?从图片、视频、音频分工到30秒交付
人工智能·prompt·aigc·音视频·wan 3.0
武子康2 小时前
CLAUDE.md 引用 AGENTS.md 后,两边真的读到同一套规则吗?
人工智能·llm·agent
动恰客流统计2 小时前
线下零售数字化浪潮下,客流统计的3个核心发展趋势
大数据·前端·人工智能
johnsong2 小时前
效率的边界:当推理突破遇见语言革命
人工智能·语言模型