12.6. 词典#
词典用于消除搜索中不应考虑的词(停用词),并规范化词,以便同一词的不同派生形式将匹配。成功规范化的词称为词素。除了提高搜索质量外,规范化和去除停用词还可以减小文档的tsvector
表示的大小,从而提高性能。规范化并不总是具有语言学意义,并且通常取决于应用程序语义。
规范化的一些示例
语言学——Ispell 词典尝试将输入词还原为规范化形式;词干提取词典去除词尾
位置可以规范化,以使等效的 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. 停用词#
停用词是非常常见的单词,几乎出现在每篇文档中,并且没有歧视价值。因此,在全文搜索的上下文中可以忽略它们。例如,每篇英文文本都包含a
和the
等单词,因此将它们存储在索引中毫无用处。但是,停用词确实会影响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
。当CaseSensitive
为false
时,同义词文件中的单词将折叠为小写,输入标记也是如此。当它为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)
...
其中冒号 (:
) 符号充当短语与其替换项之间的分隔符。
同义词词典使用子词典(在词典的配置中指定)在检查短语匹配之前对输入文本进行规范化。只能选择一个子词典。如果子词典无法识别某个单词,则会报告错误。在这种情况下,您应该停止使用该单词或向子词典教授该单词。您可以在索引单词的开头放置一个星号 (*
) 以跳过对其应用子词典,但所有示例单词必须为子词典所知。
如果有多个短语与输入匹配,同义词词典将选择最长的匹配,并使用最后一个定义来打破平局。
无法指定子词典识别的特定停用词;相反,使用?
标记任何停用词可能出现的位置。例如,假设根据子词典,a
和the
是停用词
? one ? two : swsw
匹配a one the two
和the 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_tsquery
和to_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 star
匹配thesaurus_astro
中的supernovae stars
,因为我们在同义词库定义中指定了english_stem
词干分析器。词干分析器删除了e
和s
。
要索引原始短语以及替代短语,只需将其包含在定义的右侧即可
supernovae stars : sn supernovae stars
SELECT plainto_tsquery('supernova star');
plainto_tsquery
-----------------------------
'sn' & 'supernova' & 'star'
12.6.5.Ispell字典#
Ispell字典模板支持形态字典,它可以将单词的许多不同语言形式标准化为同一词素。例如,英语Ispell字典可以匹配搜索词bank
的所有变格和变位,例如banking
、banked
、banks
、banks'
和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);
其中,DictFile
、AffFile
和StopWords
指定字典、词缀和停用词文件的基名。停用词文件具有与上述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 的一个项目,他是英语语言中流行的 Porter 词干提取算法的发明者。Snowball 现在为多种语言提供词干提取算法(有关更多信息,请参阅Snowball 网站)。每种算法都了解如何将单词的常见变体形式缩减为其语言中的基础或词干拼写。Snowball 词典需要一个language
参数来标识要使用的词干提取器,还可以选择指定一个stopword
文件名,该文件名提供要消除的单词列表。(PostgreSQL的标准停用词列表也由 Snowball 项目提供。)例如,有一个内置定义等效于
CREATE TEXT SEARCH DICTIONARY english_stem (
TEMPLATE = snowball,
Language = english,
StopWords = english
);
停用词文件格式与已解释的相同。
Snowball词典识别所有内容,无论它是否能够简化单词,因此它应该放在词典列表的末尾。在任何其他词典之前使用它毫无用处,因为令牌永远不会通过它传递到下一个词典。