- 浏览: 82661 次
- 性别:
- 来自: 江西
最新评论
-
115646002:
我请问个问题:
我在配置eclipse3.6的时候,应该是某一 ...
eclipse 3.6 Helios 最新插件 -
xangqun:
是啊,不过真的很好用
tomcat插件与Jrebel插件整合(java web开发利器) -
freecode:
jrebel收费吧
tomcat插件与Jrebel插件整合(java web开发利器) -
lydawen:
开发js相当不错的插件 aptana - http://upd ...
eclipse3.5 Galileo最新实用插件 -
hszhl:
eclipse{wtp+cdt+ve#/windowBuild ...
eclipse3.5 Galileo最新实用插件
二、Lucene搜索详细过程
为了解析Lucene对索引文件搜索的过程,预先写入索引了如下几个文件:
file01.txt: apple apples cat dog
file02.txt: apple boy cat category
file03.txt: apply dog eat etc
file04.txt: apply cat foods
2.1、打开IndexReader指向索引文件夹
代码为:
IndexReader reader = IndexReader.open(FSDirectory.open(indexDir));
其实是调用了DirectoryReader.open(Directory, IndexDeletionPolicy, IndexCommit, boolean, int) 函数,其主要作用是生成一个SegmentInfos.FindSegmentsFile对象,并用它来找到此索引文件中所有的段,并打开这些段。
SegmentInfos.FindSegmentsFile.run(IndexCommit commit)主要做以下事情:
2.1.1、找到最新的segment_N文件
- 由于segment_N是整个索引中总的元数据,因而正确的选择segment_N更加重要。
- 然而有时候为了使得索引能够保存在另外的存储系统上,有时候需要用NFS mount一个远程的磁盘来存放索引,然而NFS为了提高性能,在本地有Cache,因而有可能使得此次打开的索引不是另外的writer写入的最新信息,所以在此处用了双保险。
- 一方面,列出所有的segment_N,并取出其中的最大的N,设为genA
String[] files = directory.listAll(); long genA = getCurrentSegmentGeneration(files); |
long getCurrentSegmentGeneration(String[] files) { long max = -1; for (int i = 0; i < files.length; i++) { String file = files[i]; if (file.startsWith(IndexFileNames.SEGMENTS) //"segments_N" && !file.equals(IndexFileNames.SEGMENTS_GEN)) { //"segments.gen" long gen = generationFromSegmentsFileName(file); if (gen > max) { max = gen; } } } return max; } |
- 另一方面,打开segment.gen文件,从中读出N,设为genB
IndexInput genInput = directory.openInput(IndexFileNames.SEGMENTS_GEN); int version = genInput.readInt(); long gen0 = genInput.readLong(); long gen1 = genInput.readLong(); if (gen0 == gen1) { genB = gen0; } |
- 在genA和genB中去较大者,为gen,并用此gen构造要打开的segments_N的文件名
if (genA > genB) gen = genA; else gen = genB; String segmentFileName = IndexFileNames.fileNameFromGeneration(IndexFileNames.SEGMENTS, "", gen); //segmentFileName "segments_4" |
2.1.2、通过segment_N文件中保存的各个段的信息打开各个段
- 从segment_N中读出段的元数据信息,生成SegmentInfos
SegmentInfos infos = new SegmentInfos(); infos.read(directory, segmentFileName); |
SegmentInfos.read(Directory, String) 代码如下: int format = input.readInt(); version = input.readLong(); counter = input.readInt(); for (int i = input.readInt(); i > 0; i—) { //读出每一个段,并构造SegmentInfo对象 add(new SegmentInfo(directory, format, input)); } |
SegmentInfo(Directory dir, int format, IndexInput input)构造函数如下: name = input.readString(); docCount = input.readInt(); delGen = input.readLong(); docStoreOffset = input.readInt(); if (docStoreOffset != -1) { docStoreSegment = input.readString(); docStoreIsCompoundFile = (1 == input.readByte()); } else { docStoreSegment = name; docStoreIsCompoundFile = false; } hasSingleNormFile = (1 == input.readByte()); int numNormGen = input.readInt(); normGen = new long[numNormGen]; for(int j=0;j<numNormGen;j++) { normGen[j] = input.readLong(); } isCompoundFile = input.readByte(); delCount = input.readInt(); hasProx = input.readByte() == 1; 其实不用多介绍,看过Lucene学习总结之三:Lucene的索引文件格式 (2)一章,就很容易明白。 |
- 根据生成的SegmentInfos打开各个段,并生成ReadOnlyDirectoryReader
SegmentReader[] readers = new SegmentReader[sis.size()]; for (int i = sis.size()-1; i >= 0; i—) { //打开每一个段 readers[i] = SegmentReader.get(readOnly, sis.info(i), termInfosIndexDivisor); } |
SegmentReader.get(boolean, Directory, SegmentInfo, int, boolean, int) 代码如下: instance.core = new CoreReaders(dir, si, readBufferSize, termInfosIndexDivisor); instance.core.openDocStores(si); //生成用于读取存储域和词向量的对象。 instance.loadDeletedDocs(); //读取被删除文档(.del)文件 instance.openNorms(instance.core.cfsDir, readBufferSize); //读取标准化因子(.nrm) |
CoreReaders(Directory dir, SegmentInfo si, int readBufferSize, int termsIndexDivisor)构造函数代码如下: cfsReader = new CompoundFileReader(dir, segment + "." + IndexFileNames.COMPOUND_FILE_EXTENSION, readBufferSize); //读取cfs的reader fieldInfos = new FieldInfos(cfsDir, segment + "." + IndexFileNames.FIELD_INFOS_EXTENSION); //读取段元数据信息(.fnm) TermInfosReader reader = new TermInfosReader(cfsDir, segment, fieldInfos, readBufferSize, termsIndexDivisor); //用于读取词典信息(.tii .tis) freqStream = cfsDir.openInput(segment + "." + IndexFileNames.FREQ_EXTENSION, readBufferSize); //用于读取freq proxStream = cfsDir.openInput(segment + "." + IndexFileNames.PROX_EXTENSION, readBufferSize); //用于读取prox |
FieldInfos(Directory d, String name)构造函数如下: IndexInput input = d.openInput(name); int firstInt = input.readVInt(); size = input.readVInt(); for (int i = 0; i < size; i++) { //读取域名 String name = StringHelper.intern(input.readString()); //读取域的各种标志位 byte bits = input.readByte(); boolean isIndexed = (bits & IS_INDEXED) != 0; boolean storeTermVector = (bits & STORE_TERMVECTOR) != 0; boolean storePositionsWithTermVector = (bits & STORE_POSITIONS_WITH_TERMVECTOR) != 0; boolean storeOffsetWithTermVector = (bits & STORE_OFFSET_WITH_TERMVECTOR) != 0; boolean omitNorms = (bits & OMIT_NORMS) != 0; boolean storePayloads = (bits & STORE_PAYLOADS) != 0; boolean omitTermFreqAndPositions = (bits & OMIT_TERM_FREQ_AND_POSITIONS) != 0; //将读出的域生成FieldInfo对象,加入fieldinfos进行管理 addInternal(name, isIndexed, storeTermVector, storePositionsWithTermVector, storeOffsetWithTermVector, omitNorms, storePayloads, omitTermFreqAndPositions); } |
CoreReaders.openDocStores(SegmentInfo)主要代码如下: fieldsReaderOrig = new FieldsReader(storeDir, storesSegment, fieldInfos, readBufferSize, si.getDocStoreOffset(), si.docCount); //用于读取存储域(.fdx, .fdt) termVectorsReaderOrig = new TermVectorsReader(storeDir, storesSegment, fieldInfos, readBufferSize, si.getDocStoreOffset(), si.docCount); //用于读取词向量(.tvx, .tvd, .tvf) |
- 初始化生成的ReadOnlyDirectoryReader,对打开的多个SegmentReader中的文档编号
在Lucene中,每个段中的文档编号都是从0开始的,而一个索引有多个段,需要重新进行编号,于是维护数组start[],来保存每个段的文档号的偏移量,从而第i个段的文档号是从start[i]至start[i]+Num private void initialize(SegmentReader[] subReaders) { this.subReaders = subReaders; starts = new int[subReaders.length + 1]; for (int i = 0; i < subReaders.length; i++) { starts[i] = maxDoc; maxDoc += subReaders[i].maxDoc(); if (subReaders[i].hasDeletions()) hasDeletions = true; } starts[subReaders.length] = maxDoc; } |
2.1.3、得到的IndexReader对象如下
reader ReadOnlyDirectoryReader (id=466) //索引文件夹 //段元数据信息 //每个段的Reader |
从上面的过程来看,IndexReader有以下几个特性:
- 段元数据信息已经被读入到内存中,因而索引文件夹中因为新添加文档而新增加的段对已经打开的reader是不可见的。
- .del文件已经读入内存,因而其他的reader或者writer删除的文档对打开的reader也是不可见的。
- 打开的reader已经有inputstream指向cfs文件,从段合并的过程我们知道,一个段文件从生成起就不会改变,新添加的文档都在新的段中,删除的文档都在.del中,段之间的合并是生成新的段,而不会改变旧的段,只不过在段的合并过程中,会将旧的段文件删除,这没有问题,因为从操作系统的角度来讲,一旦一个文件被打开一个inputstream也即打开了一个文件描述符,在内核中,此文件会保持reference count,只要reader还没有关闭,文件描述符还在,文件是不会被删除的,仅仅reference count减一。
- 以上三点保证了IndexReader的snapshot的性质,也即一个IndexReader打开一个索引,就好像对此索引照了一张像,无论背后索引如何改变,此IndexReader在被重新打开之前,看到的信息总是相同的。
- 严格的来讲,Lucene的文档号仅仅对打开的某个reader有效,当索引发生了变化,再打开另外一个reader的时候,前面reader的文档0就不一定是后面reader的文档0了,因而我们进行查询的时候,从结果中得到文档号的时候,一定要在reader关闭之前应用,从存储域中得到真正能够唯一标识你的业务逻辑中的文档的信息,如url,md5等等,一旦reader关闭了,则文档号已经无意义,如果用其他的reader查询这些文档号,得到的可能是不期望的文档。
2.2、打开IndexSearcher
代码为:
IndexSearcher searcher = new IndexSearcher(reader);
其过程非常简单:
private IndexSearcher(IndexReader r, boolean closeReader) { reader = r; //当关闭searcher的时候,是否关闭其reader this.closeReader = closeReader; //对文档号进行编号 List<IndexReader> subReadersList = new ArrayList<IndexReader>(); gatherSubReaders(subReadersList, reader); subReaders = subReadersList.toArray(new IndexReader[subReadersList.size()]); docStarts = new int[subReaders.length]; int maxDoc = 0; for (int i = 0; i < subReaders.length; i++) { docStarts[i] = maxDoc; maxDoc += subReaders[i].maxDoc(); } } |
IndexSearcher表面上看起来好像仅仅是reader的一个封装,它的很多函数都是直接调用reader的相应函数,如:int docFreq(Term term),Document doc(int i),int maxDoc()。然而它提供了两个非常重要的函数:
- void setSimilarity(Similarity similarity),用户可以实现自己的Similarity对象,从而影响搜索过程的打分,详见有关Lucene的问题(4):影响Lucene对文档打分的四种方式
- 一系列search函数,是搜索过程的关键,主要负责打分的计算和倒排表的合并。
因而在某些应用之中,只想得到某个词的倒排表的时候,最好不要用IndexSearcher,而直接用IndexReader.termDocs(Term term),则省去了打分的计算。
转:http://forfuture1978.iteye.com/blog/632816
发表评论
-
Lucene学习总结之十:Lucene的分词器Analyzer(转)
2010-06-08 11:31 14121、抽象类Analyzer 其主要包含两个接口,用于生成To ... -
Lucene学习总结之九:Lucene的查询对象(3)转
2010-06-08 11:30 9456、FilteredQuery FilteredQue ... -
Lucene学习总结之九:Lucene的查询对象(2)转
2010-06-08 11:29 9395、SpanQuery 所谓SpanQuery也即在查询过 ... -
Lucene学习总结之九:Lucene的查询对象(1)转
2010-06-08 11:27 1053Lucene除了支持查询语法以外,还可以自己构造查询 ... -
Lucene学习总结之八:Lucene的查询语法,JavaCC及QueryParser(2)转
2010-06-08 11:25 965三、解析QueryParser.jj 3.1、声明Qu ... -
Lucene学习总结之八:Lucene的查询语法,JavaCC及QueryParser(1)转
2010-06-08 11:24 1115一、Lucene的查询语法 Lucene所支持的查询语法可见 ... -
Lucene学习总结之七:Lucene搜索过程解析(8)转
2010-06-08 11:22 9612.4、搜索查询对象 2.4.4、收集文档结 ... -
Lucene学习总结之七:Lucene搜索过程解析(7)转
2010-06-08 11:20 10652.4、搜索查询对象 2.4.3.2、并集Di ... -
Lucene学习总结之七:Lucene搜索过程解析(6)转
2010-06-08 11:20 15452.4、搜索查询对象 2.4.3、进行倒排表合并 ... -
Lucene学习总结之七:Lucene搜索过程解析(5)转
2010-06-08 11:19 10682.4、搜索查询对象 2.4.2、创建Sco ... -
Lucene学习总结之七:Lucene搜索过程解析(4)转
2010-06-08 11:18 8822.4、搜索查询对象 2.4.1.2、创建Weig ... -
Lucene学习总结之七:Lucene搜索过程解析(3)转
2010-06-08 11:17 10962.3、QueryParser解析查询语句生成查询对象 ... -
Lucene学习总结之七:Lucene搜索过程解析(1)转
2010-06-08 11:15 921一、Lucene搜索过程总论 搜索的过程总的来说就是将词典及 ... -
Lucene学习总结之六:Lucene打分公式的数学推导(转)
2010-06-08 11:13 965在进行Lucene的搜索过程� ... -
Lucene学习总结之五:Lucene段合并(merge)过程分析(转)
2010-06-08 11:12 1106一、段合并过程总论 IndexWriter中与段合并有关的成 ... -
Lucene学习总结之四:Lucene索引过程分析(4)转
2010-06-08 11:08 8176、关闭IndexWriter对象 代码: writ ... -
Lucene学习总结之四:Lucene索引过程分析(3)转
2010-06-08 11:07 9185、DocumentsWriter对CharBlockPool ... -
Lucene学习总结之四:Lucene索引过程分析(2)转
2010-06-08 11:06 8653、将文档加入IndexWriter 代码: write ... -
Lucene学习总结之四:Lucene索引过程分析(1)转
2010-06-08 11:05 863对于Lucene的索引过程,除了将词(Term)写入倒排表并最 ... -
Lucene学习总结之三:Lucene的索引文件格式 (3)转
2010-06-08 11:02 912四、具体格式 4.2. 反向信息 反向信息是索引文件的 ...
相关推荐
通过学习Lucene源码,我们可以定制自己的分词器、查询解析器,甚至优化搜索算法,以满足特定的搜索需求。例如,在中文环境下,可以使用IK Analyzer或者jieba分词库来增强对中文的支持。 总结,Lucene作为Java平台上...
"lucene学习pdf2" 提供的文档,无疑是对Lucene深入理解的一把钥匙,它涵盖了Lucene的核心概念、操作流程以及高级特性。 首先,Lucene的基础知识是必不可少的。Lucene的核心在于索引和搜索,它将非结构化的文本数据...
由于林良益先生在2012之后未对IKAnalyzer进行更新,后续lucene分词接口发生变化,导致不可使用,所以此jar包支持lucene6.0以上版本
**Lucene学习总结** 在深入理解Lucene之前,我们首先需要了解什么是全文检索。全文检索是一种从大量文本数据中快速查找所需信息的技术。它通过建立索引来实现高效的搜索,而Lucene正是Java环境下最著名的全文搜索...
2. **分词器(Tokenizer)**:Lucene提供了多种分词器,如StandardAnalyzer、ChineseAnalyzer等,用于将输入文本分解成可搜索的词语。 3. **文档索引(Document Indexing)**:索引是Lucene的重要环节,包括创建...
**Lucene学习指南** Lucene是一个高性能、全文检索库,由Apache软件基金会开发并维护,是Java编程语言中广泛使用的搜索引擎库。它提供了一个简单的API,使得开发者能够方便地在应用中实现全文检索功能。本篇文章将...
总结来说,Lucene 7.2.1 是一个强大的全文检索工具,通过其丰富的功能和高效性能,为开发者提供了构建强大搜索引擎的可能。对于需要处理大量文本数据的应用,使用Lucene进行索引和查询无疑是一个明智的选择。
Lucene 是一个强大的全文搜索引擎库,它以 Java 语言实现,并作为 Apache 软件基金会的 Apache Jakarta 项目的一部分开放源代码。Lucene 提供了高效、可扩展的索引和搜索功能,允许开发者轻松地在应用程序中集成高级...
**Lucene 3.3.0 学习Demo** ...总之,"Lucene3.3.0学习Demo"是一个宝贵的资源,对于想要掌握全文搜索技术的开发者来说,它提供了丰富的实践案例和学习材料,可以帮助你快速上手并深入理解Lucene的核心机制。
《解密搜索引擎技术实战:Lucene in java(第2版)源码 dvd ppt》是一部深入探讨搜索引擎技术的著作,特别关注于使用Java实现的开源全文搜索引擎库——Lucene。本书结合了理论与实践,旨在帮助读者理解搜索引擎的...
同时,它还包含分词器(Analyzer)用于将文本分割成可搜索的词元,以及查询解析器(QueryParser)将用户输入转化为搜索查询。 `lucene-analyzers-common-4.10.2.jar`是Lucene的通用分析器包。分析器是处理文本的...
【Lucene搜索技术】是一种基于Java的全文索引引擎工具包,它并非一个完整的全文搜索引擎,而是提供了一套用于构建全文检索应用的API。Lucene的主要目标是方便开发者将其嵌入到各种应用程序中,实现对特定数据源的...
《Lucene 3.6 搜索实例解析》 Apache Lucene 是一个开源全文搜索引擎库,为开发者提供了在Java应用程序中实现高效、可扩展的搜索功能的工具。在本篇文章中,我们将深入探讨Lucene 3.6版本中的搜索功能,通过实例...
《Lucene学习资料》 Lucene是一个开源的全文搜索引擎库,由Apache软件基金会维护。它提供了高级的文本分析和索引功能,使得开发者能够轻松地在应用程序中集成强大的搜索功能。这个资料包中的《Lucene in Action_2nd...
本篇文章将深入探讨 Lucene 的核心原理,从全文检索的基础概念出发,逐步解析索引创建过程以及搜索机制。 一、全文检索的基本原理 1. 总论 全文检索是通过索引机制,快速找到文档中包含特定关键词的过程。Lucene ...
总的来说,Lucene提供了一套完整的框架,涵盖了从文本处理到搜索结果返回的全过程,使开发者能够专注于构建具有高级搜索功能的应用,而无需关心底层实现细节。通过理解Lucene的基本原理和使用方法,我们可以构建出...
经典的Lucene实例代码及详细解析以及Lucene结构流程介绍 Lucene是一个功能强大且灵活的开源搜索引擎库,它提供了一个简单易用的API,允许开发者快速构建搜索应用程序。下面将对Lucene的实例代码和结构流程进行详细...
Lucene的学习过程通常包括以下几个关键部分: 1. **文本分析**:Lucene使用Analyzer进行文本预处理,包括分词、去除停用词、词形还原等。了解不同Analyzer如StandardAnalyzer、ChineseAnalyzer的区别和应用场景至关...
《Lucene.Heritrix:开发自己的搜索引擎(第2版)》是一本深入探讨如何构建搜索引擎的专业书籍,作者是邱哲。这本书详细介绍了利用Apache Lucene和Heritrix这两个强大的开源工具来创建自定义搜索引擎的技术和策略。 ...