xapian简介<五>

来源:互联网 发布:ubuntu 串口调试助手 编辑:程序博客网 时间:2024/04/29 17:22

1.Xapian之数据写

下面我给出xapian最简单的数据写的例子,以这个例子,我们走完数据写的流程。

void WriteDB::init(XapianType::Document& tmp)

{

    Xapian::Document docTmp;

 

    docTmp.add_value(0, tmp.valueV1);

    docTmp.add_value(1, tmp.valueV2);

    docTmp.add_term(tmp.valueV3);

    docTmp.set_data(tmp.Data);

    docTmp.set_sortslot(0);

    mDocList.push_back(docTmp);

}

void WriteDB::work()

{

    try

    {

        Xapian::WritableDatabase db(Xapian::Brass::open(STR_PATH, Xapian::DB_CREATE_OR_OPEN));

        for (auto i = mDocList.begin(); i < mDocList.end(); i++)

        {

            db.add_document(*i);

        }

        db.commit();

    }

    catch (const Xapian::Error& e)

    {

        std::cout << e.get_description() << std::endl;

    }

}

 

2.1  document的组装

Document类中主要维护三个成员变量,分别是:

map<Xapian::valueno, string> document_values;   对应value

map<string, OmDocumentTerm> document_terms;     对应term

string data;                                    对应data

 

document的set_data: 这个方法比较简单.把传入的data设为Document的成员变量,并把标志位data_here置为true。

document的add_term: term是第一次插入并且db已经被初始化,那么就获取did索引的全部term,OmDocumentTerm中保存了term的wdf和vector<Xapian::termpos>该term在文档中的位置信息,把这些数据都提取出来,重新写入map<string, OmDocumentTerm>中   查找map<string, OmDocumentTerm>,如果有数据就为term的wdf+1,反之构造新的OmDocumentTerm设置wdf为1,并插入该map中。

document的add_value:  检测value如果是第一次添加并且初始化了db就清空 Document::map<Xapian::valueno, string>。如果传入的value为空,就移除Document::map<Xapian::valueno, string>slot为key对应的值,反之就把slot对应的值设为该value。

 

2.2 WirteDataBase初始化

 

 以Brass为例,在DataBase中维护了七张表:

BrassPositionListTable 保存了每一个term出现在每一个document中的位置,存储类型为不压缩数据。

BrassTermListTable 保存了索引每个document的所有term。存储类型为压缩数据。

BrassRecordTable 保存了每一个document所关联的data,data不能通过query检索,只能通过document来获取。存储类型为压缩数据。

BrassPostListTable 保存了被每一个term索引的document,存储类型为不压缩数据。

BrassValueManager 保存了value的信息,这个结构并不直接存储。主要依赖于BrassPostListTable和BrassTermListTable。

BrassSynonymTable 保存了拼写纠正的数据,存储类型为压缩数据。

BrassSpellingTable 保存术语的字典,例如NBA、C#或C++等,存储类型为压缩数据。

这里最重要的三张表,在WitreDataBase会为其开辟空间的三张表是BrassTermListTable,BrassRecordTable,BrassPostListTable。Termlist和PostList的概念看我第一章说的,Record这里放的是document中的data数据,这个数据一般是把整个文章内容存在这里,然后文章分词的数据做成索引存在上面的两张表用于查找,record只能通过前面两张表查出的document的id号查出数据。

 

初始化开始自己的所有成员变量,说好的七张表,七张表均继承于BrassTable,然后每个BrassTbale都有一个Item_wr内部有个指针p这个指针就是内存块的首地址。其中Item_wr本身维护了一个指针p,此时被初始化为null。这个Item_wr数据从数据结构到内存第一步,把数据先临时存在Item_wr的空间。

在BrassWriteDataBase::create_and_open_tables中完成了对七张表的创建,主要是分配三个内存C[0].p BTREE的节点,kt保存key和data(临时存放地),buffer(暂时不知道干嘛的),也就是对BrassTable中Item_wr new分配了内存,内存大小为block_size,然后接下来的相应表的写操作就是写入到该内存中。这步也就是完成了BrassDataBase的初始化工作。

2.3 DataBaseadd_document

这步主要是把document中的结构体分解后,把record先存入到recordtable中的C[j].p中就是前面说的BTREE的节点内存块。把term先存放到termlisttable中的BTREE节点内存块,根据一个固定的数值10000判断是否把数据存入到postLst的BTREE节点内存块上。

首先判断当前的last_docid是否越界,即超过int的上限,调用add_document_。

第一步BrassRecordTable::replace_record,这个方法又调用BrassTable::add。add方法中先把序列化得到的文档号key(即did),keylength,component存入到kt的中。 再把data根据相应的指针偏移存入到kt中。最后经过find,以及xapian的算法存入把kt的内存memmove合并到Btree上,根据C[j].p定位到BTREE的层,根据C[j].c定位到该BTREE节点(块内存)的偏移量.并修改C[j].p头部保存元数据信息.eg.当前内存块的剩余量,当前内存块的总量。在这里就把document中的data数据写入到了recordTable的内存空间中。

 

第二步ValueManager::add_document,插入一个document时遍历map<Xapian::valueno, string>如果是新数据就插入到map<Xapian::valueno, ValueStats>其中valueStats维护了这个槽对应的slot的最大值和最小值。接着get_value_stats在这里,首先把slot号写入到post_List的kt空间中。然后最后再比对c[j]中的元数据和kt中的头数据如果不一致find返回false,如果找到就C[j]中获取slot的最大值最小值的信息更新ValueStats.接着add_value把数据添加到std::map<Xapian::valueno, std::map<Xapian::docid, std::string> > changes中。

 

第三步遍历map<string, OmDocumentTerm>获得wdf和term,更新成员变量BrassDatabaseStats中ubound_wdf,同时把did,tanme,wdf写入到Inverter中即std::map<std::string, PostingChanges>中。 这termcount和wdf不一样,这个整个文章所有term出现的个数之和,用来形容文件长度std::map<Xapian::docid, Xapian::termcount> doclen_changes;

 

第四步termlist_table.set_termlist,把数据先写入到term_list的kt中,接着把kt的数据写入到term_list的C[j]中。

 

第五步如果database中添加document数据超过次数超过了10000,那么就把postlist得内存开始写入数据。否则postlist中的内存等到database调用commit的时候再把数据写入postlist中。

 

这步之后数据从document载体中已经被取出存放到了相应的BrassTable的子类中的C[j].p中了。在C[j].p中打头的数据这个block剩余的size等信息。同时以上的每个数据不光存入了数据,在每个数据的所在的内存前面的位置存放的是是这个数据的key信息。查询的时候先看key,在看这些数据是否是自己要的数据,如果是再取出。Recordtable得key是did,termlisttable的key也是did。

 

2.4 DataBasecommit

Commit方法实现在WritableDatabase中,在这个方法主要分为两大步。

第一步,在flush_postlist_changes中,首先xapian在这步主要是把DataBaseStats写入到数据postlist_table的C[j].p的空间中。在这里作者把last_docid,doclen_lbound,wdf_ubound,doclen_ubound,wdf_ubound,oldest_changeset,total_doclen,数据序列化到一个缓冲区中,并把它以’\0’为key,存入到了内存中。作者把map<Xapian::docid, Xapian::termcount>存入到postlist中。

第二步,apply方法中,一开始处理valuemanage中保存的value数据,首先他遍历map<Xapian::valueno, ValueStats>,把ValueStats中保存的value出现的频率,value的最小值,value的最大值,以slot为key存入到了postlist_table中。此时value中实际保存的数据还没有提交,提交只是slot对应的value的相关状态信息而不是slot对应的值。之后在set_revision_number方法的一开始总算把value的数据提交到了内存中。

作者以文档号为key,把value的值存入到了termlist_table中,因此可以看出value的本质还是算term的。接着再以slot为key,把所有文档中的这个slot对应的值,按照这个值得长度加这个值得样子全部序列化成一个字符串。存入到postlisttable中。接着最后在brasstable的commit方法中把C[j]内存的数据写入到到文件中。

0 0
原创粉丝点击