lucene搜索实现过程详解Word格式文档下载.docx

上传人:b****6 文档编号:20927158 上传时间:2023-01-26 格式:DOCX 页数:11 大小:24.75KB
下载 相关 举报
lucene搜索实现过程详解Word格式文档下载.docx_第1页
第1页 / 共11页
lucene搜索实现过程详解Word格式文档下载.docx_第2页
第2页 / 共11页
lucene搜索实现过程详解Word格式文档下载.docx_第3页
第3页 / 共11页
lucene搜索实现过程详解Word格式文档下载.docx_第4页
第4页 / 共11页
lucene搜索实现过程详解Word格式文档下载.docx_第5页
第5页 / 共11页
点击查看更多>>
下载资源
资源描述

lucene搜索实现过程详解Word格式文档下载.docx

《lucene搜索实现过程详解Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《lucene搜索实现过程详解Word格式文档下载.docx(11页珍藏版)》请在冰豆网上搜索。

lucene搜索实现过程详解Word格式文档下载.docx

7.生成Query子对象。

Queryquery=parser.Parse(strQuery);

其中,strQuery为用户输入的待查询的字符串。

该句的主要功能是将用户输入的字符串进行分词,并记录每个Token之间的位置关系,以便于后面的查询。

当然,Lucene允许用户直接创建Query,也允许用户采用多种方法构建Query。

例如:

按词条搜索—TermQuery、“与或”搜索—BooleanQuery、在某一范围内搜索—RangeQuery、使用前缀搜索—PrefixQuery、多关键字的搜索—PhraseQuery、使用短语缀搜索—PhrasePrefixQuery、相近词语的搜索—FuzzyQuery、使用通配符搜索—WildcardQuery。

各种方法原理相同,都是将各单个Query搜索,然后再将各自结果按照“与”或者“或”的关系得出最终结果。

在本文中,我们将以代码所示的简单方式为例。

8.搜索,返回处理结果。

Hitshits=searcher.Search(query);

在该句中,不仅涉及搜索的过程,而且还有排序、过滤等过程。

7.根据搜索生成的内部编号,返回真正的结果。

Documentdoc=hits.Doc(i);

三.搜索过程详解。

1.IndexReaderreader=IndexReader.Open("

该句调用IndexReader的Open方法:

publicstaticIndexReaderOpen(System.Stringpath)

{

returnOpen(FSDirectory.GetDirectory(path,false),true);

}

1.1FSDirectory.GetDirectory(path,false),该方法获取索引文件夹的完全路径和创建临时文件路径。

其中,path为索引文件所在文件夹的名称,false表示不要创建新的文件夹。

该方法调用FSDirectory的GetDirectory方法:

publicstaticFSDirectoryGetDirectory(System.Stringpath,boolcreate)

returnGetDirectory(new,create);

1.1.1GetDirectory(new,create);

该方法是获取文夹完整路径的核心方法,其他方法都调用此方法。

首先,通过语句file=new;

获得索引文件所在文件夹的完整路径到file。

其次,根据file创建临时文件将要存放的路径:

FSDirectorydir;

dir=(FSDirectory)DIRECTORIES[file];

dir=(FSDirectory);

然后,进行初始化工作:

dir.Init(file,create);

由于现在是搜索过程,并非创建索引的过程,初始化工作只是将两个完整路径传给dir。

最后,返回dir。

1.2Open(FSDirectory.GetDirectory(path,false),true);

该方法调用IndexReader的Open(Directorydirectory,boolcloseDirectory)方法。

其中,directroy就是上面返回的dir。

在该方法中,有三个主要方法MakeLock、AnonymousClassWith、Run()。

1.2.1directory.MakeLock(IndexWriter.COMMIT_LOCK_NAME);

其中COMMIT_LOCK_NAME为锁文件名,在Lucene中为commit.lock。

此刻该文件表示有进程在读“segment”文件和打开某些段的文件。

在该方法中,首先获取索引文件目录的前缀,并以此来命名锁文件名称;

然后将临时文件夹的完整路径和锁文件名称组成完整了锁文件名称LockFile;

最后初始化该锁:

AnonymousClassLock(lockFile,this);

其中this为当前索引文件目录路径。

1.2.2AnonymousClassWith(directory,closeDirectory,directory.MakeLock(IndexWriter.COMMIT_LOCK_NAME),IndexWriter.COMMIT_LOCK_TIMEOUT);

该方法主要做一些初始化工作。

其中,COMMIT_LOCK_NAME为获取锁文件的限定时间,Lucene默认时间为10000毫秒。

1.2.3Run()这是读取索引文件的核心方法。

该方法代码如下:

publicvirtualSystem.ObjectRun()

{

boollocked=false;

try

{

locked=lock_Renamed.Obtain(lockWaitTimeout);

returnDoBody();

}

finally

if(locked)

lock_Renamed.Release();

}

1.2.3.1根据限定时间lockWaitTimeOut获取锁文件。

在获取过程中,每秒钟试一次,直到获得锁文件为止。

如果重试超过十次,则被阻塞。

1.2.3.2DoBody();

该方法首先声明一个SegmentInfos对象,用以管理SegmentInfo:

SegmentInfosinfos=newSegmentInfos();

1.2.3.2.1infos.Read(directory);

其中directory为索引文件目录。

该方法读取Segments文件信息。

1)IndexInputinput=directory.OpenInput(IndexFileNames.SEGMENTS);

创建一个Segments文件的输入流,用以读取Segments文件信息。

2)intformat=input.ReadInt();

读取索引文件格式信息。

Lucene2.0默认为-1。

3)version=input.ReadLong();

读取版本信息。

4)counter=input.ReadInt();

读取counter,counter用于给新生成的索引起名字。

5)在for循环中,inti=input.ReadInt();

读取segment的个数count。

6)SegmentInfosi=newSegmentInfo(input.ReadString(),input.ReadInt(),directory);

依次读入每个segment的名字和包含文档的个数,并用Add(si);

将每个si加入到ArrayList。

当然,在旧版本的lucene当中,可能读取的顺序不太一样,但道理是一样的,都是根据索引的结构依次读出。

1.2.3.2.2对于生成的索引,segment的个数可能不止一个,本文就以一个segment为例,多个segment与之类似。

returnSegmentReader.Get(infos,infos.Info(0),closeDirectory);

读取其他文件或为其他文件创建输入流,以供以后读取。

Get方法层层调用,现在只分析最后的核心方法:

1)instance=(SegmentReader);

创建SegmentReader实例instance。

2)instance.Init(dir,sis,closeDir,ownDir);

初始化工作。

3)instance.Initialize(si);

读取的核心部分。

首先要根据.cfs文件是否存在来判断是否是复合索引结构,本文采用非复合索引结构来说明,复合索引结构与之类似。

a)fieldInfos=newFieldInfos(cfsDir,segment+"

.fnm"

读取.fnm文件信息。

该方法实现如下:

IndexInputinput=d.OpenInput(name);

name为.fnm文件名。

为.fnm文件创建输入流input,以供读取。

Read(input);

读取信息:

intsize=input.ReadVInt();

读取字段(filed)的个数。

System.Stringname=String.Intern(input.ReadString());

读取各个字段的名称。

bytebits=input.ReadByte();

读取标志位。

下面五句是根据bits的值来判断该域是否被索引等信息。

AddInternal(name,isIndexed,storeTermVector,storePositionsWithTermVector,storeOffsetWithTermVector,omitNorms);

将该域的这些信息存储,存储的方法有两种,一种是byName,根据名字来存储;

一种是byNumber,根据编号来存储。

Input.Close();

关闭.fnm文件的输入流。

b)fieldsReader=newFieldsReader(cfsDir,segment,fieldInfos);

为.fdt和.fdx文件创建输入流fieldStream、indexStream,并记录document的数量size。

c)tis=newTermInfosReader(cfsDir,segment,fieldInfos);

读取.tis和.tii文件的相关信息,实现如下:

origEnum=newSegmentTermEnum(directory.OpenInput(segment+"

.tis"

),fieldInfos,false);

读取.tis文件信息:

directory.OpenInput(segment+"

);

创建.tis文件的输入流。

intfirstInt=input.ReadInt();

获取版本号。

Lucene2.0当中为-2。

size=input.ReadLong();

读取term的个数。

indexInterval=input.ReadInt();

读取索引间隔,默认值为128。

skipInterval=input.ReadInt();

读取跳跃间隔,默认值为16。

indexEnum=newSegmentTermEnum(directory.OpenInput(segment+"

.tii"

),fieldInfos,true);

读取.tis文件信息。

由于.tis文件结构与.tii文件结构相似,这里就不详细展开叙述。

d)if(HasDeletions(si))deletedDocs=newBitVector(Directory(),segment+"

.del"

如果之前有删除文档的记录,那么这里将删除。

关于document的删除工作,将有专题讲述,这里就不再叙述。

e)freqStream=cfsDir.OpenInput(segment+"

.frq"

创建.frq文件的输入流frqStream。

f)proxStream=cfsDir.OpenInput(segment+"

.prx"

创建.prx文件的输入流proxStream。

g)OpenNorms(cfsDir);

读取.f文件的相关信息。

4)returninstance;

返回该实例。

该实例记载了各个文件的信息。

也就是说,关于索引各个文件的信息,记录在searcher当中,后面的查询工作将用到searcher。

关于searcher的结构,请参看Lucene的源代码。

2.IndexSearchersearcher=newIndexSearcher(reader);

该方法主要是进行一些初始化工作。

需要说明的是similarity,它是用于后面排序的积分计算的。

这里调用similarity=Similarity.GetDefault();

即采用Lucene默认的值。

3.QueryParserparser=newQueryParser("

describ"

analyzer);

声明一个查询分析器,并进行一些初始化的处理。

该方法调用CharStream类型为参数的重载构造函数publicQueryParser(CharStreamstream);

然后初始化。

3.1publicQueryParser(CharStreamstream);

该方法主要是利用一个空的CharStream类型来进行初始化工作,为下面的分词工作做好准备。

初始化过程中,涉及很多参数,比如:

fuzzyMinSim-最小相似度,fuzzyPrefixLength-前缀长度,用于模糊查询;

一些以jj开头的参数,是由javaCC生成的,用于分词的工作,这点在分析器的文档中将详细介绍,这里不再累赘。

3.2将传入的分析器analyzer、字段field传给对象:

analyzer=a;

ield=f;

至此,完成查询分析器的初始化工作。

4.Queryquery=parser.Parse(strQuery);

这是对用户输入字符串处理分词和记录位置关系的最核心部分。

在该方法中,主要只有两句。

4.1ReInit(newFastCharStream(new;

4.1.1该方法首先用StringReader对象构造一个FastCharStream对象作为重新初始化(因为在声明QueryPaser对象的时候已经初始化过)的参数。

即:

newFastCharStream(new;

4.1.2调用QueryPaser的方法ReInit(CharStreamstream);

其中stream就是上面生成的FastCharStream对象,FastCharStream类是CharStream类的子类。

该方法和声明查询分析器时的构造方法相似,这里就不再说明。

4.2returnQuery(field);

其中field为要查询的字段名。

该方法比较复杂,但我们只要把握一点,就是它的主要功能是对用户输入内容进行分词,然后构建Query对象用于查询,也就很好理解了。

4.2.1mods=Modifiers();

该方法主要是判断用户是否使用复合查询,即是否使用BooleanQuery等查询。

本文为了简单起见,就以简单查询为例,复合查询的方法是类似的,这点我们在前面已经讲过了。

4.2.1.1Jj_ntk();

这个是javaCC生成的方法,用于判断得出mods的值。

mods的默认值是0,即简单查询。

return(jj_ntk=(token.next=token_source.GetNextToken()).kind);

或者return(jj_ntk=jj_nt.kind);

由此可知,该方法返回的是字符串的类型。

4.2.1.2switch((jj_ntk==-1)?

Jj_ntk():

jj_ntk),这个switch语句就是根据jj_ntk的值,来判定查询方法。

由代码我们可以看出,主要由三种方法:

PLUS、MINUS、NOT。

由于我们以简单方法为例,ret的值并没有改变,返回默认值0。

4.2.2q=Clause(field);

该方法实现分词功能。

4.2.2.1Jj_2_1

(2);

javaCC生成的方法,判断是否为术语或者冒号。

4.2.2.2这里同样有一个switch((jj_ntk==-1)?

jj_ntk)语句,不过它是用来判断字符类型的,根据不同的类型,进行不同的处理。

分词涉及的类型很多,您可以从QueryParserConstants清楚地看出来。

查询的分词和索引时的分词是类似的。

关于分词,我们将在分析器的专题中

详细介绍,这里就不再累赘。

最终返回的q便为分词的结果。

它是Query类型,记录了每个Token的位置position、所属字段field、乘积因子boost、跨度slop和内容content等详细信息。

4.2.3AddClause(clauses,CONJ_NONE,mods,q);

把每个Token加入到ArrayList的对象当中。

在这个方法中,涉及到几个判断。

4.2.3.1if(clauses.Count>

0&

&

conj==CONJ_AND),这是对于复合查询而言的,如果之前已有Token加入ArrayList对象中,并且之前与现在是与的关系,那么将现在的Token加入其中,并设置与的关系。

由于这里是简单查询,不作详细说明。

4.2.3.2if(clauses.Count>

operator_Renamed==AND_OPERATOR&

conj==CONJ_OR),这也是对复合查询而言的。

4.2.3.3if(operator_Renamed==OR_OPERATOR),用于判断当前各个Token之间的逻辑关系。

下面将根据required和prohibited两个参数的值,决定每个Token的与、或、非,然后将各个Token添加到ArrayList的对象clauses。

4.2.3.4接下来的一个while循环,根据之前判定的查询方式、token间逻辑关系等参数,返回Query对象。

在简单查询中,直接返回对象q。

5.Hitshits=searcher.Search(query);

这是整个搜索过程的核心部分,前面的部分都是为该部分作准备。

现在,索引文件信息存储在searcher中,用户输入字符串信息存储在query中,两个条件都具备了,可以开始查询了。

查询的结果存放在Hits的对象hits当中。

需要说明的是,Search有很多个重载方法,但最终都要调用Hits的构造函数实现查询工作。

本文以构造函数Hits(Searchers,Queryq,Filterf)为例。

其中s、q分别为上面传入的参数searcher、query,f为过滤器,这里默认为null。

Hits(Searchers,Queryq,Filterf)的功能实现主要通过两个关键语句。

5.1weight=q.Weight(s);

该方法主要计算权重,是查询结果的排序工作的依据之一。

Weight是一个接口类,它存在的目的是使检索不改变一个Query,使得Query可以重用,即实现Query对象的重复使用。

5.1.1Queryquery=searcher.Rewrite(this);

实现query的重写。

重写的目的是似的最终的query存放的是本次查询用户输入的信息,即第4部分返回的对象q。

它的实现方法很简单,如果是第一次查询,则直接返回;

如果不是第一次查询,则将本次查询对象赋值给query然后再返回。

5.1.2Weightweight=query.CreateWeight(searcher);

该方法中,有一个判断语句if(terms.Count==1),即如果只有一个token,那么由于不存在两个token之间的位置关系,可以直接计算权重,如果不止一个token,那么调用方法PhraseWeight(this,searcher);

this就是本次query。

5.1.2.1PhraseWeight(this,searcher)该方法首先进行一些初始化工作,相似度等参数都采用默认的值。

接着调用idf=similarity.Idf(Enclosing_Instance.terms,searcher)计算每个短语的得分因子idf。

5.1.2.2floatsum=weight.SumOfSquaredWeights();

计算得分。

5.1.2.3floatnorm=GetSimilarity(searcher).QueryNorm(sum);

计算标准化因子。

5.1.2.4weight.Normalize(norm);

对权重进行标准化处理。

关于各种因子的计算方法,请参考附件一。

5.2GetMoreDocs(50);

返回查询结果得分最高的前100条。

在lucene2.0当中,查询结果如果足够多,并不是一次性的全部返回,lucene默认先返回得分最高的100条,一般来讲,这100条已经能够满足用户的需求了;

如果还不能满足用户的需求,根据用户需要,再返回得分次高的200条,接着400条……依次类推,直到满足用户需求为止。

该方法的实现如下:

privatevoidGetMoreDocs(intmin)

if(hitDocs.Count>

min)

min=hitDocs.Count;

intn=min*2;

//double#re

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 幼儿教育 > 唐诗宋词

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1