做做网站app下载2023,控制面板网站,梅河口网站建设,太原seo哪家好2019独角兽企业重金招聘Python工程师标准 四、具体格式 上面曾经交代过#xff0c;Lucene保存了从Index到Segment到Document到Field一直到Term的正向信息#xff0c;也包括了从Term到Document映射的反向信息#xff0c;还有其他一些Lucene特有的信息。下面对这… 2019独角兽企业重金招聘Python工程师标准 四、具体格式 上面曾经交代过Lucene保存了从Index到Segment到Document到Field一直到Term的正向信息也包括了从Term到Document映射的反向信息还有其他一些Lucene特有的信息。下面对这三种信息一一介绍。 4.1. 正向信息 Index – Segments (segments.gen, segments_N) – Field(fnm, fdx, fdt) – Term (tvx, tvd, tvf) 上面的层次结构不是十分的准确因为segments.gen和segments_N保存的是段(segment)的元数据信息(metadata)其实是每个Index一个的而段的真正的数据信息是保存在域(Field)和词(Term)中的。 4.1.1. 段的元数据信息(segments_N) 一个索引(Index)可以同时存在多个segments_N(至于如何存在多个segments_N在描述完详细信息之后会举例说明)然而当我们要打开一个索引的时候我们必须要选择一个来打开那如何选择哪个segments_N呢 Lucene采取以下过程 其一在所有的segments_N中选择N最大的一个。基本逻辑参照SegmentInfos.getCurrentSegmentGeneration(File[] files)其基本思路就是在所有以segments开头并且不是segments.gen的文件中选择N最大的一个作为genA。其二打开segments.gen其中保存了当前的N值。其格式如下读出版本号(Version)然后再读出两个N如果两者相等则作为genB。 IndexInput genInput directory.openInput(IndexFileNames.SEGMENTS_GEN);//segments.gen int version genInput.readInt();//读出版本号 if (version FORMAT_LOCKLESS) {//如果版本号正确 long gen0 genInput.readLong();//读出第一个N long gen1 genInput.readLong();//读出第二个N if (gen0 gen1) {//如果两者相等则为genB genB gen0; } } 其三在上述得到的genA和genB中选择最大的那个作为当前的N方才打开segments_N文件。其基本逻辑如下 if (genA genB) gen genA; else gen genB; 如下图是segments_N的具体格式 Format 索引文件格式的版本号。由于Lucene是在不断开发过程中的因而不同版本的Lucene其索引文件格式也不尽相同于是规定一个版本号。Lucene 2.1此值-3Lucene 2.9时此值为-9。当用某个版本号的IndexReader读取另一个版本号生成的索引的时候会因为此值不同而报错。Version 索引的版本号记录了IndexWriter将修改提交到索引文件中的次数。其初始值大多数情况下从索引文件里面读出仅仅在索引开始创建的时候被赋予当前的时间已取得一个唯一值。其值改变在IndexWriter.commit-IndexWriter.startCommit-SegmentInfos.prepareCommit-SegmentInfos.write-writeLong(version)其初始值之所最初取一个时间是因为我们并不关心IndexWriter将修改提交到索引的具体次数而更关心到底哪个是最新的。IndexReader中常比较自己的version和索引文件中的version是否相同来判断此IndexReader被打开后还有没有被IndexWriter更新。 //在DirectoryReader中有一下函数。 public boolean isCurrent() throws CorruptIndexException, IOException { return SegmentInfos.readCurrentVersion(directory) segmentInfos.getVersion(); } NameCount 是下一个新段(Segment)的段名。所有属于同一个段的索引文件都以段名作为文件名一般为_0.xxx, _0.yyy, _1.xxx, _1.yyy ……新生成的段的段名一般为原有最大段名加一。如同的索引NameCount读出来是2说明新的段为_2.xxx, _2.yyy SegCount 段(Segment)的个数。如上图此值为2。SegCount个段的元数据信息 SegName 段名所有属于同一个段的文件都有以段名作为文件名。如上图第一个段的段名为_0第二个段的段名为_1SegSize 此段中包含的文档数然而此文档数是包括已经删除又没有optimize的文档的因为在optimize之前Lucene的段中包含了所有被索引过的文档而被删除的文档是保存在.del文件中的在搜索的过程中是先从段中读到了被删除的文档然后再用.del中的标志将这篇文档过滤掉。如下的代码形成了上图的索引可以看出索引了两篇文档形成了_0段然后又删除了其中一篇形成了_0_1.del又索引了两篇文档形成_1段然后又删除了其中一篇形成_1_1.del。因而在两个段中此值都是2。 IndexWriter writer new IndexWriter(FSDirectory.open(INDEX_DIR), new StandardAnalyzer(Version.LUCENE_CURRENT), true, IndexWriter.MaxFieldLength.LIMITED); writer.setUseCompoundFile(false); indexDocs(writer, docDir);//docDir中只有两篇文档 //文档一为Students should be allowed to go out with their friends, but not allowed to drink beer. //文档二为My friend Jerry went to school to see his students but found them drunk which is not allowed. writer.commit();//提交两篇文档形成_0段。 writer.deleteDocuments(new Term(contents, school));//删除文档二 writer.commit();//提交删除形成_0_1.del indexDocs(writer, docDir);//再次索引两篇文档Lucene不能判别文档与文档的不同因而算两篇新的文档。 writer.commit();//提交两篇文档形成_1段 writer.deleteDocuments(new Term(contents, school));//删除第二次添加的文档二 writer.close();//提交删除形成_1_1.del DelGen .del文件的版本号Lucene中在optimize之前删除的文档是保存在.del文件中的。在Lucene 2.9中文档删除有以下几种方式 IndexReader.deleteDocument(int docID)是用IndexReader按文档号删除。IndexReader.deleteDocuments(Term term)是用IndexReader删除包含此词(Term)的文档。IndexWriter.deleteDocuments(Term term)是用IndexWriter删除包含此词(Term)的文档。IndexWriter.deleteDocuments(Term[] terms)是用IndexWriter删除包含这些词(Term)的文档。IndexWriter.deleteDocuments(Query query)是用IndexWriter删除能满足此查询(Query)的文档。IndexWriter.deleteDocuments(Query[] queries)是用IndexWriter删除能满足这些查询(Query)的文档。原来的版本中Lucene的删除一直是由IndexReader来完成的在Lucene 2.9中虽可以用IndexWriter来删除但是其实真正的实现是在IndexWriter中保存了readerpool当IndexWriter向索引文件提交删除的时候仍然是从readerpool中得到相应的IndexReader并用IndexReader来进行删除的。下面的代码可以说明 IndexWriter.applyDeletes() - DocumentsWriter.applyDeletes(SegmentInfos) - reader.deleteDocument(doc); DelGen是每当IndexWriter向索引文件中提交删除操作的时候加1并生成新的.del文件。 IndexWriter.commit() - IndexWriter.applyDeletes() - IndexWriter$ReaderPool.release(SegmentReader) - SegmentReader(IndexReader).commit() - SegmentReader.doCommit(Map) - SegmentInfo.advanceDelGen() - if (delGen NO) { delGen YES; } else { delGen; } IndexWriter writer new IndexWriter(FSDirectory.open(INDEX_DIR), new StandardAnalyzer(Version.LUCENE_CURRENT), true, IndexWriter.MaxFieldLength.LIMITED); writer.setUseCompoundFile(false); indexDocs(writer, docDir);//索引两篇文档一篇包含school另一篇包含beer writer.commit();//提交两篇文档到索引文件形成段(Segment) _0 writer.deleteDocuments(new Term(contents, school));//删除包含school的文档其实是删除了两篇文档中的一篇。 writer.commit();//提交删除到索引文件形成_0_1.del writer.deleteDocuments(new Term(contents, beer));//删除包含beer的文档其实是删除了两篇文档中的另一篇。 writer.commit();//提交删除到索引文件形成_0_2.del indexDocs(writer, docDir);//索引两篇文档和上次的文档相同但是Lucene无法区分认为是另外两篇文档。 writer.commit();//提交两篇文档到索引文件形成段_1 writer.deleteDocuments(new Term(contents, beer));//删除包含beer的文档其中段_0已经无可删除段_1被删除一篇。 writer.close();//提交删除到索引文件形成_1_1.del 形成的索引文件如下 DocStoreOffsetDocStoreSegmentDocStoreIsCompoundFile 对于域(Stored Field)和词向量(Term Vector)的存储可以有不同的方式即可以每个段(Segment)单独存储自己的域和词向量信息也可以多个段共享域和词向量把它们存储到一个段中去。如果DocStoreOffset为-1则此段单独存储自己的域和词向量从存储文件上来看如果此段段名为XXX则此段有自己的XXX.fdtXXX.fdxXXX.tvfXXX.tvdXXX.tvx文件。DocStoreSegment和DocStoreIsCompoundFile在此处不被保存。如果DocStoreOffset不为-1则DocStoreSegment保存了共享的段的名字比如为YYYDocStoreOffset则为此段的域及词向量信息在共享段中的偏移量。则此段没有自己的XXX.fdtXXX.fdxXXX.tvfXXX.tvdXXX.tvx文件而是将信息存放在共享段的YYY.fdtYYY.fdxYYY.tvfYYY.tvdYYY.tvx文件中。DocumentsWriter中有两个成员变量String segment是当前索引信息存放的段String docStoreSegment是域和词向量信息存储的段。两者可以相同也可以不同决定了域和词向量信息是存储在本段中还是和其他的段共享。IndexWriter.flush(boolean triggerMerge, boolean flushDocStores, boolean flushDeletes)中第二个参数flushDocStores会影响到是否单独或是共享存储。其实最终影响的是DocumentsWriter.closeDocStore()。每当flushDocStores为false时closeDocStore不被调用说明下次添加到索引文件中的域和词向量信息是同此次共享一个段的。直到flushDocStores为true的时候closeDocStore被调用从而下次添加到索引文件中的域和词向量信息将被保存在一个新的段中不同此次共享一个段(在这里需要指出的是Lucene的一个很奇怪的实现虽然下次域和词向量信息是被保存到新的段中然而段名却是这次被确定了的在initSegmentName中当docStoreSegment null时被置为当前的segment而非下一个新的segmentdocStoreSegment segment于是会出现如下面的例子的现象)。好在共享域和词向量存储并不是经常被使用到实现也或有缺陷暂且解释到此。 IndexWriter writer new IndexWriter(FSDirectory.open(INDEX_DIR), new StandardAnalyzer(Version.LUCENE_CURRENT), true, IndexWriter.MaxFieldLength.LIMITED); writer.setUseCompoundFile(false); indexDocs(writer, docDir); writer.flush(); //flush生成segment _0并且flush函数中flushDocStores设为false也即下个段将同本段共享域和词向量信息这时DocumentsWriter中的docStoreSegment _0。 indexDocs(writer, docDir); writer.commit(); //commit生成segment _1由于上次flushDocStores设为false于是段_1的域以及词向量信息是保存在_0中的在这个时刻段_1并不生成自己的_1.fdx和_1.fdt。然而在commit函数中flushDocStores设为true也即下个段将单独使用新的段来存储域和词向量信息。然而这时DocumentsWriter中的docStoreSegment _1也即当段_2存储其域和词向量信息的时候是存在_1.fdx和_1.fdt中的而段_1的域和词向量信息却是存在_0.fdt和_0.fdx中的这一点非常令人困惑。 如图writer.commit的时候_1.fdt和_1.fdx并没有形成。 indexDocs(writer, docDir); writer.flush(); //段_2形成由于上次flushDocStores设为true其域和词向量信息是新创建一个段保存的却是保存在_1.fdt和_1.fdx中的这时候才产生了此二文件。 indexDocs(writer, docDir); writer.flush(); //段_3形成由于上次flushDocStores设为false其域和词向量信息是共享一个段保存的也是是保存在_1.fdt和_1.fdx中的 indexDocs(writer, docDir); writer.commit(); //段_4形成由于上次flushDocStores设为false其域和词向量信息是共享一个段保存的也是是保存在_1.fdt和_1.fdx中的。然而函数commit中flushDocStores设为true也意味着下一个段将新创建一个段保存域和词向量信息此时DocumentsWriter中docStoreSegment _4也表明了虽然段_4的域和词向量信息保存在了段_1中将来的域和词向量信息却要保存在段_4中。此时_4.fdx和_4.fdt尚未产生。 indexDocs(writer, docDir); writer.flush(); //段_5形成由于上次flushDocStores设为true其域和词向量信息是新创建一个段保存的却是保存在_4.fdt和_4.fdx中的这时候才产生了此二文件。 indexDocs(writer, docDir); writer.commit(); writer.close(); //段_6形成由于上次flushDocStores设为false其域和词向量信息是共享一个段保存的也是是保存在_4.fdt和_4.fdx中的 HasSingleNormFile 在搜索的过程中标准化因子(Normalization Factor)会影响文档最后的评分。不同的文档重要性不同不同的域重要性也不同。因而每个文档的每个域都可以有自己的标准化因子。如果HasSingleNormFile为1则所有的标准化因子都是存在.nrm文件中的。如果HasSingleNormFile不是1则每个域都有自己的标准化因子文件.fNNumField 域的数量NormGen 如果每个域有自己的标准化因子文件则此数组描述了每个标准化因子文件的版本号也即.fN的N。IsCompoundFile 是否保存为复合文件也即把同一个段中的文件按照一定格式保存在一个文件当中这样可以减少每次打开文件的个数。是否为复合文件由接口IndexWriter.setUseCompoundFile(boolean)设定。 非符合文件同符合文件的对比如下图非复合文件 复合文件 DeletionCount 记录了此段中删除的文档的数目。HasProx 如果至少有一个段omitTf为false也即词频(term freqency)需要被保存则HasProx为1否则为0。Diagnostics 调试信息。User map data 保存了用户从字符串到字符串的映射MapCheckSum 此文件segment_N的校验和。 读取此文件格式参考SegmentInfos.read(Directory directory, String segmentFileName): int format input.readInt();version input.readLong(); // read versioncounter input.readInt(); // read counterfor (int i input.readInt(); i 0; i--) // read segmentInfos add(new SegmentInfo(directory, format, input)); name input.readString();docCount input.readInt();delGen input.readLong();docStoreOffset input.readInt();docStoreSegment input.readString();docStoreIsCompoundFile (1 input.readByte());hasSingleNormFile (1 input.readByte());int numNormGen input.readInt();normGen new long[numNormGen];for(int j0;jnormGen[j] input.readLong();isCompoundFile input.readByte();delCount input.readInt();hasProx input.readByte() 1;diagnostics input.readStringStringMap();userData input.readStringStringMap();final long checksumNow input.getChecksum();final long checksumThen input.readLong(); 4.1.2. 域(Field)的元数据信息(.fnm) 一个段(Segment)包含多个域每个域都有一些元数据信息保存在.fnm文件中.fnm文件的格式如下 FNMVersion 是fnm文件的版本号对于Lucene 2.9为-2FieldsCount 域的数目一个数组的域(Fields) FieldName域名如titlemodifiedcontent等。FieldBits:一系列标志位表明对此域的索引方式 最低位1表示此域被索引0则不被索引。所谓被索引也即放到倒排表中去。 仅仅被索引的域才能够被搜到。Field.Index.NO则表示不被索引。Field.Index.ANALYZED则表示不但被索引而且被分词比如索引hello world后无论是搜hello还是搜world都能够被搜到。Field.Index.NOT_ANALYZED表示虽然被索引但是不分词比如索引hello world后仅当搜hello world时能够搜到搜hello和搜world都搜不到。一个域出了能够被索引还能够被存储仅仅被存储的域是搜索不到的但是能通过文档号查到多用于不想被搜索到但是在通过其它域能够搜索到的情况下能够随着文档号返回给用户的域。Field.Store.Yes则表示存储此域Field.Store.NO则表示不存储此域。倒数第二位1表示保存词向量0为不保存词向量。 Field.TermVector.YES表示保存词向量。Field.TermVector.NO表示不保存词向量。倒数第三位1表示在词向量中保存位置信息。 Field.TermVector.WITH_POSITIONS倒数第四位1表示在词向量中保存偏移量信息。 Field.TermVector.WITH_OFFSETS倒数第五位1表示不保存标准化因子 Field.Index.ANALYZED_NO_NORMSField.Index.NOT_ANALYZED_NO_NORMS倒数第六位是否保存payload要了解域的元数据信息还要了解以下几点 位置(Position)和偏移量(Offset)的区别 位置是基于词Term的偏移量是基于字母或汉字的。 索引域(Indexed)和存储域(Stored)的区别 一个域为什么会被存储(store)而不被索引(Index)呢在一个文档中的所有信息中有这样一部分信息可能不想被索引从而可以搜索到但是当这个文档由于其他的信息被搜索到时可以同其他信息一同返回。举个例子读研究生时您好不容易写了一篇论文交给您的导师您的导师却要他所第一作者而您做第二作者然而您导师不想别人在论文系统中搜索您的名字时找到这篇论文于是在论文系统中把第二作者这个Field的Indexed设为false这样别人搜索您的名字永远不知道您写过这篇论文只有在别人搜索您导师的名字从而找到您的文章时在一个角落表述着第二作者是您。payload的使用 我们知道索引是以倒排表形式存储的对于每一个词都保存了包含这个词的一个链表当然为了加快查询速度此链表多用跳跃表进行存储。Payload信息就是存储在倒排表中的同文档号一起存放多用于存储与每篇文档相关的一些信息。当然这部分信息也可以存储域里(stored Field)两者从功能上基本是一样的然而当要存储的信息很多的时候存放在倒排表里利用跳跃表有利于大大提高搜索速度。Payload的存储方式如下图 Payload主要有以下几种用法 存储每个文档都有的信息比如有的时候我们想给每个文档赋一个我们自己的文档号而不是用Lucene自己的文档号。于是我们可以声明一个特殊的域(Field)_ID和特殊的词(Term)_ID使得每篇文档都包含词_ID于是在词_ID的倒排表里面对于每篇文档又有一项每一项都有一个payload于是我们可以在payload里面保存我们自己的文档号。每当我们得到一个Lucene的文档号的时候就能从跳跃表中查找到我们自己的文档号。//声明一个特殊的域和特殊的词 public static final String ID_PAYLOAD_FIELD _ID; public static final String ID_PAYLOAD_TERM _ID; public static final Term ID_TERM new Term(ID_PAYLOAD_TERM, ID_PAYLOAD_FIELD); //声明一个特殊的TokenStream它只生成一个词(Term)就是那个特殊的词在特殊的域里面。 static class SinglePayloadTokenStream extends TokenStream { private Token token; private boolean returnToken false; SinglePayloadTokenStream(String idPayloadTerm) { char[] term idPayloadTerm.toCharArray(); token new Token(term, 0, term.length, 0, term.length); } void setPayloadValue(byte[] value) { token.setPayload(new Payload(value)); returnToken true; } public Token next() throws IOException { if (returnToken) { returnToken false; return token; } else { return null; } } } //对于每一篇文档都让它包含这个特殊的词在特殊的域里面 SinglePayloadTokenStream singlePayloadTokenStream new SinglePayloadTokenStream(ID_PAYLOAD_TERM); singlePayloadTokenStream.setPayloadValue(long2bytes(id)); doc.add(new Field(ID_PAYLOAD_FIELD, singlePayloadTokenStream)); //每当得到一个Lucene的文档号时通过以下的方式得到payload里面的文档号 long id 0; TermPositions tp reader.termPositions(ID_PAYLOAD_TERM); boolean ret tp.skipTo(docID); tp.nextPosition(); int payloadlength tp.getPayloadLength(); byte[] payloadBuffer new byte[payloadlength]; tp.getPayload(payloadBuffer, 0); id bytes2long(payloadBuffer); tp.close(); 影响词的评分 在Similarity抽象类中有函数public float scorePayload(byte [] payload, int offset, int length) 可以根据payload的值影响评分。读取域元数据信息的代码如下 FieldInfos.read(IndexInput, String) int firstInt input.readVInt();size input.readVInt();for (int i 0; i size; i) String name 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; 4.1.3. 域(Field)的数据信息(.fdt.fdx) 域数据文件(fdt): 真正保存存储域(stored field)信息的是fdt文件在一个段(segment)中总共有segment size篇文档所以fdt文件中共有segment size个项每一项保存一篇文档的域的信息对于每一篇文档一开始是一个fieldcount也即此文档包含的域的数目接下来是fieldcount个项每一项保存一个域的信息。对于每一个域fieldnum是域号接着是一个8位的byte最低一位表示此域是否分词(tokenized)倒数第二位表示此域是保存字符串数据还是二进制数据倒数第三位表示此域是否被压缩再接下来就是存储域的值比如new Field(title, lucene in action, Field.Store.Yes, …)则此处存放的就是lucene in action这个字符串。域索引文件(fdx) 由域数据文件格式我们知道每篇文档包含的域的个数每个存储域的值都是不一样的因而域数据文件中segment size篇文档每篇文档占用的大小也是不一样的那么如何在fdt中辨别每一篇文档的起始地址和终止地址呢如何能够更快的找到第n篇文档的存储域的信息呢就是要借助域索引文件。域索引文件也总共有segment size个项每篇文档都有一个项每一项都是一个long大小固定每一项都是对应的文档在fdt文件中的起始地址的偏移量这样如果我们想找到第n篇文档的存储域的信息只要在fdx中找到第n项然后按照取出的long作为偏移量就可以在fdt文件中找到对应的存储域的信息。读取域数据信息的代码如下 Document FieldsReader.doc(int n, FieldSelector fieldSelector) long position indexStream.readLong();//indexStream points to .fdxfieldsStream.seek(position);//fieldsStream points to fdtint numFields fieldsStream.readVInt();for (int i 0; i numFields; i) int fieldNumber fieldsStream.readVInt();byte bits fieldsStream.readByte();boolean compressed (bits FieldsWriter.FIELD_IS_COMPRESSED) ! 0;boolean tokenize (bits FieldsWriter.FIELD_IS_TOKENIZED) ! 0;boolean binary (bits FieldsWriter.FIELD_IS_BINARY) ! 0;if (binary) int toRead fieldsStream.readVInt();final byte[] b new byte[toRead];fieldsStream.readBytes(b, 0, b.length);if (compressed) int toRead fieldsStream.readVInt();final byte[] b new byte[toRead];fieldsStream.readBytes(b, 0, b.length);uncompress(b),else fieldsStream.readString() 4.1.3. 词向量(Term Vector)的数据信息(.tvx.tvd.tvf) 词向量信息是从索引(index)到文档(document)到域(field)到词(term)的正向信息有了词向量信息我们就可以得到一篇文档包含那些词的信息。 词向量索引文件(tvx) 一个段(segment)包含N篇文档此文件就有N项每一项代表一篇文档。每一项包含两部分信息第一部分是词向量文档文件(tvd)中此文档的偏移量第二部分是词向量域文件(tvf)中此文档的第一个域的偏移量。词向量文档文件(tvd) 一个段(segment)包含N篇文档此文件就有N项每一项包含了此文档的所有的域的信息。每一项首先是此文档包含的域的个数NumFields然后是一个NumFields大小的数组数组的每一项是域号。然后是一个(NumFields - 1)大小的数组由前面我们知道每篇文档的第一个域在tvf中的偏移量在tvx文件中保存而其他(NumFields - 1)个域在tvf中的偏移量就是第一个域的偏移量加上这(NumFields - 1)个数组的每一项的值。词向量域文件(tvf) 此文件包含了此段中的所有的域并不对文档做区分到底第几个域到第几个域是属于那篇文档是由tvx中的第一个域的偏移量以及tvd中的(NumFields - 1)个域的偏移量来决定的。对于每一个域首先是此域包含的词的个数NumTerms然后是一个8位的byte最后一位是指定是否保存位置信息倒数第二位是指定是否保存偏移量信息。然后是NumTerms个项的数组每一项代表一个词(Term)对于每一个词由词的文本TermText词频TermFreq(也即此词在此文档中出现的次数)词的位置信息词的偏移量信息。读取词向量数据信息的代码如下 TermVectorsReader.get(int docNum, String field, TermVectorMapper) int fieldNumber fieldInfos.fieldNumber(field);//通过field名字得到field号seekTvx(docNum);//在tvx文件中按docNum文档号找到相应文档的项long tvdPosition tvx.readLong();//找到tvd文件中相应文档的偏移量tvd.seek(tvdPosition);//在tvd文件中按偏移量找到相应文档的项int fieldCount tvd.readVInt();//此文档包含的域的个数。for (int i 0; i fieldCount; i) //按域号查找域 number tvd.readVInt();if (number fieldNumber) found i;position tvx.readLong();//在tvx中读出此文档的第一个域在tvf中的偏移量for (int i 1; i found; i) position tvd.readVLong();//加上所要找的域在tvf中的偏移量tvf.seek(position);int numTerms tvf.readVInt();byte bits tvf.readByte();storePositions (bits STORE_POSITIONS_WITH_TERMVECTOR) ! 0;storeOffsets (bits STORE_OFFSET_WITH_TERMVECTOR) ! 0;for (int i 0; i numTerms; i) start tvf.readVInt();deltaLength tvf.readVInt();totalLength start deltaLength;tvf.readBytes(byteBuffer, start, deltaLength);term new String(byteBuffer, 0, totalLength, UTF-8);if (storePositions) positions new int[freq];int prevPosition 0;for (int j 0; j freq; j) positions[j] prevPosition tvf.readVInt();prevPosition positions[j];if (storeOffsets) offsets new TermVectorOffsetInfo[freq];int prevOffset 0;for (int j 0; j freq; j)int startOffset prevOffset tvf.readVInt();int endOffset startOffset tvf.readVInt();offsets[j] new TermVectorOffsetInfo(startOffset, endOffset);prevOffset endOffset;分类: Lucene原理与代码分析 转载于:https://my.oschina.net/weiweiblog/blog/3018429