跳转到主要内容

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

返回本页常规视图.

pg_tokenizer 文档

pg_tokenizer 完整指南:安装部署、文本分析器、字符/词元过滤器、预训练与自定义词表模型、多语言分词以及 SQL 参考手册。

pg_tokenizer(由 Pigsty 在 pgsty/pg_tokenizer 维护)是一个使用 Rust(pgrx)构建的独立 PostgreSQL 多语言分词扩展。它提供可定制的文本分析器、字符与词元过滤器以及词表模型,可将原始文本转换为整数词元 ID 数组(INT[])及供 vchord_bm25 使用的稀疏 bm25vector 向量。

通过 Pigsty 原生 Linux 软件包、Docker 镜像、Debian/ZIP 归档包或源码编译安装 pg_tokenizer。

了解文本分析器(Text Analyzer)与词表模型(Model)如何协同工作,将原始文本转化为词元 ID。

配置字符过滤器、预分词器(Unicode、正则、结巴 Jieba)、词干提取器(支持 30 种语言)、停用词、同义词与 N-gram。

使用内置预训练模型(llmlingua2、bert_base_uncased、wiki_tocken、gemma2b)、训练自定义语料模型,或导入 HuggingFace 与 Lindera 模型。

面向英文、中文(Jieba)、日文(Lindera)及特定领域自定义语料库的开箱即用配置方案。

完整的 tokenizer_catalog SQL 函数签名与 TOML 配置参数参考手册。

1 - 安装指南

通过 Pigsty 原生 Linux 软件包、Docker 镜像、Debian/ZIP 归档包或 cargo-pgrx 源码安装 pg_tokenizer。

通过 pig 安装原生 Linux 软件包(推荐)

在 EL 8/9/10、Debian 12/13 与 Ubuntu 22.04/24.04(支持 PostgreSQL 14–18)上,可以直接从 Pigsty 软件仓库安装预构建的原生软件包(完整包可用性矩阵见 pgext.cloud):

curl -fsSL https://repo.pigsty.cc/pig | bash
pig repo add pgsql -u
pig ext add pg_tokenizer

使用 Docker(vchord-suite)

tensorchord/vchord-suite Docker 镜像已预装完整的 Vchord 检索扩展套件:

  • pg_tokenizer —— 独立多语言分词器(pgsty/pg_tokenizer)
  • vchord_bm25 —— 原生 BM25 排序索引(pgsty/vchord_bm25)
  • vchord —— 可扩展、磁盘友好的向量相似性检索(pgsty/vchord)
  • pgvector —— 标准 PostgreSQL 向量数据类型
docker run \
  --name vchord-suite \
  -e POSTGRES_PASSWORD=postgres \
  -p 5432:5432 \
  -d tensorchord/vchord-suite:pg18-latest

使用 psql 连接数据库并启用扩展:

CREATE EXTENSION IF NOT EXISTS pg_tokenizer CASCADE;

参数配置(shared_preload_libraries 与 search_path)

通过原生软件包或归档包安装时,请将 pg_tokenizer.so 加入 shared_preload_libraries(预加载词表模型所必需),并将 tokenizer_catalog 加入 search_path:

psql -U postgres -c 'ALTER SYSTEM SET shared_preload_libraries = "pg_tokenizer.so"'
psql -U postgres -c 'ALTER SYSTEM SET search_path TO "$user", public, tokenizer_catalog'
sudo systemctl restart postgresql.service

随后在目标数据库中创建扩展:

CREATE EXTENSION IF NOT EXISTS pg_tokenizer CASCADE;

从 Debian 软件包或 ZIP 归档包安装

预编译的 .deb 与 .zip 发行包(要求 GLIBC >= 2.35,支持 x86_64 与 aarch64)可在 pgsty/pg_tokenizer Releases 页面下载:

# Debian / Ubuntu
sudo apt install ./postgresql-17-pg-tokenizer_*.deb

从源码编译安装(cargo-pgrx)

git clone https://github.com/pgsty/pg_tokenizer.git
cd pg_tokenizer
cargo pgrx install --sudo --release

升级到 0.1.2(未发布)

以下为 0.1.2 源码中的变更,尚未发布。安装匹配的动态库和 SQL 脚本后,先重启 PostgreSQL 以加载新的预加载库,再由扩展拥有者或管理员对各数据库执行:

ALTER EXTENSION pg_tokenizer UPDATE TO '0.1.2';

0.1.0 与 0.1.1 均有升级路径。升级替换触发器及便捷函数定义,并授予目录权限,不重建自定义词表、不改变已有词元 ID,保留原六参函数的依赖关系。标准 cargo pgrx install/package 与 tools/package.sh 均会包含升级脚本。Lindera 字典版本变化引起的 ID 迁移仍须按照模型说明重新分词。

数据库拥有者权限

安装与升级向 pg_database_owner 授予 tokenizer_catalog 的 USAGE/CREATE、函数的 EXECUTE,目录表与模型表的 SELECT/INSERT/UPDATE/DELETE,以及序列的 USAGE/SELECT。安装角色以后在该模式中创建的表、序列和函数也有相同默认授权;新建自定义模型还会显式授权。授权随数据库拥有者变更而生效。

管理员安装扩展后,非超级用户的数据库拥有者可以创建、删除自己创建的模型、分析器、分词器、停用词及同义词。授权不转移对象所有权:删除其他角色创建的模型仍需原对象拥有者或管理员。其他应用角色需要显式授权;扩展不会向 PUBLIC 开放模式访问或目录写权限,函数继续以调用者权限执行。

2 - 分词器架构与基本用法

理解 pg_tokenizer 的两阶段流水线:文本分析器(Text Analyzer)与分词模型(Tokenization Model)。

两阶段分词流水线

在 pg_tokenizer 中,一个分词器(tokenizer)最多由两个阶段组成,负责将原始 TEXT 字符串转换为由整数词元 ID 构成的 INT[] 数组:

flowchart LR
    A["原始文本\n(TEXT)"] --> B["第一阶段:文本分析器 (Text Analyzer)\n1. character_filters(字符过滤器)\n2. pre_tokenizer(预分词器)\n3. token_filters(词元过滤器)"]
    B --> C["归一化词元数组\n(TEXT[])"]
    C --> D["第二阶段:模型 (Model)\n词表映射 / 子词 BPE"]
    D --> E["词元 ID 数组\n(INT[] → bm25vector)"]
  1. 文本分析器(text_analyzer):按顺序应用以下三级流水线,将原始文本切分为归一化后的字符串词元数组 TEXT[]:
    • 字符过滤器(character_filters):在切分前对原始字符串进行整体转换(例如 to_lowercase 小写转换、unicode_normalization Unicode 规范化)。
    • 预分词器(pre_tokenizer):将字符串初步切分为子串(支持 unicode_segmentation、regex 正则或中文 jieba 分词)。
    • 词元过滤器(token_filters):对单个词元进行过滤或转换(支持 skip_non_alphanumeric、stemmer 词干提取、stopwords 停用词、synonym 同义词、pg_dict、以及 ngram)。
  2. 分词模型(model):将归一化后的字符串词元(或对于 llmlingua2、bert_base_uncased、gemma2b、HuggingFace 及 lindera 等独立子词模型,直接将原始文本)映射为整数词元 ID(INT[])。
说明
  • 预训练子词模型(llmlingua2、bert_base_uncased、gemma2b)、HuggingFace 模型和 Lindera 模型已内置完整的分词规则,不需要(也不允许)配置单独的 text_analyzer 阶段。
  • 词典/语料模型(wiki_tocken 以及通过 create_custom_model 创建的自定义模型)则必须搭配 text_analyzer 阶段,先完成文本切分与归一化,再去模型的词表(Vocabulary Table)中查找词元 ID。

创建与使用分词器

pg_tokenizer 中的所有配置均采用 TOML 格式,并通过美元符号引用的字符串($$ ... $$)传入。

方式一:单阶段分词器(使用内置模型)

SELECT create_tokenizer('llm_tok', $$
model = "llmlingua2"
$$);

SELECT tokenize('PostgreSQL is an object-relational database system.', 'llm_tok');

方式二:内联文本分析器 + 模型

你可以直接在 create_tokenizer 内部定义 text_analyzer 流水线:

SELECT create_tokenizer('wiki_eng', $$
model = "wiki_tocken"

[text_analyzer.character_filters.to_lowercase]
[text_analyzer.pre_tokenizer.unicode_segmentation]
[[text_analyzer.token_filters.stopwords]]
words = "nltk_english"
[[text_analyzer.token_filters.stemmer]]
kind = "english_porter2"
$$);

SELECT tokenize('PostgreSQL is an object-relational database system.', 'wiki_eng');

方式三:可复用的命名文本分析器 + 自定义模型

你可以使用 create_text_analyzer 先创建一个独立的命名 text_analyzer,随后在多个自定义模型和分词器之间复用它:

-- 1. 创建可复用的文本分析器
SELECT create_text_analyzer('english_analyzer', $$
[character_filters.to_lowercase]
[pre_tokenizer.unicode_segmentation]
[[token_filters.skip_non_alphanumeric]]
[[token_filters.stopwords]]
words = "nltk_english"
[[token_filters.stemmer]]
kind = "english_porter2"
$$);

-- 预览文本分析器如何将文本切分为 TEXT[] 词元数组:
SELECT apply_text_analyzer('PostgreSQL is a powerful database system!', 'english_analyzer');
-- 输出:{postgresql,power,databas,system}

管理分词器

-- 查看已配置的分词器列表
SELECT name FROM tokenizer_catalog.tokenizer;

-- 删除指定分词器
SELECT drop_tokenizer('wiki_eng');

原生 PostgreSQL 全文检索(0.1.2,未发布)

analyze_to_tsvector 将文本分析器输出的字符串词元转换为 PostgreSQL 原生 tsvector,适合 Jieba 及自定义词干、停用词流水线。tokenize_to_tsvector 将模型输出的整数 ID 转为十进制词素,适用于内置、自定义、Hugging Face 和 Lindera 模型。它们不解码模型 ID,也不会再次使用 PostgreSQL 分词器归一化文本。

SELECT create_text_analyzer('fts_words', $$
pre_tokenizer = "unicode_segmentation"
[[character_filters]]
to_lowercase = {}
$$);
SELECT analyze_to_tsvector('Cat dog cat', 'fts_words');
-- 'cat':1,3 'dog':2

CREATE TABLE fts_documents (id int PRIMARY KEY, passage text, terms tsvector);
INSERT INTO fts_documents VALUES
    (1, 'Cat dog cat', analyze_to_tsvector('Cat dog cat', 'fts_words'));
CREATE INDEX ON fts_documents USING gin (terms);
SELECT id FROM fts_documents WHERE terms @@ 'cat <-> dog'::tsquery;

SELECT create_tokenizer('fts_bert', 'model = "bert_base_uncased"');
SELECT tokenize_to_tsvector('hello hello', 'fts_bert');
-- '7592':1,2

位置来自最终输出序列,默认权重为 D;它不代表原始文本偏移,因为过滤器会删除、展开或重排词元,模型也可能输出子词。引号、反斜杠、空白和标点会保留,空词元被忽略;空输出返回空向量,NULL 输入返回 NULL。

原生类型限制仍然适用:单个词素小于 2 KB,每个词素最多保存 256 个位置,超过 16,383 的位置被截到 16,383。长文本的词频与距离信息可能因此丢失,详见 PostgreSQL 全文检索限制。

文档与查询应使用相同分析器或模型、相同词素表示;plainto_tsquery('english', ...) 不会生成模型 ID,也不能复现自定义中文分析器。本次不增加文本搜索解析器或 tsquery 构造器。两个函数因读取目录配置而标记为 STABLE:先将结果存入列,再为该列创建 GIN/GiST 索引。接受原生 tsvector 的 BM25 实现也能使用这些值;迁移旧 bm25vector 索引前,应核对所安装 BM25 版本的 SQL API。

3 - 文本分析器与过滤器

pg_tokenizer 中字符过滤器、预分词器与词元过滤器的完整配置参考手册。

文本分析器(Text Analyzer) 通过三个顺序阶段处理原始文本:

  1. 字符过滤器(character_filters) —— 对完整输入字符串执行零个或多个字符级转换。
  2. 预分词器(pre_tokenizer) —— 使用唯一的一个切分器将字符串切分为初始词元。
  3. 词元过滤器(token_filters) —— 对切分出的词元执行零个或多个转换、扩展或过滤操作。
SELECT create_text_analyzer('analyzer1', $$
character_filters = [
    "to_lowercase",
    { unicode_normalization = "nfkd" },
]
pre_tokenizer = "unicode_segmentation"
token_filters = [
    "skip_non_alphanumeric",
    { stopwords = "nltk_english" },
    { stemmer = "english_porter2" },
]
$$);
提示

在将文本分析器绑定到模型之前,你可以直接运行 SELECT apply_text_analyzer('Sample text here', 'analyzer1'); 来测试并查看其输出的 TEXT[] 词元数组。


1. 字符过滤器(character_filters)

to_lowercase

将输入文本中的所有字符转换为小写。

character_filters = ["to_lowercase"]
# 或使用表语法:
[character_filters.to_lowercase]

unicode_normalization

应用 Unicode 规范化形式(UAX #15),将组合/分解字符、重音符号以及全角/半角变体标准化。

支持的模式包括:

  • "nfc" —— 规范化形式 C(标准分解后再执行标准组合)
  • "nfd" —— 规范化形式 D(标准分解)
  • "nfkc" —— 规范化形式 KC(兼容性分解后再执行标准组合)
  • "nfkd" —— 规范化形式 KD(兼容性分解)
[[character_filters]]
unicode_normalization = "nfkd"

2. 预分词器(pre_tokenizer)

每个文本分析器最多只能指定一个 pre_tokenizer。

unicode_segmentation

按照 Unicode 标准附件 #29 (UAX #29) 规范(底层基于 Rust unicode-segmentation 库)在 Unicode 词边界处切分文本。非常适合以空格分隔的西方语言。

pre_tokenizer = "unicode_segmentation"
# 或:
[pre_tokenizer.unicode_segmentation]

regex

提取匹配自定义正则表达式的词元(底层基于 Rust regex 库):

[pre_tokenizer]
regex = '''\w+'''

jieba(中文结巴分词)

使用 jieba-rs 词典与隐马尔可夫模型(HMM)将中文文本切分为词语:

参数 类型 默认值 说明
mode string "search" "full"(调用标准 jieba.cut 分词)、"precise"(调用 jieba.cut_all 扫描句中所有可成词的词语)或 "search"(搜索引擎模式,调用 jieba.cut_for_search 对长词再次切分以提高检索召回率)。
enable_hmm bool true 是否启用 HMM 新词发现能力(仅在 "full" 和 "search" 模式下生效)。
[pre_tokenizer.jieba]
mode = "search"
enable_hmm = true

3. 词元过滤器(token_filters)

词元过滤器会严格按照 TOML 数组中声明的顺序依次执行。

skip_non_alphanumeric

丢弃不包含任何字母或数字字符的词元(例如分词后残留的独立标点符号 ,、.、! 等)。

[[token_filters]]
skip_non_alphanumeric = {}
# 或使用数组简写形式:
token_filters = ["skip_non_alphanumeric"]

stemmer(Snowball 词干提取 —— 支持 30 种语言)

使用 Snowball 词干提取算法将屈折变化的单词还原为其词干形式。

[[token_filters]]
stemmer = "english_porter2"

支持的 stemmer 取值如下:

语言 stemmer 取值 语言 stemmer 取值
阿拉伯语 (Arabic) arabic 立陶宛语 (Lithuanian) lithuanian
亚美尼亚语 (Armenian) armenian 尼泊尔语 (Nepali) nepali
巴斯克语 (Basque) basque 挪威语 (Norwegian) norwegian
加泰罗尼亚语 (Catalan) catalan 葡萄牙语 (Portuguese) portuguese
丹麦语 (Danish) danish 罗马尼亚语 (Romanian) romanian
荷兰语 (Dutch) dutch 俄语 (Russian) russian
英语 (English Porter) english_porter 塞尔维亚语 (Serbian) serbian
英语 (English Porter2) english_porter2 西班牙语 (Spanish) spanish
世界语 (Esperanto) esperanto 瑞典语 (Swedish) swedish
爱沙尼亚语 (Estonian) estonian 泰米尔语 (Tamil) tamil
芬兰语 (Finnish) finnish 土耳其语 (Turkish) turkish
法语 (French) french 意第绪语 (Yiddish) yiddish
德语 (German) german
希腊语 (Greek) greek
印地语 (Hindi) hindi
匈牙利语 (Hungarian) hungarian
印度尼西亚语 (Indonesian) indonesian
爱尔兰语 (Irish) irish
意大利语 (Italian) italian

stopwords(停用词过滤)

移除几乎不携带有效语义信号的常见停用词。

  • 内置停用词典:
    • "lucene_english" —— Apache Lucene 标准英文停用词表
    • "nltk_english" —— NLTK 标准英文停用词表
    • "iso_english" —— Stopwords ISO 英文停用词表
  • 自定义停用词典:通过 create_stopwords(name, text) 创建(每行一个词),随后按名称引用。
-- 创建自定义停用词表(每行一个词)
SELECT create_stopwords('my_stopwords', $$
it
is
a
an
the
$$);

SELECT create_text_analyzer('text_analyzer1', $$
pre_tokenizer = "unicode_segmentation"
[[token_filters]]
stopwords = "my_stopwords"
$$);

SELECT apply_text_analyzer('it is a cat', 'text_analyzer1');
-- 输出:{cat}

synonym(同义词归一)

将同义词组归一化为同一个标准词元。使用 create_synonym(name, text) 创建同义词典,其中每行包含以空格分隔的一组同义词(同一行中的所有词都会被替换为该行的第一个词):

SELECT create_synonym('my_synonym', $$
postgres pgsql postgresql
index indices
$$);

SELECT create_text_analyzer('text_analyzer2', $$
pre_tokenizer = "unicode_segmentation"
[[token_filters]]
synonym = "my_synonym"
$$);

SELECT apply_text_analyzer('pgsql and postgresql have many indices', 'text_analyzer2');
-- 输出:{postgres,and,postgres,have,many,index}

pg_dict(PostgreSQL 全文检索词典)

将词元过滤委托给任意已有的 PostgreSQL 全文检索词典(pg_ts_dict,支持 Simple、Ispell、Snowball 和 Synonym 词典;注意不支持 Thesaurus 分类词典,因为后者作用于多词短语而非单个词元):

[[token_filters]]
pg_dict = "english_stem"

ngram

将每个词元切分为字符级 N-gram(适用于子串匹配、前缀匹配、无词典 CJK 检索,或代码/标识符检索):

参数 类型 默认值 取值范围 说明
min_gram int 1 1..=255 (<= max_gram) 生成 N-gram 的最小字符长度。
max_gram int 2 1..=255 (>= min_gram) 生成 N-gram 的最大字符长度。
preserve_original bool false true / false 当原始词元长度不在 [min_gram, max_gram] 范围内时,是否同时保留未切分的原始词元。
[[token_filters.ngram]]
min_gram = 2
max_gram = 3
preserve_original = true

4 - 分词词表模型

预训练模型(llmlingua2、bert_base_uncased、wiki_tocken、gemma2b)、自定义语料训练模型、HuggingFace 分词器与 Lindera 模型。

在 pg_tokenizer 中,模型(Model) 负责将字符串词元(或原始文本)映射为整数词元 ID(INT[])。你可以直接选用四个内置预训练模型、在 PostgreSQL 表上训练自定义词表模型、导入任意 HuggingFace tokenizer.json,或者使用 Lindera 形态学分析模型。

1. 内置预训练模型

模型名称 来源 / 架构 是否需要 text_analyzer? 内存占用 适用场景
llmlingua2 microsoft/llmlingua-2-xlm-roberta-large-meetingbank(XLM-RoBERTa 多语言子词模型) 否(禁止) ~200 MB(默认预加载) 开箱即用的多语言通用文本分词,无需训练自定义词表。
bert_base_uncased google-bert/bert-base-uncased(WordPiece,30,522 词表) 否(禁止) ~15 MB 通用英文文本;启动极快且词表紧凑。
wiki_tocken Snowolf/wiki-tocken(内置英文维基百科词元词典) 是(必需) ~30 MB 配合自定义词干提取器、停用词与同义词使用的英文全文检索。
gemma2b google/gemma-2b(SentencePiece BPE,256k 词表) 否(禁止) ~100 MB 代码、技术标识符以及多语言大语言模型工作负载。

示例:使用 bert_base_uncased 或 llmlingua2

SELECT create_tokenizer('bert_tok', $$
model = "bert_base_uncased"
$$);

SELECT tokenize('VectorChord-BM25 brings Block-WeakAnd to PostgreSQL.', 'bert_tok');

2. 自定义语料库模型(create_custom_model)

为了获得最高的 BM25 相关性精度与最小的内存占用,你可以直接基于自己的业务表列构建自定义词表模型。不同于大语言模型子词分词器(会将罕见的领域专有名词切碎为多个子词元),自定义模型会为 text_analyzer 产出的每个唯一归一化词元分配独立的 1:1 整数 ID,且在内存中仅占用几兆字节。

方式 A:一步到位辅助函数(create_custom_model_tokenizer_and_trigger)

该便捷函数会一步完成创建自定义模型、创建分词器、为所有存量行回填目标 bm25vector 列,并安装一个 BEFORE INSERT OR UPDATE 触发器——当数据行发生变化时,自动学习新词元并更新 bm25vector 列:

-- 1. 定义文本分析流水线
SELECT create_text_analyzer('my_analyzer', $$
character_filters = ["to_lowercase"]
pre_tokenizer = "unicode_segmentation"
token_filters = [
    "skip_non_alphanumeric",
    { stopwords = "nltk_english" },
    { stemmer = "english_porter2" },
]
$$);

-- 2. 创建模型与分词器、回填向量并挂载触发器
SELECT create_custom_model_tokenizer_and_trigger(
    tokenizer_name     => 'my_tokenizer',
    model_name         => 'my_model',
    text_analyzer_name => 'my_analyzer',
    table_name         => 'documents',
    source_column      => 'passage',
    target_column      => 'embedding'
);

方式 B:分步创建(不使用触发器的 create_custom_model)

如果你不希望挂载自动触发器(或者希望自行控制何时计算并回填 embedding 列):

-- 1. 基于现有表列构建自定义词表模型
SELECT create_custom_model('my_model', $$
table = "documents"
column = "passage"
text_analyzer = "my_analyzer"
$$);

-- 2. 创建引用该分析器与自定义模型的分词器
SELECT create_tokenizer('my_tokenizer', $$
text_analyzer = "my_analyzer"
model = "my_model"
$$);

-- 3. 手动回填或刷新词频向量列
UPDATE documents SET embedding = tokenize(passage, 'my_tokenizer');

3. 导入 HuggingFace 分词器(create_huggingface_model)

你可以使用 create_huggingface_model 将任意 HuggingFace tokenizers 的 JSON 配置文件(tokenizer.json)导入 PostgreSQL。

在 psql 中,可以使用 \set 直接从 HuggingFace Hub 下载 tokenizer.json 到变量中并传给 create_huggingface_model:

\set content `wget -q -O - https://huggingface.co/google-bert/bert-base-uncased/resolve/main/tokenizer.json`
SELECT create_huggingface_model('bert_import', :'content');

SELECT create_tokenizer('bert_import_tok', $$
model = "bert_import"
$$);

也可以直接将 tokenizer.json 文件的完整内容作为美元符号引用的字符串传入:

SELECT create_huggingface_model('bge_m3_model', $$
{
  "version": "1.0",
  ...
}
$$);

SELECT create_tokenizer('bge_m3_tok', $$
model = "bge_m3_model"
$$);

4. Lindera 形态学分词模型(create_lindera_model)

对于日文、韩文、中文等需要形态学词典分析的东亚语言,pg_tokenizer 集成了 lindera。

使用 Lindera 6.2 构建时,原生词典配置为 [segmenter] 下的 dictionary = "embedded://ipadic"。目录表与预加载文件中已有的 [segmenter.dictionary] kind = "ipadic" 配置仍然兼容,旧格式的词典及用户词典 path 表也继续支持。外部编译词典需要使用 Lindera 6 重新构建。

重要

Lindera 6.2 内嵌的 IPADIC 从 Lindera 0.42 使用的 2007-08-01 版升级到 2025-09-20 版,词元 ID 会变化。使用升级后的分词器查询前,请重新生成已存储的 Lindera 词元数组,并重建使用这些数组的索引。未知词仍然会被过滤。

说明

如果你从源码编译 pg_tokenizer,请在编译时通过 --features 启用所需的 Lindera 词典特性:

cargo pgrx install --release --features "lindera-ipadic lindera-ipadic-neologd lindera-unidic lindera-ko-dic lindera-cc-cedict"

以下完整示例展示了基于 ipadic 词典配置日文叠字符(Iteration Mark)规范化、NFKC Unicode 规范化、字符映射、复合词合并、数词归一化、日文停用词性(Stop-tags)过滤、片假名词干提取以及变音符号移除:

SELECT create_lindera_model('japanese_ipadic', $$
[segmenter]
mode = "normal"
  [segmenter.dictionary]
  kind = "ipadic"

[[character_filters]]
kind = "japanese_iteration_mark"
  [character_filters.args]
  normalize_kanji = true
  normalize_kana = true

[[character_filters]]
kind = "unicode_normalize"
  [character_filters.args]
  kind = "nfkc"

[[character_filters]]
kind = "mapping"
  [character_filters.args.mapping]
  "リンデラ" = "Lindera"

[[token_filters]]
kind = "japanese_compound_word"
  [token_filters.args]
  kind = "ipadic"
  tags = [
    "名詞,数",
    "名詞,接尾,助数詞",
  ]
  new_tag = "名詞,数"

[[token_filters]]
kind = "japanese_number"
  [token_filters.args]
  tags = ["名詞,数"]

[[token_filters]]
kind = "japanese_stop_tags"
  [token_filters.args]
  tags = [
    "接続詞",
    "助詞",
    "助詞,格助詞",
    "助詞,格助詞,一般",
    "助詞,格助詞,引用",
    "助詞,格助詞,連語",
    "助詞,係助詞",
    "助詞,副助詞",
    "助詞,間投助詞",
    "助詞,並立助詞",
    "助詞,終助詞",
    "助詞,副助詞/並立助詞/終助詞",
    "助詞,連体化",
    "助詞,副詞化",
    "助詞,特殊",
    "助動詞",
    "記号",
    "記号,一般",
    "記号,読点",
    "記号,句点",
    "記号,空白",
    "記号,括弧閉",
    "その他,間投",
    "フィラー",
    "非言語音",
  ]

[[token_filters]]
kind = "japanese_katakana_stem"
  [token_filters.args]
  min = 3

[[token_filters]]
kind = "remove_diacritical_mark"
  [token_filters.args]
  japanese = false
$$);

SELECT create_tokenizer('jp_tok', $$
model = "japanese_ipadic"
$$);

5. 模型预加载(add_preload_model)

大型模型(如约 200 MB 的 llmlingua2、约 100 MB 的 gemma2b、japanese_ipadic)在每个新建的 PostgreSQL 后端进程首次查询时,需要数秒钟时间和可观的内存来加载。通过在 shared_preload_libraries 启动阶段预加载模型,PostgreSQL 只需在 postmaster 主进程中加载一次模型,即可通过写时复制(Copy-on-Write)内存在所有后端连接之间共享(llmlingua2 默认已加入预加载列表):

-- 将模型加入预加载列表(持久化保存在 $PGDATA/pg_tokenizer/preload_models)
SELECT add_preload_model('my_model');

-- 查看当前已配置的预加载模型列表(默认为 {llmlingua2})
SELECT list_preload_models();

-- 从预加载列表中移除模型(例如不使用 llmlingua2 时可释放约 200 MB 内存)
SELECT remove_preload_model('llmlingua2');

调用 add_preload_model(...) 后(请确保已在 postgresql.conf 中设置 shared_preload_libraries = 'pg_tokenizer.so'),重启 PostgreSQL 即可使预加载对所有后端连接生效。

0.1.2 自定义模型更新(未发布)

词表触发器会去重、排序新词元,并在写入前获取按模型划分的事务级咨询锁,直到事务结束才释放。同一词表的写入在整个事务内串行执行,覆盖多行批次与多条语句,避免相反词元顺序导致的唯一索引死锁。普通分词读取及不同模型的写入仍可并发。请缩短事务;写入多个模型或锁定业务行时,仍需保持一致顺序并处理普通死锁、序列化失败重试。

生成 embedding 的触发器会在词表触发器完成后刷新 SPI 快照,因此在 READ COMMITTED 下可以读取等待期间由其他事务提交的词元 ID。在 REPEATABLE READ 或 SERIALIZABLE 下,冲突的写事务可能收到 SQLSTATE 40001,需要重试整个事务。升级保留已有词表 ID;新模型的 ID 属于内部映射,不保证与独立创建的其他模型相同。

按条件选择语料

SELECT create_custom_model('tenant_a', $$
table = 'public.documents'
column = 'passage'
text_analyzer = 'text_analyzer1'
filter = "tenant_id = 42 AND published IS TRUE"
$$);

-- 或通过便捷函数同时创建模型、分词器和 embedding 触发器。
SELECT create_custom_model_tokenizer_and_trigger(
    tokenizer_name => 'tenant_b_tok', model_name => 'tenant_b',
    text_analyzer_name => 'text_analyzer1', table_name => 'public.documents',
    source_column => 'passage', target_column => 'embedding',
    filter => 'tenant_id = 43 AND published IS TRUE'
);

filter 是使用源表列名的 SQL 布尔表达式,不带 WHERE 或 NEW.,按调用者权限求值。仅 TRUE 匹配,FALSE 和 NULL 均跳过;空表达式或非法表达式即使面对空表也会报错。条件应由普通源列确定,不应依赖 embedding 目标列;不支持 ctid 等系统列及非确定性条件。应传入可信表达式,必要时为函数加上模式限定。该功能用于选择语料,不能替代行级安全策略。

过滤条件同时作用于初始词表扫描、embedding 回填和后续插入/更新。配置过滤条件后,触发器监听所有 UPDATE,因此只修改租户字段也会重新判断。匹配行的源文本变为 NULL 时,embedding 同步设为 NULL。

不匹配行保持原值,包括已有 embedding;行退出语料范围后,不会删除其原 embedding 或词表条目。查询时应使用相同过滤条件,或由应用清空排除行的 embedding。词表只增不减,UPDATE/DELETE 不会裁剪词表。省略 filter 或向便捷函数传入 SQL NULL,仍处理整张表。原六参调用与旧模型配置兼容,并支持带模式的表名及含特殊字符的列名。

带模式的表名优先按限定名解析;如需选择名称本身含点号的表,请使用 SQL 双引号,例如 '"corpus.docs"'。

5 - 多语言分词配方与限制

面向英文、中文(结巴 Jieba)、日文(Lindera)的完整生产级分词配置方案及已知使用限制。

本页提供可直接用于生产环境的英文、中文和日文全文 BM25 检索分词配置配方,并列出在实际运维中需要注意的关键限制事项。

配方一:带词干提取与停用词过滤的英文检索(wiki_tocken)

将 Unicode 词边界切分、NLTK 停用词过滤、Porter2 词干提取与内置维基百科词表(wiki_tocken)结合使用:

SELECT create_tokenizer('eng_wiki', $$
model = "wiki_tocken"

[text_analyzer.character_filters.to_lowercase]
[text_analyzer.pre_tokenizer.unicode_segmentation]
[[text_analyzer.token_filters.skip_non_alphanumeric]]
[[text_analyzer.token_filters.stopwords]]
words = "nltk_english"
[[text_analyzer.token_filters.stemmer]]
kind = "english_porter2"
$$);

SELECT tokenize('PostgreSQL databases are running analytical and vector workloads.', 'eng_wiki');

配方二:带自动触发器的英文自定义语料库模型

源自上游 pg_tokenizer 示例文档,该工作流直接基于 documents 表构建特定领域的英文词表,并通过触发器自动同步更新词频向量:

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

SELECT create_text_analyzer('text_analyzer1', $$
pre_tokenizer = "unicode_segmentation"
[[character_filters]]
to_lowercase = {}
[[character_filters]]
unicode_normalization = "nfkd"
[[token_filters]]
skip_non_alphanumeric = {}
[[token_filters]]
stopwords = "nltk_english"
[[token_filters]]
stemmer = "english_porter2"
$$);

SELECT create_custom_model_tokenizer_and_trigger(
    tokenizer_name => 'tokenizer1',
    model_name => 'model1',
    text_analyzer_name => 'text_analyzer1',
    table_name => 'documents',
    source_column => 'passage',
    target_column => 'embedding'
);

INSERT INTO documents (passage) VALUES
('PostgreSQL is a powerful, open-source object-relational database system. It has over 15 years of active development and a proven architecture that has earned it a strong reputation for reliability, feature robustness, and performance.'),
('Full-text search is a technique for searching a computer-stored document or a collection in a full-text database. Full-text search is distinguished from searches based on metadata or on parts of the original texts represented in databases (such as titles, abstracts, selected sections, or bibliographical references).'),
('BM25 is a ranking function used by search engines to estimate the relevance of documents to a given search query. It is based on the probabilistic retrieval framework developed in the 1970s and 1980s by Stephen E. Robertson, Karen Spärck Jones, and others.'),
('PostgreSQL supports advanced data types and performance optimization features, only available in the expensive commercial databases, such as Oracle and SQL Server. It is known for its resilience, integrity, and correctness.'),
('The BM25 algorithm computes a score for each document in a collection based on the query terms appearing in the document, regardless of their proximity within the document. It is a bag-of-words retrieval function that ranks a set of documents based on the query terms appearing in each document.'),
('PostgreSQL has a wide range of extensions and tools that make it suitable for various use cases, from small single-machine applications to large internet-facing applications with many concurrent users.'),
('Full-text search in PostgreSQL allows users to perform complex queries on text data, including phrase searching, ranking of results, and support for multiple languages through configurations and dictionaries.'),
('VectorChord-BM25 is a postgresql extension designed for bm25 ranking algorithm. Block-WeakAnd algorithm is implemented in it to accelerate the ranking process.'),
('pg_tokenizer is a postgresql extension designed for full-text search. It provides a set of functions to preprocess text data and generate tokens for indexing and searching.'),
('Look for organizations which use PostgreSQL for mission-critical or scaled applications. Look for organizations that have used PostgreSQL for a significant length of time.');

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

SELECT id, passage, embedding <&> to_bm25query('documents_passage_bm25', tokenize('PostgreSQL BM25', 'tokenizer1')) AS rank
FROM documents
ORDER BY rank
LIMIT 10;

配方三:中文全文检索(jieba + 自定义模型)

处理中文文本时,推荐将 jieba 预分词器(mode = "search",enable_hmm = true)与基于业务表训练的自定义语料词表模型结合使用(以下示例采用钱钟书《围城》选段作为测试语料):

CREATE TABLE zh_docs (
    id SERIAL PRIMARY KEY,
    passage TEXT,
    embedding bm25vector
);

-- 1. 创建启用 NFKD 规范化与结巴搜索引擎模式的中文文本分析器
SELECT create_text_analyzer('text_analyzer1', $$
[character_filters.to_lowercase]
[character_filters.unicode_normalization]
kind = "nfkd"
[pre_tokenizer.jieba]
mode = "search"
enable_hmm = true
[[token_filters.skip_non_alphanumeric]]
$$);

-- 2. 创建自定义词表模型、分词器,并绑定自动更新触发器
SELECT create_custom_model_tokenizer_and_trigger(
    tokenizer_name     => 'tokenizer1',
    model_name         => 'model1',
    text_analyzer_name => 'text_analyzer1',
    table_name         => 'zh_docs',
    source_column      => 'passage',
    target_column      => 'embedding'
);

-- 3. 写入中文语料(触发器会自动分词并填充 embedding 列)
INSERT INTO zh_docs (passage) VALUES
('红海早过了,船在印度洋面上开驶着,但是太阳依然不饶人地迟落早起,侵占去大部分的夜。'),
('夜仿佛纸浸了油,变成半透明体;它给太阳拥抱住了,分不出身来,也许是给太阳陶醉了,所以夕照霞隐褪后的夜色也带着酡红。'),
('到红消醉醒,船舱里的睡人也一身腻汗地醒来,洗了澡赶到甲板上吹海风,又是一天开始。'),
('这是七月下旬,合中国旧历的三伏,一年最热的时候。在中国热得更比常年利害,事后大家都说是兵戈之象,因为这就是民国二十六年【一九三七年】。'),
('这条法国邮船白康蒂号(VicomtedeBragelonne)正开向中国来。早晨八点多钟,冲洗过的三等舱甲板湿意未干,但已坐立满了人,法国人、德国流亡出来的犹太人、印度人、安南人,不用说还有中国人。'),
('海风里早含着燥热,胖人身体给风吹干了,蒙上一层汗结的盐霜,仿佛刚在巴勒斯坦的死海里洗过澡。'),
('毕竟是清晨,人的兴致还不没给太阳晒萎,烘懒,说话做事都很起劲。那几个新派到安南或中国租界当警察的法国人,正围了那年轻善撒娇的犹太女人在调情。'),
('俾斯麦曾说过,法国公使大使的特点,就是一句外国话不会讲;这几位警察并不懂德文,居然传情达意,引得犹太女人格格地笑,比他们的外交官强多了。'),
('这女人的漂亮丈夫,在旁顾而乐之,因为几天来,香烟、啤酒、柠檬水全靠这几个警察孝敬。船上买东西用法国钱,这犹太人只有一点儿沿着那位丹麦王子汉姆烈德(Hamlet)路线逃出来的德国马克。'),
('当时德国跟日本虽在谈恋爱,尚未结婚,到中国来的犹太人还是德国公民,有他们本国的保护。这船上的犹太人有过路到香港的,有到上海居留的,聚在一起在讨论生计,声音活像小洋磁盆里托的石子。'),
('几个年轻的安南人比手画脚,像猴子跳电线一样迅捷地交换着法语,华侨在旁羡慕地听着。这船上都是回家乡的中国人,像报纸上的标题,电影的字幕,标语,籍贯姓名年纪一张表上那几格,他们老早填满了,此番回来,不过把这些公式平滑无波地重演一次。'),
('只有鼻梁上架着一副黑眼镜的苏小姐,心算着她离国这几年,家里有什么变化,她父亲是否老了,她母亲新添了什么病痛,她弟弟结婚了没有。'),
('苏小姐在里昂研究法国文学,做了一篇《中国十八世纪的法国文学里的中国》的论文,刚得了博士。'),
('方鸿渐也到甲板上来了,在苏小姐身旁走过,向她脱帽招呼。苏小姐一壁应着,一壁想他真算厚脸皮,昨天向自己借了照相机,今天见了面,提都不提。');

-- 4. 创建 BM25 索引并执行检索
CREATE INDEX zh_docs_passage_bm25 ON zh_docs USING bm25 (embedding bm25_ops);

SELECT id, passage,
       embedding <&> to_bm25query('zh_docs_passage_bm25', tokenize('苏小姐 法国', 'tokenizer1')) AS rank
FROM zh_docs
ORDER BY rank
LIMIT 10;

配方四:日文全文检索(lindera IPADIC)

从 Lindera 0.42 升级到 6.2 时,IPADIC 词元 ID 会变化,需要重新生成已存储的词元数组并重建相关索引。下方的词典配置仍然兼容,详见 Lindera 升级说明。

处理日文文本时,可创建基于 Lindera IPADIC 的形态学分词模型,并配置叠字符规范化、NFKC 规范化、日文停用词性(Stop-tags)过滤以及片假名词干提取(以下示例采用夏目漱石《吾輩は猫である》选段作为测试语料):

CREATE TABLE jp_docs (
    id SERIAL PRIMARY KEY,
    passage TEXT,
    embedding bm25vector
);

INSERT INTO jp_docs (passage) VALUES
('吾輩は猫である。名前はまだ無い。'),
('どこで生れたかとんと見当がつかぬ。何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。'),
('吾輩はここで始めて人間というものを見た。しかもあとで聞くとそれは書生という人間中で一番獰悪な種族であったそうだ。'),
('この書生というのは時々我々を捕えて煮て食うという話である。しかしその当時は何という考もなかったから別段恐しいとも思わなかった。'),
('ただ彼の掌に載せられてスーと持ち上げられた時何だかフワフワした感じがあったばかりである。'),
('掌の上で少し落ちついて書生の顔を見たのがいわゆる人間というものの見始であろう。この時妙なものだと思った感じが今でも残っている。'),
('第一毛をもって装飾されべきはずの顔がつるつるしてまるで薬缶だ。その後猫にもだいぶ逢ったがこんな片輪には一度も出会わした事がない。'),
('のみならず顔の真中があまりに突起している。そうしてその穴の中から時々ぷうぷうと煙を吹く。どうも咽せぽくて実に弱った。'),
('これが人間の飲む煙草というものである事はようやくこの頃知った。'),
('この書生の掌の裏でしばらくはよい心持に坐っておったが、しばらくすると非常な速力で運転し始めた。'),
('書生が動くのか自分だけが動くのか分らないが無暗に眼が廻る。胸が悪くなる。到底助からないと思っていると、どさりと音がして眼から火が出た。'),
('それまでは記憶しているがあとは何の事やらいくら考え出そうとしても分らない。'),
('ふと気が付いて見ると書生はいない。たくさんおった兄弟が一疋も見えぬ。肝心の母親さえ姿を隠してしまった。'),
('その上今までの所とは違って無暗に明るい。眼を明いていられぬくらいだ。果てな何でも容子がおかしいと、のそのそ這い出して見ると非常に痛い。'),
('吾輩は藁の上から急に笹原の中へ棄てられたのである。');

SELECT create_lindera_model('lindera_ipadic', $$
[segmenter]
mode = "normal"
  [segmenter.dictionary]
  kind = "ipadic"

[[character_filters]]
kind = "japanese_iteration_mark"
  [character_filters.args]
  normalize_kanji = true
  normalize_kana = true

[[character_filters]]
kind = "unicode_normalize"
  [character_filters.args]
  kind = "nfkc"

[[character_filters]]
kind = "mapping"
  [character_filters.args.mapping]
  "リンデラ" = "Lindera"

[[token_filters]]
kind = "japanese_compound_word"
  [token_filters.args]
  kind = "ipadic"
  tags = [
    "名詞,数",
    "名詞,接尾,助数詞",
  ]
  new_tag = "名詞,数"

[[token_filters]]
kind = "japanese_number"
  [token_filters.args]
  tags = ["名詞,数"]

[[token_filters]]
kind = "japanese_stop_tags"
  [token_filters.args]
  tags = [
    "接続詞", "助詞", "助詞,格助詞", "助詞,格助詞,一般", "助詞,格助詞,引用",
    "助詞,格助詞,連語", "助詞,係助詞", "助詞,副助詞", "助詞,間投助詞",
    "助詞,並立助詞", "助詞,終助詞", "助詞,副助詞/並立助詞/終助詞",
    "助詞,連体化", "助詞,副詞化", "助詞,特殊", "助動詞", "記号",
    "記号,一般", "記号,読点", "記号,句点", "記号,空白", "記号,括弧閉",
    "その他,間投", "フィラー", "非言語音"
  ]

[[token_filters]]
kind = "japanese_katakana_stem"
  [token_filters.args]
  min = 3

[[token_filters]]
kind = "remove_diacritical_mark"
  [token_filters.args]
  japanese = false
$$);

SELECT create_tokenizer('tokenizer1', $$
model = "lindera_ipadic"
$$);

UPDATE jp_docs SET embedding = tokenize(passage, 'tokenizer1');

CREATE INDEX jp_docs_passage_bm25 ON jp_docs USING bm25 (embedding bm25_ops);

SELECT id, passage,
       embedding <&> to_bm25query('jp_docs_passage_bm25', tokenize('人間 吾輩', 'tokenizer1')) AS rank
FROM jp_docs
ORDER BY rank
LIMIT 10;

运维注意事项与已知限制

  1. 对象名称命名规范:所有分词器、文本分析器、模型、停用词典和同义词典的名称只能包含字母、数字和下划线([A-Za-z0-9_]),且不能以数字开头。
  2. 事务回滚与会话级内存对象缓存:每个 PostgreSQL 后端连接都会在进程内存(DashMap)中缓存 tokenizer、text_analyzer、model、stopwords 和 synonym 对象。由于该内存对象池未挂载到 PostgreSQL 事务回滚回调机制上,当事务执行 ROLLBACK 时,磁盘上的 tokenizer_catalog 元数据表会回滚,但当前连接的内存缓存中仍会残留刚才创建的对象。在 ROLLBACK 之后,请显式调用对应的 drop_* 函数(或重新建立数据库连接)以清理内存缓存:
    BEGIN;
    SELECT create_text_analyzer('text_analyzer1', $$
    pre_tokenizer = "unicode_segmentation"
    $$);
    ROLLBACK;
    
    -- 清理当前会话内存中残留的缓存项(或者重新建立数据库连接)
    SELECT drop_text_analyzer('text_analyzer1');
    
    -- 现在可以正常重新创建同名对象
    SELECT create_text_analyzer('text_analyzer1', $$
    pre_tokenizer = "unicode_segmentation"
    $$);
  3. 只读从库上的模型预加载:add_preload_model 会将预加载模型列表写入主节点本地的 $PGDATA/pg_tokenizer 目录。由于 PostgreSQL 关系表以外的文件系统文件不会通过 WAL 日志复制到从库,如果你在只读从库上也需要预加载模型,请在从库上独立执行一次 add_preload_model。
  4. 内置大语言模型分词器的内存开销:llmlingua2(~200 MB)、gemma2b(~100 MB)和 lindera 等模型会占用较多内存。在生产环境中,请务必使用 add_preload_model 让模型在 postmaster 主进程中仅加载一次,避免在每个连接中重复占用内存;或者优先选用仅占用几兆字节内存的自定义语料模型(create_custom_model)。

6 - SQL 与 TOML 参考

pg_tokenizer 中全部 tokenizer_catalog SQL 函数与 TOML 配置选项的完整参考手册。

SQL 函数参考(tokenizer_catalog)

文本分析器与词典管理

函数签名 返回类型 说明
tokenizer_catalog.create_text_analyzer(name TEXT, config TEXT) VOID 根据 TOML 配置字符串创建命名文本分析器流水线。
tokenizer_catalog.drop_text_analyzer(name TEXT) VOID 按名称删除文本分析器。
tokenizer_catalog.apply_text_analyzer(text TEXT, text_analyzer_name TEXT) TEXT[] 使用指定文本分析器切分并过滤 text,返回词元数组以供调试预览。
tokenizer_catalog.create_stopwords(name TEXT, config TEXT) VOID 创建自定义停用词词典(每行一个停用词)。
tokenizer_catalog.drop_stopwords(name TEXT) VOID 删除自定义停用词词典。
tokenizer_catalog.create_synonym(name TEXT, config TEXT) VOID 创建自定义同义词词典(每行一组以空格分隔的同义词,统一映射为首词)。
tokenizer_catalog.drop_synonym(name TEXT) VOID 删除自定义同义词词典。

分词模型与预加载管理

函数签名 返回类型 说明
tokenizer_catalog.create_custom_model(name TEXT, config TEXT) VOID 基于现有数据表列构建自定义词表模型。
tokenizer_catalog.create_custom_model_tokenizer_and_trigger(tokenizer_name TEXT, model_name TEXT, text_analyzer_name TEXT, table_name TEXT, source_column TEXT, target_column TEXT) VOID 一键创建自定义词表模型与分词器,回填 target_column,并在 source_column 上安装自动更新触发器。
tokenizer_catalog.drop_custom_model(name TEXT) VOID 删除自定义词表模型。
tokenizer_catalog.create_huggingface_model(name TEXT, config TEXT) VOID 导入 HuggingFace tokenizer.json 模型定义。
tokenizer_catalog.drop_huggingface_model(name TEXT) VOID 删除已导入的 HuggingFace 模型。
tokenizer_catalog.create_lindera_model(name TEXT, config TEXT) VOID 根据 TOML 配置创建 Lindera 形态学分词模型。
tokenizer_catalog.drop_lindera_model(name TEXT) VOID 删除 Lindera 模型。
tokenizer_catalog.add_preload_model(name TEXT) VOID 将模型加入共享内存预加载列表(重启 PostgreSQL 后生效)。
tokenizer_catalog.remove_preload_model(name TEXT) VOID 将模型移出预加载列表。
tokenizer_catalog.list_preload_models() TEXT[] 列出当前已注册预加载的所有模型名称。

分词器执行函数

函数签名 返回类型 说明
tokenizer_catalog.create_tokenizer(name TEXT, config TEXT) VOID 创建分词器(组合可选的 text_analyzer 与必选的 model)。
tokenizer_catalog.drop_tokenizer(name TEXT) VOID 按名称删除分词器。
tokenizer_catalog.tokenize(text TEXT, tokenizer_name TEXT) INT[] 将 text 分词为整数词元 ID 数组(可隐式转换为 bm25vector)。

TOML 配置参数参考

text_analyzer 配置项

键名 类型 说明
character_filters 表数组 / 字符串数组 字符级过滤转换(to_lowercase、unicode_normalization = "nfc" | "nfd" | "nfkc" | "nfkd")。
pre_tokenizer 表 / 字符串 初始分词切分器(unicode_segmentation、regex = "..." 或 jieba)。
token_filters 表数组 / 字符串数组 词元级过滤器(skip_non_alphanumeric、stemmer、stopwords、synonym、pg_dict、ngram)。

tokenizer 配置项

键名 类型 说明
text_analyzer 字符串 已创建的 text_analyzer 名称(或内联 [text_analyzer] 表)。使用独立子词模型时无需配置。
model 字符串 内置预训练模型(llmlingua2、bert_base_uncased、wiki_tocken、gemma2b)或自定义/HuggingFace/Lindera 模型名称。

custom_model 配置项

键名 类型 说明
table 字符串 存放训练语料的数据表名称。
column 字符串 存放原始文本的 TEXT 列名称。
text_analyzer 字符串 用于提取词表词元的 text_analyzer 名称。

0.1.2 新增接口(未发布)

接口 返回类型 行为
tokenizer_catalog.analyze_to_tsvector(text TEXT, text_analyzer_name TEXT) TSVECTOR 将分析器输出的字符串词元转换为带位置的原生词素。
tokenizer_catalog.tokenize_to_tsvector(text TEXT, tokenizer_name TEXT) TSVECTOR 将模型输出的词元 ID 转为十进制词素,保留输出位置。

两个函数均为 STABLE;先存入列,再为该列创建 GIN/GiST 索引。文档与查询必须采用相同的词素表示,详见原生全文检索。

create_custom_model 的 TOML 配置新增可选字符串 filter,内容为源表列上的 SQL 布尔表达式,不包含 WHERE 或 NEW.。create_custom_model_tokenizer_and_trigger 新增七参重载,最后一参为 filter TEXT;原六参调用继续可用,省略过滤条件或传入 SQL NULL 表示选取全部行。table 支持带模式名的表名。详见过滤与并发语义。