Redrock Postgres 文档
主页 切换暗/亮/自动模式 切换暗/亮/自动模式 切换暗/亮/自动模式 返回首页

全文检索

全文检索(或者文本搜索)提供了确定满足一个查询的自然语言文档的能力,并可以选择将它们按照与查询的相关度排序。最常用的搜索类型是找到所有包含给定查询词的文档并按照它们与查询的相似性顺序返回它们。查询相似性的概念非常灵活并且依赖于特定的应用。最简单的搜索认为查询是一组词而相似性是查询词在文档中的频度。

配置

文本搜索功能包括做更多事情的能力:跳过索引特定词(停用词)、处理同义词并使用更高级的解析,例如基于空白之外的解析。这个功能由文本搜索配置控制。PostgreSQL中有多种语言的预定义配置,并且你可以很容易地创建你自己的配置(psql的\dF命令显示所有可用的配置)。

一般内置的文本搜索配置都以对应语言的名称命名,内置的中文搜索配置被称为 pg_catalog.chinese

解析器

文本搜索解析器负责把未处理的文档文本划分成记号并且标识每一个记号的类型,而可能的类型集合由解析器本身定义。注意一个解析器完全不会修改文本 — 它简单地标识看似有理的词边界。因为这种有限的视野,对于应用相关的自定义解析器的需求就没有自定义字典那么强烈。目前Redrock Postgres只提供了两种内置解析器,它们已经被证实对很多种应用都适用。

有两个内置的文本解析器,一个默认解析器被称为pg_catalog.default,一个中文解析器被称为pg_catalog.cjkparser

词典

词典被用来消除不被搜索考虑的词(停用词)、并被用来正规化词这样同一个词的不同派生形式将会匹配。一个被成功地正规化的词被称为一个词位。除了提高搜索质量,正规化和移除停用词减小了文档的tsvector表示的尺寸,因而提高了性能。正规化不会总是有语言上的意义并且通常依赖于应用的语义。

一个文本搜索配置把一个解析器和一组处理解析器输出记号的词典绑定在一起。对于每一种解析器能返回的记号类型,配置都指定了一个单独的词典列表。当该类型的一个记号被解析器找到时,每一个词典都被按照顺序查询,知道某个词典将其识别为一个已知词。如果它被标识为一个停用词或者没有一个词典识别它,它将被丢弃并且不会被索引和用于搜索。通常,第一个返回非NULL输出的词典决定结果,并且任何剩下的词典都不会被查找;但是一个过滤词典可以将给定词替换为一个被修改的词,它再被传递给后续的词典。

停用词

停用词是非常常用、在几乎每一个文档中出现并且没有任何区分度的词。因此,在全文搜索的环境中它们可以被忽略。例如,每一段中文文本都包含像“一个”和“这个”这样的词语,因此把它们存储在一个索引中是没有用处的。但是,停用词确实会影响在tsvector中的位置,这进而会影响排名:

SELECT to_tsvector('chinese','在停用词语的列表中');
       to_tsvector
--------------------------
 '停用':2 '列表':5 '词语':3

缺失的位置 1、4、6 是因为停用词。文档的排名计算在使用和不使用停用词的情况下是很不同的:

SELECT ts_rank_cd (to_tsvector('chinese','在停用词语的列表中'),
                   to_tsquery('停用 & 列表'));
 ts_rank_cd
------------
  0.0333333

SELECT ts_rank_cd (to_tsvector('chinese','停用词语列表'),
                   to_tsquery('停用 & 列表'));
 ts_rank_cd
------------
       0.05

如何对待停用词是由指定词典决定的。例如,ispell词典首先正规化词并且查看停用词列表,而Snowball词干分析器首先检查停用词的列表。这种不同行为的原因是一种降低噪声的尝试。

简单词典

simple词典模板的操作是将输入记号转换为小写形式并且根据一个停用词文件检查它。如果该记号在该文件中被找到,则返回一个空数组,导致该记号被丢弃。否则,该词的小写形式被返回作为正规化的词位。作为一种选择,该词典可以被配置为将非停用词报告为未识别,允许它们被传递给列表中的下一个词典。

下面是一个使用simple模板的词典定义的例子:

CREATE TEXT SEARCH DICTIONARY public.simple_dict (
    TEMPLATE = pg_catalog.simple,
    STOPWORDS = chinese
);

这里,chinese是一个停用词文件的基本名称。该文件的全名将是$SHAREDIR/tsearch_data/chinese.stop,其中$SHAREDIR表示PostgreSQL安装的共享数据目录,通常是/usr/pgsql-12/share(如果不确定,使用pg_config --sharedir)。该文件格式是一个词的列表,每行一个。空行和尾部的空格都被忽略,并且大写也被折叠成小写,但是没有其他对该文件内容的处理。

现在我们能够测试我们的词典:

SELECT ts_lexize('public.simple_dict','正确');
 ts_lexize
-----------
 {正确}

SELECT ts_lexize('public.simple_dict','这个');
 ts_lexize
-----------
 {}

如果没有在停用词文件中找到,我们也可以选择返回NULL而不是小写形式的词。这种行为可以通过设置词典的Accept参数为false来选择。继续该例子:

ALTER TEXT SEARCH DICTIONARY public.simple_dict ( Accept = false );

SELECT ts_lexize('public.simple_dict','正确');
 ts_lexize
-----------


SELECT ts_lexize('public.simple_dict','这个');
 ts_lexize
-----------
 {}

在使用默认值Accept = true,只有把一个simple词典放在词典列表的尾部才有用,因为它将不会传递任何记号给后续的词典。相反,Accept = false只有当至少有一个后续词典的情况下才有用。

大部分类型的词典依赖于配置文件,例如停用词文件。这些文件必须被存储为 UTF-8 编码。当它们被读入服务器时,如果存在不同,它们将被翻译成真实的数据库编码。
通常,当一个词典配置文件第一次在数据库会话中使用时,数据库会话将只读取它一次。如果你修改了一个配置文件并且想强迫现有的会话取得新内容,可以在该词典上发出一个ALTER TEXT SEARCH DICTIONARY命令。这可以是一次“假”更新,它并不实际修改任何参数值。

Snowball 词典

Snowball词典模板基于 Martin Porter 的一个项目,他是流行的英语 Porter 词干分析算法的发明者。Snowball 现在对许多语言提供词干分析算法(详见Snowball 站点)。每一个算法懂得按照其语言中的拼写,如何缩减词的常见变体形式为一个基础或词干。一个 Snowball 词典要求一个language参数来标识要用哪种词干分析器,并且可以选择地指定一个stopword文件名来给出一个要被消除的词列表(PostgreSQL的标准停用词列表也是由 Snowball 项目提供的)。例如,有一个内建的定义等效于

CREATE TEXT SEARCH DICTIONARY english_stem (
    TEMPLATE = snowball,
    Language = english,
    StopWords = english
);

停用词文件格式和已经解释的一样。

一个Snowball词典识别所有的东西,不管它能不能简化该词,因此它应当被放置在词典列表的最后。把它放在任何其他词典前面是没有用处的,因为一个记号永远不会穿过它而进入到下一个词典。

测试和调试文本搜索

一个自定义文本搜索配置的行为很容易变得混乱。本节中描述的函数对于测试文本搜索对象有用。你可以测试一个完整的配置,或者独立测试解析器和词典。

配置测试

函数ts_debug允许简单地测试一个文本搜索配置。

SELECT * FROM ts_debug('chinese', '关系数据库管理系统');

查询结果:

   alias   | description | token  | dictionaries | dictionary | lexemes
-----------+-------------+--------+--------------+------------+----------
 cjk_words | CJK words   | 关系   | {chinese}    | chinese    | {关系}
 cjk_words | CJK words   | 数据库 | {chinese}    | chinese    | {数据库}
 cjk_words | CJK words   | 管理   | {chinese}    | chinese    | {管理}
 cjk_words | CJK words   | 系统   | {chinese}    | chinese    | {系统}

解析器测试

函数ts_parse 允许直接测试一个文本搜索解析器。

SELECT * FROM ts_parse('cjkparser', '关系数据库管理系统');

查询结果:

 tokid | token
-------+--------
     1 | 关系
     1 | 数据库
     1 | 管理
     1 | 系统

词典测试

函数ts_lexize可用于词典测试。

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

如果输入的token是该词典已知的,则ts_lexize返回一个词位数组;如果记号是词典已知的但是它是一个停用词,则返回一个空数组;或者如果它对词典是未知词,则返回NULL

例子:

SELECT ts_lexize('chinese', '星星');
 ts_lexize
-----------
 {星星}

SELECT ts_lexize('chinese', '一个');
 ts_lexize
-----------
 {}