前置知识: PostgreSQL

全文检索

1 min高级

PostgreSQL全文检索:tsvector、tsquery、GIN索引、排名与多语言支持

1. 全文检索概述

PostgreSQL 内置全文检索功能,基于 tsvector(文档向量)和 tsquery(查询向量)。

2. tsvector 与 tsquery

-- 文本转向量
SELECT to_tsvector('english', 'The quick brown fox jumps over the lazy dog');
-- 'brown':3 'dog':9 'fox':4 'jumps':5 'lazi':8 'quick':2

-- 查询转向量
SELECT to_tsquery('english', 'quick & fox');
-- 'quick' & 'fox'

-- 匹配操作符 @@
SELECT * FROM articles
WHERE to_tsvector('english', content) @@ to_tsquery('english', 'database & index');

3. GIN 索引

-- 创建 GIN 全文索引
CREATE INDEX idx_articles_fts ON articles
USING GIN (to_tsvector('english', content));

-- 使用触发器自动更新 tsvector 列
ALTER TABLE articles ADD COLUMN fts tsvector
    GENERATED ALWAYS AS (to_tsvector('english', coalesce(title,'') || ' ' || coalesce(content,''))) STORED;

CREATE INDEX idx_articles_fts ON articles USING GIN (fts);

-- 查询
SELECT * FROM articles WHERE fts @@ to_tsquery('english', 'database & index');

4. 排名

-- ts_rank:基于词频排名
SELECT title, ts_rank(fts, query) AS rank
FROM articles, to_tsquery('english', 'database') query
WHERE fts @@ query
ORDER BY rank DESC;

-- ts_rank_cd:覆盖密度排名
SELECT title, ts_rank_cd(fts, query) AS rank
FROM articles, to_tsquery('english', 'database') query
WHERE fts @@ query
ORDER BY rank DESC;

5. 中文全文检索

-- 安装 zhparser 扩展
CREATE EXTENSION zhparser;

-- 创建中文配置
CREATE TEXT SEARCH CONFIGURATION zh (PARSER = zhparser);
ALTER TEXT SEARCH CONFIGURATION zh ADD MAPPING FOR n,v,a,i,e,l WITH simple;

-- 使用中文配置
SELECT to_tsvector('zh', '数据库索引优化');
CREATE INDEX idx_articles_zh ON articles USING GIN (to_tsvector('zh', content));

tsvector 与 tsquery

基本写法:创建 tsvector 文档 to_tsvector([<配置>,] <文本>)

-- 将文本转换为标准化词素(分词、去停用词、词干化)
SELECT to_tsvector('english', 'The quick brown fox jumps');
-- 输出: 'brown':3 'fox':4 'jump':5 'quick':2
-- 中文无内置配置: 需安装 zhparser 扩展并创建自定义配置(见下文"中文搜索配置"),
-- 配置名自取(如 'chinese' / 'zh'), 创建后才能使用
SELECT to_tsvector('chinese', '数据库性能优化');

基本写法:创建 tsquery 查询 to_tsquery([<配置>,] <查询表达式>) / plainto_tsquery(<文本>)

-- to_tsquery 支持运算符 & | ! <->(与/或/非/相邻)
SELECT to_tsquery('english', 'quick & brown');
-- plainto_tsquery 自动处理(不支持运算符,全部 AND)
SELECT plainto_tsquery('english', 'quick brown fox');
-- phraseto_tsquery 短语匹配(词序敏感)
SELECT phraseto_tsquery('english', 'quick brown fox');
-- websearch_to_tsquery 类似搜索引擎语法
SELECT websearch_to_tsquery('english', 'quick OR brown -slow');

基本写法:手动构造 tsvector '<词1>:<位置> <词2>:<位置>'::tsvector

-- 手动指定词与位置
SELECT '数据库:1 性能:2 优化:3'::tsvector;
-- 含权重(A 最高,D 默认)
SELECT setweight('数据库:1 性能:2'::tsvector, 'A');

搜索查询

基本写法:全文匹配 WHERE to_tsvector(<列>) @@ to_tsquery(<查询>)

-- 使用 @@ 操作符匹配
SELECT id, title
FROM articles
WHERE to_tsvector(title) @@ to_tsquery('database & performance');

基本写法:返回相关性排序 ts_rank(<tsvector>, <tsquery>)

-- 按相关性分数排序
SELECT id, title,
  ts_rank(to_tsvector(title), to_tsquery('database')) AS rank
FROM articles
WHERE to_tsvector(title) @@ to_tsquery('database')
ORDER BY rank DESC;
-- ts_rank_cd 考虑词距(覆盖密度)
SELECT id, ts_rank_cd(to_tsvector(body), query) FROM articles;

基本写法:高亮显示 ts_headline([<配置>,] <原文>, <tsquery> [, <选项>])

-- 返回带高亮标记的摘要
SELECT ts_headline('english', body, to_tsquery('database & performance'),
  'StartSel=<b>, StopSel=</b>, MaxWords=35, MinWords=15')
FROM articles
WHERE to_tsvector(body) @@ to_tsquery('database & performance');

GIN 索引

基本写法:创建 GIN 全文索引 CREATE INDEX <索引名> ON <表名> USING GIN (to_tsvector(<配置>, <列>));

-- 为表达式创建 GIN 索引加速全文搜索
CREATE INDEX idx_articles_body_fts
ON articles USING GIN (to_tsvector('english', body));

基本写法:生成列加速索引 <列> tsvector GENERATED ALWAYS AS (to_tsvector(...)) STORED

-- 使用生成列避免重复计算
CREATE TABLE articles (
  id SERIAL PRIMARY KEY,
  title TEXT,
  body TEXT,
  body_tsv tsvector GENERATED ALWAYS AS (to_tsvector('english', body)) STORED
);
-- 为生成列建索引
CREATE INDEX idx_articles_body_tsv ON articles USING GIN (body_tsv);
-- 直接查询生成列
SELECT * FROM articles WHERE body_tsv @@ to_tsquery('database');

搜索配置

基本写法:查看搜索配置 SELECT * FROM pg_ts_config;

-- 查看可用的文本搜索配置
SELECT cfgname FROM pg_ts_config;
-- 默认配置(通常为 simple 或 english)
SHOW default_text_search_config;
-- 设置默认配置
SET default_text_search_config = 'english';

基本写法:中文搜索配置 CREATE TEXT SEARCH CONFIGURATION <配置名> (...)

-- 使用 zhparser 扩展配置中文搜索
CREATE EXTENSION IF NOT EXISTS zhparser;
CREATE TEXT SEARCH CONFIGURATION chinese (PARSER = zhparser);
ALTER TEXT SEARCH CONFIGURATION chinese
  ADD MAPPING FOR n,v,a,i,e,l WITH simple;
-- 使用配置
SELECT to_tsvector('chinese', '数据库性能优化');

复合搜索

基本写法:多列加权搜索 setweight(to_tsvector(<列1>), 'A') || setweight(to_tsvector(<列2>), 'B')

-- 标题权重 A(最高),正文权重 D(默认)
SELECT id,
  setweight(to_tsvector(title), 'A') ||
  setweight(to_tsvector(body), 'D') AS document
FROM articles;
-- 加权相关性排序(标题匹配分数更高)
SELECT id, title,
  ts_rank(setweight(to_tsvector(title), 'A') ||
          setweight(to_tsvector(body), 'D'),
          to_tsquery('database')) AS rank
FROM articles
WHERE to_tsvector(title) || to_tsvector(body) @@ to_tsquery('database')
ORDER BY rank DESC;

基本写法:词组相邻搜索 phraseto_tsquery(<配置>, '<短语>') 或 <词1> <-> <词2>

-- 精确短语匹配(词序相邻)
SELECT * FROM articles
WHERE to_tsvector(body) @@ phraseto_tsquery('database performance');
-- 指定相邻距离
SELECT * FROM articles
WHERE to_tsvector(body) @@ to_tsquery('database <3> performance');

字典与停用词

基本写法:查看字典 SELECT * FROM pg_ts_dict;

-- 查看可用字典
SELECT dictname, dictinit FROM pg_ts_dict;

基本写法:自定义停用词 CREATE TEXT SEARCH DICTIONARY <名称> (TEMPLATE = pg_catalog.simple, STOPWORDS = <停用词集>);

-- 创建自定义停用词字典
CREATE TEXT SEARCH DICTIONARY my_simple (
  TEMPLATE = pg_catalog.simple,
  STOPWORDS = my_stopwords
);