分词词表模型
在 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
2. 自定义语料库模型(create_custom_model)
为了获得最高的 BM25 相关性精度与最小的内存占用,你可以直接基于自己的业务表列构建自定义词表模型。不同于大语言模型子词分词器(会将罕见的领域专有名词切碎为多个子词元),自定义模型会为 text_analyzer 产出的每个唯一归一化词元分配独立的 1:1 整数 ID,且在内存中仅占用几兆字节。
方式 A:一步到位辅助函数(create_custom_model_tokenizer_and_trigger)
该便捷函数会一步完成创建自定义模型、创建分词器、为所有存量行回填目标 bm25vector 列,并安装一个 BEFORE INSERT OR UPDATE 触发器——当数据行发生变化时,自动学习新词元并更新 bm25vector 列:
方式 B:分步创建(不使用触发器的 create_custom_model)
如果你不希望挂载自动触发器(或者希望自行控制何时计算并回填 embedding 列):
3. 导入 HuggingFace 分词器(create_huggingface_model)
你可以使用 create_huggingface_model 将任意 HuggingFace tokenizers 的 JSON 配置文件(tokenizer.json)导入 PostgreSQL。
在 psql 中,可以使用 \set 直接从 HuggingFace Hub 下载 tokenizer.json 到变量中并传给 create_huggingface_model:
也可以直接将 tokenizer.json 文件的完整内容作为美元符号引用的字符串传入:
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 词典特性:
以下完整示例展示了基于 ipadic 词典配置日文叠字符(Iteration Mark)规范化、NFKC Unicode 规范化、字符映射、复合词合并、数词归一化、日文停用词性(Stop-tags)过滤、片假名词干提取以及变音符号移除:
5. 模型预加载(add_preload_model)
大型模型(如约 200 MB 的 llmlingua2、约 100 MB 的 gemma2b、japanese_ipadic)在每个新建的 PostgreSQL 后端进程首次查询时,需要数秒钟时间和可观的内存来加载。通过在 shared_preload_libraries 启动阶段预加载模型,PostgreSQL 只需在 postmaster 主进程中加载一次模型,即可通过写时复制(Copy-on-Write)内存在所有后端连接之间共享(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 属于内部映射,不保证与独立创建的其他模型相同。
按条件选择语料
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"'。