Elasticsearch 混合搜索业务流程:BM25 + kNN + 权限过滤
在知识库、企业文档搜索等场景中,只靠关键词搜索往往不够。比较常见的方案是使用 BM25 关键词检索 + kNN 向量检索 + 权限过滤 + Rescore 重排序,同时兼顾搜索准确率、语义理解和数据安全。
一、整体搜索流程
用户在搜索框输入一句话后,整个流程可以概括为:
用户输入 → 后端解析 → Embedding → Elasticsearch 混合检索 → 权限过滤 → Rescore → 返回结果
1. 前端提交查询
用户输入:
如何申请年假?
前端向后端发送:
{
"query": "如何申请年假?",
"userId": "10001"
}
后端根据用户身份获取:
- 用户 ID
- 用户组织
- 用户可访问的标签
这些信息主要用于后面的权限过滤。
二、生成查询向量
后端调用 Embedding 模型:
"如何申请年假?"
↓
Embedding Model
↓
[0.12, -0.35, 0.78, ...]
这组向量用来做语义搜索。
例如:
用户搜索:
如何申请年假?
可能匹配:
员工休假申请流程
请假审批制度
年假使用说明
即使文档里没有完全出现“如何申请年假”,只要语义接近,也可能被召回。
三、Elasticsearch 混合搜索
系统同时进行两种检索。
BM25:关键词搜索
BM25 是 Elasticsearch 常用的关键词相关性算法。
它主要考虑:
关键词出现频率
+
关键词稀有程度
+
文档长度
例如搜索:
年假申请
包含“年假”“申请”等关键词的文档会获得更高分。
BM25 擅长解决:
用户搜什么词
↓
文档里有没有这些词
kNN:语义搜索
kNN 根据 Embedding 向量之间的距离寻找最相似的文档。
可以简单理解为:
文本
↓
Embedding
↓
向量空间
↓
寻找距离最近的 K 个文档
例如:
如何申请年假?
即使文档写的是:
员工休假审批流程
因为语义接近,也可能被搜索出来。
因此:
BM25 = 找关键词相似
kNN = 找语义相似
两者结合通常比单独使用一种方式效果更稳定。
四、权限过滤
企业搜索最重要的一点是:
搜索得到,不代表用户有权限查看。
因此 Elasticsearch 查询中需要加入权限 Filter。
例如文档满足下面任意条件才能返回:
公开文档
OR
文档属于当前用户
OR
文档组织标签 ∈ 用户可访问组织
逻辑类似:
(public = true)
OR
(owner_id = userId)
OR
(org_tag IN userOrgTags)
这样可以直接在搜索阶段过滤无权限数据,而不是搜索完以后再过滤。
五、结果重排序
BM25 和 kNN 完成第一轮召回后,可以使用 Elasticsearch rescore 对 Top N 结果进一步排序。
整体思路:
BM25 得分
+
向量相似度得分
+
业务权重
↓
重新计算排名
↓
最终 Top K
例如可以适当提高:
- 标题命中的文档
- 最新文档
- 高质量知识库文档
- 与用户组织更相关的文档
最终返回给前端:
[
{
"title": "员工年假申请流程",
"score": 9.2
},
{
"title": "员工休假管理制度",
"score": 8.6
}
]
六、最终架构
整个搜索链路可以记成:
用户输入
↓
HTTP 请求
↓
后端解析用户信息
↓
Embedding 模型
↓
Query Vector
↓
Elasticsearch
├── BM25 关键词搜索
├── kNN 语义搜索
└── 权限 Filter
↓
Rescore 重排序
↓
Top K 文档
↓
返回前端
总结
这套搜索方案本质上解决三个问题:
BM25
解决“关键词是否匹配”
kNN
解决“语义是否相似”
权限 Filter
解决“用户是否能看”
再通过 rescore 对结果进行二次排序。
因此实际实现时,可以把搜索系统拆成四个核心模块:
Embedding → 混合召回 → 权限过滤 → 重排序
后续无论接知识库搜索、RAG,还是企业内部文档搜索,基本都可以沿着这条链路继续扩展。