为了实现全文搜索,必须有一个函数来从文档中创建 tsvector
,以及从用户查询中创建 tsquery
。此外,我们需要按有用顺序返回结果,因此我们需要一个函数来根据文档与查询的相关性比较文档。能够很好地显示结果也很重要。 PostgreSQL 为所有这些函数提供了支持。
PostgreSQL 提供了 to_tsvector
函数,用于将文档转换为 tsvector
数据类型。
to_tsvector([config
regconfig
, ]document
text
) returnstsvector
to_tsvector
将文本文档解析为标记,将标记简化为词素,并返回一个 tsvector
,其中列出了词素及其在文档中的位置。文档根据指定的或默认的文本搜索配置进行处理。以下是一个简单的示例
SELECT to_tsvector('english', 'a fat cat sat on a mat - it ate a fat rats'); to_tsvector ----------------------------------------------------- 'ate':9 'cat':3 'fat':2,11 'mat':7 'rat':12 'sat':4
在上面的示例中,我们看到结果 tsvector
不包含单词 a
、on
或 it
,单词 rats
变成了 rat
,并且标点符号 -
被忽略了。
to_tsvector
函数在内部调用一个解析器,该解析器将文档文本分解为标记,并为每个标记分配一个类型。对于每个标记,都会咨询一个字典列表(第12.6节),列表可以根据标记类型而有所不同。第一个识别标记的字典会发出一个或多个规范化的词素来表示标记。例如,rats
变成了 rat
,因为其中一个字典识别出单词 rats
是 rat
的复数形式。有些词被识别为停用词(第12.6.1节),这会导致它们被忽略,因为它们出现的频率太高,在搜索中没有用。在我们的示例中,这些是 a
、on
和 it
。如果列表中的字典没有识别出标记,则它也会被忽略。在这个示例中,标点符号 -
发生了这种情况,因为实际上没有为其标记类型 (Space symbols
) 分配任何字典,这意味着空格标记永远不会被索引。解析器、字典以及要索引的标记类型的选择由所选的文本搜索配置 (第12.7节) 决定。同一个数据库中可以有多个不同的配置,并且为各种语言提供了预定义的配置。在我们的示例中,我们使用了默认的 english
配置,用于英语。
setweight
函数可用于为 tsvector
的条目标记一个给定的权重,其中权重是字母 A
、B
、C
或 D
之一。这通常用于标记来自文档不同部分的条目,例如标题与正文。之后,此信息可用于排序搜索结果。
由于 to_tsvector
(NULL
) 将返回 NULL
,因此建议在字段可能为 null 时使用 coalesce
。以下是从结构化文档创建 tsvector
的推荐方法
UPDATE tt SET ti = setweight(to_tsvector(coalesce(title,'')), 'A') || setweight(to_tsvector(coalesce(keyword,'')), 'B') || setweight(to_tsvector(coalesce(abstract,'')), 'C') || setweight(to_tsvector(coalesce(body,'')), 'D');
在这里,我们使用 setweight
为完成的 tsvector
中的每个词素标记来源,然后使用 tsvector
连接运算符 ||
合并标记的 tsvector
值。 (第12.4.1节 提供了有关这些操作的详细信息。)
PostgreSQL 提供了 to_tsquery
、plainto_tsquery
、phraseto_tsquery
和 websearch_to_tsquery
函数,用于将查询转换为 tsquery
数据类型。 to_tsquery
提供了比 plainto_tsquery
或 phraseto_tsquery
更多功能的访问权限,但它对输入的容忍度较低。 websearch_to_tsquery
是 to_tsquery
的简化版本,具有与 Web 搜索引擎使用的语法类似的替代语法。
to_tsquery([config
regconfig
, ]querytext
text
) returnstsquery
to_tsquery
从querytext
创建一个 tsquery
值,querytext
必须由用 tsquery
运算符 &
(AND)、|
(OR)、!
(NOT) 和 <->
(FOLLOWED BY) 分隔的单个标记组成,这些标记可以用括号进行分组。换句话说,to_tsquery
的输入必须已经遵循 tsquery
输入的一般规则,如第8.11.2节 中所述。区别在于,基本 tsquery
输入按字面值获取标记,而 to_tsquery
使用指定的或默认配置将每个标记规范化为词素,并丢弃根据配置为停用词的任何标记。例如
SELECT to_tsquery('english', 'The & Fat & Rats'); to_tsquery --------------- 'fat' & 'rat'
与基本 tsquery
输入一样,权重可以附加到每个词素,以将其限制为仅匹配具有这些权重的 tsvector
词素。例如
SELECT to_tsquery('english', 'Fat | Rats:AB'); to_tsquery ------------------ 'fat' | 'rat':AB
此外,*
可以附加到词素以指定前缀匹配
SELECT to_tsquery('supern:*A & star:A*B'); to_tsquery -------------------------- 'supern':*A & 'star':*AB
这样的词素将匹配 tsvector
中以给定字符串开头的任何单词。
to_tsquery
也可以接受单引号引起来的短语。这主要在配置包含词典时有用,该词典可能会触发此类短语。在下面的示例中,词典包含规则 supernovae stars : sn
SELECT to_tsquery('''supernovae stars'' & !crab'); to_tsquery --------------- 'sn' & !'crab'
如果没有引号,对于没有用 AND、OR 或 FOLLOWED BY 运算符分隔的标记,to_tsquery
将生成语法错误。
plainto_tsquery([config
regconfig
, ]querytext
text
) returnstsquery
plainto_tsquery
将未格式化的文本querytext
转换为 tsquery
值。文本的解析和规范化与 to_tsvector
非常类似,然后在保留的单词之间插入 &
(AND) tsquery
运算符。
示例
SELECT plainto_tsquery('english', 'The Fat Rats'); plainto_tsquery ----------------- 'fat' & 'rat'
请注意,plainto_tsquery
不会在其输入中识别 tsquery
运算符、权重标签或前缀匹配标签
SELECT plainto_tsquery('english', 'The Fat & Rats:C'); plainto_tsquery --------------------- 'fat' & 'rat' & 'c'
在这里,所有输入标点符号都被丢弃了。
phraseto_tsquery([config
regconfig
, ]querytext
text
) returnstsquery
phraseto_tsquery
的行为与 plainto_tsquery
非常相似,不同之处在于它在保留的单词之间插入 <->
(FOLLOWED BY) 运算符,而不是 &
(AND) 运算符。此外,停用词不会简单地被丢弃,而是通过插入 <
运算符而不是 N
><->
运算符来进行处理。当搜索确切的词素序列时,此函数很有用,因为 FOLLOWED BY 运算符检查词素顺序,而不仅仅是所有词素的存在。
示例
SELECT phraseto_tsquery('english', 'The Fat Rats'); phraseto_tsquery ------------------ 'fat' <-> 'rat'
与 plainto_tsquery
一样,phraseto_tsquery
函数在其输入中不会识别 tsquery
运算符、权重标签或前缀匹配标签
SELECT phraseto_tsquery('english', 'The Fat & Rats:C'); phraseto_tsquery ----------------------------- 'fat' <-> 'rat' <-> 'c'
websearch_to_tsquery([config
regconfig
, ]querytext
text
) returnstsquery
websearch_to_tsquery
使用替代语法从querytext
创建一个 tsquery
值,在该语法中,简单的未格式化文本是有效的查询。与 plainto_tsquery
和 phraseto_tsquery
不同,它也识别某些运算符。此外,此函数永远不会引发语法错误,这使得可以使用原始用户提供的输入进行搜索。支持以下语法
unquoted text
:引号外的文本将转换为用 &
运算符分隔的术语,就像由 plainto_tsquery
处理一样。
"quoted text"
:引号内的文本将转换为用 <->
运算符分隔的术语,就像由 phraseto_tsquery
处理一样。
OR
:单词 “or” 将转换为 |
运算符。
-
:连字符将转换为 !
运算符。
其他标点符号将被忽略。因此,与 plainto_tsquery
和 phraseto_tsquery
一样,websearch_to_tsquery
函数在其输入中不会识别 tsquery
运算符、权重标签或前缀匹配标签。
示例
SELECT websearch_to_tsquery('english', 'The fat rats'); websearch_to_tsquery ---------------------- 'fat' & 'rat' (1 row) SELECT websearch_to_tsquery('english', '"supernovae stars" -crab'); websearch_to_tsquery ---------------------------------- 'supernova' <-> 'star' & !'crab' (1 row) SELECT websearch_to_tsquery('english', '"sad cat" or "fat rat"'); websearch_to_tsquery ----------------------------------- 'sad' <-> 'cat' | 'fat' <-> 'rat' (1 row) SELECT websearch_to_tsquery('english', 'signal -"segmentation fault"'); websearch_to_tsquery --------------------------------------- 'signal' & !( 'segment' <-> 'fault' ) (1 row) SELECT websearch_to_tsquery('english', '""" )( dummy \\ query <->'); websearch_to_tsquery ---------------------- 'dummi' & 'queri' (1 row)
排名试图衡量文档与特定查询的相关性,以便在有多个匹配项时,可以将最相关的匹配项首先显示。PostgreSQL 提供了两个预定义的排名函数,它们考虑了词汇、邻近性和结构信息;也就是说,它们考虑了查询词在文档中出现的频率、词在文档中彼此之间的距离以及它们出现的文档部分的重要性。但是,相关性的概念很模糊,并且高度依赖于应用程序。不同的应用程序可能需要其他信息进行排名,例如文档修改时间。内置的排名函数只是示例。您可以编写自己的排名函数,或者将它们的结果与其他因素结合起来,以满足您的特定需求。
目前可用的两个排名函数是
ts_rank([ weights
float4[]
, ] vector
tsvector
, query
tsquery
[, normalization
integer
]) returns float4
根据向量匹配词素的频率对向量进行排名。
ts_rank_cd([ weights
float4[]
, ] vector
tsvector
, query
tsquery
[, normalization
integer
]) returns float4
此函数计算给定文档向量和查询的覆盖密度排名,如 Clarke、Cormack 和 Tudhope 在 1999 年的《信息处理与管理》杂志上发表的“一至三项查询的相关性排名”中所述。覆盖密度类似于ts_rank
排名,除了考虑匹配词素彼此之间的距离。
此函数需要词素位置信息才能执行计算。因此,它会忽略tsvector
中任何“剥离”词素。如果输入中没有未剥离的词素,则结果将为零。(有关strip
函数和tsvector
中位置信息的更多信息,请参见第 12.4.1 节。)
对于这两个函数,可选的weights
参数提供了根据词语标记方式对词语实例进行加权或减权的能力。权重数组指定了如何对每个类别词语进行加权,顺序为
{D-weight, C-weight, B-weight, A-weight}
如果没有提供weights
,则使用以下默认值
{0.1, 0.2, 0.4, 1.0}
通常,权重用于标记来自文档特殊区域(如标题或初始摘要)的词语,因此可以比文档正文中的词语赋予更多或更少的权重。
由于较长的文档更有可能包含查询词,因此考虑文档大小是合理的,例如,包含五个搜索词的 100 字文档可能比包含五个搜索词的 1000 字文档更相关。这两个排名函数都接受一个整数normalization
选项,该选项指定文档长度是否以及如何影响其排名。整数选项控制多种行为,因此它是一个位掩码:您可以使用|
指定一个或多个行为(例如,2|4
)。
0(默认值)忽略文档长度
1 将排名除以 1 加上文档长度的对数
2 将排名除以文档长度
4 将排名除以范围之间的平均调和距离(仅由ts_rank_cd
实现)
8 将排名除以文档中唯一词语的数量
16 将排名除以 1 加上文档中唯一词语数量的对数
32 将排名除以自身加 1
如果指定了多个标志位,则转换将按列出的顺序应用。
需要注意的是,排名函数不使用任何全局信息,因此无法生成公平的归一化到 1% 或 100%,正如有时所期望的那样。归一化选项 32 (rank/(rank+1)
) 可以应用于将所有排名缩放到 0 到 1 之间的范围,但这只是一个美观的改变;它不会影响搜索结果的排序。
以下示例仅选择十个排名最高的匹配项
SELECT title, ts_rank_cd(textsearch, query) AS rank FROM apod, to_tsquery('neutrino|(dark & matter)') query WHERE query @@ textsearch ORDER BY rank DESC LIMIT 10; title | rank -----------------------------------------------+---------- Neutrinos in the Sun | 3.1 The Sudbury Neutrino Detector | 2.4 A MACHO View of Galactic Dark Matter | 2.01317 Hot Gas and Dark Matter | 1.91171 The Virgo Cluster: Hot Plasma and Dark Matter | 1.90953 Rafting for Solar Neutrinos | 1.9 NGC 4650A: Strange Galaxy and Dark Matter | 1.85774 Hot Gas and Dark Matter | 1.6123 Ice Fishing for Cosmic Neutrinos | 1.6 Weak Lensing Distorts the Universe | 0.818218
这是使用归一化排名的相同示例
SELECT title, ts_rank_cd(textsearch, query, 32 /* rank/(rank+1) */ ) AS rank FROM apod, to_tsquery('neutrino|(dark & matter)') query WHERE query @@ textsearch ORDER BY rank DESC LIMIT 10; title | rank -----------------------------------------------+------------------- Neutrinos in the Sun | 0.756097569485493 The Sudbury Neutrino Detector | 0.705882361190954 A MACHO View of Galactic Dark Matter | 0.668123210574724 Hot Gas and Dark Matter | 0.65655958650282 The Virgo Cluster: Hot Plasma and Dark Matter | 0.656301290640973 Rafting for Solar Neutrinos | 0.655172410958162 NGC 4650A: Strange Galaxy and Dark Matter | 0.650072921219637 Hot Gas and Dark Matter | 0.617195790024749 Ice Fishing for Cosmic Neutrinos | 0.615384618911517 Weak Lensing Distorts the Universe | 0.450010798361481
排名可能很昂贵,因为它需要查询每个匹配文档的tsvector
,这可能是 I/O 绑定的,因此速度很慢。不幸的是,这几乎是不可避免的,因为实际的查询通常会导致大量匹配项。
为了呈现搜索结果,理想情况下,应该显示每个文档的一部分以及它与查询的关系。通常,搜索引擎会显示带有标记搜索词的文档片段。PostgreSQL 提供了ts_headline
函数来实现此功能。
ts_headline([config
regconfig
, ]document
text
,query
tsquery
[,options
text
]) returnstext
ts_headline
接受文档和查询,并返回文档中的摘录,其中突出显示查询中的词语。具体而言,该函数将使用查询选择相关的文本片段,然后突出显示查询中出现的全部词语,即使这些词语位置与查询的限制不匹配。用于解析文档的配置可以通过config
指定;如果省略config
,则使用default_text_search_config
配置。
如果指定了options
字符串,则它必须包含一个或多个option
=
value
对的逗号分隔列表。可用的选项是
MaxWords
、MinWords
(整数):这些数字确定要输出的最长和最短标题。默认值为 35 和 15。
ShortWord
(整数):长度等于或小于此值的词语将从标题的开头和结尾删除,除非它们是查询词。默认值 3 会消除常见的英语冠词。
HighlightAll
(布尔值):如果为true
,则整个文档将用作标题,忽略前面的三个参数。默认值为false
。
MaxFragments
(整数):要显示的文本片段的最大数量。默认值 0 选择非基于片段的标题生成方法。大于 0 的值选择基于片段的标题生成(见下文)。
StartSel
、StopSel
(字符串):用于分隔文档中出现的查询词的字符串,以将它们与其他摘录词区分开来。默认值为“<b>
” 和“</b>
”,这些值适合 HTML 输出。
FragmentDelimiter
(字符串):当显示多个片段时,片段将使用此字符串分隔。默认值为“ ...
”.
这些选项名称不区分大小写。如果字符串值包含空格或逗号,则必须使用双引号将其括起来。
在非基于片段的标题生成中,ts_headline
查找给定query
的匹配项并选择一个匹配项进行显示,优先选择在允许的标题长度内包含更多查询词的匹配项。在基于片段的标题生成中,ts_headline
查找查询匹配项并将每个匹配项拆分为“片段”,每个片段最多MaxWords
个词语,优先选择包含更多查询词的片段,并且在可能的情况下“拉伸” 片段以包含周围的词语。因此,基于片段的模式在查询匹配跨越文档的较大部分,或需要显示多个匹配项时更加有用。在任何模式下,如果无法识别任何查询匹配项,则将显示文档中前MinWords
个词语的单个片段。
例如
SELECT ts_headline('english', 'The most common type of search is to find all documents containing given query terms and return them in order of their similarity to the query.', to_tsquery('english', 'query & similarity')); ts_headline ------------------------------------------------------------ containing given <b>query</b> terms + and return them in order of their <b>similarity</b> to the+ <b>query</b>. SELECT ts_headline('english', 'Search terms may occur many times in a document, requiring ranking of the search matches to decide which occurrences to display in the result.', to_tsquery('english', 'search & term'), 'MaxFragments=10, MaxWords=7, MinWords=3, StartSel=<<, StopSel=>>'); ts_headline ------------------------------------------------------------ <<Search>> <<terms>> may occur + many times ... ranking of the <<search>> matches to decide
ts_headline
使用原始文档,而不是tsvector
摘要,因此它可能很慢,应该谨慎使用。
如果您在文档中看到任何不正确的内容,与您对特定功能的体验不符或需要进一步澄清,请使用此表格 报告文档问题。