Skip to content

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连字符单词,全部是 ASCIIup-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 协议识别
urlURLexample.com/stuff/index.html链接搜索
host主机名example.com域名搜索
url_pathURL 路径/stuff/index.html路径匹配
文件类file文件或路径名/usr/local/foo.txt文件路径搜索
数值类sfloat科学记数法-1.234e56科学数据搜索
float十进制记数法-1.234价格、测量值搜索
int带符号整数-1234数值搜索
uint无符号整数1234ID、数量搜索
version版本号8.3.0软件版本管理
标记类tagXML 标签<a href="dictionaries.html">HTML/XML 文档搜索
entityXML 实体&amp;实体引用搜索
其他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         -- 第三部分

分析过程:

  1. 完整匹配numhword 类型保存了完整的 "foo-bar-beta1"
  2. 部分匹配hword_asciiparthword_numpart 保存了各个组成部分
  3. 搜索灵活性:用户可以搜索完整词或任意部分都能匹配
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

性能优化建议

  1. 预处理文本:移除不必要的格式化字符
  2. 合理分段:将长文档分割成合理大小的段落
  3. 选择性索引:只对需要搜索的字段建立全文索引
  4. 定期维护:使用 VACUUMREINDEX 维护搜索性能
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 的文本搜索解析器通过其强大的令牌识别能力,为构建高效的全文搜索系统提供了坚实的基础。理解解析器的工作原理和各种令牌类型的特点,有助于:

  • 设计更精准的搜索功能
  • 优化搜索性能
  • 处理多语言和复杂格式的文档
  • 构建符合业务需求的搜索体验

通过合理利用重叠令牌特性和不同令牌类型的优势,可以实现既精确又灵活的搜索功能,满足现代应用的复杂搜索需求。