SiYuan v2.10.0 版本解析:资源文件内容搜索的落地与源码实现
【免费下载链接】siyuanAn open-source, privacy-first, self-hosted knowledge workspace where humans and AI agents work together 开源、隐私优先、自托管的知识工作空间,让人与智能体在此协作项目地址: https://gitcode.com/GitHub_Trending/si/siyuan
SiYuan v2.10.0 是该版本线中的关键节点:它首次引入了“资源文件内容搜索”能力,使全文搜索从笔记块扩展到.txt、.md、.docx、.xlsx、.pptx等附件文件内部。本篇以 v2.10.0 变更日志 为主体,完整梳理该版本的 Feature / Enhancement / Bugfix / Refactor / Development 变更条目,并结合当前仓库中 kernel/model/asset_content.go、kernel/sql/asset_content.go、kernel/api/search.go 等源码,讲清这一核心功能从“文件解析 → FTS 索引 → 搜索接口”的完整实现链路。
版本概览
根据官方变更日志,v2.10.0 的核心特性是支持搜索资源(asset)文件的内容,初始支持的格式为:
.txt.md.docx.xlsx.pptx
该功能对应上游 issue #8874,在当时属于会员付费特性(处于早鸟价阶段,官方说明见 v2.10.0.md)。注意两点适用前提:
- 付费限制是当时版本阶段的商业化策略,本文聚焦于技术实现本身;
- 当前仓库代码中的解析器覆盖面已远超 v2.10.0 发布时的五种格式(详见后文),阅读源码时应以仓库实际内容为准。
核心功能:资源内容搜索的源码实现
1. 独立的内容索引库与 FTS5 表
资源内容搜索没有复用笔记块的 FTS 索引,而是使用独立的数据库与虚拟表。从 kernel/sql/database.go 可以看到建表语句:
CREATE VIRTUAL TABLE asset_contents_fts_case_insensitive USING fts5( id UNINDEXED, name, ext, path, size UNINDEXED, updated UNINDEXED, content, tokenize="siyuan case_insensitive" );关键点:
- 表名
asset_contents_fts_case_insensitive明确表明采用大小写不敏感的 FTS5 索引; id、size、updated列标记为UNINDEXED,仅存储不参与倒排检索,而name、ext、path、content参与分词索引,与 kernel/sql/asset_content.go 中的插入语句INSERT INTO asset_contents_fts_case_insensitive (id, name, ext, path, size, updated, content)一一对应;tokenize="siyuan case_insensitive"是内核自定义的 tokenizer(对中文等 CJK 场景做适配),保证中英文混合内容的检索效果。
2. 解析器体系:每种格式一个 Parser
NewAssetsSearcher() 注册了完整的扩展名到解析器的映射表。v2.10.0 初始支持的 txt/md/docx/xlsx/pptx 在其中清晰可辨:
parsers: map[string]AssetParser{ ".txt": txtAssetParser, ".md": txtAssetParser, ".markdown": txtAssetParser, // ... 还有 .json/.log/.sql/.html/.xml/.java/.go/.py/.js/.css/.ts/.sh 等大量源码与文本扩展名 ".docx": &DocxAssetParser{}, ".pptx": &PptxAssetParser{}, ".xlsx": &XlsxAssetParser{}, ".pdf": &PdfAssetParser{}, ".epub": &EpubAssetParser{}, },各解析器实现要点(均在 kernel/model/asset_content.go):
| 解析器 | 实现方式 | 关键约束 |
|---|---|---|
TxtAssetParser | 直接读取文件文本 | 超过TxtAssetContentMaxSize(4MB)跳过;非 UTF-8 编码文件不纳入索引(utf8.Valid校验,见 L543-L564) |
DocxAssetParser | docconv.ConvertDocx转纯文本后归一化 | 仅处理.docx后缀 |
PptxAssetParser | docconv.ConvertPptx转纯文本后归一化 | 仅处理.pptx后缀 |
XlsxAssetParser | excelize.OpenFile遍历所有 Sheet 的每个单元格拼接 | 逐行逐列写入缓冲区 |
PdfAssetParser | PDFium WebAssembly 实例池逐页抽取文本 | 移动端不支持(util.IsMobileContainer()直接返回);页数上限 1024 页(PDFAssetContentMaxPage);体积上限 128MB(PDFAssetContentMaxSize,可用环境变量SIYUAN_PDF_ASSET_CONTENT_INDEX_MAX_SIZE覆盖,见 L863-L878) |
所有解析前都会经过copyTempAsset(L578-L600):把资源文件加文件锁后复制到临时目录再解析,避免解析过程中文件被并发写入或处于~开头的临时状态。normalizeNonTxtAssetContent(L573-L576)则用strings.Fields折叠 docx/pptx/xlsx/pdf 转换出的多余空白,保证索引文本紧凑。
3. 索引写入:先删后插 + 异步队列
单文件索引入口 indexAssetContent 的流程是:按扩展名取解析器 → 解析内容 →os.Stat取大小与修改时间 → 生成工作空间相对路径(形如assets/xxx.docx)→ 入队“按路径删除”与“批量插入”两个操作:
sql.DeleteAssetContentsByPathQueue(p) sql.IndexAssetContentsQueue(assetContents)入队只是追加内存队列(见 kernel/sql/queue_asset_content.go),真正的落库由定时任务周期性提交:kernel/job/cron.go 中go every(util.SQLFlushInterval, sql.FlushAssetContentTxJob)周期性调用 FlushAssetContentQueue。该函数有几个值得注意的工程细节:
- 每条操作独立开事务,失败时回滚并通过
eventbus.Publish(util.EvtSQLAssetContentRebuild)触发全量重建(订阅方是 ReindexAssetContent); - 插入采用 512 条一批的批量
INSERT(insertAssetContents); - 处理超过 16 条后每 128 条调用一次
debug.FreeOSMemory(),长事务超过 7 秒会打日志,属于典型的批处理内存与耗时治理。
此外,资源文件被删除时由 removeIndexAssetContent 入队删除操作;/api/asset/fullReindexAssetContent(路由注册见 kernel/api/router.go)提供手动全量重建入口,走task.AssetContentDatabaseIndexFull任务队列,先sql.InitAssetContentDatabase(true)重置库再全量遍历assets目录(FullIndex)。
4. 搜索 API:四种查询方式与分页参数
搜索入口POST /api/search/fullTextSearchAssetContent由 fullTextSearchAssetContent 处理,参数解析逻辑在 parseSearchAssetContentArgs:
| 参数 | 默认值 | 说明 |
|---|---|---|
page | 1 | 页码,小于 1 时回退为 1 |
pageSize | 32 | 每页条数,小于等于 0 时回退为 32 |
query | - | 查询内容 |
types | 空 | 按扩展名过滤的布尔映射(ext IN (...)) |
method | 0 | 0:关键字;1:查询语法;2:SQL;3:正则表达式 |
orderBy | 0 | 0:按相关度降序;1:按相关度升序;2:按更新时间升序;3:按更新时间降序 |
method与orderBy到 SQL 的映射在 kernel/model/asset_content.go 与 buildAssetContentOrderBy 中:
- 关键字(0)/查询语法(1):走 FTS5 的
MATCH,列过滤固定为{name content}(即同时在文件名与正文中匹配,见buildAssetContentColumnFilter),并用snippet(..., 64)截取 64 字符的上下文片段; - SQL(2):直接执行用户语句并通过改写
select *为select COUNT(path)统计匹配总数; - 正则(3):退化为
name REGEXP '...' OR content REGEXP '...'的全表扫描(L151-L165),性能上弱于 FTS 路径。
匹配高亮的处理在 fromSQLAssetContent:先EscapeHTML转义,再把内核的高亮标记替换为<mark>/</mark>,前端直接渲染。另有POST /api/search/getAssetContent(按索引 ID 取单条,支持query/queryMethod高亮)与POST /api/search/getAssetContentByPath(按assets/xxx相对路径取完整内容原文),三者路由均见 kernel/api/router.go。
5. 边界与隐私约束
源码中有两处与“隐私优先”定位直接相关的边界处理:
- 加密笔记本:全量索引遍历时通过
IsEncryptedAssetPath跳过加密笔记本的 asset(密文不能进入搜索索引,也避免泄漏文件名集合,见 FullIndex 中的注释); - 只读角色(发布站点):三个搜索接口在只读角色上下文下都会经过
model.FilterAssetContentByPublishAccess过滤,无权限的资源内容直接置空(kernel/api/search.go)。
本版 Enhancement 条目梳理
变更日志列出的增强项逐条如下(均保留原始 issue/PR 编号以便溯源):
- 列表大纲圆点/编号支持点击放大(issue #3502)——列表标记区域的交互增强;
- 光标与选中块保持一致(issue #8918)——修复选中块后光标位置不一致的问题;
- 粘靠搜索与打开标签页按应用宽度自适应右侧打开(issue #8928);
- 改进桌面端内核启动检查(issue #8929);
- ↑/↓ 选择块遇到超级块(super block)时行为一致(issue #8930);
- 修复 Enter 后 Ctrl+Z 光标错位(issue #8935);
- 改进数据库 URL 列操作(PR #8937);
- 更新“设置 - 关于 - 版本下载链接”(issue #8947);
- 平板(Pad)端支持切换工作空间(issue #8948);
- 移动端文档树减少缩进(issue #8949);
- 启用 KaTeX 的 HTML 相关功能(PR #8951);
- 标题转换为文档时不再以标题名作为文档名(issue #8959);
- 中文环境下关闭公式警告(PR #8963);
- 设置 tooltip 最大高度(issue #8978);
- 新增显示/隐藏 dock 的快捷键配置(issue #8979)。
本版 Bugfix 与 Refactor 条目
Bugfix:
- 层级标签计数错误(issue #8915);
- 粘贴的代码块无法自动识别(issue #8934);
- 表格无法调整居中布局(issue #8938);
- 插件快捷键设置列表无法折叠(PR #8946)。
Refactor:
- 升级 Electron(issue #8952)。对应地,仓库中 app/electron/main.js 与 app/package.json 承载桌面端主进程与 Electron 依赖管理。
本版 Development 条目
面向插件开发者与集成方的开发侧变更:
- 新增插件事件总线
open-siyuan-url-plugin与open-siyuan-url-block(PR #8927):插件可通过这两个事件响应 siyuan URL 打开插件页/块的请求,是插件路由机制的补充; - 修复编辑器中
command.fileTreeCallback无法触发的问题(issue #8931); - 改进内核 API
/api/file/readDir,使其返回文件修改时间(issue #8945)——该改进对插件监听文件变化、按时间排序列目录等场景直接有用。
小结
v2.10.0 的核心价值在于把 SiYuan 的全文检索边界从“笔记块”推进到“附件文件内容”:独立的 FTS5 索引库(asset_contents_fts_case_insensitive)、按扩展名分派的解析器体系、先删后插的异步索引队列、以及支持关键字/查询语法/SQL/正则四种方式的搜索 API(fullTextSearchAssetContent),共同构成了这一能力的技术底座,相关实现可分别在 kernel/sql/database.go、kernel/model/asset_content.go、kernel/sql/queue_asset_content.go、kernel/api/search.go 中逐层验证。同时本版本在编辑器光标一致性、移动端/平板端体验、插件事件机制上的大量细节打磨,也体现了 SiYuan 在核心功能落地之外对日常使用体验的持续投入。
【免费下载链接】siyuanAn open-source, privacy-first, self-hosted knowledge workspace where humans and AI agents work together 开源、隐私优先、自托管的知识工作空间,让人与智能体在此协作项目地址: https://gitcode.com/GitHub_Trending/si/siyuan
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考