Appearance
PostgreSQL 文本搜索解析器
概述
文本搜索解析器是 PostgreSQL 全文搜索系统的核心组件之一,负责将原始文档文本拆分成令牌(tokens)并识别每个令牌的类型。解析器的主要职责是识别单词边界,而不对文本内容进行修改或变换。
INFO
解析器的作用范围
解析器只负责文本的词法分析阶段,即:
- 识别单词边界
- 分类令牌类型
- 不修改原始文本内容
- 不处理语义或语法分析
工作原理
解析器在全文搜索流程中的位置和作用可以用以下流程图表示:
内置解析器:pg_catalog.default
PostgreSQL 提供了一个功能强大的内置解析器 pg_catalog.default
,它能够识别 23 种不同类型的令牌,适用于绝大多数应用场景。
解析器基本信息查询
sql
-- 查看可用的解析器
SELECT parsername, prsstart, prstoken, prsend, prslextype
FROM pg_ts_parser;
-- 查看默认解析器的详细信息
\dF+ default
输出示例:
parsername | prsstart | prstoken | prsend | prslextype
-----------+------------------------------------+------------------------------------+------------------------------------+------------------------------------
default | prsd_start | prsd_nexttoken | prsd_end | prsd_lextype
令牌类型详解
令牌类型分类表
类别 | 令牌类型 | 描述 | 示例 | 业务场景 |
---|---|---|---|---|
单词类 | asciiword | 单词,全部是 ASCII 字母 | elephant | 英文文档搜索 |
word | 单词,全部是字母 | mañana | 多语言文档搜索 | |
numword | 单词,字母和数字 | beta1 | 产品版本、型号搜索 | |
连字符类 | asciihword | 连字符单词,全部是 ASCII | up-to-date | 复合词搜索 |
hword | 连字符单词,全部是字母 | lógico-matemática | 多语言复合词 | |
numhword | 连字符单词,字母和数字 | postgresql-beta1 | 软件版本、代码标识 | |
连字符部分 | hword_asciipart | 连字符单词的 ASCII 部分 | postgresql (来自 postgresql-beta1 ) | 复合词部分匹配 |
hword_part | 连字符单词的字母部分 | lógico (来自 lógico-matemática ) | 多语言复合词部分 | |
hword_numpart | 连字符单词的数字字母部分 | beta1 (来自 postgresql-beta1 ) | 版本号部分匹配 | |
网络类 | email | 电子邮件地址 | [email protected] | 联系信息搜索 |
protocol | 协议头 | http:// | URL 协议识别 | |
url | URL | example.com/stuff/index.html | 链接搜索 | |
host | 主机名 | example.com | 域名搜索 | |
url_path | URL 路径 | /stuff/index.html | 路径匹配 | |
文件类 | file | 文件或路径名 | /usr/local/foo.txt | 文件路径搜索 |
数值类 | sfloat | 科学记数法 | -1.234e56 | 科学数据搜索 |
float | 十进制记数法 | -1.234 | 价格、测量值搜索 | |
int | 带符号整数 | -1234 | 数值搜索 | |
uint | 无符号整数 | 1234 | ID、数量搜索 | |
version | 版本号 | 8.3.0 | 软件版本管理 | |
标记类 | tag | XML 标签 | <a href="dictionaries.html"> | HTML/XML 文档搜索 |
entity | XML 实体 | & | 实体引用搜索 | |
其他 | blank | 空格符号 | (任何未识别的空格或标点符号) | 分隔符处理 |
实际业务场景示例
1. 产品文档搜索系统
场景描述: 为软件产品文档建立全文搜索功能
sql
-- 创建产品文档表
CREATE TABLE product_docs (
id SERIAL PRIMARY KEY,
title TEXT,
content TEXT,
version VARCHAR(20),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 插入测试数据
INSERT INTO product_docs (title, content, version) VALUES
('PostgreSQL-14.2 安装指南',
'PostgreSQL-14.2 支持 up-to-date 的特性,下载地址:https://postgresql.org/download/。
联系支持:[email protected],文档路径:/usr/local/pgsql/doc/README.txt',
'14.2'),
('数据库性能优化',
'查询优化技巧:使用 EXPLAIN ANALYZE,调整 shared_buffers=256MB,
监控慢查询日志 postgresql-slow.log',
'14.1');
-- 为内容创建全文搜索索引
ALTER TABLE product_docs ADD COLUMN content_tsvector TSVECTOR;
UPDATE product_docs SET content_tsvector = to_tsvector('english', content);
CREATE INDEX idx_product_docs_fts ON product_docs USING gin(content_tsvector);
2. 解析器令牌分析实例
sql
-- 分析复杂文本的令牌解析结果
SELECT alias, description, token
FROM ts_debug('english', 'PostgreSQL-14.2 installation guide: https://postgresql.org/download/ Contact: [email protected] File: /usr/local/pgsql/README.txt');
输出分析:
alias | description | token
----------------+------------------------------------------+---------------------
numhword | Hyphenated word, letters and digits | PostgreSQL-14.2 -- 识别为版本化产品名
hword_asciipart| Hyphenated word part, all ASCII | PostgreSQL -- 产品名部分
blank | Space symbols | - -- 分隔符
hword_numpart | Hyphenated word part, letters and digits| 14.2 -- 版本号部分
blank | Space symbols | -- 空格
asciiword | Word, all ASCII letters | installation -- 普通英文单词
asciiword | Word, all ASCII letters | guide -- 普通英文单词
blank | Space symbols | : -- 标点符号
blank | Space symbols | -- 空格
protocol | Protocol head | https:// -- 协议头
url | URL | postgresql.org/download/ -- 完整URL
host | Host | postgresql.org -- 主机名
url_path | URL path | /download/ -- URL路径
asciiword | Word, all ASCII letters | Contact -- 普通单词
blank | Space symbols | : -- 标点符号
blank | Space symbols | -- 空格
email | Email address | [email protected] -- 邮件地址
asciiword | Word, all ASCII letters | File -- 普通单词
blank | Space symbols | : -- 标点符号
blank | Space symbols | -- 空格
file | File or path name | /usr/local/pgsql/README.txt -- 文件路径
3. 重叠令牌的优势演示
问题陈述: 用户搜索 "postgresql" 时,希望能匹配到 "postgresql-beta1" 这样的复合词
解决方案: 利用解析器的重叠令牌特性
sql
-- 演示重叠令牌解析
SELECT alias, description, token
FROM ts_debug('foo-bar-beta1');
输出结果:
alias | description | token
-----------------+------------------------------------------+---------------
numhword | Hyphenated word, letters and digits | foo-bar-beta1 -- 完整复合词
hword_asciipart | Hyphenated word part, all ASCII | foo -- 第一部分
blank | Space symbols | - -- 连字符
hword_asciipart | Hyphenated word part, all ASCII | bar -- 第二部分
blank | Space symbols | - -- 连字符
hword_numpart | Hyphenated word part, letters and digits | beta1 -- 第三部分
分析过程:
- 完整匹配:
numhword
类型保存了完整的 "foo-bar-beta1" - 部分匹配:
hword_asciipart
和hword_numpart
保存了各个组成部分 - 搜索灵活性:用户可以搜索完整词或任意部分都能匹配
sql
-- 验证搜索效果
SELECT to_tsvector('foo-bar-beta1') @@ to_tsquery('foo') AS part_match,
to_tsvector('foo-bar-beta1') @@ to_tsquery('foo-bar-beta1') AS full_match,
to_tsvector('foo-bar-beta1') @@ to_tsquery('beta1') AS version_match;
输出:
part_match | full_match | version_match
------------+------------+---------------
t | t | t
高级应用场景
1. 多语言文档系统
sql
-- 创建多语言支持的文档表
CREATE TABLE multilang_docs (
id SERIAL PRIMARY KEY,
title TEXT,
content TEXT,
language VARCHAR(10),
content_vector TSVECTOR
);
-- 插入多语言测试数据
INSERT INTO multilang_docs (title, content, language) VALUES
('English Doc', 'PostgreSQL database system with up-to-date features', 'en'),
('Spanish Doc', 'Sistema de base de datos lógico-matemática avanzado', 'es');
-- 根据语言配置不同的解析器
UPDATE multilang_docs
SET content_vector = to_tsvector(
CASE language
WHEN 'en' THEN 'english'::regconfig
WHEN 'es' THEN 'spanish'::regconfig
ELSE 'simple'::regconfig
END,
content
);
2. 电子商务产品搜索
sql
-- 产品搜索场景
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name TEXT,
description TEXT,
model_number TEXT,
price DECIMAL(10,2),
search_vector TSVECTOR
);
-- 插入产品数据
INSERT INTO products (name, description, model_number, price) VALUES
('iPhone-14 Pro', 'Latest smartphone with A16-chip technology', 'MQ0G3LL/A', 999.99),
('MacBook-Pro M2', 'Professional laptop with M2-processor', 'MNEH3LL/A', 1299.99);
-- 创建综合搜索向量
UPDATE products SET search_vector =
setweight(to_tsvector('english', name), 'A') ||
setweight(to_tsvector('english', description), 'B') ||
setweight(to_tsvector('english', model_number), 'C');
-- 复合搜索查询
SELECT name, model_number,
ts_rank(search_vector, query) AS rank
FROM products,
to_tsquery('english', 'iPhone | Pro | A16') AS query
WHERE search_vector @@ query
ORDER BY rank DESC;
令牌类型的区域设置影响
WARNING
区域设置重要性
解析器对"字母"的识别严重依赖于数据库的区域设置,特别是 lc_ctype
参数。
查看当前区域设置
sql
-- 查看当前数据库的区域设置
SHOW lc_ctype;
SHOW lc_collate;
-- 查看所有可用的区域设置
SELECT name, setting, short_desc
FROM pg_settings
WHERE name LIKE 'lc_%';
区域设置对解析的影响
sql
-- 在不同区域设置下测试相同文本
SELECT alias, token
FROM ts_debug('简体中文文档'); -- 中文字符处理
SELECT alias, token
FROM ts_debug('café-résumé'); -- 带重音符号的法语
自定义解析器场景
虽然默认解析器已经非常强大,但某些特殊业务场景可能需要自定义解析器:
何时考虑自定义解析器
Details
自定义解析器的考虑因素
适合自定义的场景:
- 特殊的标记格式(如特定的产品编号格式)
- 专业领域的术语识别
- 特殊的分词需求
不建议自定义的场景:
- 通用文档搜索
- 标准的多语言支持
- 常见的网页内容解析
查看解析器实现细节
sql
-- 查看解析器的C函数实现
SELECT prosrc FROM pg_proc WHERE proname = 'prsd_start';
-- 查看令牌类型定义
SELECT alias, description
FROM ts_token_type('default')
ORDER BY tokid;
性能优化建议
1. 解析器性能特点
2. 优化策略
TIP
性能优化建议
- 预处理文本:移除不必要的格式化字符
- 合理分段:将长文档分割成合理大小的段落
- 选择性索引:只对需要搜索的字段建立全文索引
- 定期维护:使用
VACUUM
和REINDEX
维护搜索性能
sql
-- 性能监控查询
EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM documents
WHERE content_vector @@ to_tsquery('postgresql & performance');
-- 索引使用情况
SELECT schemaname, tablename, indexname, idx_scan, idx_tup_read
FROM pg_stat_user_indexes
WHERE indexname LIKE '%_fts%';
常见问题与解决方案
1. 电子邮件解析限制
默认解析器的 `email` 类型不支持 RFC 5322 定义的所有有效字符,仅支持:句点、破折号和下划线
解决方案:
sql
-- 处理复杂邮件地址的搜索
CREATE OR REPLACE FUNCTION normalize_email(email_text TEXT)
RETURNS TEXT AS $$
BEGIN
-- 简化邮件地址以适应解析器限制
RETURN regexp_replace(email_text, '[^a-zA-Z0-9@._-]', '', 'g');
END;
$$ LANGUAGE plpgsql;
-- 使用标准化函数
SELECT ts_debug(normalize_email('[email protected]'));
2. 复合词搜索优化
问题: 用户搜索单个词时找不到复合词结果
解决方案:
sql
-- 创建增强的搜索函数
CREATE OR REPLACE FUNCTION enhanced_search(search_term TEXT, document_vector TSVECTOR)
RETURNS BOOLEAN AS $$
BEGIN
-- 尝试精确匹配
IF document_vector @@ plainto_tsquery(search_term) THEN
RETURN TRUE;
END IF;
-- 尝试部分匹配(带连字符的词)
IF document_vector @@ to_tsquery(search_term || ':*') THEN
RETURN TRUE;
END IF;
RETURN FALSE;
END;
$$ LANGUAGE plpgsql;
3. 数值搜索精度问题
sql
-- 数值类型的精确搜索
CREATE INDEX idx_numeric_exact ON products
USING btree(price)
WHERE price IS NOT NULL;
-- 结合文本搜索和数值搜索
SELECT p.*, ts_rank(search_vector, query) as text_rank
FROM products p, to_tsquery('smartphone') as query
WHERE search_vector @@ query
AND price BETWEEN 500 AND 1500
ORDER BY text_rank DESC, price ASC;
总结
PostgreSQL 的文本搜索解析器通过其强大的令牌识别能力,为构建高效的全文搜索系统提供了坚实的基础。理解解析器的工作原理和各种令牌类型的特点,有助于:
- 设计更精准的搜索功能
- 优化搜索性能
- 处理多语言和复杂格式的文档
- 构建符合业务需求的搜索体验
通过合理利用重叠令牌特性和不同令牌类型的优势,可以实现既精确又灵活的搜索功能,满足现代应用的复杂搜索需求。