Expand description
Windows 本地全文搜索引擎的核心库。
索引层基于 tantivy 倒排索引,内容分词用 tokenizer 模块把文本按“汉字/
非汉字“切段:汉字段接 jieba 按中文习惯分词,非汉字段按字母数字切词并统一
小写。extract 模块负责文本抽取——纯文本用 chardetng/encoding_rs 探测
编码,另外支持 PDF、Office(docx/xlsx/pptx)等常见格式。ocr/ocr_worker
模块接入 Windows 系统自带的 OCR 引擎,让截图和图片里的文字也能被搜到
(仅 Windows;其余平台是诚实报“不可用“的桩实现)。mft/usn 模块在
NTFS 卷 + 管理员权限下走 MFT 快速枚举和 USN Journal 增量监听,跳过全盘
目录遍历;拿不到这个前提条件就诚实降级到基于 walkdir + notify 的目录
遍历/文件监听,两条路径产出的索引结果完全一致,调用方感知不到差别。
docs.rs 在 Linux 构建机上生成本页文档,cfg(windows) 之后的 API(OCR、
NTFS MFT/USN 快速索引)不会出现在这里;要看到完整 API,在 Windows 上本地
跑 cargo doc 即可。
§核心入口
- 建索引:
rebuild_index全量重建;多根索引的增删见add_root/remove_root。 - 搜索:
Searcher::open打开一个索引的只读句柄,Searcher::search执行查询(search_filtered/search_advanced支持扩展名过滤和排序)。 - 实时监听:
watch_roots_auto按卷能力自动选快车道或慢车道,持续把 文件系统变化落进索引。 - 启动对账:
reconcile,追平程序未运行期间发生的文件变化。 - OCR:
drain_ocr_queue一次性处理完当前排队的图片;OcrPipeline::start启动后台 worker 池持续处理新入队的图片。
§示例
建一次索引,然后在“文档类“扩展名里搜索、按修改时间从新到旧排,最后读出 每条命中的字段:
use std::path::Path;
use dowse::{Searcher, SortMode, display_path, ext_group_by_name, rebuild_index};
let index_dir = Path::new("./my-index");
let target_dir = Path::new("./my-documents");
// 全量建一次索引,拿到收录 / 跳过 / 耗时统计。
let stats = rebuild_index(index_dir, target_dir)?;
println!(
"收录 {} 个文件,跳过 {},耗时 {:.1}s",
stats.indexed, stats.skipped, stats.seconds
);
// 打开只读句柄开始搜索。
let searcher = Searcher::open(index_dir)?;
// 只在文档类扩展名(md/txt/pdf/docx…)里搜,按修改时间从新到旧排。
let hits = searcher.search_advanced(
"季度 报告",
20,
ext_group_by_name(Some("doc")),
SortMode::MtimeDesc,
)?;
for hit in &hits {
// display_path 剥掉 Windows 的 `\\?\` 扩展长度前缀,只用于展示。
println!(
"[{}] {} —— {} 处高亮",
hit.ext,
display_path(&hit.path),
hit.highlighted.len()
);
println!("{}", hit.snippet);
}Structs§
- AddRoot
Stats - 添加根的统计结果,跟
IndexStats(全量重建)同一口径:收录/跳过文件数。 - Batch
Outcome - 一批增量更新的处理结果,给日志/调试看。
- Debouncer
- 防抖队列:500ms 静默窗口内合并同一 path 的多次事件,只保留最终状态; 水位到 WATER_LEVEL 就提示宿主强制刷批。纯内存逻辑,不碰时钟也不碰磁盘—— 时间窗口由宿主的事件循环(watch.rs)用 recv 超时驱动。
- Index
Progress - 单次进度汇报:累计处理数(收录 + 跳过),和刚处理完的那个文件路径。
- Index
Stats - 一次重建索引的统计结果,CLI 拿去打报告。
- Index
Status - 索引的只读状态快照,给 CLI/MCP 的
index_status一类查询用。 - Index
Updater - 增量更新器:持有索引的写入端 IndexWriter,把防抖后的一批变更落进索引, 一批只 commit 一次(tantivy 的 commit 是重操作,绝不能一个文件一次)。
- Notify
Event Source - 基于 notify 库的跨平台事件源。把各平台原生事件翻译成 WatchEvent。
- OcrDrain
Stats drain_ocr_queue一次运行的统计结果。- OcrPipeline
- OCR 后台管线句柄:持有 worker 线程,stop() 时等待它们干净退出(当前正在跑的 那一张 OCR 会跑完,不会被中途打断留下半张写坏的文档)。
- OcrQueue
- OCR 队列:图片的 (path,mtime,size) 待处理表 + 已处理结果缓存,随索引目录持久化
在
<index_dir>-ocr-queue.json(跟 meta.rs 的<index_dir>-meta.json同一个 “索引目录旁的兄弟文件“套路,不塞进 tantivy 自己管理的索引目录里)。 - Pending
Change - 防抖合并后交给更新器的一条变更。
- Preview
Hit - 按路径取的预览内容,字段契约和 SearchHit 的 snippet/highlighted 一致, 只是窗口更大(约 1500 字而不是摘要用的 160 字)。
- Reconcile
Stats - 一次启动对账的差异统计。
- Remove
Root Stats - 移除根的统计结果:从索引里删掉的文档数。
- Search
Hit - 一条搜索命中。
- Searcher
- 一个索引的只读搜索句柄。
Enums§
- Pending
Op - 一个 path 在防抖窗口内合并后的最终意图。
- Sort
Mode - 结果排序方式。相关性是默认值——不传排序参数、或者从前端传来的字符串
没解析出已知档位时都落回这里。其余三档对应浮窗“排序器“下拉的三个非默认项,
底层用 tantivy
TopDocs::order_by_fast_field系列 API 在 mtime/size 这两个 v0.5.0 补了 FAST 属性的字段上排(见 lib.rs 的 build_schema)。 - Watch
Event - 文件系统监听的原始事件。事件源(notify,或将来里程碑 6 的 USN Journal) 把各平台的原生事件归一成这几类再喂进防抖队列。刻意只带路径、不带时间戳/inode 等平台细节,让防抖/合并逻辑是纯函数,不依赖真实文件系统。
- Watch
Progress - 监听循环对外汇报的进度,给 CLI 打日志、给托盘端刷新状态用。
Constants§
- DEFAULT_
WORKERS - CLI/托盘启动 OCR 管线时的默认线程数。
Traits§
- Event
Source - 文件系统事件源抽象。里程碑 3 用 notify 做第一个实现;里程碑 6 的 NTFS USN Journal 快速路径只要再实现这个 trait,就能接进同一条“防抖 → 更新“流水线, 上层的 run_watch 完全不用改。
- Watch
Guard - 监听句柄:留住它监听就继续,drop 掉就停。
Functions§
- add_
root - 添加一个根:不动现有索引,对新根做一次“目录树 upsert“(遍历 + 先删后加, 幂等),完成后把根追加进 meta。不需要进度直播的调用方走这个薄封装。
- add_
root_ with_ progress - 同
add_root,多一个进度回调,直播这个根的目录树 upsert 进度——设计文档 “进度直播复用现有整套“要求这条路径也能像全量重建一样把过程推给前端。 - display_
path - 剥掉 Windows 扩展长度路径语法(
\\?\/\\?\UNC\)的前缀,只给展示层用。 - drain_
ocr_ queue - 同步跑完 OCR 队列直到清空(
dowse index这类一次性命令用):建完文本索引后 紧接着启动 worker 池处理图片,直到队列见底再返回,让命令结束时索引就是完整 可搜的状态,而不是留一堆图片在后台“回头再说“——常驻的托盘程序不需要这个, 那边图片交给后台 worker 池慢慢消化就行(见设计文档“独立于文本管线“一节)。 - ext_
group_ by_ name - 按名字取预设分组的扩展名集合。
"all"、None、未知名字都表示不筛选—— 浮窗前端传字符串过来,未知输入宽松地当“全部“处理,不报错。 - index_
status - 读索引的只读状态:文档数、已注册根、落盘体积、最近更新时间。
- is_
available - 探测系统是否有可用的 OCR 语言包。管线整体启用与否的开关,UI 侧也可以拿它 提前决定要不要显示“OCR 不可用“的引导文案。
- normalize_
ranges - 把 tantivy SnippetGenerator 吐出的命中区间整理成有序且互不重叠的序列。
- ntfs_
fast_ path_ available - 供集成测试用的权限护栏:这个路径能不能走 NTFS 快速路径(MFT 枚举 +
USN Journal)。跟
dowse::is_available()(OCR 语言包探测)是同一个 用途——测试开头先探测一次,探测不到就打印原因跳过,不需要管理员权限的 测试永远能跑,需要的测试在非管理员机器/CI 上不会把构建搞红。 - rebuild_
index - v0 策略:全量重建。删掉旧索引目录,从头扫一遍。 增量更新是里程碑 3 的事,现在先把“能搜“跑通。
- rebuild_
index_ with_ progress - 同
rebuild_index,多一个进度回调:每处理PROGRESS_INTERVAL个文件就报一次 累计处理数和当前文件路径。CLI 的dowse index和浮窗的“建索引“都走这个, 一份实现两处消费,避免两端各自维护一份遍历+回调逻辑。 - rebuild_
root - 重建单根 = 移除根 + 添加根的组合,全部复用上面两个操作(设计文档“核心 操作语义“一节)。给已经注册过的根一次干净的重新收录,而不用先手动移除 再手动添加。
- rebuild_
root_ with_ progress - 同
rebuild_root,多一个进度回调,直播添加阶段的目录树 upsert 进度。 - reconcile
- 启动对账:程序没跑的时候文件照样在变,启动时把索引追平文件系统的实际状态。
- reconcile_
orphans - 孤儿文档清理(多根索引,里程碑 7):索引里若存在 path 不属于
roots任一根的文档,直接删除。 - registered_
roots - 已注册的索引根目录列表,供启动对账和文件监听使用。 顺带校验 schema 版本——版本不对时直接报错,不返回一份不可信的根列表。
- remove_
dir_ all_ retrying - 删整个目录,撞上
PermissionDenied时按指数退避重试几次再放弃。 - remove_
root - 移除一个根:前缀圈选删除该根名下的全部文档,OCR 队列 compact 掉该根的
条目,roots 里移除这一项。
root必须是registered_roots返回值里的 原样一项(不做 canonicalize/存在性校验)——移除本来就要覆盖“根所在目录 已经从磁盘上消失“这种场景,不能要求它此刻还能被 stat 到。 - run_
watch - 监听主循环:把事件源 → 防抖队列 → 增量更新器串起来,阻塞运行直到 stop 置位。
- watch_
roots_ auto - 按卷判定自动选监听路径的入口(里程碑 6):每个根探测 NTFS + 管理员权限,
拿得到就走 MFT/USN 快车道,拿不到就走现有的 walkdir + notify 慢车道
(设计文档第一节)。CLI 的
dowse watch和托盘常驻程序的监听线程都应该 调这个函数替代直接调run_watch(NotifyEventSource, ...)——这是“上层 完全不用感知快慢车道差别“在代码上的落地:调用方只需要把原来的run_watch(NotifyEventSource, &roots, ...)换成watch_roots_auto(index_dir, &roots, ...),其余逻辑不用动。