文本搜索类型
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数据。