# 工作原理

> Crib 怎样把内容、搜索索引和个人资料分开存放，以及为什么导入或更新版本时旧窗口还能继续读。

---

LLMS 索引： [llms.txt](/llms.txt)

---

Crib 由两部分组成：构建端用 Go 把手册和 Wiki 做成签名的资料包和预建索引；macOS 应用用 SwiftUI、少量
AppKit、WKWebView 和 SQLite FTS5 读取它们。应用运行时不需要网络服务，也不启动本地 HTTP 服务器
（[本地 API](/docs/integrations/local-api/) 是可选功能，默认关闭）。

```mermaid
flowchart LR
  P["手册源码与 HTML<br>Wiki 冻结快照"] --> G["Go 构建工具<br>crib-pack"]
  G --> B["签名资料包<br>.crib"]
  G --> S["出厂搜索索引<br>seed"]
  B --> V["应用：验签与安全解包"]
  V --> W["后台构建新的搜索世代"]
  S --> A["当前世代"]
  W --> A
  A --> Q["搜索"]
  A --> R["阅读器 WKWebView"]
  U["个人资料<br>user.sqlite"] --> R
  U --> Q
```

## 三类数据分开存放 {#separation}

| 数据 | 存放位置 | 谁可以写 |
| --- | --- | --- |
| 文档内容：HTML、资源、章节、双语对应、精确名称键 | 只读的资料包 | 只有构建端，写入后签名 |
| 搜索索引 | 资料库里的“世代”（generation） | 应用的后台构建器，构建完成后不再修改 |
| 笔记、书签、标签、历史、本地更正 | 用户目录中的 `user.sqlite` | 只有个人资料模块 |

资料包导入器不能写个人资料库，个人资料也不建立指向资料包的级联删除。所以卸载一个版本不会删掉挂在上面的笔记，
只会让它们暂时显示为目标不可用。

## 资料包 {#packs}

每条 PostgreSQL 版本线是一个双语原子包，Wiki 是另一个。包里有 `manifest.json`、签名、来源清单、
章节数据库、两种语言的文档和许可文件。同一个版本、同一个修订号的文件字节永远不变；内容有变化就发新的修订号。
格式细节见[资料包格式](/docs/library/packs/)。

应用只接受签名有效的包：检查清单签名、文件长度和哈希、ZIP 边界和资源清单，拒绝绝对路径和路径穿越。

## 搜索世代 {#generations}

搜索时只查一个聚合的 SQLite 数据库，不同时挂接多个包的数据库。这样所有版本的正文用同一套统计排序，结果可以直接比较。

这个聚合数据库叫一个“世代”。它由当前启用的资料包组合唯一决定：

- 完整离线版和 Core 版都随附一个出厂世代，首次启动直接使用，不需要建索引。
- 导入、更新、启用或停用资料包时，应用在后台从各包流式读取章节，写一个新的世代。
- 新世代验证通过后才原子切换。构建期间旧世代照常服务，已经打开的阅读窗口继续停在原来的世代上。
- 构建失败、被取消或磁盘不足时，保留原来的世代，不会出现一半新一半旧的状态。

只移动版本别名（例如 `pg18` 从 18.6 指向 18.7）不需要重建世代；只有实际更换资料包才重建。

## 搜索 {#search}

查询先解析成结构化条件：版本前缀和 `lang:`、`kind:` 等过滤器来自白名单，其余是文本。用户输入不会直接拼进 SQL。
排序分层进行：

1. 当前范围内的精确名称和别名（`work_mem`、`->>`、`\dC`、SQLSTATE 代码）。
2. 名称前缀。
3. 标题和章节名。
4. 正文。

中文按连续汉字的重叠双字切分：“事务隔离”切成“事务、务隔、隔离”。单个汉字（例如“锁”）走标题和名称的字符索引，
在有限范围内查，不扫描全库正文。构建端和应用使用同一个切分规则（analyzer），并用同一组测试样例核对。
具体写法见[搜索](/docs/guide/search/)。

## 阅读器 {#reader}

正文是资料包里的 HTML，由 WKWebView 通过一个受限的内部地址读取：地址里带有资料包清单的摘要，
只能访问该包内的文件，不能加载远程脚本或资源。切换语言时，按两种语言之间已验证的章节对应跳转，
没有对应就回到父节并提示。

## 个人资料 {#personal}

`user.sqlite` 使用 WAL 和完整同步写入，每次保存在一个事务里比较修订号：两个编辑器同时改同一条笔记时，
后保存的一方得到冲突提示，不会覆盖对方。迁移或备份使用 SQLite 在线备份，而不是复制正在使用的数据库文件。
个人资料库打不开时，只停用个人功能，阅读和搜索照常工作。见[笔记、书签与本地更正](/docs/guide/notes/)。

---

反链：

- [文档](/docs/)
- [Crib 是什么](/docs/intro/overview/)
