跳转到主要内容

这是本节的多页打印视图。 .

返回本页常规视图.

快速上手

vchord_bm25 与 pg_tokenizer 的架构概览、安装方式与快速入门指南。

在 PostgreSQL 中快速上手 vchord_bm25:

了解为什么 BM25 + Block-WeakAnd 在相关性与性能上显著优于 PostgreSQL 原生 ts_rank 与 GIN 索引。

通过 Pigsty、Docker、Debian 软件包或源码编译安装 vchord_bm25 与 pg_tokenizer。

在几分钟内完成文本分词、构建 bm25 索引并运行 Top-K 全文排序查询。

1 - 架构概览

了解 VectorChord-BM25 如何为 PostgreSQL 带来原生 Okapi BM25 评分与 Block-WeakAnd Top-K 检索。

PostgreSQL 通过 tsvector、tsquery、ts_rank 和 GIN 索引提供了内置的全文检索能力。虽然它在布尔关键词匹配(@@)方面表现良好,但在现代搜索与检索增强生成(RAG)工作负载下,PostgreSQL 内置的相关性排序存在两个结构性局限:

  1. 排序公式不理想(ts_rank):PostgreSQL 的 ts_rank 和 ts_rank_cd 不会在索引中维护全语料库级别的逆文档频率(IDF)或平均文档长度(avgdl\text{avgdl})。常见词与罕见的高信息量词被赋予相同的权重,且词频(TF)缺乏合理的饱和度控制。
  2. 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(IDF\text{IDF}、k1k_1、bb、avgdl\text{avgdl})
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 设计:

  1. 稳定发布版(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'))
  2. 新一代原生 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')
提示

本指南在各章节中均并列提供了这两种工作流的完整示例,你可以根据实际部署的版本直接选用对应的写法。

2 - 安装指南

通过 Pigsty、Docker、Debian 软件包或源码编译安装 VectorChord-BM25 (vchord_bm25) 与 pg_tokenizer。
重要

当前源码面向 0.4.0(尚未发布),使用原生 tsvector 和新的索引格式。标注 0.3.x 的 bm25vector 示例仅适用于旧版。两者不能原地升级;请先阅读0.4.0 迁移指南。

vchord_bm25 支持 Linux(x86_64 与 aarch64)及 macOS 平台上的 PostgreSQL 14、15、16、17 和 18。

Pigsty 为 EL 8/9/10、Debian 12/13 以及 Ubuntu 22.04/24.04 上的 PostgreSQL 14–18 提供了预构建的 vchord、vchord_bm25 和 pg_tokenizer 原生 RPM 与 DEB 软件包。

# 通过 Pigsty CLI 安装(自动识别 PG 版本与操作系统发行版)
pig ext install vchord_bm25
pig ext install pg_tokenizer

配置好 Pigsty 软件源后,也可以直接通过 apt 或 dnf 安装:

# Debian / Ubuntu(将 17 替换为你的 PostgreSQL 主版本号)
sudo apt-get install postgresql-17-vchord-bm25 postgresql-17-pg-tokenizer

# RHEL / Rocky / AlmaLinux(将 17 替换为你的 PostgreSQL 主版本号)
sudo dnf install vchord_bm25_17 pg_tokenizer_17

方式二:Docker(tensorchord/vchord-suite)

官方 tensorchord/vchord-suite Docker 镜像开箱即用地集成了 PostgreSQL、VectorChord (vchord)、VectorChord-BM25 (vchord_bm25) 以及 pg_tokenizer:

docker run \
  --name vchord-suite \
  -e POSTGRES_PASSWORD=postgres \
  -p 5432:5432 \
  -d tensorchord/vchord-suite:pg18-latest
说明

你也可以从 GitHub Container Registry 拉取镜像:ghcr.io/tensorchord/vchord-suite:pg18-latest(或 pg17-latest、pg16-latest、pg15-latest、pg14-latest)。

使用 psql 连接数据库:

psql -h localhost -p 5432 -U postgres

方式三:GitHub Release 软件包(.deb 与 RHEL .zip)

从 pgsty/vchord_bm25(上游 supervc-stack/VectorChord-bm25)与 pgsty/pg_tokenizer(上游 supervc-stack/pg_tokenizer.rs)的 GitHub Releases 页面下载对应 PostgreSQL 主版本(14–18)和 CPU 架构(amd64 / x86_64 或 arm64 / aarch64)的 .deb 或 RHEL 9+ .zip 软件包:

Debian / Ubuntu(.deb)

# 安装 vchord_bm25(以 x86_64 上的 PostgreSQL 16 为例)
curl -LO https://github.com/supervc-stack/VectorChord-bm25/releases/download/0.3.0/postgresql-16-vchord-bm25_0.3.0-1_amd64.deb
sudo apt-get install ./postgresql-16-vchord-bm25_0.3.0-1_amd64.deb

# 安装 pg_tokenizer(以 x86_64 上的 PostgreSQL 16 为例)
curl -LO https://github.com/supervc-stack/pg_tokenizer.rs/releases/download/0.1.1/postgresql-16-pg-tokenizer_0.1.1-1_amd64.deb
sudo apt-get install ./postgresql-16-pg-tokenizer_0.1.1-1_amd64.deb

RHEL 9+ / Enterprise Linux(.zip 归档包)

在 RHEL 9+ 系统(要求 glibc >= 2.34)中手动通过 Release .zip 归档包安装时:

# 1. 查询 PostgreSQL 的动态库目录与扩展共享目录
pg_config --pkglibdir
# 示例输出:/usr/pgsql-16/lib
pg_config --sharedir
# 示例输出:/usr/pgsql-16/share

# 2. 下载并解压发布归档包
wget https://github.com/supervc-stack/pg_tokenizer.rs/releases/download/0.1.1/pg16-pg_tokenizer-0.1.1-x86_64-unknown-linux-gnu.zip
unzip pg16-pg_tokenizer-0.1.1-x86_64-unknown-linux-gnu.zip

# 3. 拷贝共享库 (.so) 与扩展控制/SQL 脚本文件
sudo cp ./usr/lib/postgresql/16/lib/pg_tokenizer.so $(pg_config --pkglibdir)
sudo cp ./usr/share/postgresql/16/extension/* $(pg_config --sharedir)/extension

方式四:从源码编译

如需从源码编译 pgsty/vchord_bm25:

# 1. 安装 Rust 工具链及系统构建依赖(clang、libclang、pkg-config、PostgreSQL 开发头文件)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# 2. 编译并安装 vchord_bm25
git clone https://github.com/pgsty/vchord_bm25.git
cd vchord_bm25
PG_CONFIG=/path/to/pg_config make build
PG_CONFIG=/path/to/pg_config make install
提示

编译期启用可选的 Lindera 词典特性:如果希望将 Lindera 中日韩形态学词典(ipadic、ipadic-neologd、unidic、ko-dic、cc-cedict)直接内嵌编译到 pg_tokenizer.so 中,请在编译 pg_tokenizer 时传入 --features 参数:

cargo pgrx install --release --pg-config $(which pg_config) \
  --features "lindera-ipadic lindera-ipadic-neologd lindera-unidic lindera-ko-dic lindera-cc-cedict"

本地开发与调试

使用 dev 构建配置,在开启 BM25 编译优化的同时保留调试符号:

PG_CONFIG=/path/to/pg_config PROFILE=dev make build
PG_CONFIG=/path/to/pg_config PROFILE=dev make install

在配置预加载 vchord_bm25 后重启一个可丢弃的 PostgreSQL 测试实例,再通过 psql 连接。需要附加调试器时,可查询当前会话的后端进程 PID:

SELECT pg_backend_pid();

配置 PostgreSQL 并启用扩展

当前源码必须通过 shared_preload_libraries 预加载 vchord_bm25。请保留其他所需扩展;pg_tokenizer 对原生 to_tsvector 工作流是可选的:

shared_preload_libraries = 'vchord_bm25'

修改 shared_preload_libraries 后请重启 PostgreSQL。

2. 创建扩展并设置 search_path

连接到目标数据库并启用扩展:

CREATE EXTENSION IF NOT EXISTS vchord_bm25;
-- pg_tokenizer 是旧版 bm25vector 工作流的独立可选扩展。

在 v0.1.0–v0.3.0 版本中,vchord_bm25 会将其对象安装到 bm25_catalog 模式,而 pg_tokenizer 会安装到 tokenizer_catalog 模式。请将这两个模式加入你的 search_path:

-- 仅在当前会话生效
SET search_path TO "$user", public, bm25_catalog, tokenizer_catalog;

-- 或针对数据库 / 角色持久化保存
ALTER DATABASE postgres SET search_path TO "$user", public, bm25_catalog, tokenizer_catalog;

验证已安装的扩展版本:

SELECT extname, extversion FROM pg_extension WHERE extname IN ('vchord_bm25', 'pg_tokenizer');

3 - 快速开始

使用 vchord_bm25 在 PostgreSQL 中运行你的第一条 BM25 全文排序检索查询。
重要

当前源码面向 0.4.0(尚未发布),使用原生 tsvector 和新的索引格式。标注 0.3.x 的 bm25vector 示例仅适用于旧版。两者不能原地升级;请先阅读0.4.0 迁移指南。

本指南将带你一步步创建数据表、对文档进行分词、构建 bm25 索引,并执行 Top-K BM25 检索查询。

工作流 A:bm25vector + pg_tokenizer(v0.1 – v0.3)

在已发布的 v0.1.0 至 v0.3.0 版本中,vchord_bm25 与 pg_tokenizer 配合使用,将文本分词并存储到 bm25vector 列中。

1. 启用扩展并配置 search_path

CREATE EXTENSION IF NOT EXISTS pg_tokenizer CASCADE;
CREATE EXTENSION IF NOT EXISTS vchord_bm25 CASCADE;

SET search_path TO "$user", public, bm25_catalog, tokenizer_catalog;

2. 创建分词器

使用 create_tokenizer 搭配内置的 llmlingua2(或 bert_base_uncased)模型创建分词器:

SELECT create_tokenizer('my_tokenizer', $$
model = "llmlingua2"
$$);
提示

需要领域专属词表、中文分词(jieba)或日文分词(lindera)?请参阅 分词器 (pg_tokenizer) 章节。

3. 创建表并写入文档

CREATE TABLE documents (
    id SERIAL PRIMARY KEY,
    passage TEXT NOT NULL,
    embedding bm25vector
);

INSERT INTO documents (passage) VALUES
  ('PostgreSQL is a powerful, open-source object-relational database system. It has over 35 years of active development.'),
  ('Full-text search is a technique for searching a single computer-stored document or a collection in a full-text database.'),
  ('BM25 is a bag-of-words retrieval function that ranks a set of documents based on the query terms appearing in each document.'),
  ('VectorChord-bm25 is a PostgreSQL extension that provides native BM25 ranking index using the Block-WeakAnd algorithm.');

4. 将文档分词为 bm25vector

tokenize(text, tokenizer_name) 返回一个由词元 ID 组成的 INT[] 数组,它可以隐式转换为 bm25vector(自动将重复的词元 ID 聚合为词频):

UPDATE documents
SET embedding = tokenize(passage, 'my_tokenizer');

-- 查看稀疏的 bm25vector 表示形式 ({token_id:term_frequency, ...})
SELECT id, embedding FROM documents LIMIT 2;

5. 构建 bm25 索引

CREATE INDEX documents_embedding_bm25
ON documents
USING bm25 (embedding bm25_ops);

6. 执行 Top-K BM25 查询

在升序 ORDER BY 子句中结合 <&> 操作符与 to_bm25query(index_name, query_vector) 执行检索:

SELECT
    id,
    passage,
    -(embedding <&> to_bm25query('documents_embedding_bm25', tokenize('PostgreSQL BM25 index', 'my_tokenizer'))) AS bm25_score
FROM documents
ORDER BY embedding <&> to_bm25query('documents_embedding_bm25', tokenize('PostgreSQL BM25 index', 'my_tokenizer'))
LIMIT 10;
重要

为什么 <&> 返回负数? PostgreSQL 仅支持按升序(ASC)执行索引驱动的 ORDER BY 扫描。因此,<&> 操作符返回的是负的 BM25 得分(例如 -2.8415)。数值越负代表相关性越高,从而使 ORDER BY embedding <&> to_bm25query(...) ASC LIMIT K 能够直接利用 Block-WeakAnd 索引扫描。如果希望展示正数得分,只需在 SELECT 列表中对该表达式取反(-(embedding <&> ...))即可。


工作流 B:原生 tsvector API(main 分支)

在新一代 main 分支重构中,vchord_bm25 可直接为 PostgreSQL 原生的 tsvector 类型建立索引——对于标准语言无需安装额外的分词器扩展。

1. 创建扩展与数据表

CREATE EXTENSION IF NOT EXISTS vchord_bm25 CASCADE;

CREATE TABLE documents (
    id SERIAL PRIMARY KEY,
    passage TEXT NOT NULL,
    vector tsvector GENERATED ALWAYS AS (to_tsvector('english', passage)) STORED
);

INSERT INTO documents (passage) VALUES
  ('PostgreSQL is a powerful, open-source object-relational database system.'),
  ('Full-text search is a technique for searching documents in a full-text database.'),
  ('BM25 is a bag-of-words retrieval function that ranks documents based on query terms.'),
  ('VectorChord-bm25 provides a native BM25 ranking index in PostgreSQL.');

2. 在 tsvector 上构建 bm25 索引

CREATE INDEX documents_bm25_idx
ON documents
USING bm25 (vector bm25_ops)
WITH (options = $$
k1 = 1.2
b = 0.75
$$, limit = 100);

3. 使用 to_bm25query(tsvector, regclass) 查询

SELECT
    id,
    passage,
    -(vector <&> to_bm25query(to_tsvector('english', 'PostgreSQL BM25 index'), 'documents_bm25_idx')) AS bm25_score
FROM documents
ORDER BY vector <&> to_bm25query(to_tsvector('english', 'PostgreSQL BM25 index'), 'documents_bm25_idx')
LIMIT 10;