[1]. Index
xapian建立索引,参考https://github.com/xapian/xapian-docsprint/blob/master/code/c%2B%2B/index1.cc
主要涉及类:
数据库类 WritableDatabase
文档类 Document
Term生成工具类 TermGenerator
主要使用TermGenerator将输入内容转化为term并index,存放Document类数据中,实际保存的是Document类数据。Document分为两部分内容,一是原始数据,通过Document自己的方法添加;二是posting/index数据,通过TermGenerator添加。
TermGenerator基本工作过程:
1) set_document:设置目标doc。
2) index_text:将输入内容parse成term并添加。代码在termgenerator_internal.cc中。主要原理是将输入的内容先parse,然后逐个记录下位置,wdf,录入进doc中,如:
doc.add_posting(prefix + term, ++termpos, wdf_inc); //termgenerator_internal.cc:258
term可以添加prefix;termpos即term在输入内容中的位置;wdf_inc为本次添加term的frequency权重(通常是1)
根据不同的策略添加为posting或单纯term,以及是否使用stem模式等等。
*) termpos 为Document类内部变量,连续的调用index_text方法会连续增加termpos。有时候想区分不同field的数据,可以在不同field的index_text之间插入隔断,方法为increase_termpos,默认是100位。原理即增加Document类中的termpos,下一次index_text即产生隔断。
*) Document类有docid,但往往输入数据有原始编号,可以将原始编号map进Document类中方便使用和管理。方法是add_boolean_term(idterm),idterm为原始编号。原理是为Document增加一个wdf为0的term,这样既能被识别到又不会对index计算产生实际影响。参考api中的说明。
写入数据库:使用WritableDatabase类的replace_document方法,添加/替换一个文档。replace_document可以指定unique_term,这是前面的添加的booleam_term就派上用场了。使用替换的原因是防止同一个doc被重复添加。
[2]. Search
index之后就可以检索,参考https://github.com/xapian/xapian-docsprint/blob/master/code/c%2B%2B/search1.cc
核心为两部分,QueryParser解析query,Enquire实现检索。
检索主要由Enquire类的get_mset(offset, pagesize)实现,返回结果中第offset ~ offset+pagesize顺位的结果。返回MSet类。MSet主要方法:mset.begin(); mset.end() 返回MSetIterator类用于遍历。对于MSetIterator类变量m,*m取结果的docid,m.get_rank()取结果的顺位,m.get_document()取结果的文档。
[3].facet
分面搜索facet值得是对于文档数据,在index的字段外,额外保存一些信息,不参与index,但是可以用于检索之后的统计和限定。例如搜索商品,通过关键词检索之后,引擎出了展示靠前的若干个商品,还能展示涉及的各个品牌都分别有多少商品。
参考 xapian文档关于facet的介绍,index_facets
实现方法主要在于Document类中的set_value(),get_value()方法,以及ValueCountMatchSpy类。
index时,每个doc均可添加若干value slot,每个slot存一类信息。
ValueCountMatchSpy作用是在search返回mset过程中观测value。search时,新建ValueCountMatchSpy类spy,定义时指定slot,如
Xapian::ValueCountMatchSpy spy(1);
即指定观测slot 1上各个文档的value。将spy绑定到enquire上即可。如果需要分面搜索,使用get_mset时需要指定checkatleast,即
get_mset(offset, pagesize,checkatleast)
因为返回的mset一般只是整个March_Set中靠前的少部分结果,但是spy希望观测到较多的数据,因此可指定checkatleast值,表示至少要观测March_set中这么多的文档。如果checkatleast小于pagesize会取pagesize。
通过ValueCountMatchSpy的方法统计结果。如下:
for (Xapian::TermIterator facet = spy.values_begin(); facet != spy.values_end(); ++facet)
将spy中所有的facet value遍历,对于每个facet,调用 facet.get_termfreq() 统计该facet的频次。
注意这个遍历对于value而言是无序的(取决于spy存放value的顺序),对于有序的value类型(如数字)需要使用sortable_serialise。(TODO)