亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

Lucene-2.2.0 源代碼閱讀學習(26)

系統 1829 0

如果在初始化一個IndexWriter索引器的時候,指定 useCompoundFile =false,則在指定的索引目錄中生成的索引文件就不是.cfs復合索引文件。

通過這種方式生成的索引文件,它的不同格式表明了它鎖存儲的關于索引的不同內容。

至少,明確了在建立索引過程中,經過加工處理的數據究竟去向如何,能夠加深對Lucene索引過程的理解。

通過在文章 Lucene-2.2.0 源代碼閱讀學習(4) 中的那個例子,可以運行主函數,觀察到索引目錄中生成了大量的不同擴展名的索引文件,當然它們不是復合索引文件,如圖所示:

Lucene-2.2.0 源代碼閱讀學習(26)

這些不同擴展名的索引文件都是有一定的含義的。

如果只是根據這些文件名來說明它的含義,讓人感覺很抽象,那么就通過代碼來看,它們到底都存儲了一些什么內容。

_N.fnm文件

當向一個IndexWriter索引器實例添加Document的時候,調用了IndexWroter的addDocument()方法,在方法的內部調用如下:

buildSingleDocSegment() —> String segmentName = newRamSegmentName();

這時,調用newRamSegmentName()方法生成了一個segment的名稱,形如_ram_N,這里N為36進制數。

這個新生成的segmentName作為參數值傳遞到DocumentWriter類的addDocument()方法中:

dw.addDocument(segmentName, doc);

在DocumentWriter類中,這個segmentName依然是_ram_N形式的,再次作為參數值傳遞:

fieldInfos.write(directory, segment + ".fnm");

這個時候,就要發生變化了,在FieldInfos類的第一個write()方法中輸出System.out.println(name);,結果如下所示:

_ram_0.fnm
_ram_1.fnm
_ram_2.fnm
_ram_3.fnm
_ram_4.fnm
_ram_5.fnm
_ram_6.fnm
_ram_7.fnm
_ram_8.fnm
_ram_9.fnm
_0.fnm
_ram_a.fnm
_ram_b.fnm
_ram_c.fnm
_ram_d.fnm
_ram_e.fnm
_ram_f.fnm
_ram_g.fnm
_ram_h.fnm
_ram_i.fnm
_ram_j.fnm
_1.fnm
_ram_k.fnm

……

而且,可以從Directory看出究竟在這個過程中發生了怎樣的切換過程,在FieldInfos類的第一個write()方法中執行:

??? if(d instanceof FSDirectory){
??? System.out.println("FSDirectory");
??? }
??? else{
??? System.out.println("----RAMDirectory");
??? }

輸出結果如下所示:

----RAMDirectory
----RAMDirectory
----RAMDirectory
----RAMDirectory
----RAMDirectory
----RAMDirectory
----RAMDirectory
----RAMDirectory
----RAMDirectory
----RAMDirectory
FSDirectory
----RAMDirectory
----RAMDirectory
----RAMDirectory
----RAMDirectory
----RAMDirectory
----RAMDirectory
----RAMDirectory
----RAMDirectory
----RAMDirectory
----RAMDirectory
FSDirectory

……

可以看出,每次處理過10個.fnm文件(文件全名_ram_N.fnm),是在RAMDirectory中,然后就切換到FSDirectory中,這時輸出到本地磁盤的索引目錄中的索引文件是_N.fnm,可以從上面的實例圖中看到_0.fnm、_1.fnm等等。

真正執行向_N.fnm文件中寫入內容是在FieldInfos類的第二個write()方法中,可以從該方法的實現來看到底都寫入了哪些內容:

public void write(IndexOutput output) throws IOException {
??? output.writeVInt(size());
??? for (int i = 0; i < size(); i++) {
????? FieldInfo fi = fieldInfo(i);
????? byte bits = 0x0;
????? if (fi.isIndexed) bits |= IS_INDEXED;
????? if (fi.storeTermVector) bits |= STORE_TERMVECTOR;
????? if (fi.storePositionWithTermVector) bits |= STORE_POSITIONS_WITH_TERMVECTOR;
????? if (fi.storeOffsetWithTermVector) bits |= STORE_OFFSET_WITH_TERMVECTOR;
????? if (fi.omitNorms) bits |= OMIT_NORMS;
????? if (fi.storePayloads) bits |= STORE_PAYLOADS;
????? output.writeString(fi.name);
????? output.writeByte(bits);

??? }
}

從后兩行代碼可以看出,首先寫入了一個Field的名稱(name),然后寫入了一個byte值。這個byte的值可以根據從該FieldInfos類定義的一些標志經過位運算得到,從而從FieldIno的實例中讀取Field的信息,根據Field的一些信息(如:是否被索引、是否存儲詞條向量等等)來設置byte bits,這些標志的定義為:

static final byte IS_INDEXED = 0x1;
static final byte STORE_TERMVECTOR = 0x2;
static final byte STORE_POSITIONS_WITH_TERMVECTOR = 0x4;
static final byte STORE_OFFSET_WITH_TERMVECTOR = 0x8;
static final byte OMIT_NORMS = 0x10;
static final byte STORE_PAYLOADS = 0x20;

_N.fdt文件和_N.fdx文件

接著,在DocumentWriter類中的addDocumet()方法中,根據Directory實例、segment的名稱、一個FieldInfos的實例構造了一個FieldsWriter類的實例:

FieldsWriter fieldsWriter =?? new FieldsWriter(directory, segment, fieldInfos);

可以從FieldsWriter類的構造方法可以看出,實際上,根據生成的segment的名稱(_ram_N和_N)創建了兩個輸出流對象:

??? FieldsWriter(Directory d, String segment, FieldInfos fn) throws IOException {
??????? fieldInfos = fn;????????
??????? fieldsStream = d.createOutput(segment + ".fdt");
??????? indexStream = d.createOutput(segment + ".fdx");
??? }

這時,_N.fdt和_N.fdx文件就要生成了。

繼續看DocumentWriter類中的addDocument()方法:

fieldsWriter.addDocument(doc);

這時進入到FieldsWriter類中了,在addDocument()方法中提取Field的信息,寫入到,_N.fdt和_N.fdx文件中。FieldsWriter類的addDocument()方法實現如下:

??? final void addDocument(Document doc) throws IOException {
??????? indexStream.writeLong(fieldsStream.getFilePointer()); ?? // 向indexStream中(即_N.fdx文件)中寫入fieldsStream(_N.fdt文件)流中的當前位置,也就是寫入這個Field信息的位置

??????? int storedCount = 0;
??????? Iterator fieldIterator = doc.getFields().iterator();
??????? while (fieldIterator.hasNext()) {?? // 循環遍歷該Document中所有Field,統計需要存儲的Field的個數
??????????? Fieldable field = (Fieldable) fieldIterator.next();
??????????? if (field.isStored())
??????????????? storedCount++;
??????? }
?????? fieldsStream.writeVInt(storedCount); ?? // 存儲Document中需要存儲的的Field的個數,寫入到_N.fdt文件

??????? fieldIterator = doc.getFields().iterator();
??????? while (fieldIterator.hasNext()) {
??????????? Fieldable field = (Fieldable) fieldIterator.next();
??????????? // if the field as an instanceof FieldsReader.FieldForMerge, we're in merge mode
??????????? // and field.binaryValue() already returns the compressed value for a field
??????????? // with isCompressed()==true, so we disable compression in that case

??????????? boolean disableCompression = (field instanceof FieldsReader.FieldForMerge);
??????????? if (field.isStored()) {??? // 如果Field需要存儲,將該Field的編號寫入到_N.fdt文件
??????????????? fieldsStream.writeVInt(fieldInfos.fieldNumber(field.name()));

??????????????? byte bits = 0;
??????????????? if (field.isTokenized())
??????????????????? bits |= FieldsWriter.FIELD_IS_TOKENIZED;
??????????????? if (field.isBinary())
??????????????????? bits |= FieldsWriter.FIELD_IS_BINARY;
??????????????? if (field.isCompressed())
??????????????????? bits |= FieldsWriter.FIELD_IS_COMPRESSED;
???????????????
??????????????? fieldsStream.writeByte(bits); ?? // 將Field的是否分詞,或是否壓縮,或是否以二進制流存儲,這些信息都寫入到_N.fdt文件
????????????????
??????????????? if (field.isCompressed()) {
????????????????? // 如果當前Field可以被壓縮
????????????????? byte[] data = null;
?????????????????
????????????????? if (disableCompression) {
?????????????????????
// 已經被壓縮過,科恩那個需要進行合并優化
????????????????????? data = field.binaryValue();
????????????????? } else {
????????????????????? // 檢查Field是否以二進制存儲
????????????????????? if (field.isBinary()) {
??????????????????????? data = compress(field.binaryValue());
????????????????????? }
????????????????????? else {???? //?? 設置編碼方式,壓縮存儲處理
??????????????????????? data = compress(field.stringValue().getBytes("UTF-8"));
????????????????????? }
????????????????? }
????????????????? final int len = data.length;
????????????????? fieldsStream.writeVInt(len); ??? // 寫入Field名稱(以二進制存儲)的長度到_N.fdt文件
????????????????? fieldsStream.writeBytes(data, len); // 通過字節流的方式,寫入Field名稱(以二進制存儲)到_N.fdt文件
??????????????? }
??????????????? else {
?????????????????
// 如果當前這個Field不能進行壓縮
????????????????? if (field.isBinary()) {
??????????????????? byte[] data = field.binaryValue();
??????????????????? final int len = data.length;
???????????????????
fieldsStream.writeVInt(len);
??????????????????? fieldsStream.writeBytes(data, len);
????????????????? }
????????????????? else {
??????????????????? fieldsStream.writeString(field.stringValue()); ??? // 如果Field不是以二進制存儲,則以String的格式寫入到_N.fdt文件
????????????????? }
??????????????? }
??????????? }
??????? }
??? }

從該方法可以看出:

_N.fdx文件(即indexStream流)中寫入的內容是:一個Field在_N.fdt文件中位置。

_N.fdt文件(即fieldsStream流)中寫入的內容是:

(1) Document中需要存儲的Field的數量;

(2) 每個Field在Document中的編號;

(3) 每個Field關于是否分詞、是否壓縮、是否以二進制存儲這三個指標的一個組合值;

(4) 每個Field的長度;

(5) 每個Field的內容(binaryValue或stringValue);

_N.frq文件和_N.prx文件

仍然在DocumentWriter類的addDocument()方法中看:

writePostings(postings, segment);

因為在調用該方法之前,已經對Documeng進行了倒排,在倒排的過程中對Document中的Field進行了處理,如果Field指定了要進行分詞,則在倒排的時候進行了分詞處理,這時生成了詞條。然后調用writePostings()方法,根據生成的segment的名稱_ram_N,設置詞條的頻率、位置等信息,并寫入到索引目錄中。

在writePostings()方法中,首先創建了兩個輸出流:

????? freq = directory.createOutput(segment + ".frq");
????? prox = directory.createOutput(segment + ".prx");

這時,_N.frq文件和_N.prx文件就要在索引目錄中生成了。

經過倒排,各個詞條的重要信息都被存儲到了Posting對象中,Posting類是為詞條的信息服務的。因此,在writePostings()方法中可以遍歷Posting[]數組中的各個Posting實例,讀取并處理這些信息,然后輸出到索引目錄中。

設置_N.frq文件的起始寫入內容:

??????? int postingFreq = posting.freq;
??????? if (postingFreq == 1)?????
// 如果該詞條第一次出現造Document中
????????? freq.writeVInt(1);????
// 頻率色繪制為1
??????? else {
????????? freq.writeVInt(0);???? // 如果不是第一次出現,對應的Document的編號0要寫入到_N.frq文件
????????? freq.writeVInt(postingFreq);???? // 設置一個詞條在該Document中的頻率值
??????? }

再看prox輸出流:

??????????? if (payloadLength == lastPayloadLength) {???? // 其中,int lastPayloadLength = -1;
?????????????
// the length of the current payload equals the length
??????????? // of the previous one. So we do not have to store the length
??????????? // again and we only shift the position delta by one bit

????????????? prox.writeVInt(delta * 2); ??? //其中,int delta = position - lastPosition,int position = positions[j];
??????????? } else {
???????????
// the length of the current payload is different from the
??????????? // previous one. We shift the position delta, set the lowest
??????????? // bit and store the current payload length as VInt.

????????????
prox.writeVInt(delta * 2 + 1);
????????????? prox.writeVInt(payloadLength);
????????????? lastPayloadLength = payloadLength;
??????????? }
??????????? if (payloadLength > 0) {
??????????? // write current payload
?????????????
prox.writeBytes(payload.data, payload.offset, payload.length);
??????????? }
????????? } else {
?????????
// field does not store payloads, just write position delta as VInt
???????????
prox.writeVInt(delta);
????????? }

一個Posting包含了關于一個詞條在一個Document中出現的所有位置(用一個int[]數組來描述)、頻率(int)、該詞條對應的所有的Payload信息(用Payload[]來描述,因為一個詞條具有了頻率信息,自然就對應了多個Payload)。

關于Payload可以參考文章 Lucene-2.2.0 源代碼閱讀學習(23)

_N.prx文件文件寫入的內容都是與位置相關的數據。

從上面可以看出:

_N.frq文件(即freq流)中寫入的內容是:

(1) 一個詞條所在的Document的編號;

(2) 每個詞條在Document中頻率(即:出現的次數);

_N.prx文件(即prox流)中寫入的內容是:

其實主要就是Payload的信息,如:一個詞條對應的Payload的長度信息、起始偏移量信息;

_N.nrm文件

在DocumentWriter類的addDocument()方法中可以看到調用了writeNorms()方法:

writeNorms(segment);

也是根據生成的segment的名稱_ram_N來創建一個輸出流,看writeNorms()方法的定義:

private final void writeNorms(String segment) throws IOException {
??? for(int n = 0; n < fieldInfos.size(); n++){
????? FieldInfo fi = fieldInfos.fieldInfo(n);
????? if(fi.isIndexed && !fi.omitNorms){
??????? float norm = fieldBoosts[n] * similarity.lengthNorm(fi.name, fieldLengths[n]);
??????? IndexOutput norms = directory.createOutput(segment + ".f" + n);
??????? try {
????????? norms.writeByte(Similarity.encodeNorm(norm));
??????? } finally {
????????? norms.close();
??????? }
????? }
??? }
}

將一些標準化因子的信息,都寫入到了_N.nrm文件。其中每個segment對應著一個_N.nrm文件。

關于標準化因子可以參考文章 Lucene-2.2.0 源代碼閱讀學習(19) ,或者直接參考Apache官方網站 http://lucene.apache.org/java/docs/fileformats.html#Normalization%20Factors

關于不同格式的索引文件的內容示例

為了直觀,寫一個簡單的例子:

package org.shirdrn.lucene;

import java.io.IOException;

import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.store.LockObtainFailedException;

public class LuceneIndexFormat {
public static void main(String[] args) {
?? String indexPath = "E:\\Lucene\\myindex";
?? String maven = "Maven is a software project management and comprehension tool.";
?? String lucene = "Apache Lucene is a search engine written entirely in Java.";
?? Document doc1 = new Document();
?? doc1.add(new Field("maven",maven,Field.Store.YES,Field.Index.TOKENIZED));
?? Document doc2 = new Document();
?? doc2.add(new Field("lucene",lucene,Field.Store.YES,Field.Index.TOKENIZED));
?? try {
??? IndexWriter indexWriter = new IndexWriter(indexPath,new StandardAnalyzer(),true);
??? indexWriter.setUseCompoundFile(false);
??? indexWriter.addDocument(doc1);
??? indexWriter.addDocument(doc2);
??? indexWriter.close();
?? } catch (CorruptIndexException e) {
??? e.printStackTrace();
?? } catch (LockObtainFailedException e) {
??? e.printStackTrace();
?? } catch (IOException e) {
??? e.printStackTrace();
?? }
}
}

運行主函數后,在指定的索引目錄下生成了索引文件,而且是同一個索引段,如圖所示:

使用UltraEdit-32打開_0.fnm文件,可以看到內容如下所示:

就是我們在程序中設置的,即:

?? doc.add(new Field("maven",maven,Field.Store.YES,Field.Index.TOKENIZED));
?? doc.add(new Field("lucene",lucene,Field.Store.YES,Field.Index.TOKENIZED));

就是這兩個Field的name。

使用UltraEdit-32打開_0.fdt文件,可以看到內容如下所示:

其實,就是Field的內容。(上面的文本內容實際上存儲在一行)

使用UltraEdit-32打開_0.fdx文件,可以看到內容如下所示:

其實,就是在_0.fdt文件中,兩個Field的存放位置。

第一個Field是從0位置開始的,第二個是從42(這里是16進制,十進制為66)位置開始的。

使用UltraEdit-32打開_0.nrm文件,可以看到內容如下所示:

這里是標準化因子信息。

(關于標準化因子可以參考文章 Lucene-2.2.0 源代碼閱讀學習(19) ,或者直接參考Apache官方網站 http://lucene.apache.org/java/docs/fileformats.html#Normalization%20Factors 。)

?

Lucene-2.2.0 源代碼閱讀學習(26)


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 一级成人毛片 | 一本大道香蕉高清久久 | 日日夜夜中文字幕 | 久久国产精品老人性 | 天天操天天添 | 欧美成人小视频 | 亚洲成人一区二区 | 四虎影视库国产精品一区 | 国产成人亚洲精品久久 | 欧美黄色免费网址 | 一区二区三区日韩精品 | 啪啪99久久综合精品色 | 亚洲综合狠狠 | 成人国产精品一级毛片天堂 | 国产精品国产三级国产普通话 | 综合欧美日韩 | 深夜国产福利 | 夜夜快播| 久久99精品久久久久久首页 | 天天撸夜夜操 | 免费网站啪啪大全 | 日韩精品特黄毛片免费看 | 日本免费不卡视频一区二区三区 | 欧美操片| 在线成人aa在线看片 | 老司机午夜在线视频 | 亚洲女bbwxxxx另类 | 四虎精品影院永久在线播放 | 欧美伊人 | 91视频成人 | 99re这里有免费视频精品 | 福利姬在线播放 | 狠狠狠很橹影院 | 精品国产日韩亚洲一区91 | 亚洲国产成人久久一区久久 | 国产一级免费在线观看 | 成人一区二区免费中文字幕 | 精品国产成人高清在线 | 亚洲国产高清精品线久久 | 成人在线不卡 | 免费看欧美一级片 |