先看一段lucene3代碼java
Document doc = new Document(); 算法
doc.add(new Field("fullpath", f.getCanonicalPath(), Field.Store.YES, Field.Index.NOT_ANALYZED)) 數據庫
Field類是文檔索引期間很重要的類,控制着被索引的域值數組
變量名 | 釋義 |
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)); //索引完整路徑
_______________________________________________________________________
1.被索引 or not,將該字段(Field)通過分析(Analyisi)後,加入索引中,並非原文 。
2.若是被索引,可選擇是否保存「term vector」(向量),用於類似檢索。
3.可選擇是否存儲(store),將原文直接拷貝 ,不作索引,用於檢索後的取出。
Lucene中的文檔模型相似於數據庫,可是又不徹底相同,體如今以下幾方面:
1.無規範格式,即無需固定的Schema,無列等預先設計,同一個索引中加入的Document可包含不一樣的Field 。
2.非正規化,Lucene中的文檔模型是一個平面化 的結構,沒有遞歸定義,天然鏈接等等複雜的結構。
整體來講,索引過程爲:
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
首先建立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。
選項分爲三類: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章中提出。
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)。
索引數字
有兩種場景:
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)));
Lucene支持對字段的截斷。IndexWriter.MaxFieldLength表示字段的最大長度,默認爲MaxFieldLength.UNLIMITED,無限。
而MaxFieldLength.LIMITED表示有限制,能夠經過setMaxFieldLength(int n)進行指定。
上述設定以後,只保留前n個字符。
能夠經過setInfoStream(System.out)得到詳細日誌信息。
2.9後支持實時搜索,或者說很快的入索引–檢索過程 。
IndexReader IndexWriter.getReader()
本方法將當即刷新Index的緩存,生效後當即返回IndexReader用於搜索。
索引優化能夠提高搜索速度 ,而非索引速度。它指的是將小索引文件合併成幾個。
IndexWriter提供了幾個優化方法:
optimize():將索引合併爲一個段,完成前不會返回。可是太耗費資源。
optimize(int maxNumSegments):部分優化,優化到最多maxNumSegments個段?是優化於上述極端狀況的這種,例如5個。
optimize(boolean doWait):通optimize(),可是它將當即返回。
optimize(int maxNumSegments, boolean doWait):同optimize(int maxNumSegments),可是將當即返回。
另外:在優化中會耗費大量的額外空間 。即舊的廢棄段直到IndexWriter.commit()以後才能被移除 。
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);
線程、多JVM安全
任意多個IndexReaders可同時打開,能夠跨JVM。
同一時間 ,只能打開一個 IndexWriter,獨佔寫鎖 。內建線程安全機制。
IndexReaders能夠在IndexWriter打開的時候打開。
多線程間可共享IndexReader或者IndexWriter,他們是線程安全的,內建同步機制且性能較高。
經過遠程文件系統共享IndexWriter
注意不要反覆打開、關閉,不然會影響性能。
Index的鎖
以文件鎖的形式,名爲write.lock。
若是在已經被鎖定的狀況下再建立一個IndexWriter,會遇到LockObtainFailedException。
也支持其餘鎖定方式,可是通常狀況下無需改變它們。
IndexWriter.isLocked(Directory):檢查某目錄是否被鎖。
IndexWriter.unlock(Directory):對某目錄解鎖,危險!。
注意!每次IndexWriter不管執行了什麼操做,都要顯示的close !不會自動釋放鎖的!
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時候串行合併,使用統一線程。