lucene中Field簡析

 

http://blog.csdn.net/zhaoxiao2008/article/details/14180019 

 

先看一段lucene3代碼java

Document doc = new Document();  算法

doc.add(new Field("fullpath", f.getCanonicalPath(), Field.Store.YES, Field.Index.NOT_ANALYZED))  數據庫

 

Field類是文檔索引期間很重要的類,控制着被索引的域值數組

Field.Store.* 域存儲選項經過倒排序索引來控制文本是否能夠搜索 
變量名 釋義
Index.ANALYZED 使用分析器將域值分解成獨立的語彙單元流,並使每一個語彙單元都能被搜索,適用於普通文本域
Index.NOT_ANALYZED 對域進行索引,但不對String進行分析,將域值做爲單一的語彙單元,適用於索引那些不能被分解的域值,如URL,文件路徑,電話號碼等
Index.ANALYZED_NO_NORMS 不會在索引中存儲norms信息,norms記錄了索引中的index-time boost信息,當你進行搜索時比較費內存
Index.NOT_ANALYZED_NO_NORMS 同上,也不存儲norms信息,在搜索時減小索引空間和內存耗費Index.No 使對應的域值不被搜索
Index.No 使對應的域值不被搜索

 

Field.Index.* 域索引選項肯定是否要存儲域的真實值,以便後續繼續搜索時能恢復這個
變量名 釋義
Stroe.YES 存儲域值,該狀況下原始字符串所有被保存在索引中,對須要展現搜索結果的域有用,如URL,標題
Stroe.NO 不存儲域值,一般跟Index.ANALYZED共同用來索引大的文本域值,不用恢復爲初始格式

 

 

 

在lucene4中 ,這種寫法已經不合適了緩存

 

doc.add(new Field("contents", new FileReader(f), TextField.TYPE_NOT_STORED));      //索引文件內容  安全

 

 

變量名 釋義
TYPE_NOT_STORED 索引,分詞,不存儲
TYPE_STORED

索引,分詞,存儲多線程

 

 

TextField源代碼對應的定義,能夠看出來,它其實包含了lucene3中域存儲選項和域索引選項性能

 

static {  優化

    TYPE_NOT_STORED.setIndexed(true);  ui

    TYPE_NOT_STORED.setTokenized(true);  

    TYPE_NOT_STORED.freeze();  

  

    TYPE_STORED.setIndexed(true);  

    TYPE_STORED.setTokenized(true);  

    TYPE_STORED.setStored(true);  

    TYPE_STORED.freeze();  

 

  }

 

 

可是對於特殊的要求,好比路徑,則不須要索引,要分類,咱們就得手動去設置Field的熟悉了

FieldType fieldType = new FieldType();  

fieldType.setIndexed(false);//set 是否索引  

fieldType.setStored(true);//set 是否存儲  

fieldType.setTokenized(true);//set 是否分類  

 

doc.add(new Field("fullpath", f.getCanonicalPath(), fieldType));     //索引完整路徑  

 

 

 

 

可是對於特殊的要求,好比路徑,則不須要索引,要分類,咱們就得手動去設置Field的熟悉了

FieldType fieldType = new FieldType();  

fieldType.setIndexed(false);//set 是否索引  

fieldType.setStored(true);//set 是否存儲  

fieldType.setTokenized(true);//set 是否分類  

 

doc.add(new Field("fullpath", f.getCanonicalPath(), fieldType));     //索引完整路徑  

下一篇lucene4.5近實時搜索

_______________________________________________________________________

 

從概念理解Lucene的Index(索引)文檔模型  

 

來源:  http://blog.csdn.net/duck_genuine/article/details/6053430
 
 
Lucene主要有兩種文檔模型:Document和Field,一個Document可能包含若干個Field。
 
每個Field有不一樣的策略:

1.被索引 or not,將該字段(Field)通過分析(Analyisi)後,加入索引中,並非原文 。

2.若是被索引,可選擇是否保存「term vector」(向量),用於類似檢索。

3.可選擇是否存儲(store),將原文直接拷貝 ,不作索引,用於檢索後的取出。

Lucene中的文檔模型相似於數據庫,可是又不徹底相同,體如今以下幾方面:

1.無規範格式,即無需固定的Schema,無列等預先設計,同一個索引中加入的Document可包含不一樣的Field 。

2.非正規化,Lucene中的文檔模型是一個平面化 的結構,沒有遞歸定義,天然鏈接等等複雜的結構。

2.2  理解索引過程

整體來講,索引過程爲:

1.提取摘要:從原文提取,並建立Document和Field對象。Tika 提供了PDF、Word等非文本的文本提取。

2.分析:Analysis,首先對Document的Field進行分解,產生token流,而後通過一系列Filter(如小寫化)等。

3.創建索引:經過IndexWriter的addDocument寫入到索引中。Lunece使用了反向索引 ,即「那個Document包含單詞X」,而不是「Document包含哪些Word」

索引文件組成

爲了保證效率,每一個索引由若干segments組成:

_X.cfs  每一個segments由若干個cfs組成,X爲0,1,2….若是開啓了useCompoundFile,則只有一個.cfs文件。

segments_<N>:記載每一個分區對應的cfs文件。

每一個一段時間後,在調用IndexWriter時,會自動合併這些segment

2.3  索引的基本操做

首先建立IndexWriter

IndexWriter(dir,new WhiteSpaceAnalyser(),IndexWriter.MaxField.UNLIMITED);

dir是索引的保存路徑,WhiteSpaceAnalyser是基於空白的分詞 ,最後部限定Field的數量。

依次建立文檔Document和Field

Document doc = new Document();

doc.add(new Filed(key,value,STORE?,INDEX?)

key就是field的檢索字段名,value就是待寫入/分析的文本。

STORE ,與索引無關,是否額外存儲原文 ,能夠在搜索結果後調用出來,NO不額外存儲;YES,額外存儲。

INDEX ,NO,不索引;ANALYZED,分詞後索引;NOT_ANALYZED,不分詞索引;ANALYZED_NO_NORMS,分詞索引,不存儲NORMS;NOT_ANALYZED_NO_NORMS,不分詞,索引,不存儲NORMS。除了NO外都算索引,能夠搜索 。NORMS存儲了boost所需信息,包含了NORM可能會佔用更多內存?

刪除索引

IndexWriter提供了刪除Document的功能:

deleteDocumen(Term) 

deleteDocumen(Term[])

deleteDocumen(Query)

deleteDocumen(Query [])

特別注意Term不必定是惟一的,因此有可能誤刪除多個 。另外最好選擇惟一的、非索引的Term 以防混亂(好比惟一ID)。

刪除後commit()而後close才能真正寫入索引文件中。

刪除後只是標記爲刪除,maxDoc()返回全部文檔(含已經刪除,但未清理的);numDocs:未刪除的文檔數量

使用delete後,再optimize():壓縮刪除的空間、再commit才真正的刪除釋放空間。

更新索引

updateDocument(Term,Document),Lunce只支持所有替換,即整個Docuemnt要被替換掉,無法更新單獨的Field。

2.4  Field的選項

選項分爲三類:index、storing和term vector。

Index選項

Index.ANALYZED :分詞後索引

Index.NOT_ANALYZED : 不分詞直接索引,例如URL、系統路徑等,用於精確檢索 。

Index.ANALYZED_NO_NORMS : 相似Index.ANALYZED,但不存儲NORM TERMS,節約內存但不支持Boost。

Index.NOT_ANALYZED_NO_NORMS : 相似Index.NOT_ANALYZED,但不存儲NORM TERMS,節約內存但不支持Boost,很是經常使用 。

Index.NO : 根本不索引,因此不會被檢索到

默認狀況,Luncene會存儲全部單詞的出現位置,能夠用Field.setOmitTermFreqAndPositions(true)關閉,可是會影響PhraseQuery和SpanQuery。

Store選項

Store.YES :存儲原始value數值,可在檢索後被提取 。

Store.NO :不存儲原始數值,檢索後沒法從新提取。

CompressionTools 可用於壓縮、解壓縮byte數組。

Term Vector選項

Term Vector主要用於爲類似搜索 提供支持 ,例如搜索cat,返回cat。

TermVector.YES :記錄Term Vector

TermVector.WITH_POSITIONS :記錄Term Vector以及每一個Term出現的位置

TermVector.WITH_OFFSETS :記錄Term Vector以及每一個Term出現的偏移

TermVector.WITH_POSITIONS_OFFSETS :記錄Term Vector以及出現的位置+偏移

TermVector.NO :不存儲TermVector

若是Index選擇了No,則TermVector必須選擇No

將String外的類型做爲Field的數據源

Reader:沒法被STORE,默認TokenStream始終被分詞和索引。

TokenStream:分詞以後的結果做爲源,沒法被Store,始終analyzed並索引。

byte[] :沒法被索引,沒有TermVector,必須被Store.YES

與排序相關選項

數字Field能夠用NumericField,若是是文本Field必須Field.Index.NOT_ANALYZED,才能排序,即保證這個Field只含有一個Token才能排序 

多值Field(Multi-valued Fields)

好比一本書有多個做者,怎麼辦呢?

一種方法是,添加多個同一key,不一樣value的Field

  Document doc = new Document();
    for (int i = 0; i < authors.length; i++) {
      doc.add(new Field(「author」, authors[i],
                        Field.Store.YES,
                        Field.Index.ANALYZED));
    }

還有一種方法在第4章中提出。

2.5  Boost(提高)

boost能夠對影響搜索返回結果的排序 

boost能夠在index或者搜索時候完成,後者更具備靈活性可獨立制定但耗費更多CPU。

Booost Doument

index時候boost將存儲在NORMS TERM中。默認狀況下,全部Document有相等的Boost,即1.0,能夠手動提高一個Docuemnt的Boost數值。

Document.settBoost(float bei),bei是1.0的倍數。

Boost Field

也能夠對Field進行索引,使用Document的Boost,對下屬的Field都執行相同的Field。

單獨對Field進行Boost

Field.boost(float)

注意:Lucene的Rank算法由多種因素組成,Boost只是一個因素之一,不是決定性因素 

Norms

boost的數值存儲在Norms中,可能會致使Search時佔用大量內存。所以可將其關閉:

設置NO_NORMS,或者再Field中指定Field.setOmitNorms(true)。

 2.6  對數字、日期、時間等進行索引

索引數字

有兩種場景:

1.數字嵌入在Text中,例如「Be sure to include Form 1099 in your tax return」,而你想要搜索1099這個詞。此時須要選擇不分解數字的Analyzer ,例如WhitespaceAnalyzer或者StandardAnalyzer。而SimpleAnalyzer和StopAnalyzer會忽略數字,沒法經過1099檢出。

2.數字式單獨的Field,2.9以後,Lucene支持了數字類型,使用NumericField便可:doc.add(new NumericField(「price」).setDoubleValue(19.99));此時,對數字Field使用字典樹存儲,

可向document中添加同樣的NumericField數值,在NumericRangeQuery、NumericRangeFilter中以or的方式支持,可是排序中不支持。所以若是要排序,必須添加惟一的NumericField。

precisionStep控制了掃描精度,越小越精確但速度越慢。

索引日期和時間

方法是:將日期轉化爲時間戳(長整數) ,而後按照NumericField進行處理。

或者,若是不須要精確到毫秒,能夠轉化成秒處理

  doc.add(new NumericField(「day」) .setIntValue((int) (new Date().getTime()/24/3600)));

甚至對某一天進行索引而不是具體時間。

    Calendar cal = Calendar.getInstance();
    cal.setTime(date);
    doc.add(new NumericField(「dayOfMonth」)
            .setIntValue(cal.get(Calendar.DAY_OF_MONTH)));

 2.7  Field截斷

   Lucene支持對字段的截斷。IndexWriter.MaxFieldLength表示字段的最大長度,默認爲MaxFieldLength.UNLIMITED,無限。

而MaxFieldLength.LIMITED表示有限制,能夠經過setMaxFieldLength(int n)進行指定。

上述設定以後,只保留前n個字符。

能夠經過setInfoStream(System.out)得到詳細日誌信息。

2.8  實時搜索

2.9後支持實時搜索,或者說很快的入索引–檢索過程 

IndexReader  IndexWriter.getReader()

本方法將當即刷新Index的緩存,生效後當即返回IndexReader用於搜索。

2.9  優化索引

索引優化能夠提高搜索速度 ,而非索引速度。它指的是將小索引文件合併成幾個。

IndexWriter提供了幾個優化方法:

optimize():將索引合併爲一個段,完成前不會返回。可是太耗費資源。

optimize(int maxNumSegments):部分優化,優化到最多maxNumSegments個段?是優化於上述極端狀況的這種,例如5個。

optimize(boolean doWait):通optimize(),可是它將當即返回。

optimize(int maxNumSegments, boolean doWait):同optimize(int maxNumSegments),可是將當即返回。

另外:在優化中會耗費大量的額外空間 。即舊的廢棄段直到IndexWriter.commit()以後才能被移除 

2.10  Directory

Directory封裝了存儲的API,向上提供了抽象的接口,有如下幾類:

SimpleFSDirectory:存儲於本地磁盤使用java.io,不支持多線程,要本身加鎖 

NIOFSDirectory:多線程可拓展,使用java.nio,支持多線程安全,可是Windows下有Bug 

MMapDirectory:內存映射存儲(將文件映射到內存中進行操做,相似nmap)。

RAMDirectory:所有在內存中存儲。

FileSwitchDirectory:使用兩個目錄,切換交替使用。

使用FSDirectory.open將自動挑選合適的Directory。也能夠本身指定:

Directory ramDir = new RAMDirectory();
IndexWriter writer = new IndexWriter(ramDir, analyzer,  IndexWriter.MaxFieldLength.UNLIMITED);

RAMDirectory適用於內存比較小的狀況。

能夠拷貝索引以用於加速:

Directory ramDir = new RAMDirectory(otherDir);

或者

Directory.copy(Directory sourceDir,
               Directory destDir,
               boolean closeDirSrc);

2.11  線程安全、鎖

線程、多JVM安全

任意多個IndexReaders可同時打開,能夠跨JVM。

同一時間 只能打開一個 IndexWriter,獨佔寫鎖 。內建線程安全機制。

IndexReaders能夠在IndexWriter打開的時候打開。

多線程間可共享IndexReader或者IndexWriter,他們是線程安全的,內建同步機制且性能較高。

經過遠程文件系統共享IndexWriter

注意不要反覆打開、關閉,不然會影響性能。

Index的鎖

以文件鎖的形式,名爲write.lock。

若是在已經被鎖定的狀況下再建立一個IndexWriter,會遇到LockObtainFailedException。

也支持其餘鎖定方式,可是通常狀況下無需改變它們。

IndexWriter.isLocked(Directory):檢查某目錄是否被鎖。

IndexWriter.unlock(Directory):對某目錄解鎖,危險!。

注意!每次IndexWriter不管執行了什麼操做,都要顯示的close !不會自動釋放鎖的!

2.12  調試索引

2.14  高級的索引選項

IndexReader能夠用來完全刪除已經去除的Index,優勢以下:

1.經過Document的具體Number來刪除,更精確而IndexWriter不行。

2.IndexReader能夠在刪除後當即顯示出來,而IndexWriter必須從新打開才能顯示出來。

3.IndexReader擁有undeleteAll,能夠撤銷全部刪除的索引(只對還沒有merged的有效 )。

釋放刪除索引後的空間

能夠調用expungeDeletes顯示的釋放空間,它將執行merge從而釋放刪除但僅僅作了標記,還沒有釋放的空間。

緩存和刷新

當添加索引、刪除索引時候,在內存中創建了一個緩存以減小磁盤I/O,Lucene會按期把這些緩存中的改動放入Directory中便造成了一個segment(段)。

IndexWriter刷新緩存的條件是:

當內存中數據已經大於setRAMBufferSizeMB的指定。

當索引中的Document數量多於setMaxBufferedDocs的指定。

當索引被刪除的數量多於setMaxBufferedDeleteTerms的指定。

上述條件之一發生時,即觸發緩存刷進,它將創建新的Segment但不存入磁盤,只有當commit後才寫入磁盤的index。

索引的commit

commit將改動持久化到本次索引中。只有調用commit後,再打開的IndexReader或者IndexSearcher才能看到最近一次commit以後的結果。

關閉close也將間接調用commit。

與commit相對的是rollback方法,它將撤銷上次commit以後的全部改動。

commit很是耗時,不能常常調用。

「雙緩衝」的commit

在圖形界面開發中,常常有雙緩衝技術,即一個用於被刷新,一個用於顯示,兩個之間互換使用。Lucene也支持這樣的機制。

Lucene暴露了兩個接口:

prepareCommit

Commit

prepareCommit比較慢,而調用prepareCommit後再調用Commit則會很是快。

刪除策略

IndexDeletionPolicy決定了刪除策略。能夠決定是否保留以前的commit版本。

Lucene對ACID的事務支持

這主要是經過「同時只能打開一個IndexWriter」來實現的。

若是JVM、OS或者機器掛了,Lucene會自動恢復到上一個commit版本。

合併Merge

當索引有過多的Segnmnet的時候,須要進行合併Merge。優勢:

1.減小了Segnment的文件數量

2.減小索引文件佔用的空間大小。

MERGEPOLICY決定什麼時候須要執行合併Merge

MERGEPOLICY

選擇那些文件須要被合併,默認有兩種策略:

LogByteSizeMergePolicy :根據Index大小決定是否須要合併

LogDocMergePolicy :根據Document的數量決定是否須要合併

分別經過

setMergeFactor

和setMaxMergeDocs來指定,具體參數見API。

MERGESCHEDULER

決定如何進行合併:

ConcurrentMergeScheduler,後臺額外線程進行合併,可經過waitForMerges得知合併完成。

SerialMergeScheduler,在addDocument時候串行合併,使用統一線程。

 
 
 
 
相關文章
相關標籤/搜索