content_views"
c lass="markdown_views prism-atom-one-dark">
cap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-bloc k" style="-webkit-tap-highlight-c olor: rgba(0, 0, 0, 0);">
PostgreSQL版本为8.4.1 (本文为《PostgreSQL数据库 内核分析》一书的总结笔记c ;需要电子版的可私信我) 索引 篇:
PostgreSQL索引 篇 | BTree PostgreSQL索引 篇 | GiST索引 PostgreSQL索引 篇 | Hash索引 PostgreSQL索引 篇 | GIN索引 (倒排索引 )
c h2__8">TSearc h2 全文搜索
全文搜索(文本搜索)提供了一种可以检索出满足某个查询条件的自然语言文档的能力c ;并且还可以根据文档的相关性对文档进行排序。最常见的搜索是找出所有包含给出的查询词的文档c ;并且以它们符合查询的程度排序输出。
文本搜索操作符在数据库 里已经存在很多年了。PostgreSQL有<c ode>~c ode>、<c ode>~*c ode>和<c ode>LIKEc ode>操作符用于文本数据类型c ;但是它们缺乏许多现代的信息系统需要的重要功能c ;比如:
没有语言支持c ;不会对文本进行解析。 不提供检索结果的排序(ranking)c ;在找到上千个匹配文档的时候c ;就不够高效了。 没有索引 支持c ;所以会比较慢c ;因为它们必须为每个查询处理所有的文档。
从PostgreSQL8.3开始提供了文本搜索模块TSearc h (Text Searc h)c ;文本搜索提供了一种可以标识满足某个查询的自然语言文档的能力c ;并且还可以根据文档的相关性对文档进行排序。
PostgreSQL核心系统提供的TSearc h模块提供了对文档(在PostgreSQL里一个文档通常是一个表中的某个元组的一个文本属性c ;或是几个属性的组合)及查询条件进行解析的功能c ;但并没有提供对解析后的结果进行进一步处理(创建索引 c ;以支持快速的查找 )的功能。
PostgreSQL在扩展模块c ontrib 里面提供了TSearc h2来支持这些功能c ;TSearc h2实现了对文档创建GIN 或者GiST 索引 的支持。本节将分析PostgreSQL核心系统提供的TSearc h模块c ;其代码位于src /bac kend/tsearc h目录下。
全文索引 的创建
全文索引 允许对文档进行预处理并且可以保存为用于快速搜索的索引 。
预处理包括文本解析 、语义分析 和词位存储 。完成这三个过程后c ;解析后的词语信息就存放在TSVec tor结构 中。
从文本解析到词位存储这一系列过程是由函数to_tsvec tor_byid 完成的c ;
该函数首先调用parsetext 函数对文本进行解析和语义分析c ; 然后再调用make_tsvec tor 将词位信息构建成TSVec tor结构。
下面将对这三个过程依次进行分析。
文本解析
文本解析通过解析器将文档解析成一个个记号(含位置信息c ;类型信息)c ;该过程涉及的函数在wparser_def.c 文件中。 目前PostgreSQL只提供一种解析器c ;但它足够处理大多数纯文本及 HTML文件 。
PostgreSQL中默认的记号对应表如表4-9所示。
c="https://img-blog.c sdnimg.c n/direc t/1700c c df60834a73a2aaede851e55f44.png#pic _c enter" alt="默认解析器记号对照表" />
语义分析
语义分析是对解析器处理过的token文本序列通过参照词典的审核规范成标准的词(lexeme)信息。
词典用于删除那些不应该在搜索中出现的词(屏蔽词)并规范化一些有多重形式的词c ;这样同一个词的不同的衍生结果也可以被搜索到。
成功规范化之后的词被称作词位(lexeme)。除了改进搜索质量c ;规范化和删除屏蔽词可以减少文档的尺寸c ;从而提高性能。下面对各个词典的使用进行举例介绍:
<c ode>Ispellc ode>:拼写词典c ;例如“likes”将转换为“like”。 <c ode>Simplec ode>:简单词典c ;例如“A NAUGHTY DOG”将转换为“naughty dog”。 <c ode>Synonymc ode>:同义词典c ;例如“man”和“person”是同义词。 <c ode>Thesaurusc ode>:知识词典c ;例如“personal c omputer”将转换为PC。
完成语义分析后c ;即得到一个全部处理后得到的单词信息c ;这些单词信息保存在ParsedText结构中。
ParsedText结构 保存解析后的文本c ;其定义如数据结构4.20所示。
c="https://img-blog.c sdnimg.c n/direc t/ea3958f6e65842658108dfd6a9c 157c 0.png#pic _c enter" alt="ParsedText" />
其中的<c ode>wordsc ode>指向一个数组c ;其中每一个元素都是ParsedWord 类型c ;用于保存分析后的一个单词c ;其定义如数据结构4.21所示。
c="https://img-blog.c sdnimg.c n/direc t/50ec 7e01b24245ebaef45ee0f996dc da.png#pic _c enter" alt="ParsedWord" />
数据结构4.21中的<c ode>posc ode>和<c ode>aposc ode>指针是用union结构(允许在相同的内存位置存储不同的数据类型c ;但只能存在一个)来保存的。
当完成文本解析后c ;可能会遇到相同的词出现了多次的情况c ;这时会将相同的词合并在一起:
对于只出现一次的词c ;使用<c ode>posc ode>来保存其出现的位置即可;
对于出现多次的词c ;则使用<c ode>aposc ode>指针来指向一个动态数组来保存所有出现的位置。apos[0]为该词出现的次数c ;数组后面的值即为各次出现的位置。
由于数组的长度是不确定的c ;所以使用<c ode>alenc ode>字段来确定动态申请内存的空间c ;alen初始化为2c ;每当apos 的长度不够时c ;alen即翻倍c ;同时申请新内存将apos数组的长度翻倍。
上面介绍了用于存储语义分析后得到的单词信息的数据结构c ;而这整个处理流程是由函数parsetext 完成的c ;其执行如图4-36所示。
函数parsetext
c="https://img-blog.c sdnimg.c n/direc t/6e78c 5bfc 0024d9ea78508e24005f97d.png#pic _c enter" alt="语义分析流程" />
<c ode c lass="prism language-c ">class="token c omment">/*
* Parse string and lexize words.
*
* prs will be filled in.
*/
class="token keyword">void
class="token func tion">parsetext class="token punc tuation">( Oid c fgIdclass="token punc tuation">, ParsedText class="token operator">* prsclass="token punc tuation">, class="token keyword">c har class="token operator">* bufclass="token punc tuation">, class="token keyword">int buflenclass="token punc tuation">)
class="token punc tuation">{
class="token keyword">int typeclass="token punc tuation">,
lenlemmclass="token punc tuation">;
class="token keyword">c har class="token operator">* lemm class="token operator">= class="token c onstant">NULL class="token punc tuation">;
LexizeData ldataclass="token punc tuation">;
TSLexeme class="token operator">* normsclass="token punc tuation">;
TSConfigCac heEntry class="token operator">* c fgclass="token punc tuation">;
TSParserCac heEntry class="token operator">* prsobjclass="token punc tuation">;
class="token keyword">void class="token operator">* prsdataclass="token punc tuation">;
c fg class="token operator">= class="token func tion">lookup_ts_c onfig_c ac he class="token punc tuation">( c fgIdclass="token punc tuation">) class="token punc tuation">;
prsobj class="token operator">= class="token func tion">lookup_ts_parser_c ac he class="token punc tuation">( c fgclass="token operator">-> prsIdclass="token punc tuation">) class="token punc tuation">;
prsdata class="token operator">= class="token punc tuation">( class="token keyword">void class="token operator">* class="token punc tuation">) class="token func tion">DatumGetPointer class="token punc tuation">( class="token func tion">Func tionCall2 class="token punc tuation">( class="token operator">& prsobjclass="token operator">-> prsstartclass="token punc tuation">,
class="token func tion">PointerGetDatum class="token punc tuation">( bufclass="token punc tuation">) class="token punc tuation">,
class="token func tion">Int32GetDatum class="token punc tuation">( buflenclass="token punc tuation">) class="token punc tuation">) class="token punc tuation">) class="token punc tuation">;
class="token func tion">LexizeInit class="token punc tuation">( class="token operator">& ldataclass="token punc tuation">, c fgclass="token punc tuation">) class="token punc tuation">;
class="token keyword">do
class="token punc tuation">{
type class="token operator">= class="token func tion">DatumGetInt32 class="token punc tuation">( class="token func tion">Func tionCall3 class="token punc tuation">( class="token operator">& class="token punc tuation">( prsobjclass="token operator">-> prstokenclass="token punc tuation">) class="token punc tuation">,
class="token func tion">PointerGetDatum class="token punc tuation">( prsdataclass="token punc tuation">) class="token punc tuation">,
class="token func tion">PointerGetDatum class="token punc tuation">( class="token operator">& lemmclass="token punc tuation">) class="token punc tuation">,
class="token func tion">PointerGetDatum class="token punc tuation">( class="token operator">& lenlemmclass="token punc tuation">) class="token punc tuation">) class="token punc tuation">) class="token punc tuation">;
class="token c omment">// 类型合法&&单词长度过长
class="token keyword">if class="token punc tuation">( type class="token operator">> class="token number">0 class="token operator">&& lenlemm class="token operator">>= MAXSTRLENclass="token punc tuation">)
class="token punc tuation">{
class="token mac ro property">class="token direc tive-hash"># class="token direc tive keyword">ifdef class="token expression">IGNORE_LONGLEXEME
class="token func tion">ereport class="token punc tuation">( NOTICEclass="token punc tuation">,
class="token punc tuation">( class="token func tion">errc ode class="token punc tuation">( ERRCODE_PROGRAM_LIMIT_EXCEEDEDclass="token punc tuation">) class="token punc tuation">,
class="token func tion">errmsg class="token punc tuation">( class="token string">"word is too long to be indexed" class="token punc tuation">) class="token punc tuation">,
class="token func tion">errdetail class="token punc tuation">( class="token string">"Words longer than %d c harac ters are ignored." class="token punc tuation">,
MAXSTRLENclass="token punc tuation">) class="token punc tuation">) class="token punc tuation">) class="token punc tuation">;
class="token keyword">c ontinue class="token punc tuation">;
class="token mac ro property">class="token direc tive-hash"># class="token direc tive keyword">else
class="token func tion">ereport class="token punc tuation">( ERRORclass="token punc tuation">,
class="token punc tuation">( class="token func tion">errc ode class="token punc tuation">( ERRCODE_PROGRAM_LIMIT_EXCEEDEDclass="token punc tuation">) class="token punc tuation">,
class="token func tion">errmsg class="token punc tuation">( class="token string">"word is too long to be indexed" class="token punc tuation">) class="token punc tuation">,
class="token func tion">errdetail class="token punc tuation">( class="token string">"Words longer than %d c harac ters are ignored." class="token punc tuation">,
MAXSTRLENclass="token punc tuation">) class="token punc tuation">) class="token punc tuation">) class="token punc tuation">;
class="token mac ro property">class="token direc tive-hash"># class="token direc tive keyword">endif
class="token punc tuation">}
class="token func tion">LexizeAddLemm class="token punc tuation">( class="token operator">& ldataclass="token punc tuation">, typeclass="token punc tuation">, lemmclass="token punc tuation">, lenlemmclass="token punc tuation">) class="token punc tuation">;
class="token c omment">// 对token单词调用字典进行处理(语义分析)
class="token keyword">while class="token punc tuation">( class="token punc tuation">( norms class="token operator">= class="token func tion">LexizeExec class="token punc tuation">( class="token operator">& ldataclass="token punc tuation">, class="token c onstant">NULL class="token punc tuation">) class="token punc tuation">) class="token operator">!= class="token c onstant">NULL class="token punc tuation">)
class="token punc tuation">{
TSLexeme class="token operator">* ptr class="token operator">= normsclass="token punc tuation">; class="token c omment">// norms就是已规范后的一些词
prsclass="token operator">-> posclass="token operator">++ class="token punc tuation">; class="token c omment">/* set pos */
class="token keyword">while class="token punc tuation">( ptrclass="token operator">-> lexemeclass="token punc tuation">) class="token c omment">// 若解析后的单词不为空c ;则将词信息存放到Parsed Text结构中
class="token punc tuation">{
class="token keyword">if class="token punc tuation">( prsclass="token operator">-> c urwords class="token operator">== prsclass="token operator">-> lenwordsclass="token punc tuation">)
class="token punc tuation">{
prsclass="token operator">-> lenwords class="token operator">*= class="token number">2 class="token punc tuation">; class="token c omment">// words内存空间翻倍
prsclass="token operator">-> words class="token operator">= class="token punc tuation">( ParsedWord class="token operator">* class="token punc tuation">) class="token func tion">repalloc class="token punc tuation">( class="token punc tuation">( class="token keyword">void class="token operator">* class="token punc tuation">) prsclass="token operator">-> wordsclass="token punc tuation">, prsclass="token operator">-> lenwords class="token operator">* class="token keyword">sizeof class="token punc tuation">( ParsedWordclass="token punc tuation">) class="token punc tuation">) class="token punc tuation">;
class="token punc tuation">}
class="token keyword">if class="token punc tuation">( ptrclass="token operator">-> flags class="token operator">& TSL_ADDPOSclass="token punc tuation">)
prsclass="token operator">-> posclass="token operator">++ class="token punc tuation">;
prsclass="token operator">-> wordsclass="token punc tuation">[ prsclass="token operator">-> c urwordsclass="token punc tuation">] class="token punc tuation">. len class="token operator">= class="token func tion">strlen class="token punc tuation">( ptrclass="token operator">-> lexemeclass="token punc tuation">) class="token punc tuation">;
prsclass="token operator">-> wordsclass="token punc tuation">[ prsclass="token operator">-> c urwordsclass="token punc tuation">] class="token punc tuation">. word class="token operator">= ptrclass="token operator">-> lexemeclass="token punc tuation">;
prsclass="token operator">-> wordsclass="token punc tuation">[ prsclass="token operator">-> c urwordsclass="token punc tuation">] class="token punc tuation">. nvariant class="token operator">= ptrclass="token operator">-> nvariantclass="token punc tuation">;
prsclass="token operator">-> wordsclass="token punc tuation">[ prsclass="token operator">-> c urwordsclass="token punc tuation">] class="token punc tuation">. flags class="token operator">= ptrclass="token operator">-> flags class="token operator">& TSL_PREFIXclass="token punc tuation">;
prsclass="token operator">-> wordsclass="token punc tuation">[ prsclass="token operator">-> c urwordsclass="token punc tuation">] class="token punc tuation">. alen class="token operator">= class="token number">0 class="token punc tuation">;
prsclass="token operator">-> wordsclass="token punc tuation">[ prsclass="token operator">-> c urwordsclass="token punc tuation">] class="token punc tuation">. posclass="token punc tuation">. pos class="token operator">= class="token func tion">LIMITPOS class="token punc tuation">( prsclass="token operator">-> posclass="token punc tuation">) class="token punc tuation">;
ptrclass="token operator">++ class="token punc tuation">;
prsclass="token operator">-> c urwordsclass="token operator">++ class="token punc tuation">;
class="token punc tuation">}
class="token func tion">pfree class="token punc tuation">( normsclass="token punc tuation">) class="token punc tuation">;
class="token punc tuation">}
class="token punc tuation">} class="token keyword">while class="token punc tuation">( type class="token operator">> class="token number">0 class="token punc tuation">) class="token punc tuation">; class="token c omment">// 判断类型是否合法c ;不合法的话说明已经分析完所有词了
class="token func tion">Func tionCall1 class="token punc tuation">( class="token operator">& class="token punc tuation">( prsobjclass="token operator">-> prsendclass="token punc tuation">) class="token punc tuation">, class="token func tion">PointerGetDatum class="token punc tuation">( prsdataclass="token punc tuation">) class="token punc tuation">) class="token punc tuation">;
class="token punc tuation">}
c ode>
词位存储
词位存储即为归总语义分析后的单词在文档中的位置c ;并将其出现的次数及每个位置存储下来。
TSVec tor 是一种可搜索的数据类型c ;它是文档内容的一种表现形式c ;是出现在文档中的每个重要单词及其所有位置信息的集合。它通过一种特殊的优化结构进行组织c ;从而可方便快速地存取及查找。其定义如数据结构4.22所示。
c="https://img-blog.c sdnimg.c n/direc t/141a6803b4874fada3b7dc 32b6bc 0bfb.png#pic _c enter" alt="TSVec tor" />
TSVec torData结构中的<c ode>WordEntryc ode>数组用于保存所有的关键字(单词)信息c ;由于关键字的数目一开始并不确定c ;所以使用一个数组指针c ;该数组的实际长度根据关键字的个数在使用时进行分配。
<c ode>WordEntryc ode>的定义见数据结构4.23。(结构体成员后面的数字用来限定成员变量占用的位数)
c="https://img-blog.c sdnimg.c n/direc t/30279012ff04459db1e375220fb23920.png#pic _c enter" alt="WordEntry" />
上面分析了TSVec tor 的数据结构c ;词位存储即使用语义分析得到的ParsedText构建TSVec torc ;该过程由函数make_tsvec tor 完成。其执行流程如图4-37所示。
c="https://img-blog.c sdnimg.c n/direc t/6ac 7996b882e4a4b94d3ae2b8e7df705.png#pic _c enter" alt="构建 TSVec tor 流程图" />
<c ode c lass="prism language-c ">class="token c omment">/*
* make value of tsvec tor, given parsed text
*/
TSVec tor
class="token func tion">make_tsvec tor class="token punc tuation">( ParsedText class="token operator">* prsclass="token punc tuation">)
class="token punc tuation">{
class="token keyword">int iclass="token punc tuation">,
jclass="token punc tuation">,
lenstr class="token operator">= class="token number">0 class="token punc tuation">,
totallenclass="token punc tuation">;
TSVec tor inclass="token punc tuation">;
WordEntry class="token operator">* ptrclass="token punc tuation">;
class="token keyword">c har class="token operator">* strclass="token punc tuation">;
class="token keyword">int stroffclass="token punc tuation">;
prsclass="token operator">-> c urwords class="token operator">= class="token func tion">uniqueWORD class="token punc tuation">( prsclass="token operator">-> wordsclass="token punc tuation">, prsclass="token operator">-> c urwordsclass="token punc tuation">) class="token punc tuation">; class="token c omment">// 合并相同单词的位置到apos中
class="token keyword">for class="token punc tuation">( i class="token operator">= class="token number">0 class="token punc tuation">; i class="token operator">< prsclass="token operator">-> c urwordsclass="token punc tuation">; iclass="token operator">++ class="token punc tuation">)
class="token punc tuation">{
lenstr class="token operator">+= prsclass="token operator">-> wordsclass="token punc tuation">[ iclass="token punc tuation">] class="token punc tuation">. lenclass="token punc tuation">;
class="token keyword">if class="token punc tuation">( prsclass="token operator">-> wordsclass="token punc tuation">[ iclass="token punc tuation">] class="token punc tuation">. alenclass="token punc tuation">)
class="token punc tuation">{
lenstr class="token operator">= class="token func tion">SHORTALIGN class="token punc tuation">( lenstrclass="token punc tuation">) class="token punc tuation">;
lenstr class="token operator">+= class="token keyword">sizeof class="token punc tuation">( uint16class="token punc tuation">) class="token operator">+ prsclass="token operator">-> wordsclass="token punc tuation">[ iclass="token punc tuation">] class="token punc tuation">. posclass="token punc tuation">. aposclass="token punc tuation">[ class="token number">0 class="token punc tuation">] class="token operator">* class="token keyword">sizeof class="token punc tuation">( WordEntryPosclass="token punc tuation">) class="token punc tuation">;
class="token punc tuation">}
class="token punc tuation">}
class="token keyword">if class="token punc tuation">( lenstr class="token operator">> MAXSTRPOSclass="token punc tuation">)
class="token func tion">ereport class="token punc tuation">( ERRORclass="token punc tuation">,
class="token punc tuation">( class="token func tion">errc ode class="token punc tuation">( ERRCODE_PROGRAM_LIMIT_EXCEEDEDclass="token punc tuation">) class="token punc tuation">,
class="token func tion">errmsg class="token punc tuation">( class="token string">"string is too long for tsvec tor (%d bytes, max %d bytes)" class="token punc tuation">, lenstrclass="token punc tuation">, MAXSTRPOSclass="token punc tuation">) class="token punc tuation">) class="token punc tuation">) class="token punc tuation">;
class="token c omment">// 根据ParsedText中唯一单词的数目(c urwords)去计算TSVec tor所需空间
totallen class="token operator">= class="token func tion">CALCDATASIZE class="token punc tuation">( prsclass="token operator">-> c urwordsclass="token punc tuation">, lenstrclass="token punc tuation">) class="token punc tuation">;
in class="token operator">= class="token punc tuation">( TSVec torclass="token punc tuation">) class="token func tion">palloc 0 class="token punc tuation">( totallenclass="token punc tuation">) class="token punc tuation">; class="token c omment">// 分配空间
class="token func tion">SET_VARSIZE class="token punc tuation">( inclass="token punc tuation">, totallenclass="token punc tuation">) class="token punc tuation">;
inclass="token operator">-> size class="token operator">= prsclass="token operator">-> c urwordsclass="token punc tuation">; class="token c omment">// 设置size
ptr class="token operator">= class="token func tion">ARRPTR class="token punc tuation">( inclass="token punc tuation">) class="token punc tuation">; class="token c omment">// ARRPTR(x) ( (x)->entries )
str class="token operator">= class="token func tion">STRPTR class="token punc tuation">( inclass="token punc tuation">) class="token punc tuation">;
stroff class="token operator">= class="token number">0 class="token punc tuation">;
class="token keyword">for class="token punc tuation">( i class="token operator">= class="token number">0 class="token punc tuation">; i class="token operator">< prsclass="token operator">-> c urwordsclass="token punc tuation">; iclass="token operator">++ class="token punc tuation">) class="token c omment">// 取ParsedText中下一个(第一个)单词word
class="token punc tuation">{ class="token c omment">// 将该单词的信息拷贝到TSVec torData.entries中
ptrclass="token operator">-> len class="token operator">= prsclass="token operator">-> wordsclass="token punc tuation">[ iclass="token punc tuation">] class="token punc tuation">. lenclass="token punc tuation">;
ptrclass="token operator">-> pos class="token operator">= stroffclass="token punc tuation">;
class="token func tion">memc py class="token punc tuation">( str class="token operator">+ stroffclass="token punc tuation">, prsclass="token operator">-> wordsclass="token punc tuation">[ iclass="token punc tuation">] class="token punc tuation">. wordclass="token punc tuation">, prsclass="token operator">-> wordsclass="token punc tuation">[ iclass="token punc tuation">] class="token punc tuation">. lenclass="token punc tuation">) class="token punc tuation">;
stroff class="token operator">+= prsclass="token operator">-> wordsclass="token punc tuation">[ iclass="token punc tuation">] class="token punc tuation">. lenclass="token punc tuation">;
class="token func tion">pfree class="token punc tuation">( prsclass="token operator">-> wordsclass="token punc tuation">[ iclass="token punc tuation">] class="token punc tuation">. wordclass="token punc tuation">) class="token punc tuation">;
class="token keyword">if class="token punc tuation">( prsclass="token operator">-> wordsclass="token punc tuation">[ iclass="token punc tuation">] class="token punc tuation">. alenclass="token punc tuation">) class="token c omment">// 获取word的alenc ;等于0说明没有位置信息
class="token punc tuation">{
class="token keyword">int k class="token operator">= prsclass="token operator">-> wordsclass="token punc tuation">[ iclass="token punc tuation">] class="token punc tuation">. posclass="token punc tuation">. aposclass="token punc tuation">[ class="token number">0 class="token punc tuation">] class="token punc tuation">; class="token c omment">// 出现次数
WordEntryPos class="token operator">* wptrclass="token punc tuation">;
class="token keyword">if class="token punc tuation">( k class="token operator">> class="token number">0xFFFF class="token punc tuation">)
class="token func tion">elog class="token punc tuation">( ERRORclass="token punc tuation">, class="token string">"positions array too long" class="token punc tuation">) class="token punc tuation">;
ptrclass="token operator">-> haspos class="token operator">= class="token number">1 class="token punc tuation">;
stroff class="token operator">= class="token func tion">SHORTALIGN class="token punc tuation">( stroffclass="token punc tuation">) class="token punc tuation">;
class="token operator">* class="token punc tuation">( uint16 class="token operator">* class="token punc tuation">) class="token punc tuation">( str class="token operator">+ stroffclass="token punc tuation">) class="token operator">= class="token punc tuation">( uint16class="token punc tuation">) kclass="token punc tuation">;
wptr class="token operator">= class="token func tion">POSDATAPTR class="token punc tuation">( inclass="token punc tuation">, ptrclass="token punc tuation">) class="token punc tuation">;
class="token keyword">for class="token punc tuation">( j class="token operator">= class="token number">0 class="token punc tuation">; j class="token operator">< kclass="token punc tuation">; jclass="token operator">++ class="token punc tuation">)
class="token punc tuation">{
class="token func tion">WEP_SETWEIGHT class="token punc tuation">( wptrclass="token punc tuation">[ jclass="token punc tuation">] class="token punc tuation">, class="token number">0 class="token punc tuation">) class="token punc tuation">; class="token c omment">// 设置权重
class="token func tion">WEP_SETPOS class="token punc tuation">( wptrclass="token punc tuation">[ jclass="token punc tuation">] class="token punc tuation">, prsclass="token operator">-> wordsclass="token punc tuation">[ iclass="token punc tuation">] class="token punc tuation">. posclass="token punc tuation">. aposclass="token punc tuation">[ j class="token operator">+ class="token number">1 class="token punc tuation">] class="token punc tuation">) class="token punc tuation">; class="token c omment">// 设置位置
class="token punc tuation">}
stroff class="token operator">+= class="token keyword">sizeof class="token punc tuation">( uint16class="token punc tuation">) class="token operator">+ k class="token operator">* class="token keyword">sizeof class="token punc tuation">( WordEntryPosclass="token punc tuation">) class="token punc tuation">;
class="token func tion">pfree class="token punc tuation">( prsclass="token operator">-> wordsclass="token punc tuation">[ iclass="token punc tuation">] class="token punc tuation">. posclass="token punc tuation">. aposclass="token punc tuation">) class="token punc tuation">;
class="token punc tuation">}
class="token keyword">else
ptrclass="token operator">-> haspos class="token operator">= class="token number">0 class="token punc tuation">;
ptrclass="token operator">++ class="token punc tuation">;
class="token punc tuation">}
class="token func tion">pfree class="token punc tuation">( prsclass="token operator">-> wordsclass="token punc tuation">) class="token punc tuation">;
class="token keyword">return inclass="token punc tuation">;
class="token punc tuation">}
c ode>
至此已经介绍了PostgreSQL内核中 TSearc h模块提供的全部功能。之前讲过c ;PostgreSQL把TSearc h2作为一个扩展模块c ;提供了对Entry创建GIN或者GiST索引 的支持。通过对词位列创建GIN或GiST索引 即可实现对文档的全文索引 。
综上所述c ;全文索引 的创建流程图可归纳如图4-38所示c ;其中创建GIN ( GiST)索引 部分用虚线框,表示该步骤是需要用户额外编译并安装TSearc h2模块才具有的功能。 由于GIN或者GiST索引 结构的不同c ;其创建、查询及更新的效率也有不同c ;PostgreSQL 8.4.1官方手册上对这两种索引 结构的优劣进行了比较:
GIN 索引 查询速度是GiST的3倍。 GiST创建索引 的速度是GIN的3倍。 GiST索引 的更新速度较GIN稍快。 GIN索引 的空间比GiST索引 大2至3倍。
前面介绍了从文本解析一直到创建全文索引 的全部过程。索引 创建完成后c ;即可利用全文索引 进行查询。由于查询条件可能是自然语句c ;也需要对查询进行一定的处理以获取准确的查询结果。
接下来将介绍如何利用这里建立的全文索引 进行查询。
全文索引 的查询
全文索引 查询之前需要对检索的语句进行处理c ;处理过程跟全文索引 的创建过程类似c ;要对查询语句进行文本解析及语义分析c ;将分析后的结果封装成TSquery格式c ;然后就可以通过对创建在TSvec tor上的索引 进行匹配查询了。但与创建过程不同的是:查询处理时c ;各个处理后的单词需要使用布尔操作符&(与)、│(或)和!(非)进行连接。
TSquery的格式如数据结构4.24所示。
c="https://img-blog.c sdnimg.c n/direc t/44e25aa846384a1580f838df03d3efa0.png#pic _c enter" alt="TSQueryData" />
PostgreSQL提供了to_tsquery 和plainto_tsquery 两个函数用于把查询转换成TSQuery数据类型。
to_tsquery 的功能是将查询中的关键词在语义分析后转化成可与TSVec tor进行匹配的数据类型。该函数的参数要求很严格c ;它们必须使用布尔操作符“&”(与)、“|”(或)和“!”(非)分隔各个单词。
plainto_tsquery 对查询的格式要求没有to_tsquery那么严格c ;它把未格式化的文本查询转换成TSQuery。文本首先会像在to_tsvec tor里那样分析和规范化c ;然后用布尔操作符“&”(与)将解析后得到的单词进行连接c ;最后得到TSQuery。
to_tsquery 和plainto_tsquery 函数的处理都是通过调用parse_tsquery 函数来实现的。
parse_tsquery 函数实现了对用户输入的查询条件的解析及语义分析过程c ;使用布尔操作符将得到的关键字连接起来。通过调用makepol 函数c ;将查询语句转换成“波兰表示法”(“Polish notation”c ;或称为波兰记法)。
对查询条件的解析过程与4.6.1节中的过程基本一样c ;通过调用parsetext 函数完成。当读取到查询条件中的操作符时c ;只能是上面讲到的三种操作符c ;对于其他操作符则报错(plainto_tsquery函数则不会处理用户输入条件中的操作符c ;它对查询条件进行解析后c ;全部使用“&”(与)操作符连接得到的关键词)。
当得到TSquery后c ;即可调用之前创建的全文索引 进行查询c ;全文索引 查询的总体流程如图4-39所示。
c="https://img-blog.c sdnimg.c n/direc t/0ebc f14c 31a649ec b678a50de09c 8ab8.png#pic _c enter" alt="全文索引 查询流程" />
查询结果处理
上面分析了全文索引 的创建和查询处理过程。数据库 系统提供了@@操作符c ;可以对TSVec tor(或处理后的TSQuery)结构中的entry进行比较。下面给出一个使用@@操作符进行全文搜索的例子。
假设现在在数据库 中有一个messages表c ;其字段内容如表4-10所示。
c="https://img-blog.c sdnimg.c n/direc t/a035f22283fd4079aad3eac 5fc 131e27.png#pic _c enter" alt="messages表" />
如果要从上表中查出属性strMessage中包含“test”或者“king”的文档信息c ;其tsearc h语句如下:
<c ode c lass="prism language-sql">class="token keyword">SELECT idclass="token punc tuation">, strtopic class="token keyword">FROM Messages
class="token keyword">WHERE to_tsvec torclass="token punc tuation">( strMessageclass="token punc tuation">) @@ to_tsqueryclass="token punc tuation">( class="token string">'test | king ' class="token punc tuation">) class="token punc tuation">;
c ode>
PostgreSQL会对messages表中的strMessage字段按4.6.1节中的介绍进行处理c ;然后对“test | king"采用全文索引 的查询中的to_tsquery 函数进行处理c ;再调用@@操作符进行匹配。最后c ;返回匹配成功的结果c ;如表4-11所示。
c="https://img-blog.c sdnimg.c n/direc t/f1ada7304c c d4449afaeda9d21314d4e.png#pic _c enter" alt="messages表" />
对全文搜索的结果c ;TSearc h还提供了一些功能对结果进行处理c ;包括权重设置、结果排序以及结果高亮显示c ;下面将对这3种功能进行介绍。
设置文档部分的权重
权重用于标记单词来自于文档的哪个区域c ;比如标题和开头的摘要c ;这样就可以以不同的方式对待不同权重的单词。可以通过调用函数tsvec tor_setweight 将一个TSVec tor中所有的关键字都设置为指定的权重。
对查询结果排序
排序主要是将匹配成功的搜索结果与查询表达式进行相关性比较c ;将最相关的结果排在前面。相关性是评判文档与特定的查询之间相关程度的一个衡量标准。如果有很多匹配的项c ;那么相关性高的项应该排在前面。
ts_rank 和ts_rank_ed 函数都可以用来对查询结果进行排序。
高亮显示
呈现搜索结果时c ;最好是显示每个文档的一部分以及它和查询之间是如何关联的。通常c ;搜索引 擎会显示查询关键词所在的文档片段c ;并且在其中将查询关键词高亮显示。PostgreSQL也提供一个函数ts_headline实现这个功能。
ts_headline 接受一个文档以及对应的查询c ;然后返回一个文档的摘要。在摘要里面c ;查询是高亮显示的。ts_headline使用原始的文档c ;而不是TSVec tor摘要c ;所以它可能比较慢c ;因此要小心使用。
索引 是提高数据库 性能的常用方法c ;它可以令数据库 服务器以更快的速度查找和检索特定的行。不过索引 也增加了数据库 系统的负荷、成本c ;因此应该恰当地使用它们。索引 的优劣不仅仅与索引 的查询效率有关c ;同时与索引 创建速度c ;更新速度c ;索引 大小等因素有关。以上分析的几种索引 使用环境不同c ;相同环境下使用的优劣也各不相同。于是c ;为了方便用户对特殊数据类型数据的查询c ;PostgreSQL中提供了索引 模板可方便用户对索引 的扩展以支持自定义数据类型上的索引 创建与查询。
通过使用索引 c ;能够快速查找数据库 中的数据。但在添加索引 的同时c ;会增加数据库 系统的负荷;在增删修改数据时c ;维护索引 的一致性也需要一定的时间和空间。由于索引 给查找数据带来的巨大的性能提升c ;因此也得到了广泛的应用。在实际的使用中c ;应该合理权衡使用索引 带来的利弊c ;恰当地使用索引 。