词典用于消除不应该在搜索中考虑的词语(首要术语停用词),并规范词语,以使同一词语的不同派生形式相匹配。成功规范的词语称为词素。除了提高搜索质量之外,规范和移除停用词可以减小文档的 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 词典识别所有内容,无论是否能够简化单词,因此应将其置于词典列表的末尾。在任何其他词典之前拥有它毫无用处,因为标记永远不会通过它传递到下一个词典。