2024 年 9 月 26 日: PostgreSQL 17 发布!
支持版本:当前 (17) / 16 / 15 / 14 / 13 / 12
开发版本:devel
不支持的版本:11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3

12.6. 字典 #

字典用于消除在搜索中不应考虑的词语(停用词),并规范化词语,以便相同词语的不同派生形式能够匹配。一个成功规范化的词语被称为词素。除了提高搜索质量外,规范化和去除停用词还会减小文档的tsvector表示的大小,从而提高性能。规范化并不总是具有语言学意义,通常取决于应用程序语义。

规范化的一些示例

  • 语言学——Ispell 字典试图将输入词语缩减为规范化形式;词干词典会去除词语的结尾

  • URL位置可以被规范化以使等效的 URL 匹配

    • http://www.pgsql.ru/db/mw/index.html

    • http://www.pgsql.ru/db/mw/

    • http://www.pgsql.ru/db/../db/mw/index.html

  • 颜色名称可以被替换为它们的十六进制值,例如,red, green, blue, magenta -> FF0000, 00FF00, 0000FF, FF00FF

  • 如果索引数字,我们可以删除一些小数位以减少可能的数字范围,例如,3.14159265359,3.1415926,3.14 规范化后将是相同的,如果只保留小数点后两位数字。

字典是一个程序,它接收一个标记作为输入,并返回

  • 一个词素数组,如果输入标记是字典已知的(注意,一个标记可以产生多个词素)

  • 一个带有TSL_FILTER标志设置的词素,用一个新标记替换原始标记,以便传递给后续字典(执行此操作的字典称为过滤字典

  • 一个空数组,如果字典知道标记,但它是一个停用词

  • NULL,如果字典不识别输入标记

PostgreSQL 为多种语言提供了预定义的字典。还有一些预定义的模板可用于创建具有自定义参数的新字典。下面描述了每个预定义的字典模板。如果现有的模板不适合,则可以创建新的模板;有关示例,请参见PostgreSQL 发行版中的contrib/区域。

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

配置字典列表的一般规则是,首先放置最窄、最具体的字典,然后放置更通用的字典,最后放置一个非常通用的字典,例如Snowball 词干词典或simple,它识别所有内容。例如,对于特定于天文学的搜索(astro_en配置),可以将标记类型asciiword(ASCII 词语)绑定到天文术语的同义词字典、通用英语字典和Snowball 英语词干词典

ALTER TEXT SEARCH CONFIGURATION astro_en
    ADD MAPPING FOR asciiword WITH astrosyn, english_ispell, english_stem;

过滤字典可以放置在列表中的任何位置,除了最后一个位置,因为在最后一个位置它将毫无用处。过滤字典用于部分规范化词语,以简化后续字典的任务。例如,过滤字典可以用于从带重音字母中删除重音符号,就像unaccent 模块所做的那样。

12.6.1. 停用词 #

停用词是那些非常常见、几乎出现在每份文档中且没有区分价值的词语。因此,在全文搜索的上下文中可以忽略它们。例如,每个英语文本都包含像athe这样的词语,因此将它们存储在索引中毫无用处。但是,停用词确实会影响tsvector中的位置,而位置反过来会影响排名

SELECT to_tsvector('english', 'in the list of stop words');
        to_tsvector
----------------------------
 'list':3 'stop':5 'word':6

缺少的位置 1、2、4 是因为停用词。使用和不使用停用词计算的排名截然不同

SELECT ts_rank_cd (to_tsvector('english', 'in the list of stop words'), to_tsquery('list & stop'));
 ts_rank_cd
------------
       0.05

SELECT ts_rank_cd (to_tsvector('english', 'list stop words'), to_tsquery('list & stop'));
 ts_rank_cd
------------
        0.1

具体字典如何处理停用词取决于字典本身。例如,ispell 字典首先规范化词语,然后查看停用词列表,而Snowball 词干词典首先检查停用词列表。这种不同行为的原因是试图减少噪声。

12.6.2. 简单字典 #

simple 字典模板通过将输入标记转换为小写并将其与停用词文件进行比对来操作。如果在文件中找到它,则返回一个空数组,导致标记被丢弃。如果没有,则返回词语的小写形式作为规范化的词素。或者,字典可以配置为将非停用词报告为未识别的,允许它们传递给列表中的下一个字典。

这是一个使用simple 模板定义字典的示例

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

这里,english 是停用词文件的基名称。文件的完整名称将是$SHAREDIR/tsearch_data/english.stop,其中$SHAREDIR 表示PostgreSQL 安装的共享数据目录,通常是/usr/local/share/postgresql(如果您不确定,请使用pg_config --sharedir 来确定它)。文件格式只是一个单词列表,每行一个。空行和尾部空格将被忽略,大写字母将折叠为小写字母,但不会对文件内容进行其他处理。

现在我们可以测试我们的字典

SELECT ts_lexize('public.simple_dict', 'YeS');
 ts_lexize
-----------
 {yes}

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

我们也可以选择返回NULL,而不是小写词语,如果它在停用词文件中找不到。这种行为是通过将字典的Accept 参数设置为false 来选择的。继续上面的示例

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

SELECT ts_lexize('public.simple_dict', 'YeS');
 ts_lexize
-----------


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

使用默认设置Accept = true,只有将simple 字典放置在字典列表的末尾才有用,因为它永远不会将任何标记传递给后续字典。相反,Accept = false 只有在至少有一个后续字典的情况下才有用。

注意

大多数类型的字典依赖于配置文件,例如停用词文件。这些文件必须存储在 UTF-8 编码中。它们将在读入服务器时转换为实际的数据库编码(如果不同)。

注意

通常,数据库会话只会读取一次字典配置文件,即在会话中第一次使用它时。如果您修改了配置文件并希望强制现有会话获取新内容,请对字典发出一个ALTER TEXT SEARCH DICTIONARY 命令。这可以是一个虚拟 更新,它实际上不会更改任何参数值。

12.6.3. 同义词字典 #

此字典模板用于创建将词语替换为同义词的字典。不支持短语(使用词库模板(第 12.6.4 节)来实现)。同义词字典可以用来克服语言问题,例如,阻止英语词干词典将词语Paris 缩减为pari。在同义词字典中有一个Paris paris 行就足够了,并将它放在english_stem 字典之前。例如

SELECT * FROM ts_debug('english', 'Paris');
   alias   |   description   | token |  dictionaries  |  dictionary  | lexemes
-----------+-----------------+-------+----------------+--------------+---------
 asciiword | Word, all ASCII | Paris | {english_stem} | english_stem | {pari}

CREATE TEXT SEARCH DICTIONARY my_synonym (
    TEMPLATE = synonym,
    SYNONYMS = my_synonyms
);

ALTER TEXT SEARCH CONFIGURATION english
    ALTER MAPPING FOR asciiword
    WITH my_synonym, english_stem;

SELECT * FROM ts_debug('english', 'Paris');
   alias   |   description   | token |       dictionaries        | dictionary | lexemes
-----------+-----------------+-------+---------------------------+------------+---------
 asciiword | Word, all ASCII | Paris | {my_synonym,english_stem} | my_synonym | {paris}

synonym 模板唯一需要的参数是SYNONYMS,它是其配置文件的基名称——上面示例中的my_synonyms。文件的完整名称将是$SHAREDIR/tsearch_data/my_synonyms.syn(其中$SHAREDIR 表示PostgreSQL 安装的共享数据目录)。文件格式只是每行一个要替换的词语,词语后面是它的同义词,用空格隔开。空行和尾部空格将被忽略。

synonym 模板还有一个可选参数CaseSensitive,它默认为false。当CaseSensitivefalse 时,同义词文件中的词语将被折叠为小写,输入标记也是如此。当它为true 时,词语和标记不会被折叠为小写,而是按原样进行比较。

在配置文件中,同义词的末尾可以添加星号(*)。这表示该同义词是一个前缀。当该条目在 to_tsvector() 中使用时,星号会被忽略,但在 to_tsquery() 中使用时,结果将是一个带有前缀匹配标记的查询项(参见 第 12.3.2 节)。例如,假设我们在 $SHAREDIR/tsearch_data/synonym_sample.syn 中有以下条目

postgres        pgsql
postgresql      pgsql
postgre pgsql
gogle   googl
indices index*

那么我们将得到以下结果

mydb=# CREATE TEXT SEARCH DICTIONARY syn (template=synonym, synonyms='synonym_sample');
mydb=# SELECT ts_lexize('syn', 'indices');
 ts_lexize
-----------
 {index}
(1 row)

mydb=# CREATE TEXT SEARCH CONFIGURATION tst (copy=simple);
mydb=# ALTER TEXT SEARCH CONFIGURATION tst ALTER MAPPING FOR asciiword WITH syn;
mydb=# SELECT to_tsvector('tst', 'indices');
 to_tsvector
-------------
 'index':1
(1 row)

mydb=# SELECT to_tsquery('tst', 'indices');
 to_tsquery
------------
 'index':*
(1 row)

mydb=# SELECT 'indexes are very useful'::tsvector;
            tsvector
---------------------------------
 'are' 'indexes' 'useful' 'very'
(1 row)

mydb=# SELECT 'indexes are very useful'::tsvector @@ to_tsquery('tst', 'indices');
 ?column?
----------
 t
(1 row)

12.6.4. 同义词词典 #

同义词词典(有时缩写为TZ)是一个词语集合,包含有关词语和短语关系的信息,即更广泛的术语(BT),更狭窄的术语(NT),首选术语,非首选术语,相关术语等。

基本上,同义词词典将所有非首选术语替换为一个首选术语,并可选地保留原始术语以进行索引。 PostgreSQL 当前同义词词典的实现是同义词词典的扩展,增加了 短语 支持。同义词词典需要以下格式的配置文件

# this is a comment
sample word(s) : indexed word(s)
more sample word(s) : more indexed word(s)
...

其中冒号(:)符号充当短语及其替换项之间的分隔符。

同义词词典使用一个 子词典(在词典配置中指定)来规范化输入文本,然后再检查短语匹配。只能选择一个子词典。如果子词典无法识别一个词,就会报错。在这种情况下,您应该删除该词的使用或教子词典识别该词。您可以在索引词的开头放置一个星号(*)来跳过对其应用子词典,但所有示例词都 必须 为子词典所知。

如果有多个短语与输入匹配,同义词词典将选择最长的匹配,并通过使用最后一个定义来打破平局。

子词典识别的特定停用词无法指定;而是使用 ? 来标记任何停用词可以出现的位置。例如,假设 athe 都是子词典中的停用词

? one ? two : swsw

匹配 a one the twothe one a two;两者都将被替换为 swsw

由于同义词词典能够识别短语,因此它必须记住自己的状态并与解析器交互。同义词词典使用这些赋值来检查它是否应该处理下一个词或停止累积。同义词词典必须小心配置。例如,如果同义词词典被分配为仅处理 asciiword 令牌,那么像 one 7 这样的同义词词典定义将无法工作,因为令牌类型 uint 没有分配给同义词词典。

注意

同义词在索引期间使用,因此对同义词词典参数的任何更改都 需要 重新索引。对于大多数其他词典类型,添加或删除停用词等小更改不会强制重新索引。

12.6.4.1. 同义词配置 #

要定义一个新的同义词词典,请使用 thesaurus 模板。例如

CREATE TEXT SEARCH DICTIONARY thesaurus_simple (
    TEMPLATE = thesaurus,
    DictFile = mythesaurus,
    Dictionary = pg_catalog.english_stem
);

这里

  • thesaurus_simple 是新词典的名称

  • mythesaurus 是同义词配置文件的基名。(它的全名将为 $SHAREDIR/tsearch_data/mythesaurus.ths,其中 $SHAREDIR 表示安装共享数据目录。)

  • pg_catalog.english_stem 是用于同义词规范化的子词典(此处为 Snowball 英语词干提取器)。请注意,子词典将有自己的配置(例如,停用词),此处未显示。

现在可以将同义词词典 thesaurus_simple 绑定到配置中所需的令牌类型,例如

ALTER TEXT SEARCH CONFIGURATION russian
    ALTER MAPPING FOR asciiword, asciihword, hword_asciipart
    WITH thesaurus_simple;

12.6.4.2. 同义词示例 #

考虑一个简单的天文同义词词典 thesaurus_astro,它包含一些天文词语组合

supernovae stars : sn
crab nebulae : crab

下面我们创建一个词典,并将一些令牌类型绑定到天文同义词词典和英语词干提取器

CREATE TEXT SEARCH DICTIONARY thesaurus_astro (
    TEMPLATE = thesaurus,
    DictFile = thesaurus_astro,
    Dictionary = english_stem
);

ALTER TEXT SEARCH CONFIGURATION russian
    ALTER MAPPING FOR asciiword, asciihword, hword_asciipart
    WITH thesaurus_astro, english_stem;

现在我们可以看看它是如何工作的。 ts_lexize 对测试同义词词典不太有用,因为它将其输入视为单个令牌。相反,我们可以使用 plainto_tsqueryto_tsvector,它们将把它们的输入字符串分解成多个令牌

SELECT plainto_tsquery('supernova star');
 plainto_tsquery
-----------------
 'sn'

SELECT to_tsvector('supernova star');
 to_tsvector
-------------
 'sn':1

原则上,如果您引用参数,可以使用 to_tsquery

SELECT to_tsquery('''supernova star''');
 to_tsquery
------------
 'sn'

请注意,supernova starthesaurus_astro 中与 supernovae stars 匹配,因为我们在同义词词典定义中指定了 english_stem 词干提取器。词干提取器删除了 es

要同时索引原始短语和替代短语,只需将其包含在定义的右侧部分

supernovae stars : sn supernovae stars

SELECT plainto_tsquery('supernova star');
       plainto_tsquery
-----------------------------
 'sn' & 'supernova' & 'star'

12.6.5. Ispell 词典 #

Ispell 词典模板支持 形态词典,它可以将同一个词的许多不同的语言形式规范化为同一个词素。例如,一个英文 Ispell 词典可以匹配搜索词 bank 的所有变格和变位,例如 bankingbankedbanksbanks'bank's

标准 PostgreSQL 发行版不包含任何 Ispell 配置文件。许多语言的词典可以从 Ispell 获取。此外,还支持一些更现代的词典文件格式——MySpell(OO < 2.0.1)和 Hunspell(OO >= 2.0.2)。在 OpenOffice Wiki 上可以找到大量的词典列表。

要创建一个 Ispell 词典,请执行以下步骤

  • 下载词典配置文件。 OpenOffice 扩展文件具有 .oxt 扩展名。需要提取 .aff.dic 文件,将扩展名更改为 .affix.dict。对于某些词典文件,还需要使用命令将字符转换为 UTF-8 编码(例如,对于挪威语词典)

    iconv -f ISO_8859-1 -t UTF-8 -o nn_no.affix nn_NO.aff
    iconv -f ISO_8859-1 -t UTF-8 -o nn_no.dict nn_NO.dic
    
  • 将文件复制到 $SHAREDIR/tsearch_data 目录

  • 使用以下命令将文件加载到 PostgreSQL 中

    CREATE TEXT SEARCH DICTIONARY english_hunspell (
        TEMPLATE = ispell,
        DictFile = en_us,
        AffFile = en_us,
        Stopwords = english);
    

这里,DictFileAffFileStopWords 指定词典、词缀和停用词文件的基名。停用词文件与上面解释的 simple 词典类型具有相同的格式。其他文件的格式此处未指定,但可以在上述网站上找到。

Ispell 词典通常只识别有限的词语集,因此它们后面应该跟着另一个更广泛的词典;例如,Snowball 词典,它可以识别所有词语。

Ispell.affix 文件具有以下结构

prefixes
flag *A:
    .           >   RE      # As in enter > reenter
suffixes
flag T:
    E           >   ST      # As in late > latest
    [^AEIOU]Y   >   -Y,IEST # As in dirty > dirtiest
    [AEIOU]Y    >   EST     # As in gray > grayest
    [^EY]       >   EST     # As in small > smallest

.dict 文件具有以下结构

lapse/ADGRS
lard/DGRS
large/PRTY
lark/MRS

.dict 文件的格式为

basic_form/affix_class_name

.affix 文件中,每个词缀标志以以下格式描述

condition > [-stripping_letters,] adding_affix

这里,条件的格式类似于正则表达式的格式。它可以使用分组 [...][^...]。例如,[AEIOU]Y 表示该词的最后一个字母是 "y",倒数第二个字母是 "a""e""i""o""u"[^EY] 表示最后一个字母既不是 "e" 也不是 "y"

Ispell 词典支持拆分复合词;一个有用的功能。请注意,词缀文件应该使用 compoundwords controlled 语句指定一个特殊标志,该标志标记可以参与复合词形成的词典词

compoundwords  controlled z

以下是一些挪威语的示例

SELECT ts_lexize('norwegian_ispell', 'overbuljongterningpakkmesterassistent');
   {over,buljong,terning,pakk,mester,assistent}
SELECT ts_lexize('norwegian_ispell', 'sjokoladefabrikk');
   {sjokoladefabrikk,sjokolade,fabrikk}

MySpell 格式是 Hunspell 的一个子集。 Hunspell.affix 文件具有以下结构

PFX A Y 1
PFX A   0     re         .
SFX T N 4
SFX T   0     st         e
SFX T   y     iest       [^aeiou]y
SFX T   0     est        [aeiou]y
SFX T   0     est        [^ey]

词缀类的第一行是标题。词缀规则的字段列在标题之后

  • 参数名称(PFX 或 SFX)

  • 标志(词缀类的名称)

  • 从词语开头(在词缀前)或结尾(在词缀后)剥离的字符

  • 添加词缀

  • 条件,其格式类似于正则表达式的格式。

.dict 文件看起来像 Ispell.dict 文件

larder/M
lardy/RT
large/RSPMYT
largehearted

注意

MySpell 不支持复合词。 Hunspell 对复合词有强大的支持。目前, PostgreSQL 仅实现了 Hunspell 的基本复合词操作。

12.6.6. Snowball 词典 #

Snowball 词典模板基于 Martin Porter 的一个项目,Martin Porter 是流行的 Porter 词干提取算法的发明者,用于英语。Snowball 现在为许多语言提供词干提取算法(有关更多信息,请参见 Snowball 网站)。每个算法都了解如何将词语的常见变体形式还原为其语言中的基本形式或词干形式。Snowball 词典需要一个 language 参数来标识要使用的词干提取器,并且可以选择指定一个 stopword 文件名,该文件名给出要消除的词语列表。(PostgreSQL 的标准停用词列表也由 Snowball 项目提供。)例如,有一个内置的定义等效于

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

停用词文件的格式与前面解释的相同。

Snowball 词典识别所有词语,无论它是否能够简化该词语,因此它应该放在词典列表的最后。在任何其他词典之前使用它是无用的,因为令牌永远不会通过它传递到下一个词典。

提交更正

如果您在文档中发现任何不正确的内容、与您对特定功能的体验不符或需要进一步说明的内容,请使用 此表格 报告文档问题。