xapian简介<四>

2022-04-27 00:00:00 数据 模式 内存 方式 指针

1. xapian基本概念
开篇先聊点相关的基本概念,再说xapian。
1.1 IR 信息检索(Information Retrieval)
在信息检索(IR)中,我们企图要获取的项称之为“document”,每一个document是被一个terms集合所描述的。“document”和“term”这两个词汇是IR中的术语,它们是来自“图书馆管理学”的。通常一个document认为是一块文本,而一个term则是一个词语或短语以用作描述document的,在document中大多数会存在着多个term,例如某个document是跟口腔卫生相关的,那么可能会存在着以下的terms:“tooth”、“teeth”、“toothbrush”、“decay”、 “cavity”、“plaque”或“diet”等等。

如果在一个IR系统中,存在一个名为D的document,此document被一个名为t的term所描述,那么t被认为索引了D,可以用以下式子表示:t->D。在实际应用的一个IR系统中通常是多个documents,如D1, D2, D3 ...组成的集合,且有多个term,如t1, t2, t3 ...组成的集合,从而有以下关系:ti -> Dj。

如果某个特定的term索引了某个特定的document,那么称之为posting,说白了posting就是带position信息的term,在相关度检索中可能有一定的用途的。

给定一个名为D的document,存在着一个terms列表索引着它,我们称之为D的term list。

给定一个名为t的term,它索引着一个documents列表,这称之为t的posting list。

在一个存在于计算机的IR系统中,terms是存储于索引文件中的。term可以用作有效地查找它的posting list,在posting list里,每一个document带有一个很短的标识符,就是document id。简单来说,一个posting list可以被认为是一个由document ids组成的集合,而term list则是一个字符串组成的集合。在某些IR系统的内部是使用数字来表示term的,因此在这些系统中,term list则是数字组成的集合,而Xapian则不是这样,它使用原汁原味的term。

1.2 B-TREE
在xapian之中,采用了B-TREE的块存储方式来存储数据,数据从数据结构到临时内存kt中,kt的数据在存储在B-TREE的节点上。以backend为Brass为例这个就是Brass::Cursor C[BTREE_CURSOR_LEVELS]就是B-TREE的节点。

下面简单的介绍下B-TREE。

B-TREE可以看做是有序数组和平衡多叉树的合集,树的节点是链表。 每个非终端结点中包含有n个关键字信息: (n,P0,K1,P1,K2,P2,......,Kn,Pn)。其中:

a) Ki (i=1...n)为关键字,且关键字按顺序排序K(i-1)< Ki。

b) Pi为指向子树根的接点,且指针P(i-1)指向子树种所有结点的关键字均小于Ki,但都大于K(i-1)。

c) 关键字的个数n必须满足: ceil(m / 2)-1 <= n <= m-1。

说点通俗的,在xapian中这个树被设计为大为10层,而默认的每个节点大小即内存块大小为8192字节。在BTREE中如果把块设置的足够大那么相对而言树的深度就越小,那么查找到的数据就越快。但是块的太大的话就面对的是后在块中的折半查找耗时更多。

Xapian设计默认块大小为2页内存,在计算机中内存分配小值并非是我们malloc的字节数,计算机会出于寻址和内存对齐等因素,内存随即分配后留下内存碎片等原因按照小单位内存页来分配内存,而一页内存就是4k。可能是xapian考虑到内存对齐等原因给到的不同环境下的块大小优解。内网搞个B-TREE的图片都不好弄,大家自己百度之~。

下面是xapian的B-TREE内存块的模样,xapian的内存数据协议我大概说下。

首先红色前面的存放的是数据中key的信息,key就是document id经过序列化把id 1序列化为’0x1’。上面白色数据我依次介绍,05代表的是这个key的长度,这个长度是2但是还要加上K1和C2,得到‘0x5’,后面的01放的就是key即did序列化后的样子,后面的01是component。之后红色的就是数据

‘21 de zkb gao 173’,不过是ASCII的形式。这段内存大家可以理解为就是xapian的B-TREE上的一个节点(即内存块)的数据。

1.3 浅谈xapian中的设计模式
Xapian的编码是偏向C++风格的代码,代码做到了面向对象式的编程,下面我给大家浅谈下xapian中常见的两种设计模式,这个还是有必要说的,利于代码的分析。

1.3.1 PIMPL模式
在xapian的主要功能类中,大多有类似这样的结构:

class XAPIAN_VISIBILITY_DEFAULT Database {undefined

public:

class Internal;

std::vector<Xapian::Internal::RefCntPtr<Internal> > internal;

}

在这里把这个类的实现其实全部隐藏在了Internal中,这种设计模式符合了C++编程思想,把类的实现和申明分开,这个时候如果修改了这个类的实现,其实只需要重新编译Internal类即可,因为Database内部聚合了interl的指针而已,所以并不需要重新编译这个类,编译速度提高。

Xapian高明处在于这里把Internal写成了共享式指针,这样再类的拷贝的时候,不给出自定义的拷贝即采用C++默认的浅拷贝方式时,采用智能指针能保证拷贝后的对象和当前对象的指针虽然指向一块内存,但是在释放的时候由于共享式指针的引用计数原因不会造成double free的错误。避免了本来要自己重写拷贝

构造函数的麻烦。

这个设计模式是在xapian中大量用到的设计模式之一。下面文章在介绍主要类的时候,直接从Internal类开始。



1.3.2 迭代器模式

在Xapian中还有一种常见的设计模式迭代器模式。比如TermIterator这种类,这种模式避免了调用者直接访问类内部实现的数据结构。而且统一了数据访问时的接口。比如:



在这里,对外提供了统一的接口TermIterator,调用者只需要实现这个类提供的方法,按照迭代器的样子去访问就可以了,但是他的内部实现方式却多达四种!但是对于外部根本不需要因为不同的方式而写不同的方式去调用,代码显得更简洁优美!

实现迭代器模式的主要是利用了运算符重载的C++语法,让这个类的使用跟STL的迭代器访问操作一致。主要要实现这四个运算符:operator!=、operator==、operator++、operator*。

1.4 xapian源码目录组成


1.api目录下的.cc和include目录下的.h-文件是成对的,即include下提供了对面的使用的类。Api目录下给出该类的的实现。不过这些实现都不是真正的做事的地方。

2.backends目录主要给出了实际对外提供类的真正的做事方法,代码通过多态的方式,通过宏或者其他的方式指定好实例化真正的类,该文件夹下都是做实事的。Xapian提供了brass,chert,flint等存储方式,此外inmemory的方式使用内存存储,即只是使用了4G的进程的虚拟内存,不产生实际的文件。顾使用这种方式的比较少。



3.common文件夹提供了一些公共的方法。另外还有部分的类的Internal的头文件。即把实际做事的各个真正做事的成员私有类Internal类的头文件另外写一个头文件,与对外的头文件区分开。但是与对面提供的头文件名称相同。例如:database,document.等

4.matcher文件下提供了查询时get_mset下用到的类,该文件夹下类大多继承于 Xapian::PostingIterator::Internal,通过Query的参数OP,决定实例化哪个子类。

5.net文件夹下主要是封装了网络通信类。

6.queryparser文件夹主要是queryparser类。

7.weight主要是计算term的rank方式计算的类。

8.Unicode字符串字符编码处理的类。


————————————————
版权声明:本文为CSDN博主「RobotZKB」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ZHOUKUANBIN/article/details/51578513

相关文章