查询库:dic_rust.zip (40.0 KB)
写入器:dic_writer.zip (39.9 KB)
文件类型
索引文件(.key)
- 存储检索用的索引结构
- 根据需求选择不同的索引类型
- 指向对应的标题文件
标题文件(.head)
- 存储词条的标题信息
- 记录正文位置和锚点
- 包含去重的字符串表
正文文件(.rsc/.nrsc)
- 存储实际的HTML内容
- 按块压缩存储
- 支持分片
元数据文件(.metadata)
- 记录所有文件的索引
- 定义搜索模式
- 存储词典基本信息
索引类型
1. 词头Trie索引
用于精确的词头查询,比如输入"app"能匹配到"apple"、"application"等。
适用场景: 词典、术语表这类需要前缀匹配的场景
数据结构:
TrieNode {
children: map<char, TrieNode> // 子节点映射
head_ids: list<int> // 这个节点能匹配到的所有词条ID
is_end: bool // 标记是否为完整词
}
字符映射表把字符映射成连续的ID,节点表记录每个节点的子节点偏移和匹配结果偏移。查询时从根节点开始,逐字符跳转,收集路径上的所有匹配结果。
2. 字符级倒排索引
把文本拆成单个字符建立倒排索引,适合CJK文本的任意子串查询。
适用场景: CJK例句短语搜索?
数据结构:
CharIndex {
char → list<(entry_id, [positions])> // 字符到词条+位置列表的映射
}
存储时会根据字符分布密度选择策略:
- 直接数组:字符范围集中时(密度>30%),用Unicode值直接索引
- 稀疏数组:字符分散时,存储
(unicode, offset)对的排序列表
查询"例句"时,先找到包含"例"的所有词条,再从中筛选出同时包含"句"且位置相邻的结果。
3. 分词级倒排索引
对文本进行分词后建立倒排索引,配合B+树结构支持前缀、后缀、精确匹配。
适用场景: 英文全文检索
数据结构:
InvertedIndex {
term → list<(doc_id, [positions])> // 词项到文档+位置列表
}
B+Tree {
forward_tree // 正向树:支持前缀查询("app*")
reverse_tree // 反向树:支持后缀查询("*ing")
}
B+树的叶节点存实际数据,内部节点存索引。叶节点间通过next指针串联,方便范围查询。
标题文件结构
标题文件是连接索引和正文的桥梁。
文件头:
magic: 0x48454144
version: 1
entry_count: 词条总数
encoding: UTF-16LE
mode_name: 搜索模式名称
索引段: 固定16字节一条,记录(head_id, data_offset)
数据段: 每条记录包含:
head_id // 词条ID
page_id // 正文ID
item_id // 正文内的锚点ID
string_index // 在字符串表中的索引
content_info // 正文文件ID、偏移、长度
字符串表: 去重存储所有标题文本
StringTableEntry {
content_hash // CRC32校验
data_offset // 在字符串数据区的偏移
data_length // 长度
ref_count // 引用计数
}
正文文件结构
RSC格式(常规正文)
把多条正文打包成块,压缩后存储。
块结构:
[4字节压缩长度][压缩数据]
压缩数据解开后是:
[4字节长度][UTF-16LE文本][4字节长度][UTF-16LE文本]...
映射文件(.map):
MapRecord {
zoffset // 压缩块在文件中的位置
ioffset // 内容在解压后块内的偏移
flags
}
索引文件(.idx): 把content_id映射到map_index
块大小默认64KB,文件大小上限10MB后自动分片。
NRSC资源文件(这部分其实还没做好)
用于音频、图片等二进制资源,不分块直接存。
NrscMapRecord {
fileseq // 第几个分片文件
file_offset // 在分片中的偏移
length // 数据长度
compression // 压缩标志(0=原始 1=zlib)
}
分片上限50MB,资源单独压缩,压缩率不到90%就不压。
实际用例
假设有这样一段HTML:
<html>
<div id="1">这是例句1</div>
<div id="2">这是例句2</div>
<div id="3">这是例句3</div>
</html>
封装流程:
- 整段HTML作为page_id=0存入.rsc
- 把三个例句分别提取,建立标题记录:
head_id:0, page_id:0, item_id:1, headline:"这是例句1"
head_id:1, page_id:0, item_id:2, headline:"这是例句2"
head_id:2, page_id:0, item_id:3, headline:"这是例句3"
- 对"这是例句1"、“这是例句2”、"这是例句3"建立字符级索引
查询流程:
搜索"这是例句"时:
- 字符索引返回三条匹配
- 点击"这是例句1"
- 根据head_id=0找到标题记录
- 获取page_id=0, item_id=1
- 从.rsc读取page_id=0的HTML
- 定位到
<div id="1">锚点 - 渲染显示
元数据文件
统一管理所有文件。
搜索模式:
SearchMode {
mode_name // 内部标识
display_name // 显示名称
key_file_id // 索引文件ID
priority // 优先级(数字越小越靠前)
}
文件记录:
FileEntry {
file_id // 文件ID
filename // 文件名
file_size // 大小
file_type // 类型(KEY/HEAD/CONTENT)
shard_index // 分片序号
}
元数据里不直接存文件路径,用file_id关联。查询时先读元数据,拿到file_id,再去对应文件里读数据。
文件ID规则
- 1-99:索引和标题文件
- 100+:正文文件
- 100: .idx
- 101: .map
- 102+: .rsc分片