词典用于消除不应在搜索中考虑的词(停用词),并规范化词,以便同一词的不同派生形式能够匹配。成功规范化后的词称为词元。除了提高搜索质量,规范化和删除停用词还可以减小文档的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模块所做的那样。
停用词是非常常见、几乎出现在每个文档中且没有区分价值的词。因此,它们可以在全文搜索的上下文中被忽略。例如,所有英文文本都包含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词干提取器先检查停用词列表。行为不同的原因是为了尝试降低噪声。
该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.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)
主题词典(有时缩写为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未分配给主题词典。
主题词典在索引期间使用,因此主题词典参数的任何更改都需要重新索引。对于大多数其他词典类型,小的更改(例如添加或删除停用词)不会强制重新索引。
要定义一个新的主题词典,请使用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;
考虑一个简单的天文主题词典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'
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的基本复合词操作。
Snowball词典模板基于Martin Porter的项目,他是英语流行词干提取算法Porter的发明者。Snowball现在为许多语言提供词干提取算法(有关更多信息,请参见Snowball网站)。每种算法都能够将单词的常见变体形式减少到其语言中的基本或词干拼写。Snowball词典需要一个language参数来标识要使用的词干提取器,并且可以选择指定一个stopword文件名,其中包含要消除的单词列表。(PostgreSQL的标准停用词列表也由Snowball项目提供。)例如,有一个内置定义等同于
CREATE TEXT SEARCH DICTIONARY english_stem (
TEMPLATE = snowball,
Language = english,
StopWords = english
);
停用词文件的格式与前面解释的相同。
Snowball词典识别所有内容,无论它是否能够简化单词,因此它应该放在词典列表的末尾。在它之前放置任何其他词典都是没有意义的,因为令牌永远不会传递到下一个词典。