您的位置:首页 > 其它

Sphinx源码分析——Indexer

2016-06-26 11:25 246 查看
Sphinx作为一款优秀的全文检索开源软件确实是很不错,最近工作需要,要求在其上进行二次开发,第一次接触这样一款开源软件,兴奋和紧张心情难免,作为一个刚毕业的应届生,看了一周的源代码,现在奉上一篇博文来对其Indexer部分代码进行分析,共各位及自己做一个参考,其中只代表个人的一些粗浅看法,如果不对,请各位大神一定要指正,这样才能提高,谢谢!
   Indexer作为Sphinx的重要组成部分之一,其主要作用是将数据源进行索引化操作,其中涉及到了索引结构的问题,我会单独开一篇博文去详细讲解这个问题,下面我们开始进行代码分析。

//Indexer.cpp —— int main ( int argc, char ** argv )

这一部分主要是命令行下参数的读入,我们这里采用的是Indexer.exe -c d:/csft_mysql.conf -All命令,这里,argc = 4,即有3个参数,argv[1] :-c就是读取指定config文档,argv[2]: d: / csft_mysql.conf就配置文件config的位置,argv[3]就代表对config文件中指定的所有数据源进行索引构建

   下一步则是进行Config文件的读取工作,这涉及到两个重要的类,在后面的索引及查询过程中会多次使用它们,一个是CSphConfigParser,一个是CSphConfig。首先看下CSphConfigParser的结构:

//Sphinxutils.h

它是解析Config文档的主要的数据结构,其中存储Config文档信息的就是我们这里提到的第二个数据结构,它同样也是CSphConfigParser中的一个成员变量,即CSphConfig,我们可以看到CSphConfig实际上是一个哈希表,通过代码发现它是一个拥有256个键值对的哈希表,后面我们会讲到,通过CSphConfigParser类函数,将Config文件解析,读取到某一Config名字插入CsphConfig哈希表中的Key值,读取到该Config对应的值插入到Value中,方便后面构建索引时使用。

//Sphinxutils.h

   说完了这两个数据结构我们来看下Indexer是如何读取Config信息的,其中主要是通过一个sphLoadConfig函数完成读取操作,将相关Config信息以键值对的形式存入cp.m_tConf中,然后检查重要的参数是否读入且存在,例如Source相关信息,数据源是否被读入,Sphinx中Mysql默认Source对应值为mysql,Indexer,即全局Index定义中是否定义了mem_limit的值,即索引过程中最大缓存限制,等等。

//Indexer.cpp —— int main ( int argc, char ** argv )

这其中,主要解析函数为CSphConfig中的Parser函数,其里面比较复杂,大意就是按照字符流读取Config文档,遇到配置信息及其值就存储到CSphconfig这个哈希表中

//Sphinxutils.h





当我们顺利的读取完Config信息后,我们进入构建索引阶段,前面我们提到了第三个参数,我们选用的是ALL即为所有的数据源构建索引,故bMerge(合并索引)为false,bIndexALL为true,我们开始为每一数据源构建索引,程序会开始在类型为CSphConfig的hConf哈希表中搜索Key为index的值,即需要构建的索引,然后取出该索引的名称,结合数据源Source信息构建索引,执行DoIndex函数

//Indexer.cpp —— int main ( int argc, char ** argv )

DoIndex为整个Indexer中最核心的函数,下面我们来详细分析下.

//Indexer.cpp ——DoIndex(const CSphConfigSection & hIndex,
const char * sIndexName,
const CSphConfigType & hSource)


首先判断数据源类型是否为分布式,是否采用quiet模式只输出error信息

然后检查hIndex信息中的配置信息是否齐全正确

接着开始准备分词器,其中主要就是初始化一些参数,例如在sphConfTokenizer中会根据Config配置文件中设置的charsetType类型选择合适的编码解析字符,以及采用何种中文分词器来对中文进行分词操作。然后就是初始化参数后创建分析器实例指定分词所用的词典的地址位置等

然后是前缀后缀索引设置(这个地方研究的不细致,先把代码贴出来,待补充)

然后设置boundary信息(词组边界符列表,此列表控制哪些字符被视作分隔不同词组的边界,每到一个这样的边界,其后面的词的“位置”值都会被加入一个额外的增量,可以借此用近似搜索符来模拟词组搜索。)

接下来准备数据源,其实发现Indexer在准备这些工作时很繁琐,一遍又一遍的检查相关配置信息是否完全,前面检查了后面还查,可能是出于严谨的考虑吧,这里提一下dSource是一个CSphSource的数组,每一个CSphSource类型的pSource对应一个数据源,因为配置信息中可能会存在多个数据源,所以会有多个pSource。程序会在hIndex中搜索Key值为Source的键值对,提取出对应的值作为pSourceName ,在本例中,我们只有配置文件中的一个Source即mysql。我们看一下CSphSource类型结构。其中包含有三个大部分,第一大部分存储文本分词后的word信息,每一个word(也许是字也许是词)对应一个WordHit,这个WordHit描述该word的相关信息,唯一标示该word。其中WordHit中又包含三部分,分别为word的文档ID,表示该word属于哪一篇文档;word的ID,表示该word在字典中的对应ID;Word的位置,表示该word在文档中的偏移量。第二大部分存储Source中文档的相关信息,其中亦包含了三部分,分别问文档ID;文档中列的数目,以及列对应的指针。第三大部分存储的就是doc中的属性字段信息。

123

Source信息准备好后,开始准备Index的构建工作,首先检测该Index是否被使用,即是否被上锁,其次通过CSphIndexSettings类型的tSettings对创建好的pIndex进行初始化,主要是一些索引构建的信息,例如缓存大小,Boudary大小,停用词初始化,分词器初始化等等。准备完相关信息后,重要的就是Build函数,这是索引构建的核心函数,我们来仔细分析

对于Build函数而言,它是单次处理一个数据源并为此构建索引信息,

//sphinx.cpp Build (
const CSphVector<CSphSource*> & dSources, int
iMemoryLimit, int iWriteBuffer )


首先是准备Source,还是把dSource中的每一个pSource检查下是否都存在,词典是否都准备好,各种初始化是否都齐备

链接第一个数据源,获取数据源的Schema信息,就是数据源的Doc中哪些是属性,哪些列是要构建索引的信息

后面就是初始化一些存储结构,其中重点说下缓存出来的几个临时文件分别的作用。结尾时tmp0的存储的是被上锁的Index,有些Index正在被查询使用故上锁。tmp1,即对应将来生成的spp文件,存储词汇的位置信息,包含该词所在的文档ID,该词所在词典对应的ID,以及该词在本文档中的位置信息。tmp2,即对应将来生成的spa文件存储的是文档信息,包含了DocID以及DocInfo信息。tmp7对应的是多值查询,感兴趣的可以度娘,这是一种查询方式,这里不做过多解释

下面具体处理每一个Source取出的每一个文档,主要是通过这个IterateHitsNext实现的

具体到该函数可以看到,该函数主要是有两部分组成,即提取索引列(NextDocument),针对该索引列构建索引(BuildHits)

具体看一下NexDocument的操作,通过Sql.h中的API——sqlFetchRow,取出一条记录,验证该记录是否合法

将条记录按照Schema分成Feild部分,即需要构建索引的部分,以及Attribute部分,即排序需要用到的属性部分

提取出相关数据后,针对每一条需要索引的item开始构建索引,进入BuildHit函数,首先先初始化相关参数,准备分词器缓存

然后开始分词,分词的过程在这里不具体讲了,这不属于Sphinx的主要涉足领域,当我们把iField即要索引的字段放入分词器中依次解析,然后将分出的词赋值给sWord,将sWord的位置计算后赋值给ipos

将分词后的sWord去词典中查找它对应的词ID,这样我们就收集全了这个词的所有详细信息,创建一个类型为CSphWordHit类型的tHit,其中存储了该sWord所在的DocID,在词典中对应的词ID,以及在文档中词的位置信息Pos

处理完该词后,如果是中文的话还会进一步去判断其是否有近义词出现,其主要的函数为GetThesaurus,这里要简单说明下采用的MMSEG分词法,比如我们分词得到了中华,那么它还会继续从词典中去找是否存在其扩展词段(这里姑且翻译成近义词)如中华人民,中华人民共和国,然后也会把他也存入进去(对于MMSEG的中文分词方法还有待进一步研究,这我只能照着代码念了),最后将所有的sWord的信息tHit都放入到m_dHits中去,形成我们的词索引spp索引

当该iField索引字段全部都索引完成后,在dHit中添加结束标记

本文出自 “博の客” 博客,请务必保留此出处http://frankiewb.blog.51cto.com/8202664/1359897
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: