Macro全文搜索管道深度解析:从Block到OpenSearch的索引与检索
【免费下载链接】macroMacro is a unified workspace for teams: email, chat, docs, tasks, agents, calls, and CRM — @-linked together with shared AI memory.项目地址: https://gitcode.com/GitHub_Trending/macro3/macro
Macro 是一款集邮箱、聊天、文档、任务、通话和 CRM 于一体的团队工作区,而它"50ms 内搜到一切"的全文搜索能力正是这套体验的核心。本文带你拆解 Macro 的搜索管道:每一条数据变化如何被索引进OpenSearch,以及搜索框如何一次检索邮件、文档、频道消息、通话纪要等所有类型的内容。整条管道可以概括为两句话:事件驱动写入索引,统一入口完成检索。
一、什么是 Block?全文搜索的统一起点 🧱
在 Macro 中,一切都是一个 Block(块):文档(md)、邮件、频道消息、Agent 会话、任务(project)、通话记录、CRM 公司……每种内容都被抽象成同一种数据结构,通过 @mention 互相引用。
这一设计对搜索的意义非常直接:一个索引就能覆盖所有类型,不需要为邮件、文档、聊天各建一套搜索。
块与块的引用、预览机制详见官方文档 blocks.mdx。
二、索引管道:把 Block 的变化推进 OpenSearch 🔍
1. 事件驱动的实时索引
负责"写入"的是 services/search_processing_service/(下称 SPS)。它订阅Kafka主题(如通话生命周期的macro.calls、Agent 会话的macro.agent_session_lifecycle)以及遗留的 SQS 队列,监听各实体数据的增删改事件。
这里有一个关键设计:Kafka 事件只是"失效提示",不是数据本身。每收到一条事件,SPS 都会回读主数据库中的最新状态,然后对整个文档做一次全量覆盖式 upsert(通话、Agent 会话等场景均是如此)。好处是:
- 重复投递天然安全(upsert 幂等)
- 改名、删除等变更最终收敛到当前持久化状态
- 删除操作则是幂等的 delete-by-query
架构与事件映射表的完整说明见 search_processing_service/README.md。
2. 文本提取:先让文件"可被搜索"
PDF、DOCX、Markdown 等二进制或带格式内容不能直接进索引。services/document_text_extractor/ 服务负责把文件解析成纯文本(并保留引用位置),供后续索引使用。SPS 内部同样内置了 parsers/(docx、markdown、pdf)解析器,保证索引内容与文档实际内容一致。
3. 回填与索引修复:索引"丢了"怎么办?
每个可索引实体都有一个内部回填接口,例如POST /internal/backfill/calls、POST /internal/backfill/emails,返回job_id后可轮询进度。空请求体即重灌全量,也可以只传指定 ID 修复单条记录。索引的映射(schema)统一由 create_indices.ts 管理,部署新映射前会先重建对应索引,避免旧文档残留。
三、检索管道:搜索框背后的 50ms ⚡
1. 统一入口:一个接口搜所有类型
负责"读取"的是 crates/search_service/。它对外只暴露一个统一搜索接口(POST /search),调用方可以按名称或正文检索,并可选择是否包含 CRM 结果;服务端再按实体类型(邮件、文档、频道、通话、Agent 会话……)拆分结果,并并行完成权限校验与结果富化。入口实现在 unified.rs。
2. 查询构建与高亮
crates/opensearch_client/ 把搜索参数翻译成 OpenSearch 查询 JSON,例如 unified.rs 中注入 1000 字符的高亮片段长度,并剔除不必要的正文回传以压缩响应体积。更底层的 DSL 拼装(bool 查询、多字段匹配、游标分页)由 crates/opensearch_query_builder/ 完成。
3. 权限过滤与产品体验
每条结果都会按当前用户的访问权限过滤(CRM 更是按需换取"能力凭证",无权限则返回空切片而非报错)。产品层面,按/打开搜索、cmd+k按名称查找、用标签的 Any/All 匹配模式收窄结果、再叠加 @某人 过滤——这些体验背后的实现细节见 search.mdx。
四、值得借鉴的三个设计 ⭐
- 事件只是提示,数据回读主库:索引永远收敛于数据库的最新状态,重复消费无害,排障也更简单。
- 读写彻底分离:写入由 SPS 独占,读取由 search_service 独占,两边都可以独立扩缩容。
- 单一索引 + 单一入口:类型过滤下沉到查询构建层,新增一种可搜实体只需加一个查询构建器,前端零改动。
五、从哪些文件开始阅读 📚
| 模块 | 路径 |
|---|---|
| 索引服务(Kafka 消费、回填) | services/search_processing_service/ |
| 文本提取(PDF/DOCX/MD) | services/document_text_extractor/ |
| 统一搜索 API | crates/search_service/ |
| OpenSearch 查询客户端 | crates/opensearch_client/ |
| 查询 DSL 构建器 | crates/opensearch_query_builder/ |
| 索引映射定义 | infra/stacks/opensearch/helpers/scripts/create_indices.ts |
| 产品文档(搜索/Block) | apps/docs/product/search.mdx · apps/docs/concepts/blocks.mdx |
总结:Macro 的全文搜索并不神秘——"Block 统一数据模型 + 事件驱动幂等索引 + OpenSearch 单索引统一检索"三者组合,才换来了在邮件、文档、通话、Agent 会话之间"一框搜遍、50ms 出结果"的体验。如果你想在自己的产品里复刻类似能力,不妨直接从 SPS 的事件消费与回填接口这两个模块读起。
【免费下载链接】macroMacro is a unified workspace for teams: email, chat, docs, tasks, agents, calls, and CRM — @-linked together with shared AI memory.项目地址: https://gitcode.com/GitHub_Trending/macro3/macro
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考