背景
全文搜索(文本搜索)提供了一种可以检索出满足某个查询条件的自然语言文档的能力,并且还可以根据文档的相关性对文档进行排序。最常见的搜索是找出所有包含给出的查询词的文档,并且以它们符合查询的程度排序输出。
文本搜索操作符在数据库里已经存在很多年了。PostgreSQL有~、~*和LIKE操作符用于文本数据类型,但是它们缺乏许多现代的信息系统需要的重要功能。
概念
从PostgreSQL8.3开始提供了文本搜索模块TSearch(Text Search),文本搜索提供了一种可以标识满足某个查询的自然语言文档的能力,并且还可以根据文档的相关性对文档进行排序。
PostgreSQL核心系统提供的TSearch模块提供了对文档(在PostgreSQL里一个文档通常是一个表中的某个元组的一个文本属性,或是几个属性的组合)及查询条件进行解析的功能,但并没有提供对解析后的结果进行进一步处理(创建索引,以支持快速的查找)的功能。
PostgreSQL在扩展模块contrib里面提供了TSearch2来支持这些功能,TSearch2实现了对文档创建GIN或者GiST索引的支持。本节将分析PostgreSQL核心系统提供的TSearch模块,其代码位于src/backend/tsearch目录下。
PostgreSQL把TSearch2作为一个扩展模块,提供了对Entry创建GIN或者GiST索引的支持。通过对词位列创建GIN或GiST索引即可实现对文档的全文索引。
全文索引
全文索引允许对文档进行预处理并且可以保存为用于快速搜索的索引。
预处理包括文本解析、语义分析和词位存储。完成这三个过程后,解析后的词语信息就存放在TSVector结构中。
从文本解析到词位存储这一系列过程是由函数to_tsvector_byid完成的,
该函数首先调用parsetext函数对文本进行解析和语义分析,
然后再调用make_tsvector将词位信息构建成TSVector结构。
下面将对这三个过程依次进行分析。
文本解析
文本解析通过解析器将文档解析成一个个记号(含位置信息,类型信息),该过程涉及的函数在wparser_def.c文件中。
目前PostgreSQL只提供一种解析器,但它足够处理大多数纯文本及 HTML文件。
语义分析
语义分析是对解析器处理过的token文本序列通过参照词典的审核规范成标准的词(lexeme)信息。
词典用于删除那些不应该在搜索中出现的词(屏蔽词)并规范化一些有多重形式的词,这样同一个词的不同的衍生结果也可以被搜索到。
成功规范化之后的词被称作词位(lexeme)。除了改进搜索质量,规范化和删除屏蔽词可以减少文档的尺寸,从而提高性能。下面对各个词典的使用进行举例介绍
Ispell:拼写词典,例如“likes”将转换为“like”。
Simple:简单词典,例如“A NAUGHTY DOG”将转换为“naughty dog”。
Synonym:同义词典,例如“man”和“person”是同义词。
Thesaurus:知识词典,例如“personal computer”将转换为PC。
完成语义分析后,即得到一个全部处理后得到的单词信息,这些单词信息保存在ParsedText结构中。
其中的words
指向一个数组,其中每一个元素都是ParsedWord类型,用于保存分析后的一个单词,数据结构中的pos和apos指针是用union结构(允许在相同的内存位置存储不同的数据类型,但只能存在一个)来保存的。当完成文本解析后,可能会遇到相同的词出现了多次的情况,这时会将相同的词合并在一起:对于只出现一次的词,使用pos来保存其出现的位置即可;
对于出现多次的词,则使用apos指针来指向一个动态数组来保存所有出现的位置。apos[0]为该词出现的次数,数组后面的值即为各次出现的位置。
由于数组的长度是不确定的,所以使用alen字段来确定动态申请内存的空间,alen初始化为2,每当apos 的长度不够时,alen即翻倍,同时申请新内存将apos数组的长度翻倍。
Parsetext的运行图
词位存储
词位存储即为归总语义分析后的单词在文档中的位置,并将其出现的次数及每个位置存储下来。
TSVector是一种可搜索的数据类型,它是文档内容的一种表现形式,是出现在文档中的每个重要单词及其所有位置信息的集合。它通过一种特殊的优化结构进行组织,从而可方便快速地存取及查找。其定义如数据结构4.22所示。
TSVectorData结构中的WordEntry数组用于保存所有的关键字(单词)信息,由于关键字的数目一开始并不确定,所以使用一个数组指针,该数组的实际长度根据关键字的个数在使用时进行分配。
WordEntry的定义见数据结构4.23。(结构体成员后面的数字用来限定成员变量占用的位数)
上面分析了TSVector 的数据结构,词位存储即使用语义分析得到的ParsedText构建TSVector,该过程由函数make_tsvector完成。其执行流程如图4-37所示。