上面曾經交代過,Lucene保存了從Index到Segment到Document到Field一直到Term的正向信息,也包括了從Term到Document映射的反向信息,還有其餘一些Lucene特有的信息。下面對這三種信息一一介紹。數組
Index –> Segments (segments.gen, segments_N) –> Field(fnm, fdx, fdt) –> Term (tvx, tvd, tvf)app
上面的層次結構不是十分的準確,由於segments.gen和segments_N保存的是段(segment)的元數據信息(metadata),實際上是每一個Index一個的,而段的真正的數據信息,是保存在域(Field)和詞(Term)中的。函數
一個索引(Index)能夠同時存在多個segments_N(至於如何存在多個segments_N,在描述完詳細信息以後會舉例說明),然而當咱們要打開一個索引的時候,咱們必需要選擇一個來打開,那如何選擇哪一個segments_N呢? spa
Lucene採起如下過程:3d
IndexInput genInput = directory.openInput(IndexFileNames.SEGMENTS_GEN);//"segments.gen" |
if (genA > genB) |
以下圖是segments_N的具體格式:blog
//在DirectoryReader中有一下函數。索引 public boolean isCurrent() throws CorruptIndexException, IOException { |
IndexWriter writer = new IndexWriter(FSDirectory.open(INDEX_DIR), new StandardAnalyzer(Version.LUCENE_CURRENT), true, IndexWriter.MaxFieldLength.LIMITED); //文檔一爲: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"));//刪除文檔二 |
IndexWriter.applyDeletes() -> DocumentsWriter.applyDeletes(SegmentInfos) -> reader.deleteDocument(doc); |
IndexWriter.commit() -> IndexWriter.applyDeletes() -> IndexWriter$ReaderPool.release(SegmentReader) -> SegmentReader(IndexReader).commit() -> SegmentReader.doCommit(Map) -> SegmentInfo.advanceDelGen() -> if (delGen == NO) { |
IndexWriter writer = new IndexWriter(FSDirectory.open(INDEX_DIR), new StandardAnalyzer(Version.LUCENE_CURRENT), true, IndexWriter.MaxFieldLength.LIMITED); indexDocs(writer, docDir);//索引兩篇文檔,一篇包含"school",另外一篇包含"beer" 造成的索引文件以下: |
IndexWriter writer = new IndexWriter(FSDirectory.open(INDEX_DIR), new StandardAnalyzer(Version.LUCENE_CURRENT), true, IndexWriter.MaxFieldLength.LIMITED); //flush生成segment "_0",而且flush函數中,flushDocStores設爲false,也即下個段將同本段共享域和詞向量信息,這時DocumentsWriter中的docStoreSegment= "_0"。 indexDocs(writer, docDir); //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); //段"_2"造成,因爲上次flushDocStores設爲true,其域和詞向量信息是新建立一個段保存的,倒是保存在_1.fdt和_1.fdx中的,這時候才產生了此二文件。 indexDocs(writer, docDir); //段"_3"造成,因爲上次flushDocStores設爲false,其域和詞向量信息是共享一個段保存的,也是是保存在_1.fdt和_1.fdx中的 indexDocs(writer, docDir); //段"_4"造成,因爲上次flushDocStores設爲false,其域和詞向量信息是共享一個段保存的,也是是保存在_1.fdt和_1.fdx中的。然而函數commit中flushDocStores設爲true,也意味着下一個段將新建立一個段保存域和詞向量信息,此時DocumentsWriter中docStoreSegment= "_4",也代表了雖然段"_4"的域和詞向量信息保存在了段"_1"中,未來的域和詞向量信息卻要保存在段"_4"中。此時"_4.fdx"和"_4.fdt"還沒有產生。 indexDocs(writer, docDir); //段"_5"造成,因爲上次flushDocStores設爲true,其域和詞向量信息是新建立一個段保存的,倒是保存在_4.fdt和_4.fdx中的,這時候才產生了此二文件。 indexDocs(writer, docDir); //段"_6"造成,因爲上次flushDocStores設爲false,其域和詞向量信息是共享一個段保存的,也是是保存在_4.fdt和_4.fdx中的 |
非複合文件: |
複合文件: |
讀取此文件格式參考SegmentInfos.read(Directory directory, String segmentFileName):
|
一個段(Segment)包含多個域,每一個域都有一些元數據信息,保存在.fnm文件中,.fnm文件的格式以下:
要了解域的元數據信息,還要了解如下幾點:
//聲明一個特殊的域和特殊的詞 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 { SinglePayloadTokenStream(String idPayloadTerm) { void setPayloadValue(byte[] value) { public Token next() throws IOException { //對於每一篇文檔,都讓它包含這個特殊的詞,在特殊的域裏面 SinglePayloadTokenStream singlePayloadTokenStream = new SinglePayloadTokenStream(ID_PAYLOAD_TERM); long id = 0; |
FieldInfos.read(IndexInput, String)
|
Document FieldsReader.doc(int n, FieldSelector fieldSelector)
|
詞向量信息是從索引(index)到文檔(document)到域(field)到詞(term)的正向信息,有了詞向量信息,咱們就能夠獲得一篇文檔包含那些詞的信息。
TermVectorsReader.get(int docNum, String field, TermVectorMapper)
|