Lucene Payload 的研究與應用

http://www.ibm.com/developerworks/cn/opensource/os-cn-lucene-pl/index.html

2008 年 11 月 06 日

Payload (元數據) 誕生於 Lucene 的2.2 版本,它是在 Lucene 2.1 索引文件格式的基礎上擴展而來,提供了一種可以靈活配置的高級索引技術,在某些特定應用場景下能優化基於 Lucene 構建的應用的搜索性能。本文重點研究了 Payload 的實現原理、索引結構的變化、接口 API ,在本文的最後舉例說明了 Payload 是如何幫助改善搜索體驗的。
Lucene 是最初是由 Douglass R. Cutting 博士發佈在自己主頁上的一個 Java 全文信息檢索工具包,後來成爲 Apache Jakarta 家族中的一個開源項目,目前已經成爲 Apache 基金會的頂級項目。索引是現代搜索引擎的核心,建立索引的過程就是把源數據處理成方便查詢的索引文件的過程。 Lucene 採用的是一種被稱爲倒排索引 (Inverted Index) 的機制,倒排索引也是大多現代搜索引擎的基礎。

Payload (元數據) 誕生於 Lucene 的2.2 版本,它是在 Lucene 2.1 索引文件格式的基礎上擴展而來,提供了一種可以靈活配置的高級索引技術。本文重點研究了 Payload 的實現原理、索引結構的變化、接口 API ,在本文的最後舉例說明了 Payload 是如何幫助改善搜索體驗的。

Payload 的出現

倒排索引就是說我們維護了一個詞條表,對於這個表中的每個詞條,都有一個鏈表描述了有哪些文檔包含了這個詞條。假定我們有三篇文檔 D0,D1,D2:

D0 = "it is what it is"
D1 = "what is it"
D2 = "it is a banana"



那麼,我們可以創建如下倒排索引結構:

Term Posting-list
"a": {2}
"banana": {2}
"is": {0, 1, 2}
"it": {0, 1, 2}
"what": {0, 1}



一般情況下,將一個詞條所索引的文檔(一般用文檔編號表示)稱之爲 Posting,那麼一個詞條索引的多個文檔就稱之爲 Posting-list。除了在 Posting-list 中記錄文檔編號,Lucene 也在Posting-list 中添加了詞頻和位置信息。詞頻的添加有助於結果的排序,位置信息的添加解決了短語檢索的問題。

在Lucene 2.1 中,記錄位置信息的即 .prx 文件,它的格式如下:

ProxFile (.prx) --> <TermPositions> TermCount
TermPositions --> <Positions> DocFreq
Positions --> <PositionDelta> Freq
PositionDelta --> VInt



仔細觀察我們可以發現,文檔 D0 中 what 詞條是加粗顯示而文檔 D1 中的 what 則沒有。顯然,Lucene 2.1 的索引結構是無法表示出兩者的差異。爲了解決這個問題,從 Lucene2.2 開始,引入了 Payload 的概念。 Payload 即詞條 (Term) 的元數據或稱載荷, Lucene 支持用戶在索引的過程中將詞條的元數據添加的索引庫中,同時也提供了在檢索結果時讀取 Payload 信息的功能。Payload 的誕生爲用戶提供了一種可靈活配置的高級索引技術,爲支持更加豐富的搜索體驗創造了條件。

那麼 Lucene 是如何改進索引文件以支持 Payload 功能的呢?





回頁首



索引結構的變化

爲了更加形象的描述改進後的索引結構,我們用不同的顏色表示出文檔 ID ,詞頻,位置和 Payload,如圖 1 所示,(U表示下劃線,B表示粗體)


圖 1:Lucene的索引結構


對比 Lucene2.1 的索引結構,Lucene2.2 的索引結構的表達式如下:

ProxFile (.prx) --> <TermPositions> TermCount
TermPositions --> <Positions> DocFreq
Positions --> <PositionDelta,Payload?> Freq
Payload --> <PayloadLength?,PayloadData>
PositionDelta --> VInt
PayloadLength --> VInt
PayloadData --> bytePayloadLength



從上面的索引表達式中可以看出只有當一個詞彙包含 Payload 信息時,Lucene 纔會爲之分配相應的 Payload 存儲空間,這是一種高效率的空間實現。Lucene2.2 之後的 Payload 特指詞條的元數據,那麼文檔的元數據如何表示呢?

我們知道,Lucene 中 Document 由 Field 組成,而 Field 由 Term 組成,文檔的 Payload 可以用存儲的 Field 表示。這樣存在的問題是,如果需要讀取大量的文檔的元數據,因爲 Field 的索引信息與存儲信息是分開的,那麼 I/O 效率將是較差的。而 Payload 信息則是直接存儲在索引中,因此可以利用詞條的 Payload 功能存儲文檔級別的元數據。文檔級 Payload 可表示爲如下圖2所示格式(省略了詞頻和位置信息):


圖 2:文檔級的 Payload 表示


如圖2所示,以文檔的 url 信息爲例,通過爲每一個文檔構造一個特殊的詞條 ”url” ,將每個文檔的 ur l值作爲 payload 信息,把 Payload 與文檔編號關聯起來,這樣就可以實現文檔級的 Payload。





回頁首



Payload 相關的 API

從 Lucene2.2 的索引結構可以看出,Payload 的存儲與詞條的位置信息是緊密聯繫在一起的,因此 Payload 的存儲和檢索 API 位於Token類和 TermPositions 類當中。

向詞條中存儲 Payload 信息

org.apache.lucene.analysis.Token
void setPayload(Payload payload)



Payload 信息的構造函數

org.apache.lucene.index.Payload
Payload(byte[] data)

Payload(byte[] data,
int offset,
int length)



從位置信息中檢索 Payload

org.apache.lucene.index.TermPositions
boolean next();
int doc()
int freq();

int nextPosition();

int getPayloadLength();
byte[] getPayload(byte[] data,
int offset)






回頁首



Payload 的應用場景舉例

場景一:改進的 Lucene 的區間檢索

日期檢索是區間檢索的常見例子,如用戶需要在圖書館中檢索特定年代的圖書,滿足如下條件:

Date>1954/08/01 & Date<1955/08/01



常見的做法就是將日期作爲一個獨立 Field 進行存儲,利用 RangeQuery 進行區間檢索,Posting-list 的格式如圖3中左圖所示。如果圖書日期分佈區間很廣,用 Field 存儲日期信息,我們需要將日期細化到年月日進行存儲,因此詞條數目是非常龐大的。這種情況下,我們可以利用 Payload 功能來減少詞條的數目,提高檢索效率,可以將日期的年月作爲詞條,日作爲 Payload 信息,這幾乎將詞條數目減小了近 30 倍,改進後的存儲結構如圖3右圖所示:


圖 3:使用 Payload 存儲日期信息


場景二:提高特定詞彙的評分

利用 Payload 功能,還可以提高文檔中特定詞彙的評分,如黑體詞彙、斜體詞彙等,從而優化搜索結果排序。

下面還以文檔 D0 和 D1 爲例說明如何設置和檢索 Payload。

D0 = "it is what it is"
D1 = "what is it"



Step1:在 Analyzer 處理過程中,爲特殊詞彙添加評分 Payload

final byte BoldBoost = 5;

final byte ItalicBoost = 5;

Token token = new Token(…);

if (isBold) {//如果是黑體字
token.setPayload(
new Payload(new byte[] {BoldBoost}));
}else if(isItalic){//如果是斜體字
token.setPayload(
new Payload(new byte[] { ItalicBoost }));
}

return token;



Step2:重寫 Similarity (主要負責排名和評分)

Similarity boostingSimilarity =
new DefaultSimilarity() {
// @override
public float scorePayload(byte [] payload,
int offset,
int length) {

//讀取payload的值,payload存儲的即爲詞彙的評分。
if (length == 1) return payload[offset];
};



Step3:使用重寫的 boostingSimilarity 進行檢索

Query btq = new BoostingTermQuery(
new Term(“field”, “what”));


Searcher searcher = new IndexSearcher(…);
Searcher.setSimilarity(boostingSimilarity);

Hits hits = searcher.search(btq);






回頁首



總結

Payload 是 Lucene 一個允許在索引中爲詞條儲存元數據信息。希望通過閱讀本文,你可以對Payload 功能有一個整體的瞭解,進而可以靈活運用 Payload 的功能來優化具體的應用。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章