Skip to content

PostgreSQL 文本搜索测试和调试

概述

在 PostgreSQL 中,自定义文本搜索配置的行为容易变得复杂和难以理解。为了帮助开发者更好地测试和调试文本搜索功能,PostgreSQL 提供了一套专门的函数工具。这些函数允许您测试完整的文本搜索配置,或者分别测试解析器和词典的行为。

文本搜索调试流程

1. 配置测试 - ts_debug 函数

1.1 函数语法

ts_debug 函数是文本搜索配置测试的核心工具,它能够展示文本处理的完整过程。

sql
ts_debug([ config regconfig, ] document text,
         OUT alias text,
         OUT description text,
         OUT token text,
         OUT dictionaries regdictionary[],
         OUT dictionary regdictionary,
         OUT lexemes text[])
         returns setof record

1.2 函数参数和返回值

参数/返回列类型描述
configregconfig可选,指定使用的文本搜索配置,默认使用 default_text_search_config
documenttext要分析的文本内容
aliastext标记类型的简称
descriptiontext标记类型的详细描述
tokentext从文本中提取的标记
dictionariesregdictionary[]配置为此标记类型选择的词典列表
dictionaryregdictionary实际识别该标记的词典,未识别则为 NULL
lexemestext[]词典生成的词素,空数组表示停用词

1.3 基础示例分析

问题陈述

需要分析英语文本 "a fat cat sat on a mat - it ate a fat rats" 的处理过程,了解每个词是如何被解析和处理的。

解决方案

sql
SELECT * FROM ts_debug('english', 'a fat  cat sat on a mat - it ate a fat rats');

输出结果

   alias   |   description   | token |  dictionaries  |  dictionary  | lexemes
-----------+-----------------+-------+----------------+--------------+---------
 asciiword | Word, all ASCII | a     | {english_stem} | english_stem | {}
 blank     | Space symbols   |       | {}             |              |
 asciiword | Word, all ASCII | fat   | {english_stem} | english_stem | {fat}
 blank     | Space symbols   |       | {}             |              |
 asciiword | Word, all ASCII | cat   | {english_stem} | english_stem | {cat}
 blank     | Space symbols   |       | {}             |              |
 asciiword | Word, all ASCII | sat   | {english_stem} | english_stem | {sat}
 blank     | Space symbols   |       | {}             |              |
 asciiword | Word, all ASCII | on    | {english_stem} | english_stem | {}
 blank     | Space symbols   |       | {}             |              |
 asciiword | Word, all ASCII | a     | {english_stem} | english_stem | {}
 blank     | Space symbols   |       | {}             |              |
 asciiword | Word, all ASCII | mat   | {english_stem} | english_stem | {mat}
 blank     | Space symbols   |       | {}             |              |
 blank     | Space symbols   | -     | {}             |              |
 asciiword | Word, all ASCII | it    | {english_stem} | english_stem | {}
 blank     | Space symbols   |       | {}             |              |
 asciiword | Word, all ASCII | ate   | {english_stem} | english_stem | {ate}
 blank     | Space symbols   |       | {}             |              |
 asciiword | Word, all ASCII | a     | {english_stem} | english_stem | {}
 blank     | Space symbols   |       | {}             |              |
 asciiword | Word, all ASCII | fat   | {english_stem} | english_stem | {fat}
 blank     | Space symbols   |       | {}             |              |
 asciiword | Word, all ASCII | rats  | {english_stem} | english_stem | {rat}

分析过程

TIP

关键观察点

  1. 词汇分类:所有英文单词被识别为 asciiword 类型
  2. 停用词识别aonit 被识别为停用词(lexemes 为空数组 {}
  3. 词干提取rats 被转换为词根形式 rat
  4. 空格处理:所有空格和标点符号被标记为 blank 类型,不进行词典处理

1.4 高级配置示例

问题陈述

需要创建一个使用 Ispell 词典的自定义英语配置,以更好地处理词汇变形和同义词。

解决方案

sql
-- 创建基于默认英语配置的自定义配置
CREATE TEXT SEARCH CONFIGURATION public.english ( COPY = pg_catalog.english );

-- 创建 Ispell 词典
CREATE TEXT SEARCH DICTIONARY english_ispell (
    TEMPLATE = ispell,
    DictFile = english,
    AffFile = english,
    StopWords = english
);

-- 修改配置,为 ASCII 词汇添加 Ispell 词典
ALTER TEXT SEARCH CONFIGURATION public.english
   ALTER MAPPING FOR asciiword WITH english_ispell, english_stem;
sql
SELECT * FROM ts_debug('public.english', 'The Brightest supernovaes');

输出结果

   alias   |   description   |    token    |         dictionaries          |   dictionary   |   lexemes
-----------+-----------------+-------------+-------------------------------+----------------+-------------
 asciiword | Word, all ASCII | The         | {english_ispell,english_stem} | english_ispell | {}
 blank     | Space symbols   |             | {}                            |                |
 asciiword | Word, all ASCII | Brightest   | {english_ispell,english_stem} | english_ispell | {bright}
 blank     | Space symbols   |             | {}                            |                |
 asciiword | Word, all ASCII | supernovaes | {english_ispell,english_stem} | english_stem   | {supernova}

分析过程

INFO

词典处理优先级

  1. 词典链式处理:配置了两个词典 english_ispellenglish_stem,按优先级顺序处理
  2. Ispell 优先处理Brightestenglish_ispell 成功识别并转换为 bright
  3. 后备处理机制supernovaesenglish_ispell 中未找到,转给 english_stem 处理
  4. 停用词识别Theenglish_ispell 识别为停用词

1.5 输出优化

对于实际应用,通常不需要查看所有列,可以选择性地显示关键信息:

sql
SELECT alias, token, dictionary, lexemes
FROM ts_debug('public.english', 'The Brightest supernovaes');

输出:

   alias   |    token    |   dictionary   |   lexemes
-----------+-------------+----------------+-------------
 asciiword | The         | english_ispell | {}
 blank     |             |                |
 asciiword | Brightest   | english_ispell | {bright}
 blank     |             |                |
 asciiword | supernovaes | english_stem   | {supernova}

2. 解析器测试

2.1 ts_parse 函数

函数语法

sql
ts_parse(parser_name text, document text,
         OUT tokid integer, OUT token text) returns setof record
ts_parse(parser_oid oid, document text,
         OUT tokid integer, OUT token text) returns setof record

实际应用示例

问题陈述

需要了解解析器如何识别包含数字、文字和符号的混合文本。

解决方案
sql
SELECT * FROM ts_parse('default', '123 - a number');
输出结果
 tokid | token
-------+--------
    22 | 123
    12 |
    12 | -
     1 | a
    12 |
     1 | number
分析过程
Details

标记类型解释

  • tokid = 22:无符号整数 (uint)
  • tokid = 12:空格符号 (blank)
  • tokid = 1:ASCII 单词 (asciiword)

这个例子展示了解析器如何将文本分解为不同类型的基本标记。

2.2 ts_token_type 函数

函数语法

sql
ts_token_type(parser_name text, OUT tokid integer,
              OUT alias text, OUT description text) returns setof record
ts_token_type(parser_oid oid, OUT tokid integer,
              OUT alias text, OUT description text) returns setof record

实际应用示例

问题陈述

需要了解默认解析器能够识别的所有标记类型,以便正确配置文本搜索。

解决方案
sql
SELECT * FROM ts_token_type('default');
输出结果
 tokid |      alias      |               description
-------+-----------------+------------------------------------------
     1 | asciiword       | Word, all ASCII
     2 | word            | Word, all letters
     3 | numword         | Word, letters and digits
     4 | email           | Email address
     5 | url             | URL
     6 | host            | Host
     7 | sfloat          | Scientific notation
     8 | version         | Version number
     9 | hword_numpart   | Hyphenated word part, letters and digits
    10 | hword_part      | Hyphenated word part, all letters
    11 | hword_asciipart | Hyphenated word part, all ASCII
    12 | blank           | Space symbols
    13 | tag             | XML tag
    14 | protocol        | Protocol head
    15 | numhword        | Hyphenated word, letters and digits
    16 | asciihword      | Hyphenated word, all ASCII
    17 | hword           | Hyphenated word, all letters
    18 | url_path        | URL path
    19 | file            | File or path name
    20 | float           | Decimal notation
    21 | int             | Signed integer
    22 | uint            | Unsigned integer
    23 | entity          | XML entity

业务应用场景

TIP

配置策略建议

根据不同的业务需求,您可以选择性地配置词典:

  • 内容管理系统:重点配置 asciiwordwordemailurl
  • 技术文档搜索:额外配置 versionfiletag
  • 科学数据检索:重点配置 sfloatintnumword
  • 多语言平台:重点配置 word、避免只依赖 asciiword

3. 词典测试 - ts_lexize 函数

3.1 函数语法

sql
ts_lexize(dict regdictionary, token text) returns text[]

3.2 函数返回值类型

返回值含义
text[] 数组词典识别的标记,返回对应的词素
{} 空数组词典识别的停用词
NULL词典不认识的未知单词

3.3 基础测试示例

问题陈述

需要测试英语词干提取词典对不同单词的处理效果。

解决方案

sql
SELECT ts_lexize('english_stem', 'stars');
sql
SELECT ts_lexize('english_stem', 'a');

输出结果

sql
-- 普通单词处理
 ts_lexize
-----------
 {star}

-- 停用词处理
 ts_lexize
-----------
 {}

分析过程

INFO

词素化过程

  1. 词根提取starsstar,去除复数形式
  2. 停用词识别a 被识别为停用词,返回空数组
  3. 词典覆盖:英语词干词典能够处理大部分英语单词变形

3.4 同义词库词典测试

问题陈述

测试同义词库词典对短语的处理能力,需要注意单个标记与短语的区别。

错误示例

sql
-- ❌ 错误的测试方法
SELECT ts_lexize('thesaurus_astro', 'supernovae stars') is null;

输出:

 ?column?
----------
 t

WARNING

常见错误

ts_lexize 函数只能处理单个标记,不能处理包含空格的短语。即使同义词库词典认识 "supernovae stars" 这个短语,ts_lexize 也会失败。

正确的测试方法

问题陈述

需要正确测试同义词库词典对短语的处理能力。

解决方案
sql
SELECT plainto_tsquery('supernovae stars');
sql
SELECT to_tsvector('supernovae stars');
输出结果
sql
-- plainto_tsquery 结果
 plainto_tsquery
-----------------
 'sn'

分析过程

TIP

同义词库测试最佳实践

  1. 使用正确的函数:用 plainto_tsqueryto_tsvector 测试短语
  2. 理解函数局限ts_lexize 仅适用于单个标记
  3. 完整测试流程:同义词库需要在完整的文本搜索流程中测试

4. 实际业务应用场景

4.1 电商搜索优化

问题陈述

电商网站需要优化商品搜索功能,确保用户输入的各种商品名称变形都能被正确识别。

解决方案

sql
-- 测试商品名称搜索
SELECT alias, token, dictionary, lexemes
FROM ts_debug('english', 'iPhone 13 Pro Max 256GB smartphone');

预期能够识别:

  • 品牌名称:iPhone
  • 型号信息:13, Pro, Max
  • 存储容量:256GB
  • 产品类别:smartphone

4.2 内容管理系统

问题陈述

新闻网站需要确保文章标题和内容的关键词能够被正确提取和索引。

解决方案

sql
-- 测试新闻标题处理
SELECT * FROM ts_debug('english',
  'Breaking: Tesla Stock Rises 15% After Q3 Earnings Report');

关注点:

  • 特殊标记:Breaking, Tesla, Q3
  • 数字识别:15%, Q3
  • 财经术语:Stock, Earnings

4.3 多语言内容处理

问题陈述

国际化平台需要处理包含多种语言和特殊字符的内容。

解决方案

sql
-- 创建多语言测试配置
CREATE TEXT SEARCH CONFIGURATION multilang (COPY = simple);

-- 测试多语言内容
SELECT * FROM ts_debug('multilang',
  'Hello 世界 café résumé naïve');

5. 性能优化建议

5.1 调试函数性能影响

WARNING

生产环境注意事项

  1. ts_debug 是调试工具,在生产环境中应谨慎使用
  2. 大文本处理 可能消耗大量资源,建议限制输入文本长度
  3. 频繁调用 应避免在高并发场景中使用调试函数

5.2 优化策略

sql
-- 限制调试文本长度
SELECT * FROM ts_debug('english',
  substring('very long text...', 1, 1000));

-- 只查询必要的列
SELECT token, lexemes
FROM ts_debug('english', 'test text')
WHERE lexemes IS NOT NULL AND lexemes != '{}';

6. 故障排除指南

6.1 常见问题诊断

问题症状可能原因诊断方法
词汇未被索引被识别为停用词检查 lexemes 是否为 {}
搜索结果不准确词典配置不当使用 ts_debug 检查词典选择
特殊字符处理异常解析器类型不匹配使用 ts_token_type 查看支持的类型
同义词不生效同义词库配置错误plainto_tsquery 测试短语

6.2 调试流程

通过这些测试和调试函数,您可以深入了解 PostgreSQL 文本搜索的内部工作机制,从而优化搜索配置,提升搜索质量和用户体验。