跳到主要内容

文本搜索类型

PostgreSQL 全文搜索使用 tsvector 保存规范化后的词项及其位置,使用 tsquery 表达搜索条件。它们不是普通字符串,而是为词法分析、相关性排序和索引设计的专用类型。

tsvector:文档的搜索表示​

SELECT to_tsvector('english', 'The quick brown fox jumps');

结果会去掉停用词、统一词形,并保留词项位置。配置名称决定分词、停用词和词干化规则;english 不适合直接处理中文。

可以把多个字段加权合并:

SELECT setweight(to_tsvector('simple', coalesce(title, '')), 'A') ||
setweight(to_tsvector('simple', coalesce(body, '')), 'D')
FROM articles;

coalesce 避免 NULL 让整个连接结果变成 NULL。

tsquery:搜索条件​

SELECT to_tsquery('english', 'quick & fox');
SELECT plainto_tsquery('english', 'quick fox');
SELECT websearch_to_tsquery('english', '"quick fox" -cat');

to_tsquery 接受带操作符的查询语法;plainto_tsquery 更适合把普通输入转换为 AND 条件;websearch_to_tsquery 模仿搜索框语法并减少用户输入语法错误。不要把未经验证的用户字符串直接拼进 to_tsquery。

匹配和高亮​

SELECT article_id
FROM articles
WHERE search_vector @@ plainto_tsquery('simple', $1);

SELECT ts_headline('simple', body, plainto_tsquery('simple', $1))
FROM articles;

@@ 判断向量是否匹配查询。ts_headline 适合生成摘要,但会解析和处理文本,不能当作安全 HTML;输出到网页时仍需正确转义或使用可信渲染流程。

生成列和索引​

ALTER TABLE articles
ADD COLUMN search_vector tsvector
GENERATED ALWAYS AS (
setweight(to_tsvector('simple', coalesce(title, '')), 'A') ||
setweight(to_tsvector('simple', coalesce(body, '')), 'D')
) STORED;

CREATE INDEX articles_search_vector_gin
ON articles USING gin (search_vector);

GIN 通常适合全文搜索;GiST 也能支持但可能有损,需重新检查候选行。使用表达式索引时,查询中的配置和表达式必须一致。

配置、语言和中文​

全文搜索不是“把文本拆成空格”。不同语言需要不同词法分析器、停用词表和词干规则。PostgreSQL 内置配置对中文分词能力有限,中文项目应评估 simple、扩展、应用层分词或外部搜索服务,并用真实语料测试召回率和排序质量。

排序和维护​

SELECT article_id,
ts_rank_cd(search_vector, query) AS rank
FROM articles,
plainto_tsquery('simple', $1) AS query
WHERE search_vector @@ query
ORDER BY rank DESC;

词典和配置升级可能改变向量结果,需要重建生成数据和索引。大规模更新全文列时要关注 WAL、锁和索引膨胀。

常见误区​

  • 把 tsvector 当作原文,直接用它展示给用户。
  • 用 to_tsquery 直接接收任意搜索框输入,造成语法错误。
  • 认为全文搜索自动支持中文、拼音和同义词。
  • 只建立 GIN 索引,却没有把查询写成可使用索引的 @@。
  • 修改配置后不重建已有的 tsvector 数据。