ES 与 Milvus 结合实现高效文档搜索的实战指南
目录
- 背景介绍
- 场景与效果概述
- 架构对比与问题分析
- Milvus 向量搜索架构
- ES + Milvus 搜索架构
- 详细流程解析
- Milvus 搜索配置详解
- ES 搜索策略与 DSL 配置
- 结果合并与排序策略
- 总结与未来优化
1. 背景介绍
随着团队和公司的发展,积累了大量各类文档,如技术分享文档、接口设计文档、业务对接文档、教程、Changelog 等。文档数量的增加和用户数量的增长使得高效的文档搜索功能变得至关重要。高效的搜索功能能够帮助用户快速查找和定位所需内容,显著提升工作效率。
Elasticsearch(ES) 擅长关键词匹配,适用于基于关键词的全文搜索。然而,随着语义搜索需求的增加,ES 在处理同义词、近义词等语义相近的内容时表现欠佳。
Milvus 是一个高性能的向量数据库,具备强大的语义搜索能力,能够基于向量的相似性进行检索,弥补 ES 在语义搜索上的不足。
本博客介绍了如何结合使用 ES 和 Milvus,充分发挥它们各自的优势,实现高效且准确的文档搜索。
2. 场景与效果概述
当前场景
- 文档数量:约 8 万篇文档。
- 文档类型:技术分享、接口设计、业务对接、教程、Changelog 等。
- 用户反馈
- 仅使用 ES 时,无法有效搜索同义词、近义词或语义相近的词语,影响搜索质量。
- 仅使用 Milvus 时,用户通过直接搜索代码、API 无法找到对应文档。
效果指标
- 搜索点击率:约 65%。
3. 架构对比与问题分析
单独使用 ES 的问题
- 语义搜索能力有限:无法有效处理同义词、近义词等语义相近的内容,导致搜索结果不准确。
- 用户体验不佳:用户在搜索时可能找不到相关的文档,影响工作效率。
单独使用 Milvus 的问题
- 关键词匹配不足:用户通过直接搜索代码、API 名称等关键词时,无法找到对应文档。
- 非中文场景下的搜索不准确:在处理非中文内容时,搜索效果欠佳。
结合使用 ES + Milvus 的优势
- ES 擅长关键词匹配:能够快速找到包含特定关键词的文档。
- Milvus 擅长语义搜索:能够根据向量相似性找到语义相近的文档。
- 互补优势:通过结合两者的能力,提升整体搜索效果和用户体验。
4. Milvus 向量搜索架构
单独使用 Milvus 的搜索流程
- 用户请求:用户提交搜索关键词。
- 特征化处理:后端将关键词转换为向量(词嵌入)。
- 向量搜索:调用 Milvus 的向量搜索接口,获取相似文档。
- 自定义评分:根据文档类型(如标题、正文)给予不同权重。
- 返回结果:将处理后的搜索结果返回给用户。
自定义评分示例
- 标题:权重系数为 2。
- h2 类型标题:权重系数为 1.5。
- 正文:权重系数为 1。
5. ES + Milvus 搜索架构
结合使用 ES + Milvus 的搜索流程
-
用户请求:用户提交搜索关键词。
-
特征化处理:后端将关键词转换为向量(词嵌入)。
-
检索阶段
:
- Milvus 向量搜索:基于向量数据进行相似性搜索。
- ES 全文检索:通过 DSL(Domain Specific Language)进行关键词匹配搜索。
-
数据召回阶段
:
- 自定义评分调整:根据文档类型给予不同权重。
- 结果合并与排序:归一化处理、去重、重新评分后排序。
-
返回结果:将整合后的搜索结果返回给用户。
架构图说明
- 左部分:数据清洗与落库。
- 右部分:用户搜索流程,重点关注检索与召回阶段。
6. 详细流程解析
用户提交查询阶段
- 词嵌入处理:将用户的搜索关键词通过词嵌入模型转换为向量。
- 生成 ES 搜索 DSL:根据搜索需求生成 ES 的搜索查询语句。
检索阶段
-
Milvus 向量搜索
:
- 基于向量数据进行相似性搜索,获取相关文档。
-
ES 全文检索
:
- 使用生成的 DSL 对文档进行关键词匹配搜索,获取相关文档。
数据召回阶段
-
自定义评分调整
:
- 根据文档类型(如标题、正文)给予不同的权重系数。
-
合并结果
:
- 对 Milvus 和 ES 的搜索结果进行归一化处理。
- 去重处理,避免同一文档重复出现。
- 重新计算分数,根据调整后的评分进行排序。
-
关键词高亮
:
- 对 ES 返回的结果进行关键词高亮显示,提升用户体验。
结果返回用户
- 高亮显示:向量搜索结果不具备关键词高亮,需要通过 ES 的搜索结果进行实现。
- 最终返回:将合并、排序后的搜索结果返回给用户。
7. Milvus 搜索配置详解
使用 Node.js SDK 调用 Milvus 搜索
以下是一个使用 Node.js SDK 调用 Milvus 搜索的示例代码:
javascript
await app.milvus.search({
collection_name: collection, // 集合名称
partition_names, // 分区名称
vector: text_embs, // 向量数据
params: { nprobe: 16 }, // 搜索精度相关参数
metric_type: searchConfig.metric_type, // 度量方式
limit: limit ?? searchConfig.limit, // 查询数量
timeout: searchConfig.timeout, // 超时时间
offset: 0, // 从第0条开始查询
filter: filter ?? null, // 过滤条件
});
配置参数说明
collection_name
:指定要搜索的 Milvus 集合。partition_names
:因为文档分为中、英、日三种语言,所以根据用户当前搜索的语言选择相应的分区。vector
:搜索向量数据,即用户输入的关键词经过词嵌入生成的向量。params.nprobe
:用于 IVF 检索类型,影响搜索精度和耗时。值越大,搜索精度越高,但耗时也增加。metric_type
:度量方式,常用的有IP
(内积)和L2
(欧氏距离)。limit
:每次查询返回的结果数量。timeout
:查询的超时时间。filter
:一些过滤条件,例如只搜索特定类型的文档。
向量搜索的策略
-
分区存储:根据文档的语言(中、英、日)进行分区存储,提升搜索准确性。
-
评分策略
:
- Milvus 评分:对搜索结果进行归一化处理,根据分数进行调整。
- ES 评分:通过自定义系数提升 ES 的搜索结果排名。
8. ES 搜索策略与 DSL 配置
Elasticsearch DSL 配置
ES 使用 DSL(Domain Specific Language)进行搜索配置,主要包括四部分:
- 标题搜索 DSL
- 正文搜索 DSL
- 代码块搜索 DSL
- 增强搜索 DSL
此外,还包括关键词高亮的 DSL 配置。
搜索关键字配置
match
和match_phrase_prefix
:- 标题 :主要使用
match
和match_phrase_prefix
。由于标题字符较少,配置minimum_should_match: '80%'
,允许拆词后命中 80%。同时配置slop: 2
,允许 2 个词的偏移。
- 标题 :主要使用
match_phrase
:- 正文 :仅配置
match_phrase
,要求完全匹配用户搜索词,因为正文内容较多,且用户通过正文直接搜索的场景较少。
- 正文 :仅配置
match_phrase_prefix
和wildcard
:- 代码块 :使用
match_phrase_prefix
和wildcard
,适用于用户通过方法名、API 接口路径等关键词搜索代码块。
- 代码块 :使用
- 增强搜索 :
- 针对非中文搜索,配置
wildcard
关键字,提升检索命中率。
- 针对非中文搜索,配置
DSL 配置示例
javascript
const DSL = {
query: {
bool: {
filter: [{ term: { language } }],
should: [
...getTitleDSL(), // 标题
...getContentDSL(), // 正文
...getCodeDSL(), // 代码块
...enhanceApiSearchDSL(), // API 增强
...enhanceNumberSearchDSL(), // 纯数字增强
],
},
},
highlight: getHighlightDSL(),
from,
size,
_source: {
exclude: ['h*.*'],
},
min_score: 1.0
};
配置策略
- 标题 :
- 使用
match
和match_phrase_prefix
。 minimum_should_match: '80%'
:拆词后命中 80% 即可。slop: 2
:允许 2 个词的偏移。
- 使用
- 正文 :
- 使用
match_phrase
。 - 仅当用户完全匹配搜索词时,才算命中。
- 使用
- 代码块 :
- 使用
match_phrase_prefix
和wildcard
。 - 针对方法名、API 接口路径等关键词进行模糊匹配。
- 使用
- 增强搜索 :
- 针对非中文搜索词,额外配置
wildcard
关键字,提升检索精度。
- 针对非中文搜索词,额外配置
性能优化
- 避免使用耗时的检索配置:
- 例如,避免使用
wildcard
和fuzzy
,减少搜索超时问题。 - 根据业务需求合理配置 DSL,确保搜索效率。
- 例如,避免使用
9. 结果合并与排序策略
数据合并流程
- 确定数据比例 :
- 根据场景决定 ES 和 Milvus 的数据占比。
- 例如,正常情况下 ES 与 Milvus 的结果比例为 6:4。
- 搜索词为变量名、纯数字等情况时,调整为 8:2。
- 评分归一化 :
- 由于 Milvus 和 ES 的评分体系不同,需要进行归一化处理。
- 每组数据的分数分别除以其最高分,统一评分标准。
- 精细化评分处理 :
- ES 结果:乘以一个大于 1 的系数,确保 ES 的结果在合并后排名优先。
- Milvus 结果:当 Milvus 搜索结果的分数(归一化前)超过 0.7 时,额外加分,提升其排名。
- 数据去重与合并 :
- 合并 ES 和 Milvus 的搜索结果。
- 去重处理,避免同一文档重复出现。
- 根据最终评分排序,确保高相关性的结果排在前列。
示例代码
javascript
// Milvus 搜索调用示例
await app.milvus.search({
collection_name: collection, // 集合名称
partition_names, // 分区名称
vector: text_embs, // 向量数据
params: { nprobe: 16 }, // 搜索精度相关参数
metric_type: searchConfig.metric_type, // 度量方式
limit: limit ?? searchConfig.limit, // 查询数量
timeout: searchConfig.timeout, // 超时时间
offset: 0, // 从第0条开始查询
filter: filter ?? null, // 过滤条件
});
配置参数说明
partition_names
:根据文档的语言(中、英、日)选择相应的分区,提升搜索准确性。nprobe
:用于 IVF 检索类型,控制搜索的广度和精度。值越大,精度越高,但耗时增加。metric_type
:度量方式,如IP
(内积)或L2
(欧氏距离)。limit
和offset
:一次性取多条数据,后端实现分页。filter
:如只搜索某个父类文档中的子文档,通过过滤条件缩小搜索范围。
分页处理
由于在文档存储阶段将长文档切割成多个片段并进行词嵌入,直接通过 Milvus 进行分页可能导致数据重复。因此,采用一次性取多条数据并在后端实现分页,确保用户体验和数据准确性。
性能优化
- Milvus 检索速度:在当前数据量下,Milvus 的检索速度非常快,基本在几百毫秒内返回结果。
- 资源配置 :合理配置
nprobe
和搜索参数,平衡搜索精度和性能。
10. 总结与未来优化
关键要点总结
- ES 与 Milvus 的互补优势 :
- ES:擅长关键词匹配,适用于基于关键词的全文搜索。
- Milvus:擅长语义搜索,能够基于向量相似性找到语义相近的文档。
- 结合使用:通过结合两者的优势,提升整体搜索效果和用户体验。
- 搜索架构设计 :
- Milvus 辅助 ES:主要通过 Milvus 进行语义搜索,ES 进行关键词匹配,最终合并结果。
- 评分策略:通过自定义评分调整,确保 Milvus 和 ES 的结果合理排序。
- 技术实现细节 :
- 向量嵌入:使用词嵌入模型将搜索关键词转换为向量。
- ES DSL 配置:根据文档类型(标题、正文、代码块)配置不同的搜索策略,提升搜索效率。
- 结果合并与去重:通过归一化处理和去重,确保最终返回的搜索结果准确且不重复。
- Milvus Lite 的使用限制 :
- 仅支持 FLAT 索引类型。
- 不支持分区及用户管理等高级功能。
未来优化建议
- 深入优化 ES 的 DSL 配置 :
- 针对不同的搜索场景,进一步优化 ES 的 DSL 配置,提高搜索的精度和效率。
- 避免使用耗时的搜索关键字,如
wildcard
和fuzzy
,减少搜索超时问题。
- 模型优化与调优 :
- 尝试不同的词嵌入模型,提升 Milvus 的语义搜索效果。
- 根据业务需求,调整向量维度和搜索参数,优化搜索性能。
- 扩展 Milvus 的功能 :
- 如果业务需要,考虑使用 Milvus 服务器部署,支持分区、索引类型扩展等高级功能。
- 实现多语言支持,提升非中文场景下的搜索效果。
- 增强评分与排序策略 :
- 根据用户反馈和数据分析,优化评分系数和排序策略,提升搜索结果的相关性。
- 引入更多的评分维度,如文档的新旧程度、访问频率等,丰富评分体系。
- 用户体验提升 :
- 实现关键词高亮显示,提升用户在搜索结果中的阅读体验。
- 增加搜索结果的分类和过滤功能,帮助用户更快速地找到所需文档。
- 监控与维护 :
- 实时监控 ES 和 Milvus 的运行状态,及时发现和解决性能瓶颈。
- 定期备份 Milvus 数据,确保数据安全和可恢复性。
总结
通过结合 Elasticsearch 和 Milvus 的优势,本文展示了如何实现一个高效且准确的文档搜索系统。尽管目前的搜索点击率约为 65%,但相比单独使用 ES 或 Milvus,已经有了显著的提升。未来,通过持续优化搜索策略、模型和架构设计,预计搜索效果和用户体验将进一步提升。
参考资源
- Milvus 官方文档 :Milvus Docs
- Milvus Migration 工具文档 :Milvus Migration Tool
- Milvus GitHub 仓库 :Milvus GitHub
- Elasticsearch 官方文档 :Elasticsearch Docs
- LangChain 与 Milvus 集成示例 :LangChain Milvus Integration