ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# 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.14_159265359,_3.14_15926将归一化为一样的_3.14_ 字典是一个程序,它接受标记作为输入和返回: * 词条数组如果输入标记是已知的词典(注意,一个标记可以产生一个以上的词) * 用`TSL_FILTER`标志设置的单词,与被传递到随后的词典的新的标记代替原来的(称这是_过滤词典_) * 如果词典认为标记是空数组,但它是一个屏蔽词。 * 如果词典不能识别输入标记,则为`空` PostgreSQL提供了多种语言的预定义字典。也有几个预定义的模板,可用于创建自定义参数的新词典。 每个预定义的字典模板描述如下。如果没有现成的模板是合适的,它可以创建一个新的; 参见PostgreSQL发布的`contrib/`部分例子 文本搜索配置将解析器和处理解析器输出标记绑定在一起。为了每个标记类型, 返回解析器,单独的词典列表通过配置指定。当标记类型是由解析器发现时, 列表中的每个字典依次查阅,直到一些词典作为一个已知的单词识别它。如果它被确定为一个屏蔽词, 或者如果没有词典识别标记,它将被丢弃,并且没有索引或搜索。通常,返回一个非-`空`输出的第一个词典将决定结果, 并且不查阅任何剩余的词典;但过滤词典可以替换带有修饰词的给定词,然后被传递给后继词典。 配置一个字典列表的一般规则是放在第一个最窄的,最具体的词典中,然后是更一般的词典, 整理一个非常普遍的词典,像Snowball词干或`simple`可以识别一切。例如, 一个天文学的特定搜索(`astro_en`配置)可以将标记类型`asciiword`(ASCII字)绑定到天文术语的同义词词典, 一般英语词典和Snowball 英文词干分析器: ``` ALTER TEXT SEARCH CONFIGURATION astro_en ADD MAPPING FOR asciiword WITH astrosyn, english_ispell, english_stem; ``` 过滤词典可以放置在列表中的任何地方,除了在结束的地方会是无用的。 过滤词典部分规范化词以简化后继词典的任务是非常有用的。 例如,过滤词典可以用来从重音字母中删除重音,按照[unaccent](#calibre_link-405)模块执行。 ## 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`字典模板通过转换输入标记为小写字母进行,并且屏蔽词文件前检查它。 如果在文档中找到并返回空数组,则丢弃这个标记。如果没有,单词的小写字母形式作为归一化的词返回。 另外,词典可以为报告未识别的非屏蔽词进行配置,允许将它们传递到列表中的后继词典中。 这有使用`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`是唯一有用的。 | **Caution** | |:--- | | 词典大部分类型依赖于配置文档,如屏蔽词文件。这些文件_必须_存储在UTF-8编码中。 当他们读到服务器中,如果是不同的,他们将被转化为实际的数据库编码。 | | **Caution** | |:--- | | 通常情况下,当它在会话中第一次使用时,数据库会话将只读一次词典的配置文档, 如果你修改一个配置文档,想强制现有会话获取新的内容,则在词典中使用命令`ALTER TEXT SEARCH DICTIONARY`。 这是一个"虚拟"的更新,实际上并没有改变任何参数值。 | ## 12.6.3\. 同义词词典 这个字典模板用于创建替代词和同义词的词典。不支持短语(使用同义词库模板(节[Section 12.6.4](#calibre_link-1121))。 一个同义词词典可以用来克服语言上的问题,例如,防止英语词干词典使单词"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()`中时, 结果将是带前缀匹配标记的查询记录(参见节[Section 12.3.2](#calibre_link-1108))。例如,假设我们在`$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`不分配给同义词词典。 | **Caution** | |:--- | | 索引中使用词典,同义词词典的任何参数变化都_需要_重新索引。对于大多数其他词典类型, 小的变化,比如添加或去除屏蔽词不强迫重新索引。 | ### 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`与`supernovae stars`在`thesaurus_astro`中匹配,因为我们在词典的定义中指定了`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](http://ficus-www.cs.ucla.edu/geoff/ispell.html)获得。 同时,— [MySpell](http://en.wikipedia.org/wiki/MySpell) (OO < 2.0.1) 和[Hunspell](http://sourceforge.net/projects/hunspell/)(OO >= 2.0.2)支持一些更现代的词典文件格式。 大的词典列表在[OpenOffice Wiki](http://wiki.services.openoffice.org/wiki/Dictionaries)中可用。 使用内置的`ispell`模板创建Ispell 词典,并指定几个参数: ``` CREATE TEXT SEARCH DICTIONARY english_ispell ( TEMPLATE = ispell, DictFile = english, AffFile = english, StopWords = english ); ``` 这里,`DictFile`, `AffFile`和`StopWords`指定词典基础的名字,词缀,和屏蔽词文件。 屏蔽词文件具有和上面解释的`simple`词典类型相同的格式。其它文件的格式不在这里指定, 但可以从上面提到的网站获取。 Ispell词典通常识别有限的一组词,所以他们应该遵循另一个更广泛的词典; 例如,一个Snowball词典,它可以识别一切。 Ispell词典支持分裂复合词;一个有用的功能。请注意, 词缀文件应使用`compound words controlled`语句指定一个特殊标记, 标记可以参与复合信息的词典单词: ``` compoundwords controlled z ``` 这有一些Norwegian语言的例子: ``` SELECT ts_lexize('norwegian_ispell', 'overbuljongterningpakkmesterassistent'); {over,buljong,terning,pakk,mester,assistent} SELECT ts_lexize('norwegian_ispell', 'sjokoladefabrikk'); {sjokoladefabrikk,sjokolade,fabrikk} ``` > **Note:** 注意:MySpell不支持复合词。Hunspell对复合词有复杂支持。 目前,PostgreSQL只实现了Hunspell的基本复合词操作。 ## 12.6.6\. Snowball词典 Snowball词典模板是基于Martin Porter的项目,他是英语语言的著名的Porter的词干提取算法的发明者。 现在Snowball提供了许多语言的词干提取算法(更多信息请见[Snowball site](http://snowball.tartarus.org))。 每个算法都知道如何改变词到基础,或词根,或其语言拼写的常见变异形式。 一个Snowball词典需要`language`参数标识要使用的词干,并且可以指定一个删除词的列表的`stopword`文件名。 (PostgreSQL的标准的屏蔽词列表也由Snowball项目提供)例如,有一个等价的内置定义。 ``` CREATE TEXT SEARCH DICTIONARY english_stem ( TEMPLATE = snowball, Language = english, StopWords = english ); ``` 屏蔽词的文件格式和已经解释过的一样。 一个Snowball词典可以识别一切,是否能够简化字,所以它应该放在词典列表的末尾。 它放在任何其他的词典之前都是无用的,因为一个标记将不会经过它到下一个词典。