Lucene3.5自学4--建索引相关知识总结
Lucene簡單介紹(該部分摘自網絡)
Lucene是一個高效的,基于Java的全文檢索庫。
所以在了解Lucene之前要費一番工夫了解一下全文檢索。
那么什么叫做全文檢索呢?這要從我們生活中的數據說起。
我們生活中的數據總體分為兩種:結構化數據和非結構化數據。
- 結構化數據:指具有固定格式或有限長度的數據,如數據庫,元數據等。
- 非結構化數據:指不定長或無固定格式的數據,如郵件,word文檔等。
當然有的地方還會提到第三種,半結構化數據,如XML,HTML等,當根據需要可按結構化數據來處理,也可抽取出純文本按非結構化數據來處理。
非結構化數據又一種叫法叫全文數據。
?
按照數據的分類,搜索也分為兩種:
- 對結構化數據的搜索:如對數據庫的搜索,用SQL語句。再如對元數據的搜索,如利用windows搜索對文件名,類型,修改時間進行搜索等。
- 對非結構化數據的搜索:如利用windows的搜索也可以搜索文件內容,Linux下的grep命令,再如用Google和百度可以搜索大量內容數據。
對非結構化數據也即對全文數據的搜索主要有兩種方法:
一種是順序掃描法(Serial?Scanning):所謂順序掃描,比如要找內容包含某一個字符串的文件,就是一個文檔一個文檔的看,對于每一個文檔,從頭看到尾,如果此文檔包含此字符串,則此文檔為我們要找的文件,接著看下一個文件,直到掃描完所有的文件。如利用windows的搜索也可以搜索文件內容,只是相當的慢。如果你有一個80G硬盤,如果想在上面找到一個內容包含某字符串的文件,不花他幾個小時,怕是做不到。Linux下的grep命令也是這一種方式。大家可能覺得這種方法比較原始,但對于小數據量的文件,這種方法還是最直接,最方便的。但是對于大量的文件,這種方法就很慢了。
有人可能會說,對非結構化數據順序掃描很慢,對結構化數據的搜索卻相對較快(由于結構化數據有一定的結構可以采取一定的搜索算法加快速度),那么把我們的非結構化數據想辦法弄得有一定結構不就行了嗎?
這種想法很天然,卻構成了全文檢索的基本思路,也即將非結構化數據中的一部分信息提取出來,重新組織,使其變得有一定結構,然后對此有一定結構的數據進行搜索,從而達到搜索相對較快的目的。
這部分從非結構化數據中提取出的然后重新組織的信息,我們稱之索引。
這種說法比較抽象,舉幾個例子就很容易明白,比如字典,字典的拼音表和部首檢字表就相當于字典的索引,對每一個字的解釋是非結構化的,如果字典沒有音節表和部首檢字表,在茫茫辭海中找一個字只能順序掃描。然而字的某些信息可以提取出來進行結構化處理,比如讀音,就比較結構化,分聲母和韻母,分別只有幾種可以一一列舉,于是將讀音拿出來按一定的順序排列,每一項讀音都指向此字的詳細解釋的頁數。我們搜索時按結構化的拼音搜到讀音,然后按其指向的頁數,便可找到我們的非結構化數據——也即對字的解釋。
這種先建立索引,再對索引進行搜索的過程就叫全文檢索(Full-text?Search)。
?
下面這幅圖來自《Lucene?in?action》,但卻不僅僅描述了Lucene的檢索過程,而是描述了全文檢索的一般過程。
??
全文檢索大體分兩個過程,索引創建(Indexing)和搜索索引(Search)。
- 索引創建:將現實世界中所有的結構化和非結構化數據提取信息,創建索引的過程。
- 搜索索引:就是得到用戶的查詢請求,搜索創建的索引,然后返回結果的過程
詳情可以參考http://forfuture1978.iteye.com/blog/546771?
Lucene3.5?建立索引總結
Lucene的索引結構是有層次結構的,主要分以下幾個層次:
- 索引(Index):
- 在Lucene中一個索引是放在一個文件夾中的。
- 如上圖,同一文件夾中的所有的文件構成一個Lucene索引。
- 段(Segment):
- 一個索引可以包含多個段,段與段之間是獨立的,添加新文檔可以生成新的段,不同的段可以合并。
- 如上圖,具有相同前綴文件的屬同一個段,圖中共兩個段?"_0"?和?"_1"。
- segments.gen和segments_5是段的元數據文件,也即它們保存了段的屬性信息。
- 文檔(Document):
- 文檔是我們建索引的基本單位,不同的文檔是保存在不同的段中的,一個段可以包含多篇文檔。
- 新添加的文檔是單獨保存在一個新生成的段中,隨著段的合并,不同的文檔合并到同一個段中。
- 域(Field):
- 一篇文檔包含不同類型的信息,可以分開索引,比如標題,時間,正文,作者等,都可以保存在不同的域里。
- 不同域的索引方式可以不同,在真正解析域的存儲的時候,我們會詳細解讀。
- 詞(Term):
- 詞是索引的最小單位,是經過詞法分析和語言處理后的字符串。
(一)Field相當于數據庫表的字段
1.其中的Field(字段或域)的構造方法有以下幾種
Field(String?name,?boolean?internName,?String?value,?Field.Store?store,?Field.Index?index,?Field.TermVector?termVector)?常用的
Field(String?name,?byte[]?value)?
Field(String?name,?byte[]?value,?int?offset,?int?length)?
Field(String?name,?Reader?reader)?
Field(String?name,?Reader?reader,?Field.TermVector?termVector)?該Reader當在值是從文件中讀取很長的數據時用,單身磁盤I/O操作太多效率太低了
Field(String?name,?String?value,?Field.Store?store,?Field.Index?index)
Field(String?name,?String?value,?Field.Store?store,?Field.Index?index,?Field.TermVector?termVector)?
Field(String?name,?TokenStream?tokenStream)?
Field(String?name,?TokenStream?tokenStream,?Field.TermVector?termVector)?
l?其中?Field.Store表示是否存儲,值分別為YES?,NO
l??Field.Index?表示是否索引(允許檢索),它已經包括了是否分詞,其值有以下幾種
ANALYZED?分詞并索引
??????????Index?the?tokens?produced?by?running?the?field's?value?through?an?Analyzer.
ANALYZED_NO_NORMS?分詞索引?但是禁用存儲規范
??????????Expert:?Index?the?tokens?produced?by?running?the?field's?value?through?an?Analyzer,?and?also?separately?disable?the?storing?of?norms.
NO??不索引,當然肯定不會去分詞
??????????Do?not?index?the?field?value.
NOT_ANALYZED?索引但不會分詞
??????????Index?the?field's?value?without?using?an?Analyzer,?so?it?can?be?searched.
NOT_ANALYZED_NO_NORMS?不索引?同時禁用存儲規范
?
l?Field.TermVector?表示對Field詞條向量是否進行存儲,(具體什么意思我也還沒弄明白)其值有以下幾種
NO??不存儲詞條向量
??????????Do?not?store?term?vectors.
WITH_OFFSETS?存儲詞條和其偏移量
??????????Store?the?term?vector?+?Token?offset?information
WITH_POSITIONS?存儲詞條和其位置
??????????Store?the?term?vector?+?token?position?information
WITH_POSITIONS_OFFSETS?存儲詞條和?詞條偏移量及其位置
??????????Store?the?term?vector?+?Token?position?and?offset?information
YES?存儲每個document的詞條向量
???????Store?the?term?vectors?of?each?document.
2.document:簡單的說就像數據庫中的一條記錄,它的每個字段就是Field。注意:數據庫中你建立了字段,所有的記錄具有相同記錄,即數據庫是以字段為主導的;而Lucene是以document為主導的,同一個索引(相當于數據庫中一張表)中不同的document是可以不一樣的。下面介紹docuemnt常用幾種方法:
add(Fieldable?field)?將一個Field?加入document,注意Field是實現了Fieldable接口的類
get(String?name)?返回當前docuement指定Field名的Field值
getBoost()?返回索引時設置的加權因子(具體到底是什么沒搞明白)
??????????Returns,?at?indexing?time,?the?boost?factor?as?set?by?setBoost(float).
getFieldable(String?name)?看英語吧,這個很簡單
??????????Returns?a?field?with?the?given?name?if?any?exist?in?this?document,?or?null.
getFieldables(String?name)??看英語吧,這個很簡單,只不過返回的是多個
??????????Returns?an?array?of?Fieldables?with?the?given?name.
getFields()?
??????????Returns?a?List?of?all?the?fields?in?a?document.
getFields(String?name)?
??????????Deprecated.?use?getFieldable(java.lang.String)?instead?and?cast?depending?on?data?type.
getValues(String?name)?返回指定Field名的Field值的數組
??????????Returns?an?array?of?values?of?the?field?specified?as?the?method?parameter.
removeField(String?name)?移除當前document的指定的Field?
??????????Removes?field?with?the?specified?name?from?the?document.
removeFields(String?name)?移除多個Field
??????????Removes?all?fields?with?the?given?name?from?the?document.
setBoost(float?boost)?
??????????Sets?a?boost?factor?for?hits(詞條)?on?any?field?of?this?document.
toString()?
??????????Prints?the?fields?of?a?document?for?human?consumption.
(三)、Analyzer是Lucene的分析工具,無論是建立索引還是搜索過程,該類是一個抽象類,所以網上常用適合Lucene的分詞器都是它的繼承,例如用Lucene自帶的StandardAnalyzer;如?Analyzer?analyzer?=?new?StandardAnalyzer(Version.LUCENE_35)?
?
(四)IndexWriter?他是建立索引的類,至于該類如何實現建立索引請參考前面的文章
IndexWriter.setMaxFieldLength(int)表示索引數據時對數據源的最大長度進行現在,如設置為100,則表示?取數據源前100個長度進行索引分析,當然存儲是全都存儲,當然長度不足100的就全都索引。
l?、一個索引可以有多個segment(段),但一個索引目錄里只有一個segments(該文件記錄了所有的segmet);一個segment有多個document(可以設置最大值),同一個segment在磁盤上各種物理的文件的前綴是相同的:如圖
其中?
l?.frq?記錄的是詞條頻率信息,
l?.prx記錄的詞條的未知信息,
l?.fnm記錄了所有Field的信息,
l?.fdt/.fdx?
- 域數據文件(fdt):
- 真正保存存儲域(stored?field)信息的是fdt文件
- 在一個段(segment)中總共有segment?size篇文檔,所以fdt文件中共有segment?size個項,每一項保存一篇文檔的域的信息
- 對于每一篇文檔,一開始是一個fieldcount,也即此文檔包含的域的數目,接下來是fieldcount個項,每一項保存一個域的信息。
- 對于每一個域,fieldnum是域號,接著是一個8位的byte,最低一位表示此域是否分詞(tokenized),倒數第二位表示此域是保存字符串數據還是二進制數據,倒數第三位表示此域是否被壓縮,再接下來就是存儲域的值,比如new?Field("title",?"lucene?in?action",?Field.Store.Yes,?…),則此處存放的就是"lucene?in?action"這個字符串。
- 域索引文件(fdx)
- 由域數據文件格式我們知道,每篇文檔包含的域的個數,每個存儲域的值都是不一樣的,因而域數據文件中segment?size篇文檔,每篇文檔占用的大小也是不一樣的,那么如何在fdt中辨別每一篇文檔的起始地址和終止地址呢,如何能夠更快的找到第n篇文檔的存儲域的信息呢?就是要借助域索引文件。
- 域索引文件也總共有segment?size個項,每篇文檔都有一個項,每一項都是一個long,大小固定,每一項都是對應的文檔在fdt文件中的起始地址的偏移量,這樣如果我們想找到第n篇文檔的存儲域的信息,只要在fdx中找到第n項,然后按照取出的long作為偏移量,就可以在fdt文件中找到對應的存儲域的信息。
l?詞典文件(tis)
- TermCount:詞典中包含的總的詞數
- IndexInterval:為了加快對詞的查找速度,也應用類似跳躍表的結構,假設IndexInterval為4,則在詞典索引(tii)文件中保存第4個,第8個,第12個詞,這樣可以加快在詞典文件中查找詞的速度。
- SkipInterval:倒排表無論是文檔號及詞頻,還是位置信息,都是以跳躍表的結構存在的,SkipInterval是跳躍的步數。
- MaxSkipLevels:跳躍表是多層的,這個值指的是跳躍表的最大層數。
- TermCount個項的數組,每一項代表一個詞,對于每一個詞,以前綴后綴規則存放詞的文本信息(PrefixLength?+?Suffix),詞屬于的域的域號(FieldNum),有多少篇文檔包含此詞(DocFreq),此詞的倒排表在frq,prx中的偏移量(FreqDelta,?ProxDelta),此詞的倒排表的跳躍表在frq中的偏移量(SkipDelta),這里之所以用Delta,是應用差值規則。
l?詞典索引文件(tii)
- 詞典索引文件是為了加快對詞典文件中詞的查找速度,保存每隔IndexInterval個詞。
- 詞典索引文件是會被全部加載到內存中去的。
- IndexTermCount?=?TermCount?/?IndexInterval:詞典索引文件中包含的詞數。
- IndexInterval同詞典文件中的IndexInterval。
- SkipInterval同詞典文件中的SkipInterval。
- MaxSkipLevels同詞典文件中的MaxSkipLevels。
- IndexTermCount個項的數組,每一項代表一個詞,每一項包括兩部分,第一部分是詞本身(TermInfo),第二部分是在詞典文件中的偏移量(IndexDelta)。假設IndexInterval為4,此數組中保存第4個,第8個,第12個詞...
l?標準化因子文件(nrm)
為什么會有標準化因子呢?從第一章中的描述,我們知道,在搜索過程中,搜索出的文檔要按與查詢語句的相關性排序,相關性大的打分(score)高,從而排在前面。相關性打分(score)使用向量空間模型(Vector?Space?Model),在計算相關性之前,要計算Term?Weight,也即某Term相對于某Document的重要性。在計算Term?Weight時,主要有兩個影響因素,一個是此Term在此文檔中出現的次數,一個是此Term的普通程度。顯然此Term在此文檔中出現的次數越多,此Term在此文檔中越重要。
這種Term?Weight的計算方法是最普通的,然而存在以下幾個問題:
- 不同的文檔重要性不同。有的文檔重要些,有的文檔相對不重要,比如對于做軟件的,在索引書籍的時候,我想讓計算機方面的書更容易搜到,而文學方面的書籍搜索時排名靠后。
- 不同的域重要性不同。有的域重要一些,如關鍵字,如標題,有的域不重要一些,如附件等。同樣一個詞(Term),出現在關鍵字中應該比出現在附件中打分要高。
- 根據詞(Term)在文檔中出現的絕對次數來決定此詞對文檔的重要性,有不合理的地方。比如長的文檔詞在文檔中出現的次數相對較多,這樣短的文檔比較吃虧。比如一個詞在一本磚頭書中出現了10次,在另外一篇不足100字的文章中出現了9次,就說明磚頭書應該排在前面碼?不應該,顯然此詞在不足100字的文章中能出現9次,可見其對此文章的重要性。
由于以上原因,Lucene在計算Term?Weight時,都會乘上一個標準化因子(Normalization?Factor),來減少上面三個問題的影響。
標準化因子(Normalization?Factor)是會影響隨后打分(score)的計算的,Lucene的打分計算一部分發生在索引過程中,一般是與查詢語句無關的參數如標準化因子,大部分發生在搜索過程中,會在搜索過程的代碼分析中詳述。
標準化因子(Normalization?Factor)在索引過程總的計算如下:
?
它包括三個參數:
- Document?boost:此值越大,說明此文檔越重要。
- Field?boost:此域越大,說明此域越重要。
- lengthNorm(field)?=?(1.0?/?Math.sqrt(numTerms)):一個域中包含的Term總數越多,也即文檔越長,此值越小,文檔越短,此值越大。
從上面的公式,我們知道,一個詞(Term)出現在不同的文檔或不同的域中,標準化因子不同。比如有兩個文檔,每個文檔有兩個域,如果不考慮文檔長度,就有四種排列組合,在重要文檔的重要域中,在重要文檔的非重要域中,在非重要文檔的重要域中,在非重要文檔的非重要域中,四種組合,每種有不同的標準化因子。
(五)索引優化,合并策略,以前的版本中通過合并因子Mergefactor以及maxMergegeDocs都是通過IndexWriter來設置的,在3.5版本中這些方法都過時了,現在這些優化方法都集成到MergePolicy(一個抽象類),所以索引優化時可以用如下方法來設置
MergePolicy?me?=?new?LogMergePolicy()該類繼承MergePolicy;?然后通過IndexWriterConfig調用setMergePolicy(MergePolicy?mergePolicy)??來設置,然后通過3.5版本中IndexWriter的唯一推薦的構造方法IndexWriter(Directory?d,?IndexWriterConfig?conf)??來設置,其他構造方法都過時了。
(六)、IndexReader類,該類主要負責索引的操作,如讀取,修改,刪除操作,具體可以參考http://xiaozu.renren.com/xiaozu/258210/336375170?,很代碼已經詳細說明了。
轉載于:https://www.cnblogs.com/qingfeideyi/archive/2012/03/04/2379526.html
總結
以上是生活随笔為你收集整理的Lucene3.5自学4--建索引相关知识总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 常用工具汇总
- 下一篇: 为什么会存在乱码?什么是编解码?为什么会