上一篇说到了Directory为我们搜索提供了基础,下面我们就要开始打开这个Directory了。
用IndexReader打开一个Directory还是包含了不少细节的,下面我们一起来探讨下
首先IndexReader其实是一个abstract的类,所以我们打开不可能实例化这个类,那我们打开一个FSDreictory究竟打开的是啥呢?看看下面这断代码就明了了
public static IndexReader open(final Directory directory) throws CorruptIndexException, IOException { return DirectoryReader.open(directory, null, null, true, DEFAULT_TERMS_INDEX_DIVISOR); }
IndexReader里面还有很多个open方法的重载版本,其实就是调用了DirectoryReader.open了,这个DirectoryReader是个啥玩意呢,我们去探探究竟。看名字就很简单,这个DirectoryReader就是用来打开一个Directory的嘛,但是有的童鞋要问了,lucene里面的索引不都是保存在Directory里面吗?不就是用DirectoryReader就行了么?
这个嘛,其实lucene在搜索的时候为了将搜索任务细分到不同的Reader(比如并行搜索),因此在DirectoryReader里面其实是有很多的子Reader的,这里就是SegmentReader(段索引Reader)了,这个就是最终的底层Reader了,所有的操作都是通过它来进行的,在open一个目录的时候,会读取索引文件里面的segment_*这个文件的相关信息,得出这个目录里面的索引断详细信息,然后每个段都会打开一个SegmentReader,单独负责这个断的信息读取,我们最终的所有的索引搜索操作就都是由他们完成了,这样做的目的前面我也说了,我们可以用一个线程池,然后再每个断上用一个线程对其进行搜索,然后将结果合并起来,这样是不是提高了很大的效率,其实lucene就用这样的功能的,这个等我们说到IndexSearcher的时候再细说。
然后根据是否可以用这个Reader可以进行索引写操作,还有ReaderOnlySegmentReader。我们知道可以通过IndexReader来删除文档,用了ReaderOnly的IndexReader的话就相当于将这一操作给禁止了,相信用过luke的童鞋都知道luke有个以只读模式打开索引的选项,其实就是用的这个IndexReader,这里啰嗦一句,其实我也建议大家不要用IndexReader对索引进相关的操作,因为涉及到了锁和并发各方面的原因,结果也许会出现不可预想的后果(比如索引损坏),那个时候就只有哭了T_T,而且读和写分开,对我们的程序的设计有很大的方便,听说在4.0里面已经不能再IndexReader里面删除了,不知道是不是真的?
下面来介绍几个IndexReader的重要方法:
public TermEnum terms();
这个方法的作用是获取一个所有索引里面保存的词的一个枚举,通过这个枚举我们可以用类似while循环获取这个索引里面保存的所有的分词信息(不是保存的信息,是一个字段通过分词器分词后的词信息),这个方法有大用,以后我们讲查询的时候详细再说。
public TermEnum terms(Term t) throws IOException ;
这个方法和上面的类似,但是这个方法可以通过一个词(Term)对要获取的词进行定位,比如索引中存在如下词
Field:0
Field:1
Field:2
Field:3
Field:4
如果这里我们传入一个new Term("Field","3")的话,就可以获得一个从(Filed:4)开始的词枚举。
如果传入的词不存在呢?如果不存在。Lucene会找到一个比我们传入的词大的词,这个大是怎么解释呢,其实就是Term实现了Comparable接口,然后compareTo返回大于或等于1的就行了,这个方法也是非常重要的,对查询重写有很大的意义,这里暂时也还是不讲。
下面看看这两个方法:
public TermDocs termDocs() throws IOException
public TermDocs termDocs(Term term) throws IOException;
这两个方法的作用从词义上将就是获取一个文档枚举器,我们当然同样可以通过这个枚举获取这个Reader里面读取的文档ID号,这里为啥不是目录而是Reader里面的呢?因为我们知道文档ID只有在索引打开的时候存在,只存在于运行时,并不是一个持久化的标示符。上面那个方法和下面那个方法的区别在于,上面的方法或得得TermDocs并不能直接使用,因为是未定位的(unposition),在使用前我们要先使用seek来定位,下面则可以直接通过一个词来定位指定的文档,Lucene的索引保存方式是以倒排表的方式来存储的,因此可以通过这个词来快速定位包含这个词的文档,这个方法就是这种倒排表的体现,另外这两个方法获取出来的文档都是不包含已经删除的文档的哦。
public int maxDoc(); public int numDocs()
这两个方法也是两兄弟,上面按个方法是获取比当前索引中的最大的那个文档号还要大1的一个int值,下面那个下面按个一般来说就是调用上面那个方法然后减去已经删除掉的文档的数量,就是这个索引包含的全部的文档的数量,两个的区别在于定义上上面一个是获取最大的文档号,下面只是获取文档的数量。
最后两个方法
public abstract Document document(int n, FieldSelector fieldSelector) throws CorruptIndexException, IOException; public final Document document(int n) throws CorruptIndexException, IOException ;
最后两个方法也是我们的重中只重了,光能搜索不行,我们还要获取我们保存在索引里面的相关信息,这个两个方法就是通过一个文档ID来获取指定文档保存的信息,下面那个方法是final的,其实就是调用了上面的方法的一个重载,关于那个FieldSelctor说明下,这个是一个接口,相当于一个过滤器,用来过滤我们获取文档里面的数据的时候对字段进行一些过滤操作(也不完全是过滤,但是包含过滤),这个接口里面定义了一个单独的方法:
FieldSelectorResult accept(String fieldName);
就是Lucene读取到指定的字段的时候会调用这个方法,然后根据返回的FieldSelectorResult来进行不同的操作,这个等到后面我们说到document的时候在详解。
OK,今天这篇博文先写到这里,如果有什么看不懂或者不妥的地方欢迎联系我,最近人都变懒了,还有最近本人在追北京青年这部电视剧,每天差不多都是2点去了。。。。表示很累啊(T_T),更新都比较慢,希望能够对大家学习Lucene的过程中有所帮助!