《lucene搜索实现过程详解》由会员分享,可在线阅读,更多相关《lucene搜索实现过程详解(12页珍藏版)》请在金锄头文库上搜索。
1、lucene 搜索实现过程详解搜索实现过程详解郭玉璞 2008-05-05一一文档说明文档说明1关于 lucene 索引生成过程和生成文件结构,在关于 lucene 建立索引的详细过程及相关文件结构一文中已经说明,本文涉及到的文件结构,请参考该文档,这里不再累赘,并且我们假设您已经对索引文件格式有了一定的了解。2为了对搜索的实现过程有一个清晰的认识,本文采用分级的形势,层层深入。3对于分词这一块,将有专题讲解,本文在不影响理解的情况下,没有详解。二二搜索过程简介搜索过程简介索引的过程主要分为以下几个步骤:1打开索引文件,并将索引文件的相关信息读入打开索引文件,并将索引文件的相关信息读入。代码如
2、下:IndexReader reader = IndexReader.Open(“index“);其中,index 为索引文件所在的文件夹名称。该句执行完以后,索引文件的相关信息将存放在 reader 中。2实例化实例化 IndexSearcher,以便于后面的搜索操作。,以便于后面的搜索操作。代码如下:IndexSearcher searcher = new IndexSearcher(reader);该句执行完后,索引文件信息将存放在 searcher 中,后面的的搜索将运用searcher,而不是运用 reader。当然,初始化方式有很多种,这里就以代码所示为例。3声明查询分析器声明查询
3、分析器 QueryPaser。代码如下:QueryParser parser = new QueryParser(field, analyzer);其中,field 为要查询的字段,analyzer 为所选择的分析器。4设置设置 Query 间的逻辑关系。间的逻辑关系。代码如下:parser.SetDefaultOperator(QueryParser.Operator.AND);该句的功能是,如果用户以空格隔开两个字符串,设置这两个字符串之间的关系,代码所示的是与的关系,根据需要也可以设置成 OR 等关系。此句可有可无,Lucene 默认的是 OR 的关系。5生成生成 Query 子对象。子
4、对象。代码如下:Query query = parser.Parse(strQuery);其中,strQuery 为用户输入的待查询的字符串。该句的主要功能是将用户输入的字符串进行分词,并记录每个 Token 之间的位置关系,以便于后面的查询。当然,Lucene 允许用户直接创建 Query,也允许用户采用多种方法构建Query。例如:按词条搜索TermQuery、 “与或”搜索BooleanQuery、在某一范围内搜索RangeQuery、使用前缀搜索PrefixQuery、多关键字的搜索PhraseQuery、使用短语缀搜索PhrasePrefixQuery、相近词语的搜索FuzzyQue
5、ry、使用通配符搜索WildcardQuery。各种方法原理相同,都是将各单个 Query 搜索,然后再将各自结果按照“与”或者“或”的关系得出最终结果。在本文中,我们将以代码所示的简单方式为例。6搜索,返回处理结果。搜索,返回处理结果。代码如下:Hits hits = searcher.Search(query);在该句中,不仅涉及搜索的过程,而且还有排序、过滤等过程。7根据搜索生成的内部编号,返回真正的结果。根据搜索生成的内部编号,返回真正的结果。代码如下:Document doc = hits.Doc(i);三三搜索过程详解。搜索过程详解。1IndexReader reader = In
6、dexReader.Open(“index“);该句调用 IndexReader的 Open 方法:public static IndexReader Open(System.String path)return Open(FSDirectory.GetDirectory(path, false), true);11 FSDirectory.GetDirectory(path,false),该方法获取索引文件夹的完全路径和创建临时文件路径。其中,path 为索引文件所在文件夹的名称,false 表示不要创建新的文件夹。该方法调用 FSDirectory 的 GetDirectory 方法:pu
7、blic static FSDirectory GetDirectory(System.String path, bool create)return GetDirectory(new System.IO.FileInfo(path), create);111 GetDirectory(new System.IO.FileInfo(path), create);该方法是获取文夹完整路径的核心方法,其他方法都调用此方法。首先,通过语句 file = new System.IO.FileInfo(file.FullName);获得索引文件所在文件夹的完整路径到 file。其次,根据 file 创建
8、临时文件将要存放的路径:FSDirectory dir;dir = (FSDirectory) DIRECTORIESfile;dir = (FSDirectory) System.Activator.CreateInstance(IMPL);然后,进行初始化工作:dir.Init(file, create);由于现在是搜索过程,并非创建索引的过程,初始化工作只是将两个完整路径传给 dir。最后,返回 dir。12 Open(FSDirectory.GetDirectory(path, false), true);该方法调用 IndexReader 的Open(Directory direct
9、ory, bool closeDirectory)方法。其中,directroy 就是上面返回的 dir。在该方法中,有三个主要方法MakeLock、AnonymousClassWith、Run()。121 directory.MakeLock(IndexWriter.COMMIT_LOCK_NAME);其中COMMIT_LOCK_NAME 为锁文件名,在 Lucene 中为 commit.lock。此刻该文件表示有进程在读“segment”文件和打开某些段的文件。在该方法中,首先获取索引文件目录的前缀,并以此来命名锁文件名称;然后将临时文件夹的完整路径和锁文件名称组成完整了锁文件名称 Loc
10、kFile;最后初始化该锁:AnonymousClassLock(lockFile, this);其中 this 为当前索引文件目录路径。122 AnonymousClassWith(directory, closeDirectory, directory.MakeLock(IndexWriter.COMMIT_LOCK_NAME), IndexWriter.COMMIT_LOCK_TIMEOUT);该方法主要做一些初始化工作。其中,COMMIT_LOCK_NAME 为获取锁文件的限定时间,Lucene 默认时间为10000 毫秒。123 Run()这是读取索引文件的核心方法。该方法代码如下:
11、public virtual System.Object Run()bool locked = false;trylocked = lock_Renamed.Obtain(lockWaitTimeout);return DoBody();finallyif (locked)lock_Renamed.Release();1231 根据限定时间 lockWaitTimeOut 获取锁文件。在获取过程中,每秒钟试一次,直到获得锁文件为止。如果重试超过十次,则被阻塞。1232 DoBody();该方法首先声明一个 SegmentInfos 对象,用以管理SegmentInfo:SegmentInfos
12、 infos = new SegmentInfos();12321 infos.Read(directory);其中 directory 为索引文件目录。该方法读取 Segments 文件信息。1)IndexInput input = directory.OpenInput(IndexFileNames.SEGMENTS);创建一个Segments 文件的输入流,用以读取 Segments 文件信息。2)int format = input.ReadInt();读取索引文件格式信息。Lucene2.0 默认为1。3)version = input.ReadLong();读取版本信息。4)cou
13、nter = input.ReadInt();读取 counter,counter 用于给新生成的索引起名字。5)在 for 循环中,int i = input.ReadInt();读取 segment 的个数 count。6)SegmentInfo si = new SegmentInfo(input.ReadString(), input.ReadInt(), directory);依次读入每个 segment 的名字和包含文档的个数,并用 Add(si);将每个 si 加入到ArrayList。当然,在旧版本的 lucene 当中,可能读取的顺序不太一样,但道理是一样的,都是根据索引的结
14、构依次读出。12322 对于生成的索引,segment 的个数可能不止一个,本文就以一个segment 为例,多个 segment 与之类似。return SegmentReader.Get(infos, infos.Info(0), closeDirectory);读取其他文件或为其他文件创建输入流,以供以后读取。Get 方法层层调用,现在只分析最后的核心方法:1)instance = (SegmentReader) System.Activator.CreateInstance(IMPL);创建SegmentReader 实例 instance。2)instance.Init(dir, s
15、is, closeDir, ownDir);初始化工作。3)instance.Initialize(si);读取的核心部分。首先要根据.cfs 文件是否存在来判断是否是复合索引结构,本文采用非复合索引结构来说明,复合索引结构与之类似。a)fieldInfos = new FieldInfos(cfsDir, segment + “.fnm“);读取.fnm 文件信息。该方法实现如下:IndexInput input = d.OpenInput(name);name 为.fnm 文件名。为.fnm 文件创建输入流 input,以供读取。Read(input);读取信息:int size = in
16、put.ReadVInt();读取字段(filed)的个数。System.String name = String.Intern(input.ReadString();读取各个字段的名称。byte bits = input.ReadByte();读取标志位。下面五句是根据 bits 的值来判断该域是否被索引等信息。AddInternal(name, isIndexed, storeTermVector, storePositionsWithTermVector, storeOffsetWithTermVector, omitNorms);将该域的这些信息存储,存储的方法有两种,一种是 byName,根据名字来存储;一种是 byNumber,根据编号来存储。Input.Close();关闭.fnm 文件的输入流。b)fieldsReader = new FieldsReader(cfsDir, segment, fieldInfos);为.fdt 和.fdx