小数据量用strings.contains+filepath.walkdir,中等规模优先sqlite fts5,本地全文索引推荐bleve;勿自行实现倒排索引或硬套elasticsearch,需注意编码归一化、文件类型过滤、路径排除、id稳定性、查询dsl规范、文本预处理及索引实时更新。

小数据量(几百个文件以内)直接用 strings.Contains + filepath.WalkDir 就够了;中等规模(几千文件以上)优先选 SQLite FTS5;真要建本地全文索引,bleve 比 bluge 更成熟、文档更全、踩坑更少。
为什么别一上来就写倒排索引或硬套 Elasticsearch
自己解析分词、维护词频、处理同义词和停用词,等于重造轮子——Go 标准库没提供分词器,regexp 或 strings.Split 做不出 “running” 匹配 “run” 的效果。而 olivere/elastic 已归档,go-elasticsearch/v8 强制 TLS 和认证,body 构造错一个字段就 400,连中文分词都要手动配 analyzer 字段,调试成本远超收益。
- 搜 “café” 报
invalid UTF-8 sequence?说明没做编码归一化 - 搜 “test.go” 返回一堆二进制文件结果?没过滤
mimetype或文件头 - 索引目录里有
.git或node_modules?遍历时没加路径排除逻辑
bleve 索引 ID 必须稳定,不能用文件路径
bleve.New 要求索引路径是空目录,bleve.Index 的 Index 方法必须传唯一 ID。很多人直接把 filepath.Abs(path) 当 ID,结果 Windows 路径含 \、URL 编码后含 %20,导致后续查询失败或索引损坏。
- 绝对不要用
filepath.Base(path)—— 多个README.md会覆盖同一 ID - 推荐用内容哈希 + 修改时间:例如
fmt.Sprintf("%x-%d", sha256.Sum256(content)[:8], fi.ModTime().Unix()) - 如果已有数据库,ID 可直接用主键,但必须确保文件变更时能同步更新索引
搜索时别手拼 JSON 查询 DSL
用 map[string]interface{} 写查询结构体看着灵活,实际极易出错:fuzziness 写成 fuzzy、整数 1 传成字符串 "1",bleve 不报错但查不到结果。
- 一律用
bleve.NewQueryStringQuery(userInput)—— 它自动处理引号、AND/OR、通配符te?t、模糊roam~ - 高亮需额外调
search.Highlight并传入字段名,不是默认开启 - HTML 或 Markdown 文件务必先用
blackfriday或html2text提纯文本,再喂给bleve
SQLite FTS5 是中等规模最省心的选择
几千条文本文件,建一张带 FTS5 的表,插入就是 INSERT INTO docs(content) VALUES(?),搜索就是 SELECT * FROM docs WHERE content MATCH 'keyword'。不用运维、不占额外内存、响应稳定。
- 别写成
WHERE content LIKE '%keyword%'—— FTS5 不支持LIKE,静默返回空 - 用
GORM必须走Raw(),Where()不识别MATCH语法 - 大文本字段(如
content)别和id、path一起SELECT *,只查需要的字段防传输拖慢
真正容易被忽略的是:索引不是一劳永逸的。文件移动、重命名、修改后,必须触发对应 ID 的更新或删除,否则搜出来的路径早就不存在了——这个逻辑比建索引本身还关键。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











