lucene 全文檢索索引簡介

來源:互聯網
上載者:User

一,資訊檢索的過程簡介

全文檢索索引和資料庫應用最大的不同在於:讓最相關的頭100條結果滿足98%以上使用者的需求
1,構建文本庫
在開發功能前,一個資訊檢索系統需要做些準備工作,首先,必須要構建一個文本資料庫,這個文本資料庫用來儲存所有使用者可能檢索的資訊。在這些資訊的基礎上,確定索引中

的文本類型,文本類型是被系統所認可的一種資訊格式,這種格式應當具有可識別,冗餘程度低的特點。一旦文本模型確定下來後,就不應當對其進行大的行動。
2,建立索引
有了這種文本模型後,就應該根據資料庫內的文本建立索引。索引可以大大的提高資訊檢索的速度。目前,有許多索引的建立方式。採用哪種方式取決於資訊檢索系統的規模。大型資訊檢索系統(百度,google這樣的搜尋)均採用倒排的方式來建立索引。
3,進行搜尋
在文檔建立索引之後,就可以開始對其進行搜尋。這時,通常都是由使用者提交一個檢索請求,請求將被分析,然後利用文本操作進行處理。對於真實的資訊檢索系統,在真正處理請求前,還可以對請求進行一些預先處理,然後再將請求送到後台,並返回使用者需要的資訊。
4,對結果進行過濾
通常,在資訊檢索系統檢索到使用者需要的資訊後,還要做一步操作,就是將資訊以一定的規則進行排序或過濾,再返回給使用者。這一步實際上關乎到終端使用者的體驗。

二,Lucene 索引

   1,使用索引提高檢索速度常用的3種索引方式為
(1)倒排
倒排是一種面向單詞的索引機制。通常它由(關鍵字)和出現情況兩部分組成。對於索引中的每個詞(關鍵字),都跟隨一個列表(位置表),用來記錄單詞在所有文檔中出現的位置。
倒排的特點:
在倒排索引中,關鍵字的數量並非隨著常值內容的增長也線性增長。這是因為無論多大數量的文本資料庫,總能夠規範出一個關鍵字表。這種關鍵字受到實際語言因素的限制,他的增長率在文本資料庫達到一定規模後可以忽略不計。

(2)尾碼數組

(3)簽名檔案

2,索引的Segment

每 個segment代表lucene的一個完整索引段,通常,一個索引中,包含有多個segment,每個segment都有一個統一的首碼,這個首碼是根 據當前索引的document的數量而確立的,首碼名是document 數量轉成36進位後,在前面加上“_”而構成的。

segment的格式
(1).fnm格式
包含了document中的所有field的名稱。
(2).fdx 和.fdt格式
.fdx 和.fdt是綜合使用的兩個檔案,其中.fdt類型檔案用於儲存具有store.YES屬性的field的資料。而.fdx類型檔案則是一個索引,用於儲存document在.fdt中的位置。

(3).tii 與.tis格式
.tis 檔案用於儲存分詞的詞條(Term),而.tii就是它的索引檔案,它標明了每個.tis檔案的詞條的位置。
(4).cfs複合格式
在indexWriter 中有一個屬性:useCompoundFile,它的預設值為True,這個屬性的含義是:是否使用複合索引格式來儲存索引。索引的內容可能非常大,檔案 的數量可能非常多,如果遇到這種情況,系統開啟檔案數量巨大將會極大地耗費系統資源。因此, lucene提供了一種簡單檔案索引格式,也就是所謂的複合索引格式。

   3,索引的最佳化

(1)合并因子mergeFactor

當mergeFactor取比較小的 值時,記憶體中注入的文檔數量少,向磁碟寫入segment的操作比較多,故此時將佔用較少的記憶體,但是索引的建立由於i/o操作頻繁所以會比較慢.而當 mergeFactor取較大的值時,記憶體中駐留的document數量比較多.向磁碟寫入segment的操作較少,故此時將佔用較多的記憶體,但索引的 建立速度比較快.

maxMergeDocs
對索引的合并的最多文檔數量.
mixMergeDocs(maxBufferedDocs)

(2)索引的合并與索引的最佳化

FSDirectory 和 RAMDirectory目錄檔案

FSDirectory 是與檔案系統目錄有關的,而RAMDirectory則是與記憶體相關的。
對 於lucene 來說,兩中目錄都可以作為索引的儲存路徑。在初始化indexwriter的時候需要傳入一個directory類型的對象作為參數之一,當 indexwriter接收這樣的參數時(無論是fsdirectory還是ramdirectory),它都會在指定的位置下將索引進行儲存。但是檔案 系統目錄就會直接將索引寫到磁碟上。而ramdirectory則是在記憶體中一個地區,雖然向其中添加document的過程與使用 fsdirectory一樣,但是由於它是記憶體中的一塊地區,因此如果不將ramdirectory中的記憶體寫入磁碟,當虛擬機器退出後,裡面的內容也會隨 之消失。因此,需要將ramdirectory中的內容轉到fsdirectory中。

     (3)使用indexWriter來合并索引
document 可以被放置在 ramdirectory中,使用它的優點就是索引的速度很快。當document被加入到ramdirectory中後, ramdirectory在邏輯上就是一個完整的索引了,它在邏輯上就應當包括如前所說的所有索引格式的檔案(但是不能被持久的儲存起來)。
indexwriter的addindexs()方法,可以實現索引的合并。addindexs()方法的參數是一個directory類型的數組,因此,可以同時合并多個目錄下的索引,只要分別為這些目錄建立其對應的directory類型的對象就可以了。

     (4)索引的最佳化
indexwriter 的optimize()方法正是為了這個目的而設定的,該方法能夠對當前indexwriter所制定的索引目錄以及其所使用的緩衝目錄下的所有 segment 進行最佳化,使所有的segments合并成一個完整的segement,即整個索引目錄內出現一種檔案首碼。
對於系統的最佳化會有 什麼效能上的損失呢?由於最佳化時需要對已有的索引內的檔案進行操作,因此需要耗費更多的記憶體和磁碟空間,索引最佳化採用的策略是建立新的segment來取 代那些被合并的segements,所以在舊的segement還未被刪除之前,索引內的磁碟空間消耗將會非常大,甚至可能使原來索引的兩倍。同理,在進 行最佳化時的磁碟i/o也會非常多,所以這是一個耗費資源的過程。

    2,索引中刪除文檔

索引的讀取工具 IndexReader,IndexReader中的getVersion方法可以查看當前索引的版本,這個version是索引建立時的精確到毫秒的時 間,IndexReader的indexReader.numDosc()方法,可以查看當前索引內總共有多少個document, IndexReader.document(int)方法可以從索引中取出相應的document.

      (1)使用文檔的id號來刪除特定文檔

在建立索引的過程中 lucene會為每一個加入索引的document賦予一個id號,這個id號將唯一的標識每個文檔.reader.deleteDocument (int),int參數為id 號刪除完畢後需要執行reader.close()方法關閉.使刪除操作寫入索引的deletable檔案中,如果不關閉怎沒有刪除掉,實際上 lucene的刪除機製為資源回收筒機制,刪除操作沒有真正刪除檔案,而是做了一標記,可以進行還原;reader.undeleteall()方法可以協助 實現反刪除(當使用indexwriter對索引optimize一次時,lucene 為每個document重新分配id,這樣那些被標記為已刪除的document真正的被物理刪除了).

     (2)使用field資訊來刪除批量文檔
reader.deleteDocuments (term)該方法是一個能夠大量刪除索引的方法,它刪除索引是按照詞條來進行的. term類是用於表示詞條的一個工具,它能夠將詞條表示成<field,value>(例如: 詞條為<bookename,男>也就是indexReader就會刪除所有在"bookname"這個field中含有"男"這個term 的document).

   3, lucene的同步問題

writer.lock
出現在向索引中添加文檔時,或是將文檔從索引中刪除時,在indexwriter的close()方法被調用時被釋放
commit.lock
主要是與segment合并和讀取的操作相關.

indexModifier類

    三,lucene 的搜尋

     1,indexSearcher進行搜尋

      (1)indexSearcher的簡單使用:
         indexSearcher searcher=new IndexSearcher("索引路徑");
         //構建一個term對象
         Term term=new Term("name","女")
         //構建一個query對象
         Query q =new TermQuery (term);
         //檢索
         Hits hits=searcher.search(q);
         //顯示結果
         for(int i=0;i<hits.lengtth();i++){
             system.out.println(hits.doc(i));
         }

     上面的例子中介紹了indexsearcher的search方法,search方法是整個檢索系統的核心。
indexSearcher有多種重載search方法,這些方法有些在於indexSearcher的父類Search中,有些在本身,Search類實現了一個介面Searchable,該介面提供了可以搜尋的功能。

     (2)Hits對象是搜尋結果的集合 主要有下面幾個方法 [list=1]

length() ,這個方法記錄有多少條結果返回(lazy loading)
doc(n) 返回第n個記錄
id(in) 返回第n個記錄的Document ID
score(n) 第n個記錄的相關度(積分)
由於搜尋的結果一般比較大,從效能上考慮,Hits對象並不會真正把所有的結果全部取回,預設情況下是保留前100個記錄(對於一般的搜尋引擎,100個記錄足夠了).

hits類,在上面的例子中使用了hits,從Hits的doc(int n)方法來研究Hits的工作原理.
doc(int n)方法用於搜尋索引的返回結果中取出相應的文檔。參數n代表結果中的第n個文檔。而doc(int n)方法的第一步就是使用hitDoc(int n)方法從緩衝中取去相應的文檔。
在hitDoc(int n)方法中,會先判斷目前使用者需要取出的文檔是不是已經超過了緩衝的大小。如果是,則先調用getMoreDocs(int min)方法來擴大緩衝,然後再從緩衝中返回需要的文檔。

      2,搜尋結構的評分

     (1)文檔的得分演算法公式:略
         搜尋的結果可以按照分數來排序。

    3,lucene內建的query對象
(1) 內建的query對象主要包括:
(2)TermQuery詞條搜尋 
(3) BooleanQuery布爾搜尋 
(4)RangeQuery範圍搜尋 
(5) PrefixQuery首碼搜尋 
(6) PhraseQuery短語搜尋 
(7)MultiPhraseQuery多短語搜尋 
(8)FuzzyQuery模糊搜尋 
(9)WildcardQuery萬用字元搜尋 
(10)SpanQuery跨度搜尋 
(11)還有第三方提供的Query對象:RegexQuery
上面的這些內建的query對象都是可以用來做根據不同的情況來進行搜尋。(具體略)

     4,Lucene查詢總結:

Lucene 面向全文檢索索引的最佳化在於首次索引檢索後,並不把所有的記錄(Document)具體內容讀取出來,而起只將所有結果中匹配度最高的頭100條結果 (TopDocs)的ID放到結果集緩衝中並返回,這裡可以比較一下資料庫檢索:如果是一個10,000條的資料庫檢索結果集,資料庫是一定要把所有記錄 內容都取得以後再開始返回給應用結果集的。所以即使檢索匹配總數很多,Lucene的結果集佔用的記憶體空間也不會很多。對於一般的模糊檢索應用是用不到這 麼多的結果的,頭100條已經可以滿足90%以上的檢索需求。

如果首批緩衝結果數用完後還要讀取更 後面的結果時Searcher會再次檢索並產生一個上次的搜尋緩衝數大1倍的緩衝,並再重新向後抓取。所以如果構造一個Searcher去查1-120條 結果,Searcher其實是進行了2次搜尋過程:頭100條取完後,緩衝結果用完,Searcher重新檢索再構造一個200條的結果緩衝,依此類推, 400條緩衝,800條緩衝。由於每次Searcher對象消失後,這些緩衝也訪問那不到了,你有可能想將結果記錄緩衝下來,緩衝數盡量保證在100以下 以充分利用首次的結果緩衝,不讓Lucene浪費多次檢索,而且可以分級進行結果緩衝。

Lucene的另外一個特點是在收集結果的過程中將匹配度低的結果自動過濾掉了。這也是和資料庫應用需要將搜尋的結果全部返回不同之處.

四,排序、過濾、分頁

     1, 自然排序

相關度排序是一種最簡單的排序方式,所謂相關度,其實就是文檔的得分。
searcher的explain方法可以每一個文檔的得分是怎麼樣的算出來的,他們的idf,tf,lengthNorm的值得情況。如:searcher.explain(q,hits.id(i).toString());
通過改變boost值來改變文檔的得分在進行相關度排序的時候,如果想人為的增加某個文檔的相關度,使其在搜尋的結構中排在考前的位置上,可以使用boost。
如:索引寫入document 的時,在寫入之前,使用document方法(document.setBoost(3f))
原理:在lucene中,文檔的boost的值一般情況預設為1.0,但當某個文檔的boost值大於1.0後,所有的文檔boost值均會除以這個最大值,以此來為每個文檔擷取一個小於1.0的數作為新的boost值。

2,使用Sort來排序

Sort是lucene內建的一個排序工具,通過它,可以方便地對檢索結果進行排序。
Sort 所提供的排序功能是以field為基礎的,也就是說,最終的排序準則,總是以某個field(或多個)的值為基礎,經過這樣的處理,最終的排序就轉變成對 所有文檔中同一個field(或多個field)的值的排序。方法:Sort(String field,boolean reverse),field表示參照制定的field排序,第二個參數reverse 表示排序的順序,升序還是降序(reverse的預設值為false,升序排序)。
SortField是一個封裝類型,通過它的封裝,可以使Sort類清楚地瞭解要進行排序的field的各種資訊。
建構函式(略)
按文檔的內部id號來排序
如:Hits hits=searcher.search(q,Sort.INDEXORDER);
這個內部需要是在建立索引的時候自動建立的。
按一個或多個Field來排序
如:Sort sort=new Sort();//定義一個Sort對象
SortField f=new SortField("bookno",SortField.INT,false);//定義SortField對象,同時是按照bookno升序來排序的。
sort.setSort(f);
//下面就可以尋找排序了.

3,搜尋的過濾器

lucene 中有兩種過濾器,一個是搜尋時的過濾器,一個是分析的過濾.
搜尋時的過濾是一種減小搜尋範圍的方式.同時也可以實現一種安全機制,即保護某些文檔無法被檢索.
搜尋時的過濾器來自於一個抽象基類Filter,它定義了過濾器的基本行為
public abstract BitSet bits(IndexReader reader);可以看到,這個方法返回一個bitSet類型的對象,filter是一種過濾行為,這種過濾行為在搜尋時的表現就是"視而不見" ,即遇到該文檔時,發現它被"過濾"了,於是就忽略它,BitSet是一種"位集合"隊列,這個隊列中的每個元素都只有兩種取值,即true或 false,這倆種值代表文檔是否被過濾,也就是說,返回結果時,會首先遍曆BitSet盡將那些對應值為true的文檔返回。在BitSet集合中,將 其索引號看作是文檔的內部id。
lucene中內建了幾個Filter,
RangeFilter(範圍過濾,詳細略)
QueryFilter(重要)在結果中查詢
實際應用:在filter的行為可以看到,它總是在搜尋前,首先對索引進行一次遍曆,然後返回一個被商務邏輯處理好的BitSet對象,這種做法無可厚非,但是卻存在很嚴重的效能

問題,這相當於對索引進行了兩次遍曆,這樣會降低效能。
CachingWrapppeFilter將一個Filter作為建構函式的參數傳入,在需要使用原Filter的地方,將這個CachingWrapppeFilter的對象傳入,就可以在原來的filter進行過濾了。
CachingWrapppeFilter的原理: 其中使用了緩衝,在調用的時候,查看緩衝中是否存在處理的結構,如果存在,則直接取出後返回,如果沒有執行被注入的Filter.

     4,Lucene翻頁

(1)依賴於session的翻頁
是指將搜尋的結果儲存於session中,使用者翻頁的時候就從session中取出hits集合,這種方式簡單不需要什麼演算法,一次查詢就可以獲得結果,但是這樣很容易造成伺服器的記憶體溢出。
(2)多次查詢
使用完全無狀態保持的開發方式,即使用者每次翻頁,都對索引進行重新檢索,然後取得當前頁的結果並返回。
(3)緩衝+多次查詢
使 用session方式的查詢有記憶體問題,但是如果採用完全無狀態的查詢方式,又會出現磁碟i/o太過頻繁的問題,以致降低了效率。可以採用在 session或者記憶體中其他空間,另外在緩衝一部分結果,比如後5頁10頁等,這樣當進行翻頁的時候,就可以從緩衝中取出內容,不用重新查索引,如果沒 有緩衝,則重新查詢,更新緩衝。
   (4)緩衝+多次查詢+資料庫
這種方式是在上面的基礎上增加的,如果索引的量很大可以考慮把內容多的東西放在資料庫中,索引中的id和資料庫庫中的id同步。

搜尋時的過濾器可以自己定義.

     5,lucene的分析器

資訊檢索所要處理的主要對象就是資訊,在實際應用中,大部分時候資訊是以一種文本的方式呈現的。而資訊檢索的第一件事,就是要對這種文本進行分析,以便能夠繼續下面的處理。
     (1) 分詞
分詞就是將一段文本拆分成多個詞。(需要注意的一點是,在建立索引時使用的分詞工具,與在分析使用者的檢索請求時使用的分詞工具應當是同一個)。

      (2)分詞器的結構
一 個標準的分詞器是由兩部分組成,一部分是分詞器,被稱為Tokenizer;另一部分是過濾器,被稱為TokenFilter。一個分析器往往是由一個分 詞器和多個過濾器組成。這裡所說的過濾器,與前面所說的檢索時使用的過濾器完全是不同的兩個概念。此處的Filter主要是用於對使用者切出來的詞進行一些 處理,如去掉一些敏感詞、轉換大小寫、轉換單複數等。
lucene內部提供了過濾器,StandarFilter,StopFilter,LowerCaseFilter

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.