在说索引之前,先说说索引是什么?为什么要索引?怎么索引?
先想想看,假如现在有一个文本,我们会怎么去搜索。比如,有一个string = "abcdefghijklmnopqrstuvwxyz",这都是26个字母。现在要看看里面是不是有a,用IndexOf就可以很方便实现。现在数据量大了,在数据库里已经有100多条数据了,当然,利用数据库提供的操作方法,也可以很方便的查找。而这里先抛开数据库,把这100多条记录放到N个文本文件中,现在要在里面搜索含有“Lucene”这个词的记录,那怎么办呢?如果只简单地使用逐个文件逐字扫描的话,那和用Windows内置的搜索,搜索一张图片名,或者文本没什么分别。那么每次搜索都会需要大量时间,而google和baidu为什么能做到那么快呢?既然刚才我们是抛开数据库,是不是使用了数据库就能实现呢?从我长期使用的经验来看,数据库肯定是不行的(这里指关系型数据库,要是出某些专门为搜索做的数据库那就另外说了。)。什么东西在搜索的时候足够快呢?在C#类型里就有这么几个,比如Hashtable,Dictionary。做搜索引擎是否也可以应用这样的思想?显然是可以的!有很多东西在微观(这里指一小个算法或者一个小型的数据结构应用)和宏观(这里指框架级或者系统级)上名字不一样,但是都有很多的相似性和可比性,无疑Lucene.Net就是这样一个框架,实现了Hashtable更加宏观的现象!当然也是有很多差别。
Lucene.Net用的就是倒排索引的一种数据结构,记得以前看过一篇文章,讲在面向对象时代,数据结构的作用被减弱了,我觉得这个观点至少在Lucene.Net上必须被抛弃。回答原先的问题,什么是索引?纵观搜索引擎的发展历史,早期的搜索引擎都是基于关键字和目录的,而现在已经转变成全文搜索。若要问什么是索引,那就是你有一本书,你看到了第八页是写一个故事,你用一张纸把故事名称记录下来,那就是索引。书的目录,是的页数,书的编号等等都可以算是索引。Lucene.Net用的就是倒排索引,那什么是倒排索引?那就是对一段文本就行分析,按分析结果,把分析得到的关键字建立索引。比如“我在用Lucene.Net。”,用StandardAnalyzer分词器索引后,就会存储“我”,“在”,“用”,“Lucene”,“.”,“Net”,“。”这些词,也就是说,索引是以词为单位存储的。同时记录下了这些词出现在了哪个文档中,以及出现的位置和频率。很像在数据库里做冗余不是吗?这些需要计算的数据都已经记录下来,直接读取就可以看了。从理论上说,在定义使用M种语言的情况下,出现词的数量总是有限的。从这里也可以看出分词是如此重要,因为分词让N个字连成了一个整体,用这个整体中的任何一个字是查不到这个整体的,除非以牺牲速度为代价。以前说google水土不服,就是因为google在中文分词上不如baidu,但是这个差距在缩小。
而另外一个问题,为什么要索引,也就不难解答了。至于怎么索引,这又是一个很长的故事了。
1、逻辑存储结构
词在倒排索引中是最小的单位,在Lucene.Net中衡量单位是Term,由N个Term构成了Filed,而又有N个Filed组成了Document,N个Document又会组成一个Segment,N个Segment会被写到Lucene.Net的文件系统。文件系统留到后面再讲,因为Lucene.Net自己实现了自己的文件系统,而这个系统的最小单位由3个文件组成,可以放到一个目录下,也可以放到内存中。总的来说Lucene.Net的文件系统可以理解为一个个的文件,在Windows下就是一个目录,里面包含了三个文件,但是从Lucene.Net的逻辑上来说,这就是一个文件。然后文件里的文本分词N个章节,那就是Segment,每个段落又会有N个段落(Document),段落里的每句话就是Filed,而Term就是每个字。和我们处理习惯很像不是吗?而其中最重要的就是Term,其他都是为它排版用的。
而这个索引相对于分词,也可以用另外一个类来衡量,那就是——Token,是不是很熟悉?Term和Token的文本是一样的,只是记录的关于这个文本的属性不一样。
前面写到了两次写入索引的操作,代码大同小异。都是先建立一个分词器,然后把分词器交给IndexWriter。接着创建N个Document,往Docuemnt里填充Field,再把Document交给IndexWriter操作,就完成了整个索引过程。关于Segment的处理被黑箱子掉了,而Term的处理也仅仅能从分词器看到个大概。
(PS:不行了,睡觉了。ZZzzzzz~~)