★ .frq??
詞語(yǔ)頻率數(shù)據(jù)
文件 ?? .prx? 詞語(yǔ)位置數(shù)據(jù)文件
?
1、frq 保存了
詞語(yǔ)所在文檔的文檔列表(docID)和該詞語(yǔ)出現(xiàn)在文檔中的頻率信息。
?
FreqFile (.frq) --> <TermFreqs, SkipData>
TermCount
????? frq文件包含TermCount個(gè)項(xiàng)。每一項(xiàng)都代表一個(gè)詞,按照tis中的term的順序排列。它分成兩個(gè)部分:一部分是倒排表本身,也即一串的文檔號(hào)及詞頻;另一部分是跳躍表,為了更快的訪(fǎng)問(wèn)和定位倒排表中文檔號(hào)及詞頻的位置。
?
TermFreqs --> <TermFreq>
DocFreq
????
TermFreq
按文檔編號(hào)遞增的順序排序。DocFreq表示
出現(xiàn)
該詞的文檔數(shù)量。
?
TermFreq --> DocDelta[, Freq?]?? 一個(gè)TermFreq結(jié)構(gòu)是由一個(gè)DocDelta后面或許跟著Freq組成。DocDelta是存儲(chǔ)包含此Term的文檔的ID號(hào)了,F(xiàn)req是在此文檔中出現(xiàn)的次數(shù)。
注意:TermFreq的存儲(chǔ)數(shù)據(jù)時(shí)比較特別的,其中用到了差值規(guī)則和或然跟隨規(guī)則。我們?cè)谙旅娼忉屢幌耇ermFreq的存儲(chǔ)數(shù)據(jù)。
(關(guān)于差值規(guī)則和或然跟隨規(guī)則,請(qǐng)參見(jiàn):《
索引文件格式(1):基礎(chǔ)知識(shí)
》
(http://lucene.apache.org/java/3_0_1/fileformats.html#Frequencies)
Lucene官方網(wǎng)站解析freq文件中我們可以看到這樣一段話(huà)
For example, the TermFreqs for a term which occurs once in document seven and three times in document eleven, with omitTf false, would be the following sequence of VInts: 15, 8, 3
If omitTf were true it would be this sequence of VInts instead: 7,4
(1) 首先我們看omitTf=false的情況,也即我們?cè)谒饕袝?huì)存儲(chǔ)一個(gè)文檔中term出現(xiàn)的次數(shù)。
例子中說(shuō)了,表示在文檔7中出現(xiàn)1次,并且又在文檔11中出現(xiàn)3次的文檔用以下序列表示:15,8,3.
那這三個(gè)數(shù)字是怎么計(jì)算出來(lái)的呢?
首先,根據(jù)定義TermFreq --> DocDelta[, Freq?],一個(gè)TermFreq結(jié)構(gòu)是由一個(gè)DocDelta后面或許跟著Freq組成,也即上面我們說(shuō)的A+B?結(jié)構(gòu)。
DocDelta自然是想存儲(chǔ)包含此Term的文檔的ID號(hào)了,F(xiàn)req是在此文檔中出現(xiàn)的次數(shù)。
所以根據(jù)例子,應(yīng)該存儲(chǔ)的完整信息為[DocID = 7, Freq = 1] [DocID = 11, Freq = 3](見(jiàn)全文檢索的基本原理章節(jié))。
然而為了節(jié)省空間,Lucene對(duì)編號(hào)此類(lèi)的數(shù)據(jù)都是用差值來(lái)表示的,也即上面說(shuō)的規(guī)則2,Delta規(guī)則,于是文檔ID就不能按完整信息存了,就應(yīng)該存放如下:
[DocDelta = 7, Freq = 1][DocDelta = 4 (11-7), Freq = 3]
然而Lucene對(duì)于A(yíng)+B?這種或然跟隨的結(jié)果,有其特殊的存儲(chǔ)方式,見(jiàn)規(guī)則3,即A+B?規(guī)則,如果DocDelta后面跟隨的Freq為1,則用 DocDelta最后一位置1表示。
如果DocDelta后面跟隨的Freq大于1,則DocDelta得最后一位置0,然后后面跟隨真正的值,從而對(duì)于第一個(gè)Term,由于Freq 為1,于是放在
DocDelta的最后一位表示,DocIDDelta = 7的二進(jìn)制是000 0111,必須要左移一位,且最后一位置1,000 1111 = 15,對(duì)于第二個(gè)Term,由于Freq大于1,于是放在DocDelta的最后一位置零,DocIDDelta = 4的二進(jìn)制是0000 0100,必須要左移一位,且最后一位置零,0000 1000 = 8,然后后面跟隨真正的Freq = 3。
于是得到序列:[DocDleta = 15][DocDelta = 8, Freq = 3],也即序列,15,8,3。
(2) 如果omitTf=true,也即我們不在索引中存儲(chǔ)一個(gè)文檔中Term出現(xiàn)的次數(shù),則只存DocID就可以了,因而不存在A(yíng)+B?規(guī)則的應(yīng)用。[DocID = 7][DocID = 11],然后應(yīng)用規(guī)則2,Delta規(guī)則,于是得到序列[DocDelta = 7][DocDelta = 4 (11 - 7)].
?
?
SkipData --> <<SkipLevelLength, SkipLevel>
NumSkipLevels-1
, SkipLevel> <SkipDatum>??
SkipLevel --> <SkipDatum>
DocFreq/(SkipInterval^(Level + 1))
SkipDatum --> DocSkip,PayloadLength?,FreqSkip,ProxSkip,SkipChildLevelPointer?
?
跳躍表可根據(jù)倒排表本身的長(zhǎng)度(DocFreq)和跳躍的幅度(SkipInterval)而分不同的層次,層次數(shù)為NumSkipLevels = Min(MaxSkipLevels, floor(log(DocFreq/log(SkipInterval)))).
?
第Level層的節(jié)點(diǎn)數(shù)為DocFreq/(SkipInterval^(Level + 1)),level從零計(jì)數(shù)。
?
除了最高層之外,其他層都有SkipLevelLength來(lái)表示此層的二進(jìn)制長(zhǎng)度(而非節(jié)點(diǎn)的個(gè)數(shù)),方便讀取某一層的跳躍表到緩存里面。
?
低層在前,高層在后,當(dāng)讀完所有的低層后,剩下的就是最后一層,因而最后一層不需要SkipLevelLength。這也是為什么Lucene文 檔中的格式描述為
NumSkipLevels-1
, SkipLevel,也即低NumSKipLevels-1層有SkipLevelLength,最后一層只有SkipLevel,沒(méi)有 SkipLevelLength。
?
除最低層以外,其他層都有SkipChildLevelPointer來(lái)指向下一層相應(yīng)的節(jié)點(diǎn)。
?
每一個(gè)跳躍節(jié)點(diǎn)包含以下信息:文檔號(hào),payload的長(zhǎng)度,文檔號(hào)對(duì)應(yīng)的倒排表中的節(jié)點(diǎn)在frq中的偏移量,文檔號(hào)對(duì)應(yīng)的倒排表中的節(jié)點(diǎn)在 prx中的偏移量。
?
?
2、
prx文件容納了每一個(gè)term出現(xiàn)在所有文檔中的位置的列表。注意如果在fields中的omitTf設(shè)置為 true時(shí)將不會(huì)在此文件中存儲(chǔ)任何信息,并且如果索引中所有fields中的omitTf都設(shè)置為true,此.prx文件將不會(huì)存在
?
ProxFile (.prx) --> <TermPositions>
TermCount
TermPositions --> <Positions>
DocFreq
Positions --> <PositionDelta,Payload?>
Freq
Payload --> <PayloadLength?,PayloadData>
PositionDelta --> VInt
PayloadLength --> VInt
PayloadData --> byte
PayloadLength
?
此文件包含TermCount個(gè)項(xiàng),每一個(gè)詞都有一項(xiàng),因?yàn)槊恳粋€(gè)詞都有自己的詞位置倒排表。
?
對(duì)于每一個(gè)詞的 都有一個(gè)DocFreq大小的數(shù)組,每項(xiàng)代表一篇文檔,記錄此文檔中此詞出現(xiàn)的位置。這個(gè)文檔數(shù)組也是和frq文件中的跳躍表有關(guān) 系的,從上面我們知道,在frq的跳躍表節(jié)點(diǎn)中有ProxSkip,當(dāng)SkipInterval為3的時(shí)候,frq的跳躍表節(jié)點(diǎn)指向prx文件中的此數(shù)組 中的第1,第4,第7,第10,第13,第16篇文檔。
?
而有一個(gè)Freq大小的數(shù)組,每一項(xiàng)代表此詞在此文檔中出現(xiàn) 一次,則有一個(gè)位置信息。
?
每一個(gè)位置信息包含:PositionDelta(采用差值規(guī)則),還可以保存 payload,應(yīng)用或然跟隨規(guī)則。
?
?
?
?
?
★?
專(zhuān)題用例 :
?
?關(guān)于例子的詳細(xì)信息參見(jiàn)《
索引文件格式 (2):文件 結(jié)構(gòu)總體框架
》最后的說(shuō)明。
?
?
(1)
解釋一下frq文件的數(shù)據(jù)
◆
? frq文件的數(shù)據(jù)很顯然使用了差值規(guī)則和或然跟隨規(guī)則,比如“term”詞語(yǔ)。其存儲(chǔ)結(jié)構(gòu)應(yīng)該為[DocID=0,F(xiàn)req=1][DocID=1,F(xiàn)req=2][DocID=2,F(xiàn)req=3][DocID=3,F(xiàn)req=1]。
差值規(guī)則: [DocCode=0,F(xiàn)req=1][DocCode=1(1-0),F(xiàn)req=2][DocCode=1(2-1),F(xiàn)req=2][DocCode=1(3-2),F(xiàn)req=2]
或然跟隨規(guī)則:[DocCode=0,F(xiàn)req=1] 如果DocCode后面的數(shù)據(jù)為1,則將DocCode<<1后將最后的bit存儲(chǔ)Freq內(nèi)容。即DocCode=00000000<<1=00000000 再將最后一個(gè)bit存儲(chǔ)Freq=1,則變成00000001。因此、在文件中本來(lái)需要2個(gè)byte空間,壓縮成1個(gè)byte來(lái)存儲(chǔ)DocCode+Freq。
???????????????????? [DocCode=1,F(xiàn)req=2]如果DocCode后面的數(shù)據(jù)不為1,則將DocCode<<1得到DocCode=00000001<<1=2后,在用另外一個(gè)byte來(lái)存儲(chǔ)Freq=1。這里沒(méi)有壓縮為什么還要左移呢?為了能夠?qū)嚎s數(shù)據(jù)和沒(méi)有壓縮的數(shù)據(jù)區(qū)別開(kāi)來(lái)。
?
(2) 解釋一下prx文件的數(shù)據(jù)
◆
? 同上,依然使用了差值規(guī)則。
?
注意: 這里詞頻和位置信息并沒(méi)有使用到跳表結(jié)構(gòu),是因?yàn)樗挥玫睦又?篇文檔內(nèi)容很少。如果Dictionary非常大,則會(huì)向上面敘述的那樣使用跳表結(jié)構(gòu)來(lái)進(jìn)行存儲(chǔ)。
【Lucene3.0 初窺】索引文件格式(5):posting數(shù)據(jù)[.frq/.prx]