Lucene 基礎理論

1. 全文檢索系統與Lucene簡介

1.1 什麼是全文檢索與全文檢索系統

全文檢索是指計算機索引程序經過掃描文章中的每個詞,對每個詞創建一個索引,指明該詞在文章中出現的次數和位置,當用戶查詢時,檢索程序就根據事先創建的索引進行查找,並將查找的結果反饋給用戶的檢索方式。這個過程相似於經過字典中的檢索字表查字的過程。java

全文檢索的方法主要分爲按字檢索和按詞檢索兩種。按字檢索是指對於文章中的每個字都創建索引,檢索時將詞分解爲字的組合。對於各類不一樣的語言而言,字有不一樣的含義,好比英文中字與詞其實是合一的,而中文中字與詞有很大分別。按詞檢索指對文章中的詞,即語義單位創建索引,檢索時按詞檢索,而且能夠處理同義項等。英文等西方文字因爲按照空白切分詞,所以實現上與按字處理相似,添加同義處理也很容易。中文等東方文字則須要切分字詞,以達到按詞索引的目的,關於這方面的問題,是當前全文檢索技術尤爲是中文全文檢索技術中的難點,在此不作詳述。程序員

全文檢索系統是按照全文檢索理論創建起來的用於提供全文檢索服務的軟件系統。通常來講,全文檢索須要具有創建索引和提供查詢的基本功能,此外現代的全文檢索系統還須要具備方便的用戶接口、面向WWW[1]的開發接口、二次應用開發接口等等。功能上,全文檢索系統核心具備創建索引、處理查詢返回結果集、增長索引、優化索引結構等等功能,外圍則由各類不一樣應用具備的功能組成。結構上,全文檢索系統核心具備索引引擎、查詢引擎、文本分析引擎、對外接口等等,加上各類外圍應用系統等等共同構成了全文檢索系統。圖1.1展現了上述全文檢索系統的結構與功能。算法

clip_image002

在上圖中,咱們看到:全文檢索系統中最爲關鍵的部分是全文檢索引擎,各類應用程序都須要創建在這個引擎之上。一個全文檢索應用的優異程度,根本上由全文檢索引擎來決定。所以提高全文檢索引擎的效率便是咱們提高全文檢索應用的根本。另外一個方面,一個優異的全文檢索引擎,在作到效率優化的同時,還須要具備開放的體系結構,以方便程序員對整個系統進行優化改造,或者是添加原有系統沒有的功能。好比在當今多語言處理的環境下,有時須要給全文檢索系統添加處理某種語言或者文本格式的功能,好比在英文系統中添加中文處理功能,在純文本系統中添加XML或者HTML格式的文本處理功能,系統的開放性和擴充性就十分的重要。apache

1.2 什麼是Lucene

Lucene是apache軟件基金會jakarta項目組的一個子項目,是一個開放源代碼的全文檢索引擎工具包,即它不是一個完整的全文檢索引擎,而是一個全文檢索引擎的架構,提供了完整的查詢引擎和索引引擎,部分文本分析引擎(英文與德文兩種西方語言)。Lucene的目的是爲軟件開發人員提供一個簡單易用的工具包,以方便的在目標系統中實現全文檢索的功能,或者是以此爲基礎創建起完整的全文檢索引擎。編程

Lucene的原做者是Doug Cutting,他是一位資深全文索引/檢索專家,曾經是V-Twin搜索引擎的主要開發者,後在Excite擔任高級系統架構設計師,目前從事於一些Internet底層架構的研究。早先發布在做者本身的http://www.lucene.com/,後來發佈在SourceForge,2001年年末成爲apache軟件基金會jakarta的一個子項目:http://jakarta.apache.org/lucene/數組

1.3 Lucene的應用、特色及優點

做爲一個開放源代碼項目,Lucene從問世以後,引起了開放源代碼社羣的巨大反響,程序員們不只使用它構建具體的全文檢索應用,並且將之集成到各類系統軟件中去,以及構建Web應用,甚至某些商業軟件也採用了Lucene做爲其內部全文檢索子系統的核心。apache軟件基金會的網站使用了Lucene做爲全文檢索的引擎,IBM的開源軟件eclipse的2.1版本中也採用了Lucene做爲幫助子系統的全文索引引擎,相應的IBM的商業軟件Web Sphere中也採用了Lucene。Lucene以其開放源代碼的特性、優異的索引結構、良好的系統架構得到了愈來愈多的應用。服務器

Lucene做爲一個全文檢索引擎,其具備以下突出的優勢:網絡

(1)索引文件格式獨立於應用平臺。Lucene定義了一套以8位字節爲基礎的索引文件格式,使得兼容系統或者不一樣平臺的應用可以共享創建的索引文件。數據結構

(2)在傳統全文檢索引擎的倒排索引的基礎上,實現了分塊索引,可以針對新的文件創建小文件索引,提高索引速度。而後經過與原有索引的合併,達到優化的目的。架構

(3)優秀的面向對象的系統架構,使得對於Lucene擴展的學習難度下降,方便擴充新功能。

(4)設計了獨立於語言和文件格式的文本分析接口,索引器經過接受Token流完成索引文件的創立,用戶擴展新的語言和文件格式,只須要實現文本分析的接口。

(5)已經默認實現了一套強大的查詢引擎,用戶無需本身編寫代碼即便系統可得到強大的查詢能力,Lucene的查詢實現中默認實現了布爾操做、模糊查詢(Fuzzy Search)、分組查詢等等。

面對已經存在的商業全文檢索引擎,Lucene也具備至關的優點:

首先,它的開發源代碼發行方式(遵照Apache Software License),在此基礎上程序員不只僅能夠充分的利用Lucene所提供的強大功能,並且能夠深刻細緻的學習到全文檢索引擎製做技術和麪相對象編程的實踐,進而在此基礎上根據應用的實際狀況編寫出更好的更適合當前應用的全文檢索引擎。在這一點上,商業軟件的靈活性遠遠不及Lucene。其次,Lucene秉承了開放源代碼一向的架構優良的優點,設計了一個合理而極具擴充能力的面向對象架構,程序員能夠在Lucene的基礎上擴充各類功能,好比擴充中文處理能力,從文本擴充到HTML、PDF等等文本格式的處理,編寫這些擴展的功能不只僅不復雜,並且因爲Lucene恰當合理的對系統設備作了程序上的抽象,擴展的功能也能輕易的達到跨平臺的能力。最後,轉移到apache軟件基金會後,藉助於apache軟件基金會的網絡平臺,程序員能夠方便的和開發者、其它程序員交流,促成資源的共享,甚至直接得到已經編寫完備的擴充功能。最後,雖然Lucene使用Java語言寫成,可是開放源代碼社區的程序員正在不懈的將之使用各類傳統語言實現(例如.net framework),在遵照Lucene索引文件格式的基礎上,使得Lucene可以運行在各類各樣的平臺上,系統管理員能夠根據當前的平臺適合的語言來合理的選。

 

2. Lucene系統結構分析

2.1 系統結構組織

Lucene做爲一個優秀的全文檢索引擎,其系統結構具備強烈的面向對象特徵。首先是定義了一個與平臺無關的索引文件格式,其次經過抽象將系統的核心組成部分設計爲抽象類,具體的平臺實現部分設計爲抽象類的實現,此外與具體平臺相關的部分好比文件存儲也封裝爲類,通過層層的面向對象式的處理,最終達成了一個低耦合高效率,容易二次開發的檢索引擎系統。

如下將討論Lucene系統的結構組織,並給出系統結構與源碼組織圖:

clip_image004

從圖中咱們清楚的看到,Lucene的系統由基礎結構封裝、索引核心、對外接口三大部分組成。其中直接操做索引文件的索引核心又是系統的重點。Lucene的將全部源碼分爲了7個模塊(在java語言中以包即package來表示),各個模塊所屬的系統部分也如上圖所示。須要說明的是org.apache.lucene.queryPaser是作爲org.apache.lucene.search的語法解析器存在,不被系統以外實際調用,所以這裏沒有看成對外接口看待,而是將之獨立出來。

從面象對象的觀點來考察,Lucene應用了最基本的一條程序設計準則:引入額外的抽象層以下降耦合性。首先,引入對索引文件的操做org.apache.lucene.store的封裝,而後將索引部分的實現創建在(org.apache.lucene.index)其之上,完成對索引核心的抽象。在索引核心的基礎上開始設計對外的接口org.apache.lucene.search與org.apache.lucene.analysis。在每個局部細節上,好比某些經常使用的數據結構與算法上,Lucene也充分的應用了這一條準則。在高度的面向對象理論的支撐下,使得Lucene的實現容易理解,易於擴展。

Lucene在系統結構上的另外一個特色表現爲其引入了傳統的客戶端服務器結構之外的的應用結構。Lucene能夠做爲一個運行庫被包含進入應用自己中去,而不是作爲一個單獨的索引服務器存在。這天然和Lucene開放源代碼的特徵分不開,可是也體現了Lucene在編寫上的原本意圖:提供一個全文索引引擎的架構,而不是實現。

2.2 數據流分析

瞭解數據流分析的重要性:

理解Lucene系統結構的另外一個方式是去探討其中數據流的走向,並以此摸清楚Lucene系統內部的調用時序。在此基礎上,咱們可以更加深刻的理解Lucene的系統結構組織,以方便之後在Lucene系統上的開發工做。這部分的分析,是深刻Lucene系統的鑰匙,也是進行重寫的基礎。

Lucene系統中的主要的數據流以及它們之間的關係圖:

clip_image006

圖2.2很好的代表了Lucene在內部的數據流組織狀況,而且沿着數據流的方向咱們也能夠對與Lucene內部的執行時序有一個清楚的瞭解。如今將圖中的涉及到的流的類型與各個邏輯對應系統的相關部分的關係說明一下。

圖中共存在4種數據流,分別是文本流、token流、字節流與查詢語句對象流。文本流表示了對於索引目標和交互控制的抽象,即用文本流表示了將要索引的文件,用文本流向用戶輸出信息;在實際的實現中,Lucene中的文本流採用了UCS-2做爲編碼,以達到適應多種語言文字的處理的目的。Token流是Lucene內部所使用的概念,是對傳統文字中的詞的概念的抽象,也是Lucene在創建索引時直接處理的最小單位;簡單的講Token就是一個詞和所在域值的組合,後面在敘述文件格式時也將繼續涉及到token,這裏不詳細展開。字節流則是對文件抽象的直接操做的體現,經過固定長度的字節(Lucene定義爲8比特位長,後面文件格式將詳細敘述)流的處理,將文件操做解脫出來,也作到了與平臺文件系統的無關性。查詢語句對象流則是僅僅在查詢語句解析時用到的概念,它對查詢語句抽象,經過類的繼承結構反映查詢語句的結構,將之傳送到查找邏輯來進行查找的操做。

圖中的涉及到了多種邏輯,基本上直接對應於系統某一模塊,可是也有跨模塊調用的問題發生,這是由於Lucene的重用程度很是好,所以不少實現直接調用了之前的工做成果,這在某種程度上實際上是增強了模塊耦合性,可是也是爲了不繫統的過於龐大和沒必要要的重複設計的一種折衷體現。詞法分析邏輯對應於org.apache.lucene.analysis部分。查詢語句語法分析邏輯對應於org.apache.lucene.queryParser部分,而且調用了org.apache.lucene.analysis的代碼。查詢結束以後向評分排序邏輯輸出token流,繼而由評分排序邏輯處理以後給出文本流的結果,這一部分的實現也包含在了org.apache.lucene.search中。索引構建邏輯對應於org.apache.lucene.index部分。索引查找邏輯則主要是org.apache.lucene.search,可是也大量的使用了org.apache.lucene.index部分的代碼和接口定義。存儲抽象對應於org.apache.lucene.store。沒有提到的模塊則是作爲系統公共基礎設施存在。

2.3 基於Lucene的應用開發

首先,咱們須要的是按照目標語言的詞法結構來構建相應的詞法分析邏輯,實現Lucene在org.apache.lucene.analysis中定義的接口,爲Lucene提供目標系統所使用的語言處理能力。Lucene默認的已經實現了英文和德文的簡單詞法分析邏輯(按照空格分詞,並去除經常使用的語法詞,如英語中的is,am,are等等)。在這裏,主要須要參考實現的接口在org.apache.lucene.analysis中的Analyzer.java和Tokenizer.java中定義,Lucene提供了不少英文規範的實現樣本,也能夠作爲實現時候的參考資料。其次,須要按照被索引的文件的格式來提供相應的文本分析邏輯,這裏是指除開詞法分析以外的部分,好比HTML文件,一般須要把其中的內容按照所屬於域分門別類加入索引,這就須要從org.apache.lucene.document中定義的類document繼承,定義本身的HTMLDocument類,而後就能夠將之交給org.apache.lucene.index模塊來寫入索引文件。完成了這兩步以後,Lucene全文檢索引擎就基本上完備了。這個過程能夠用下圖表示:

clip_image008

下面是使用java語言開發,Lucene系統可以方便的嵌入到整個系統中去,做爲一個API集來調用。這個過程十分簡單,如下即是一個示例程序,配合註釋理解起來很容易。

clip_image009

2.4 Lucene索引文件格式

首先在Lucene的文件格式中,以字節爲基礎,定義了以下的數據類型:

 3.1 Lucene文件格式中定義的數據類型

數據類型

所佔字節長度(字節)

說明

Byte

1

基本數據類型,其餘數據類型以此爲基礎定義

UInt32

4

32位無符號整數,高位優先

UInt64

8

64位無符號整數,高位優先

VInt

不定,最少1字節

動態長度整數,每字節的最高位代表還剩多少字節,每字節的低七位代表整數的值,高位優先。能夠認爲值能夠爲無限大。其示例以下

字節1

字節2

字節3

0

00000000

   

1

00000001

   

2

00000010

   

127

01111111

   

128

10000000

00000001

 

129

10000001

00000001

 

130

10000010

00000001

 

16383

10000000

10000000

00000001

16384

10000001

10000000

00000001

16385

10000010

10000000

00000001

 

Chars

不定,最少1字節

採用UTF-8編碼[20]的Unicode字符序列

String

不定,最少2字節

由VInt和Chars組成的字符串類型,VInt表示Chars的長度,Chars則表示了String的值

以上的數據類型就是Lucene索引文件格式中用到的所有數據類型,因爲它們都以字節爲基礎定義而來,所以保證了是平臺無關,這也是Lucene索引文件格式平臺無關的主要緣由。接下來咱們看看Lucene索引文件的概念組成和結構組成。

clip_image010

以上就是Lucene的索引文件的概念結構。Lucene索引index由若干段(segment)組成,每一段由若干的文檔(document)組成,每個文檔由若干的域(field)組成,每個域由若干的項(term)組成。項是最小的索引概念單位,它直接表明了一個字符串以及其在文件中的位置、出現次數等信息。域是一個關聯的元組,由一個域名和一個域值組成,域名是一個字串,域值是一個項,好比將「標題」和實際標題的項組成的域。文檔是提取了某個文件中的全部信息以後的結果,這些組成了段,或者稱爲一個子索引。子索引能夠組合爲索引,也能夠合併爲一個新的包含了全部合併項內部元素的子索引。咱們能夠清楚的看出,Lucene的索引結構在概念上即爲傳統的倒排索引結構。

從概念上映射到結構中,索引被處理爲一個目錄(文件夾),其中含有的全部文件即爲其內容,這些文件按照所屬的段不一樣分組存放,同組的文件擁有相同的文件名,不一樣的擴展名。此外還有三個文件,分別用來保存全部的段的記錄、保存已刪除文件的記錄和控制讀寫的同步,它們分別是segments,deletable和lock文件,都沒有擴展名。每一個段包含一組文件,它們的文件擴展名不一樣,可是文件名均爲記錄在文件segments中段的名字。讓咱們看以下的結構圖3.2:

clip_image012

每一個段的文件中,主要記錄了兩大類的信息:域集合與項集合。這兩個集合中所含有的文件在圖3.2中均有代表。因爲索引信息是靜態存儲的,域集合與項集合中的文件組採用了一種相似的存儲辦法:一個小型的索引文件,運行時載入內存;一個對應於索引文件的實際信息文件,能夠按照索引中指示的偏移量隨機訪問;索引文件與信息文件在記錄的排列順序上存在隱式的對應關係,即索引文件中按照「索引項一、索引項2…」排列,則信息文件則也按照「信息項一、信息項2…」排列。好比在圖3.2所示文件中,segment1.fdx與segment1.fdt之間,segment1.tii與segment1.tis、segment1.prx、segment1.frq之間,都存在這樣的組織關係。而域集合與項集合之間則經過域的在域記錄文件(好比segment1.fnm)中所記錄的域記錄號維持對應關係,在圖3.2中segment1.fdx與segment1.tii中就是經過這種方式保持聯繫。這樣,域集合和項集合不只僅聯繫起來,並且其中的文件之間也相互聯繫起來。此外,標準化因子文件和被刪除文檔文件則提供了一些程序內部的輔助設施(標準化因子用在評分排序機制中,被刪除文檔是一種僞刪除手段)。這樣,整個段的索引信息就經過這些文檔有機的組成。

 

2.5 一些公用的基礎類

基礎結構封裝,或者基礎類,由org.apache.lucene.util和org.apache.lucene.document兩個包組成,前者定義了一些常量和優化過的經常使用的數據結構和算法,後者則是對於文檔(document)和域(field)概念的一個類定義。如下咱們用列表的方式來分析這些封裝類,指出其要點;

 

 3.2 基礎類包org.apache.lucene.util

 

說明

Arrays

一個關於數組的排序方法的靜態類,提供了優化的基於快排序的排序方法sort

BitVector

C/C++語言中位域的java實現品,可是加入了序列化能力

Constants

常量靜態類,定義了一些常量

PriorityQueue

一個優先隊列的抽象類,用於後面實現各類具體的優先隊列,提供常數時間內的最小元素訪問能力,內部實現機制是哈析表和堆排序算法

 

 3.3 基礎類包org.apache.lucene.document

 

說明

Document

是文檔概念的一個實現類,每一個文檔包含了一個域表(fieldList),並提供了一些實用的方法,好比多種添加域的方法、返回域表的迭代器的方法

Field

是域概念的一個實現類,每一個域包含了一個域名和一個值,以及一些相關的屬性

DateField

提供了一些輔助方法的靜態類,這些方法將java中Date和Time數據類型和String相互轉化

 

2.6 存儲抽象

org.apache.lucene.store包:存儲抽象是惟一可以直接對索引文件存取的包,所以其主要目的是抽象出和平臺文件系統無關的存儲抽象,提供諸如目錄服務(增、刪文件)、輸入流和輸出流。在分析其實現以前,首先咱們看一下UML圖;

clip_image013

 3.3 存儲抽象實現UML圖(一)

clip_image014

 3.4 存儲抽象實現UML圖(二)

clip_image015

 3.4 存儲抽象實現UML圖(三)

圖3.2到3.4展現了整個org.apache.lucene.store中主要的繼承體系。共有三個抽象類定義:Directory、InputStream和OutputStrem,構成了一個完整的基於抽象文件系統的存取體系結構,在此基礎上,實做出了兩個實現品:(FSDirectory,FSInputStream,FSOutputStream)和(RAMDirectory,RAMInputStream和RAMOutputStream)。前者是以實際的文件系統作爲基礎實現的,後者則是創建在內存中的虛擬文件系統。前者主要用來永久的保存索引文件,後者的做用則在於索引操做時是在內存中創建小的索引,而後一次性的輸出合併到文件中去,這一點咱們在後面的索引邏輯部分可以看到。此外,還定以了org.apache.lucene.store.lock和org.apache.lucene.store.with兩個輔助內部實現的類用在實現Directory方法的makeLock的時候,以在鎖定索引讀寫以前來讓客戶程序作一些準備工做。

(FSDirectory,FSInputStream,FSOutputStream)的內部實現依託於java語言中的io類庫,只是簡單的作了一個外部邏輯的包裝。這固然要歸功於java語言所提供的跨平臺特性,同時也帶了一些隱患:文件存取的效率提高須要依耐於文件類庫的優化。若是須要繼續優化文件存取的效率,應該還提供一個文件與目錄的抽象,以根據各類文件系統或者文件類型來提供一個優化的機會。固然,這是應用開發者所不須要關係的問題。

(RAMDirectory,RAMInputStream和RAMOutputStream)的內部實現就比較直接了,直接採用了虛擬的文件RAMFile類(定義於文件RAMDirectory.java中)來表示文件,目錄則看做一個String與RAMFile對應的關聯數組。RAMFile中採用數組來表示文件的存儲空間。在此的基礎上,完成各項操做的實現,就造成了基於內存的虛擬文件系統。由於在實際使用時,並不會牽涉到很大字節數量的文件,所以這種設計是簡單直接的,也是高效率的。

 

 

3. Lucene索引構建邏輯模塊分析

3.1對象體系與UML圖

1. 項(Term

項(Term):包括概念所實際涉及的類、永久化類。項(Term)所表示的是一個字符串,它擁有域、頻數和位置信息等等屬性。所以,Lucene中設計了兩個類來表示這個概念,以下圖

clip_image016

 4.1 UML圖(-)

 

上圖中,有意的突出了類Term和TermInfo中的數據成員,由於它反映了對於項(Term)這個概念的具體表示。同時上圖中也同時列出了用於永久化項(Term)的代理類TermInfosWriter和TermInfosReader,它們完成永久化的功能,須要注意的是,TermInfosReader內部使用了數組indexTerms和indexInfos來存儲一系列項;而TermInfosWriter則是一個相似於鏈表的結構,經過一個other指向下一個TermInfosWriter,每個TermInfosWriter只負責自己那個lastTerm和lastTi的永久化工做。這是一個設計上的技巧,經過批量讀取(或者稱爲緩衝的方式)來得到讀入時候的效率優化;而經過一個鏈表式的、各負其責的方式,來得到寫出時候的設計簡化。

項(term)這部分的設計中,還有一些重要的接口和類:

clip_image017

圖 4.2 UML圖(二)

 

圖4.2中,咱們看到三個類:TermEnum、TermDocs與TermPositions,第一個是抽象類,後兩個都是接口。TermEnum的設計主要用在後面Segment和Document等等的實現中,以提供枚舉其中每個項(Term)的能力。TermDocs是一個接口,用來繼承以提供返回<document, frequency>值對的能力,經過這個接口就能夠得到某個項(Term)在某個文檔中出現的頻數。TermPositions則是在TermDocs上的擴展,將項(Term)在文檔中的位置信息也表示出來。TermDocs(TermPositions)接口的使用方式相似於java中的Enumration接口,即經過next方法跳轉,經過doc,freq等方法得到當前的屬性值。

2. 域(Field

因爲Field的基本概念在org.apache.lucene.document中已經作了定義,所以在這部分主要是針對項文件(.fnm文件、.fdx文件、.fdt文件)所須要的信息再來設計一些類。

clip_image018

圖 4.3 UML圖(三)

 

圖 4.3中展現的,就是表示與域(Field)所關聯的屬性信息的類。其中isIndexed表示的這個域的值是否被索引過,即值是否被分詞而後索引;另外兩個屬性所表示的意思則很明顯:一個是域的名字,一個是域的編號。

關於域表和存取邏輯的UML圖:

clip_image019

FieldInfos即爲域表的概念表示,內部採用了冗餘的方式以獲取在經過域的編號訪問或者經過域的名字來訪問時候的高效率。FieldsReader與FieldsWriter則分別是寫出和讀入的代理類。在功能和實現上,這兩個類都比較簡單。

3. 文檔(document)

文檔(document)一樣也是在org.apache.lucene.document中定義過的結構。因爲對於這部分比較重要,咱們也來看看其UML圖:

clip_image020

圖 4.5 UML圖(五)

在圖4.5中咱們看到,Document的設計基本上沿用了鏈表的處理方法。左邊的Document類做爲一個數據外包類,用來提供對於內部結構DocumentFieldList的增長刪除訪問操做等等。DocumentFieldList纔是實際上的數據存儲單位,它用了鏈表的處理方法,直接指向一個當前的Field對象和下一個DocumentFieldList對象,這個與前面的相似。爲了可以逐個訪問鏈表中的節點,還設計了DocumentFieldEnumeration枚舉類。

clip_image021

圖 4.6 UML圖(六)

 

 

實際上定義於org.apache.lucene.index中的有關於Document的就是永久化的代理類。在圖4.6中給出了其UML圖。須要說明的是爲何沒有出現讀入的方法:這個方法已經隱含在圖4.5中Document類中的add方法中了,結合圖2.4中的程序代碼段,咱們就可以清楚的理解這種設計。

4. 段(segment

段(Segment)這一部分設計的比較特殊,在實現簡單的對象結構之上,還特地的設計了用於段之間合併的類。接下來,咱們仍然採起對照UML分析的方式逐個敘述。接下來咱們看Lucene中如何表示段這個概念。

clip_image022

圖 4.7 UML圖(七)

Lucene定義了一個類SegmentInfo用來表示每個段(Segment)的信息,包括名字(name)、含有的文檔的數目(docCount)和段所位於的目錄的位置(dir)。根據索引文件中的段的意義,有了這三點,就能惟一肯定一個段了。SegmentInfos這個類則是用來表示一個段的鏈表(從標準的java.util.Vector繼承而來),實際上,也就是索引(index)的意思了。須要注意的是,這裏並無在SegmentInfo中安插一個文檔(document)的鏈表。這樣作的緣由牽涉到Lucene內部對於文檔(至關於一個被索引文件)的處理;Lucene內部採用了賦予文檔編號,給域賦值的方式來處理文檔,即加入的文檔順次編號,之後用文檔號表示文檔,而路徑信息,文件名字等等在之後索引查找須要的屬性,都做爲域存儲下來;所以SegmentInfo中並無另外存儲一個文檔(document)的鏈表,對於這些的寫出和讀入,則交給了永久化的代理類來作。

clip_image023

圖 4.8 UML圖(八)

圖4.8給出了負責段(segment)的讀入操做的代理類,而負責段(segment)的寫出操做也一樣沒有定義,這些操做都直接實如今了類IndexWriter類中。段的操做一樣採用了以前的數組或者說是緩衝的處理方式。

針對前面項(term)那部分定義的幾個接口,段(segment)這部分也須要作相應的接口實現,由於提供直接遍歷訪問段中的各個項的能力對於檢索來講,無疑是十分重要的。即這部分的設計,實際上都是在爲了檢索在服務。

clip_image024

圖 4.9 UML圖(九)

clip_image025

圖 4.10 UML圖(十)

圖4.9和圖4.10分別展現了前面項(term)那裏定義的接口是如何在這裏經過繼承實現的。Lucene在處理這部分的時候,也是分紅兩部分(Segment與Segments開頭的類)來實現,並且很合理的運用了數組的技法,以及注意了繼承重用。可是細化到局部,終歸是比較簡單的按照語義來得到結果而已了。

Lucene爲了兼顧創建索引時的效率和讀取索引查找的速度,引入了分小段創建索引的方式,即每一次批量創建索引時,先在內存中的虛擬文件系統中爲每個文檔單獨創建一個段,而後在輸出的時候將這些段合併以後輸出成爲索引文件,這時僅僅存在一個段。屢次創建的索引後,若是想優化索引文件,也可採起合併段的方法,將索引中的段合併成爲一個段。咱們來看一下在IndexWriter類中相應的方法的實現,來了解一下這中創建索引的實現。

clip_image026

 

在mergeSegments函數中,將用到幾個重要的類結構,它們記錄了合併時候的一些重要信息,完成合並時候的工做。接下來,咱們來看這幾個類的UML圖:

clip_image027

圖 4.12 UML圖(十一)

 

從圖4.12中,咱們看到Lucene設計一個類SegmentMergeInfo用來保存每個被合併的段的信息,也保存可以訪問其內部的接口句柄,也就是說合並時的操做使用這個類做爲對被合併的段的操做代理。類SegmentMergeQueue則設計爲org.apache.lucene.util.PriorityQueue的子類,作爲SegmentMergeInfo的容器類,並且附帶可以自動排序。SegmentMerger是主要進行操做的類,主要完成合並各個數據項的問題。

5. IndexReader類與IndexWirter類

最後剩下的,就是整個索引邏輯部分的使用接口類了。外界經過這兩個類以及文檔(document)類的構造函數調用之,好比圖2.4中的代碼示例所示。下面咱們來看一下這部分最後兩個類的UML圖:

clip_image028

圖 4.13 UML圖(十二)

 

IndexWriter的設計與IndexReader的設計很不相同,前者是一個實現類,然後者是一個抽象類,帶有沒有實現的接口。IndexWriter的主要做用就是接收新加入的文檔(document),而後在內部爲之生成相應的小段,最後再合併並向索引文件中輸出,圖4.11中已經給出了一些實現的代碼。因爲Lucene在面向對象上封裝的努力,經過各個構造函數就已經完成了對於各個概念的構造過程,剩下部分的代碼主要是依據各個數組或者是鏈表中的信息,逐個逐個的將信息寫出到相應的文件中去了。IndexReader部分則只是作了接口設計,沒有具體的實現,這個和本部分所完成的主要功能有關:索引構建邏輯。設計這個抽象類的目的是,預先完成一些函數,爲之後的檢索(search)部分的各類形式的IndexReader鋪平道路,也是利用了在同一個包內能夠方便訪問其它類的保護變量這個java語言的限制。

3.2 數據流邏輯

從宏觀上明白一個系統的設計,理清楚其中的運行規律,最好的方式應該是經過數據流圖。在分析了各個位於索引構建邏輯部分的類的設計以後,咱們接下來就經過分析數據流圖的方式來總結一下。可是因爲以前提到的緣由:索引讀入部分在這一部分並無徹底實現,因此咱們在數據流圖中主要給出的是索引構建的數據流圖。

clip_image030

對於圖4.14中所描述的內容,結合Lucene源代碼中的一些文件看,可以加深理解。準備階段能夠參考demo文件夾中的org.apache.lucene.demo.IndexFiles類和java文件夾中的org.apache.lucene.document文件包。索引構建階段的主要源碼位於java文件夾中org.apache.lucene.index.IndexWriter類,所以這部分能夠結合這個類的實現來看。至於內存文件系統,比較複雜,可是這時的邏輯相對簡單,所以也不難理解。

上面的數據流圖十分清楚的勾畫除了整個索引構建邏輯這部分的設計:經過層層嵌套的類結構,在構建時候即分步驟有計劃的生成了索引結構,將之存儲到內存中的文件系統中,而後經過對內存中的文件系統優化合並輸出到實際的文件系統中。

相關文章
相關標籤/搜索