Lucene原理與代碼分析解讀筆記

Lucene是一個基於Java的高效的全文檢索庫。

那麼什麼是全文檢索,爲什麼需要全文檢索?

目前人們生活中出現的數據總的來說分爲兩類:結構化數據和非結構化數據。很容易理解,結構化數據是有固定格式和結構的或者有限長度的數據,比如數據庫,元數據等。非結構化數據則是不定長或者沒有固定格式的數據,如圖片,郵件,文檔等。還有一種較少的分類爲半結構化數據,如XML,HTML等,在一定程度上我們可以將其按照結構化數據來處理,也可以抽取純文本按照非結構化數據來處理。

非結構化數據又稱爲全文數據。,對其搜索主要有兩種方式:

1)  順序掃描法(SerialScanning):顧名思義,要找內容包含某一個字符串的文檔,就挨着文檔一個個找,對照每一個文檔從頭到尾,一直掃描,指導掃描完所有的文檔。類似於Windows中搜索文件的功能。

2)  第二種則爲索引。就是從非結構化數據中提取出信息重新組織,使其變得有一定的組織,從而提高檢索效率。比如我們的電話簿,從電話簿中查找聯繫人,我們根據首字母拼音可以索引定位到某一個聯繫人。

先建立索引在對索引進行搜索的過程就叫做全文檢索(Full-text Search)。下圖爲全文檢索的一般過程,也是Lucene檢索的過程。

           

全文檢索主要分爲兩個過程,索引創建和搜索索引。

索引創建:將顯示中所有結構化和非結構化的數據提取信息,創建索引的過程。

搜索索引:就是得到用戶的查詢請求,搜索創建的索引,然後返回結果的過程。

這裏引出全文檢索的三個問題:

1.索引中存儲了怎樣的數據

順序掃描速度慢的原因是由於用戶想要搜索的信息和非結構化數據中所存儲的信息不一致。索引保存從字符串到文件的映射,則會大大提高搜索速度。從字符串到文件的映射是從文件到字符串映射的反向過程,因此保存這種信息的索引稱爲反向索引。

          

左邊保存稱爲詞典的一系列字符串,右邊每個字符串指向包含自己的文檔鏈表(倒排表,Posting List)。索引使得保存的信息與要搜索的信息一致,因而提高搜索的速度。

對於大量數據來說,創建索引確實是一個耗時的過程,但是相比於順序掃描,創建索引只需要一次之後每次搜索不需要再創建索引,而順序掃描是每一次都需要重新掃描。

2.如何創建索引

全文檢索創建索引有以下幾步:

1)  一些要索引的原文檔(Document)

2)  將原文檔傳給分詞組件(Tokenizer)

分詞組件主要做的事情是將文檔分成一個一個單獨的單詞,去掉標點符號和停詞(句子中最普通的出現頻率最高的一些詞,沒有實際和特別的意義,比如冠詞the, a, an等)

3)  將得到的詞元(Token)傳給語言處理組件(LinguisticProcessor)

語言處理組件主要是對得到的詞元做一些處理:大寫變爲小寫,將單詞縮寫成詞根形式(Stemming)和將單詞換邊爲詞根形式(Lemmatization)

Stemming 和lemmatization的異同:

 相同之處:Stemming 和lemmatization 都要使詞彙成爲詞根形式。

 兩者的方式不同:

 Stemming 採用的是“縮減”的方式:“cars”到“car”,“driving”到“drive”。

 Lemmatization 採用的是“轉變”的方式:“drove”到“drove”,“driving”到“drive”。

 兩者的算法不同:

 Stemming 主要是採取某種固定的算法來做這種縮減,如去除“s”,去除“ing”加“e”,

將“ational”變爲“ate”,將“tional”變爲“tion”。

 Lemmatization 主要是採用保存某種字典的方式做這種轉變。比如字典中有“driving”

到“drive”,“drove”到“drive”,“am, is,are”到“be”的映射,做轉變時,只要查字典就可以了。

 Stemming 和lemmatization不是互斥關係,是有交集的,有的詞利用這兩種方式都能達到相同的轉換。

詞元轉換之後的詞,我們稱之爲詞(Term)。

4)  將得到的詞(Term)傳給索引組件(Indexer)

索引組件主要做:

A. 將得到的詞(Term)創建一個字典;

B. 對字典按照字母順序進行排序;

C. 合併相同的詞(Term)成爲文檔倒排(PostingList)鏈表,在該表中需要注意兩個定義:一個是Document Frequency,文檔頻次,表示總共有多少文件包含該詞,另一個是Frequency詞頻,表示此文件中包含了幾個該詞

3.如何搜索索引

我們搜索到的結果往往不是隻有幾個,也往往不一定就是我們想要找的那幾個,也可能返回成百上千上萬個文件我們無法一眼就找到我們需要的文檔。因此,我們需要在搜索結果中找到和查詢語句最相關的文檔才行。

因此搜索分爲以下幾步:

1)  用戶輸入查詢語句

查詢語句也是由語法的,基本的包含AND,OR,NOT等。例如:用戶輸入lucene AND learned NOT hadoop,則表示用戶想要尋找一個包含lucene和learned但是不包含hadoop的文檔。

2)  對查詢語句進行詞法分析,語法分析和語言處理

詞法分析就是發現不合法的關鍵字會出現錯誤。比如關鍵字NOT拼寫錯誤的話就會被當作普通單詞參與查詢

語法分析主要是根據查詢語句的語法規則來形成一棵語法樹

              

語言處理的過程與索引過程中的語言處理幾乎相同,learned變爲learn

              

3)  搜索索引,得到符合語法樹的文檔

該步驟有幾步:

A. 在反向索引表中,分別找出包含lucene,learn,hadoop的文檔鏈表

B. 對包含lucene和learn的鏈表進行AND操作,得到一個包含兩個詞的文檔鏈表

C. 將此鏈表與hadoop的文檔鏈表進行差操作,去除包含hadoop的文檔,得到的文檔鏈表就是我們需要的文檔

4)  根據得到的文檔和查詢語句的相關性,對結果進行排序
需要對文檔與文檔之間的相關性進行打分,分數高的相關性好,應該排在前面。那麼如何判斷文檔之間的關係呢?一個文檔由許多詞組成,對於不同的文檔,不同的term重要性不同。因此,判斷文檔之間的關係,首先找出哪些詞對於文檔之間的關係最重要,然後判斷這些詞之間的關係,也就是計算詞的權重。計算詞的權重有兩個參數,第一個是詞(Term),第二個是文檔(Document)。詞的權重(Term weight)表示此詞(Term)在此文檔中的重要程度,越重要的詞(Term)有越大的權重(Term weight),因而在計算文檔之間的相關性中將發揮更大的作用。判斷詞(Term)之間的關係從而得到文檔相關性的過程應用一種叫做向量空間模型的算法(Vector Space Model)。計算權重的過程中,影響一個詞的重要性主要由Term Frequency (tf)(即此Term 在此文檔中出現了多少次。tf 越大說明越重要。)和Document Frequency (df)(即有多少文檔包含次Term。df 越大說明越不重要。)影響。然使用向量空間模型的算法來判斷Term之間的關係從而得到文檔相關性。

 

總結一下,創建索引和搜索索引的過程如下圖:

           

1. 索引過程:

1) 有一系列被索引文件

2) 被索引文件經過語法分析和語言處理形成一系列詞(Term)。

3) 經過索引創建形成詞典和反向索引表。

4) 通過索引存儲將索引寫入硬盤。

2. 搜索過程:

a) 用戶輸入查詢語句。

b) 對查詢語句經過語法分析和語言分析得到一系列詞(Term)。

c) 通過語法分析得到一個查詢樹。

d) 通過索引存儲將索引讀入到內存。

e) 利用查詢樹搜索索引,從而得到每個詞(Term)的文檔鏈表,對文檔鏈表進行交,差,並得到結果文檔。

f) 將搜索到的結果文檔對查詢的相關性進行排序。

g) 返回查詢結果給用戶。

 

 

關於信息檢索的相關理論差不多就是這些。

Lucene是對以上這種理論的一種基本實踐,是一個高效的可擴展的全文檢索庫,全部用java實現,無需配置,但是僅僅支持純文本文件的索引的搜索,不負責由其他格式的文件抽取純文本文件,或者從網絡中抓取文件的過程。

Lucene的構架如下圖所示:

                

從圖中而已看出,Lucene有索引和搜索兩個過程,包含索引創建,索引,搜索三個要點。

索引的組件:

           

被索引的文檔用Document對象表示。

l  IndexWriter通過函數addDocument將文檔添加到索引中,實現創建索引的過程。

l  Lucene的索引是應用反向索引。

l  當用戶有請求時,Query代表用戶的查詢語句。

l  IndexSearcher通過函數search搜索Lucene Index。

l  IndexSearcher計算term weight和score並且將結果返回給用戶。

l  返回給用戶的文檔集合用TopDocsCollector表示。

索引過程:

               

l  創建一個IndexWriter用來寫索引文件,它有幾個參數,INDEX_DIR 就是索引文件所存放的位置,Analyzer 便是用來對文檔進行詞法分析和語言處理的。

l  創建一個Document 代表我們要索引的文檔。

l  將不同的Field 加入到文檔中。我們知道,一篇文檔有多種信息,如題目,作者,修改時間,內容等。不同類型的信息用不同的Field 來表示,在本例子中,一共有兩類信息進行了索引,一個是文件路徑,一個是文件內容。其中FileReader 的SRC_FILE 就表示要索引的源文件。

l  IndexWriter 調用函數addDocument 將索引寫到索引文件夾中。

 

搜索過程如下:

l  IndexReader 將磁盤上的索引信息讀入到內存,INDEX_DIR 就是索引文件存放的位置。

l  創建IndexSearcher準備進行搜索。

l  創建Analyer 用來對查詢語句進行詞法分析和語言處理。

l  創建QueryParser用來對查詢語句進行語法分析。

l  QueryParser 調用parser 進行語法分析,形成查詢語法樹,放到Query 中。

l  IndexSearcher 調用search 對查詢語法樹Query 進行搜索,得到結果TopScoreDocCollector。

 

Lucene的各個模塊:

l  Lucene 的analysis 模塊主要負責詞法分析及語言處理而形成Term。

l  Lucene的index模塊主要負責索引的創建,裏面有IndexWriter。

l  Lucene的store模塊主要負責索引的讀寫。

l  Lucene 的QueryParser主要負責語法分析。

l  Lucene的search模塊主要負責對索引的搜索。

l  Lucene的similarity模塊主要負責對相關性打分的實現。

 

Lucene 的索引過程,就是按照全文檢索的基本過程,將倒排表寫成此文件格式的過程。

Lucene 的搜索過程,就是按照此文件格式將索引進去的信息讀出來,然後計算每篇文檔打分(score)的過程。


Lucene 的索引結構是有層次結構的,主要分以下幾個層次:

l  索引(Index):

Ø  在 Lucene 中一個索引是放在一個文件夾中的。

Ø  如上圖,同一文件夾中的所有的文件構成一個Lucene 索引。

l  段(Segment):

Ø  一個索引可以包含多個段,段與段之間是獨立的,添加新文檔可以生成新的段,不同的段可以合併。

Ø  如上圖,具有相同前綴文件的屬同一個段,圖中共兩個段 "_0" 和 "_1"。

Ø  segments.gen 和segments_5 是段的元數據文件,也即它們保存了段的屬性信息。

l  文檔(Document):

Ø  文檔是我們建索引的基本單位,不同的文檔是保存在不同的段中的,一個段可以包含多篇文檔。

Ø  新添加的文檔是單獨保存在一個新生成的段中,隨着段的合併,不同的文檔合併到同一個段中。

l  域(Field):

Ø  一篇文檔包含不同類型的信息,可以分開索引,比如標題,時間,正文,作者等,都可以保存在不同的域裏。

Ø  不同域的索引方式可以不同,在真正解析域的存儲的時候,我們會詳細解讀。

l  詞(Term):

Ø  詞是索引的最小單位,是經過詞法分析和語言處理後的字符串。

 

Lucene 保存了從Index 到Segment 到Document 到Field 一直到Term 的正向信息,也包括了從Term到Document 映射的反向信息,還有其他一些Lucene 特有的信息。

Index –> Segments (segments.gen,segments_N) –> Field(fnm, fdx, fdt) –> Term (tvx, tvd, tvf)

上面的層次結構不是十分的準確,因爲segments.gen 和segments_N 保存的是段(segment)

的元數據信息(metadata),其實是每個Index 一個的,而段的真正的數據信息,是保存在域

(Field)和詞(Term)中的。

段的元數據信息(segments_N)

域(Field)的元數據信息(.fnm),域(Field)的數據信息(.fdt,.fdx)

詞向量(Term Vector)的數據信息(.tvx,.tvd,.tvf)

 

反向信息是索引文件的核心,也即反向索引。

反向索引包括兩部分,左面是詞典(Term Dictionary),右面是倒排表(Posting List)。

在Lucene 中,這兩部分是分文件存儲的,詞典是存儲在tii,tis 中的,倒排表又包括兩部分,一部分是文檔號及詞頻,保存在frq 中,一部分是詞的位置信息,保存在prx 中。

Term Dictionary (tii, tis)

Frequencies (.frq)

Positions (.prx)

                             

圖爲其Lucene索引文件的總體結構,

l  屬於整個索引(Index)的segment.gen,segment_N,其保存的是段(segment)的元數據信息,然後分多個segment保存數據信息,同一個segment 有相同的前綴文件名。

l  對於每一個段,包含域信息,詞信息,以及其他信息(標準化因子,刪除文檔)

l  域信息也包括域的元數據信息,在fnm 中,域的數據信息,在fdx,fdt 中。

l  詞信息是反向信息,包括詞典(tis,tii),文檔號及詞頻倒排表(frq),詞位置倒排表(prx)。





參考鏈接:http://www.cnblogs.com/forfuture1978/archive/2009/12/14/1623594.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章