架构概览
了解 VectorChord-BM25 如何为 PostgreSQL 带来原生 Okapi BM25 评分与 Block-WeakAnd Top-K 检索。
PostgreSQL 通过 tsvector、tsquery、ts_rank 和 GIN 索引提供了内置的全文检索能力。虽然它在布尔关键词匹配(@@)方面表现良好,但在现代搜索与检索增强生成(RAG)工作负载下,PostgreSQL 内置的相关性排序存在两个结构性局限:
- 排序公式不理想(
ts_rank):PostgreSQL 的ts_rank和ts_rank_cd不会在索引中维护全语料库级别的逆文档频率(IDF)或平均文档长度()。常见词与罕见的高信息量词被赋予相同的权重,且词频(TF)缺乏合理的饱和度控制。 ORDER BY ... LIMIT K必须全量扫描:PostgreSQL 的GIN索引只能加速布尔匹配(WHERE tsv @@ tsquery)。为了返回得分最高的 Top-10 文档,PostgreSQL 必须从堆表(Heap)中取出所有匹配行,逐一计算ts_rank,再对整个结果集进行排序。
VectorChord-BM25 如何解决这两个问题
VectorChord-BM25 (vchord_bm25) 引入了专为 Top-K 词法检索设计的 PostgreSQL 索引访问方法(USING bm25)和稀疏 BM25 评分操作符(<&>):
| 能力维度 | PostgreSQL 内置 (GIN + ts_rank) |
VectorChord-BM25 (vchord_bm25) |
|---|---|---|
| 排序模型 | 局部词频 / 词距 (ts_rank) |
全语料库 Okapi BM25(、、、) |
| Top-K 执行方式 | 取出并计算所有匹配元组后再排序 | 索引内 Block-WeakAnd (Block-Max WAND) 提前剪枝 |
| 倒排链压缩 | 未压缩或基础变长整数 TID 列表 | 128 文档分块、对 SIMD 友好的差分位打包(Delta-Bitpacking) |
| 文档长度归一化 | 可选的粗粒度位掩码标志 | 单字节 Fieldnorm 量化(256 项查找表) |
| 查询语义 | 要求布尔 AND/OR 精确匹配 |
跨所有查询词的非合取(Non-conjunctive)Top-K 相关性排序 |
| 分词能力 | 内置 ts_debug / Snowball |
pg_tokenizer(LLM 分词器、jieba、lindera、自定义词表)或原生 tsvector |
架构概览
flowchart LR
subgraph Tokenization["1. 分词层 (Tokenization Layer)"]
T1["原始文本 (Raw Text)"] --> T2["pg_tokenizer (v0.1–v0.3)\n或 to_tsvector (main)"]
T2 --> T3["bm25vector / tsvector\n(词元 ID + 词频)"]
end
subgraph Index["2. BM25 索引 (USING bm25)"]
I1["增长段 (Growing Segment)\n(仅追加页)"] --> I2["封闭段 (Sealed Segment)\n(Delta-Bitpacked 128 文档块)"]
I2 --> I3["词项统计信息 (DF)\n+ 单字节 Fieldnorm 数组"]
end
subgraph Query["3. Top-K 查询 (ORDER BY vec <&> bm25query LIMIT k)"]
Q1["块级最大得分上界 (Block-Max)"] --> Q2["Block-WeakAnd 枢轴剪枝"]
Q2 --> Q3["Top-K 最小堆 (-BM25 得分)"]
end
T3 --> Index
Index --> Query
两代 API 演进
在使用 vchord_bm25 时,请注意不同版本间的两代 API 设计:
-
稳定发布版(
v0.1.0–v0.3.0,模式bm25_catalog):- 使用自定义数据类型
bm25vector(稀疏的token_id:frequency映射,例如{1:1, 2:2})和bm25query。 - 配合
pg_tokenizer扩展(tokenizer_catalog)使用,将文本转换为INT[]词元 ID 数组,并可隐式转换为bm25vector。 - 创建索引:
CREATE INDEX idx ON documents USING bm25 (embedding bm25_ops); - 构造查询:
to_bm25query('idx', tokenize('query text', 'tokenizer_name'))
- 使用自定义数据类型
-
新一代原生
tsvector架构(main分支):- 直接作用于 PostgreSQL 内置的
tsvector数据类型(CREATE OPERATOR CLASS bm25_ops FOR TYPE tsvector USING bm25),无需额外维护单独的bm25vector列。 - 创建索引:
CREATE INDEX idx ON documents USING bm25 (vector bm25_ops) WITH (options = $$ k1 = 1.2, b = 0.75 $$); - 构造查询:
to_bm25query(to_tsvector('english', 'query text'), 'idx')
- 直接作用于 PostgreSQL 内置的
提示
本指南在各章节中均并列提供了这两种工作流的完整示例,你可以根据实际部署的版本直接选用对应的写法。