Lucene學(xué)習(xí)筆記收藏1.Lucene的工作流程:
(1) 使用IndexWriter,在指定的目錄建立索引的文件
(2) 將需要檢索的數(shù)據(jù)轉(zhuǎn)換位Document的Filed對象,然后將Document用IndexWriter添加倒索引的文件中
(3) 處理索引信息,關(guān)閉IndexWriter流
(4) 創(chuàng)建搜索的Query
(5) 給IndexSearcher
2.Lucene的字段類型
Lucene有四種不同的字段類型:Keyword,UnIndexed,UnStored和Text,用于指定建立最佳索引。
Keyword字段是指不需要分析器解析但需要被編入索引并保存到索引中的部分。JavaSourceCodeIndexer類使用該字段來保存導(dǎo)入類的聲明。
UnIndexed字段是既不被分析也不被索引,但是要被逐字逐句的將其值保存到索引中。由于我們一般要存儲文件的位置但又很少用文件名作為關(guān)鍵字來搜索,所以用該字段來索引Java文件名。
UnStored字段和UnIndexed字段相反。該類型的Field要被分析并編入索引,但其值不會被保存到索引中。由于存儲方法的全部源代碼需要大量的空間。所以用UnStored字段來存儲被索引的方法源代碼。可以直接從Java源文件中取出方法的源代碼,這樣作可以控制我們的索引的大小。
Text字段在索引過程中是要被分析、索引并保存的。類名是作為Text字段來保存。下表展示了JavaSourceCodeIndexer類使用Field字段的一般情況。
3.基本概念(與傳統(tǒng)表的對比):
Lucene
傳統(tǒng)表
說明
IndexWriter
table
Document
一條記錄
Field
每個字段
分為可被索引的,可切分的,不可被切分的,不可被索引的幾種組合類型
Hits
RecoreSet
結(jié)果集
IndexWriter提供了一些參數(shù)可供設(shè)置,列表如下
屬性
默認(rèn)值
說明
mergeFactor
org.apache.lucene.mergeFactor
10
控制index的大小和頻率,兩個作用
1.一個段有多少document
2.多少個段合成一個大段
maxMergeDocs
org.apache.lucene.maxMergeDocs
Integer.MAX_VALUE
限制一個段中的document數(shù)目
minMergeDocs
org.apache.lucene.minMergeDocs
10
緩存在內(nèi)存中的document數(shù)目,超過他以后會寫入到磁盤
maxFieldLength
1000
一個Field中最大Term數(shù)目,超過部分忽略,不會index到field中,所以自然也就搜索不到
這些參數(shù)的的詳細(xì)說明比較復(fù)雜:mergeFactor有雙重作用
(1)設(shè)置每mergeFactor個document寫入一個段,比如每10個document寫入一個段
(2)設(shè)置每mergeFacotr個小段合并到一個大段,比如10個document的時候合并為1小段,以后有10個小段以后合并到一個大段,有10個大段以后再合并,實(shí)際的document數(shù)目會是mergeFactor的指數(shù)
簡單的來說mergeFactor 越大,系統(tǒng)會用更多的內(nèi)存,更少磁盤處理,如果要打批量的作index,那么把mergeFactor設(shè)置大沒錯, mergeFactor 小了以后, index數(shù)目也會增多,searhing的效率會降低,但是mergeFactor增大一點(diǎn)一點(diǎn),內(nèi)存消耗會增大很多(指數(shù)關(guān)系),所以要留意不要”out of memory”
把maxMergeDocs設(shè)置小,可以強(qiáng)制讓達(dá)到一定數(shù)量的document寫為一個段,這樣可以抵消部分mergeFactor的作用.
minMergeDocs相當(dāng)于設(shè)置一個小的cache,第一個這個數(shù)目的document會留在內(nèi)存里面,不寫入磁盤。這些參數(shù)同樣是沒有最佳值的,必須根據(jù)實(shí)際情況一點(diǎn)點(diǎn)調(diào)整。
maxFieldLength可以在任何時刻設(shè)置,設(shè)置后,接下來的index的Field會按照新的length截取,之前已經(jīng)index的部分不會改變。可以設(shè)置為Integer.MAX_VALUE
4.幾種查詢方式
查詢方式
說明
TermQuery
條件查詢
例如:TermQuery tquery=new TermQuery(new Term("name","jerry"));
name:字段名
jerry:要搜索的字符串
MultiTermQuery
多個字段進(jìn)行同一關(guān)鍵字的查詢
Query query= null;
Query =MultiFieldQueryParser.parse("我",new String[]
{"title","content"},analyzer);
Searcher searcher=new IndexSearcher(indexFilePath);
Hits hits=searcher.search(query);
BooleanQuery
例如:BooleanQuery bquery=new BooleanQuery();
bquery.add(query,true,false);
bquery.add(mquery,true,false);
bquery.add(tquery,true,false);
Searcher searcher=new IndexSearcher(indexFilePath);
Hits hits=searcher.search(bquery);
WildcardQuery
語義查詢(通配符查詢)
例:Query query= new WildcardQuery(new Term("sender","*davy*"));
PhraseQuery
短語查詢
PrefixQuery
前綴查詢
PhrasePrefixQuery
短語前綴查詢
FuzzyQuery
模糊查詢
RangeQuery
范圍查詢
SpanQuery
范圍查詢
在全文檢索時建議大家先采用語義時的搜索,先搜索出有意義的內(nèi)容,之后再進(jìn)行模糊之類的搜索
(1)聯(lián)合兩個索引查詢,已解決:
IndexSearcher[] searchers = new IndexSearcher[2];
searchers[0] = new IndexSearcher(m_indexpath);
searchers[1] = new IndexSearcher(m_outindexpath);
MultiSearcher multiSearcher = new MultiSearcher(searchers);
(2)還有個進(jìn)行多條件搜索 and 與 or 的操作————
用 MultiFieldQueryParser
建議重新封裝
MultiFieldQueryParser.Parser(p[],d[],f[],analyer) 成or 與 and操作合一
或者
BooleanQuery m_BooleanQuery = new BooleanQuery();
Query query = QueryParser.Parse(m_SearchText, "INSTRUMENT_NAME", analyzer);
Query query2 = QueryParser.Parse(m_SearchText2, "INSTRUMENT_NAME2", analyzer);
m_BooleanQuery.Add(query, true, false);
m_BooleanQuery.Add(query2, true, false);
(3)復(fù)合查詢(多種查詢條件的綜合查詢)
Query query=MultiFieldQueryParser.parse("索引”,new String[]
{"title","content"},analyzer);
Searcher searcher=new IndexSearcher(indexFilePath);
Hits hits=searcher.search(query);
for (int i = 0; i < hits.length(); i++)
{
System.out.println(hits.doc(i).get("name"));
}
5.為查詢優(yōu)化索引(index)
Indexwriter.optimize()方法可以為查詢優(yōu)化索引(index),之前提到的參數(shù)調(diào)優(yōu)是為indexing過程本身優(yōu)化,而這里是為查詢優(yōu)化,優(yōu)化主要是減少index文件數(shù),這樣讓查詢的時候少打開文件,優(yōu)化過程中,lucene會拷貝舊的index再合并,合并完成以后刪除舊的index,所以在此期間,磁盤占用增加, IO符合也會增加,在優(yōu)化完成瞬間,磁盤占用會是優(yōu)化前的2倍,在optimize過程中可以同時作search。
4.org.apache.lucene.document.Field
即上文所說的“字段”,它是Document的片段section。
Field的構(gòu)造函數(shù):
Field(String name, String string, boolean store, boolean index, boolean token)。
Indexed:如果字段是Indexed的,表示這個字段是可檢索的。
Stored:如果字段是Stored的,表示這個字段的值可以從檢索結(jié)果中得到。
Tokenized:如果一個字段是Tokenized的,表示它是有經(jīng)過Analyzer轉(zhuǎn)變后成為一個tokens序列,在這個轉(zhuǎn)變過程tokenization中, Analyzer提取出需要進(jìn)行索引的文本,而剔除一些冗余的詞句(例如:a,the,they等,詳見 org.apache.lucene.analysis.StopAnalyzer.ENGLISH_STOP_WORDS和 org.apache.lucene.analysis.standard.StandardAnalyzer(String[] stopWords)的API)。Token是索引時候的.
類型
Analyzed
Indexed
Stored
說明
Field.Keyword(String,String/Date)
N
Y
Y
這個Field用來儲存會直接用來檢索的比如(編號,姓名,日期等)
Field.UnIndexed(String,String)
N
N
Y
不會用來檢索的信息,但是檢索后需要顯示的,比如,硬件序列號,文檔的url地址
Field.UnStored(String,String)
Y
Y
N
大段文本內(nèi)容,會用來檢索,但是檢索后不需要從index中取內(nèi)容,可以根據(jù)url去load真實(shí)的內(nèi)容
Field.Text(String,String)
Y
Y
Y
檢索,獲取都需要的內(nèi)容,直接放index中,不過這樣會增大index
Field.Text(String,Reader)
Y
Y
N
如果是一個Reader, lucene猜測內(nèi)容比較多,會采用Unstored的策略.
5.Lucene 的檢索結(jié)果排序
Lucene的排序主要是對org.apache.lucene.search.Sort的使用。Sort可以直接根據(jù)字段Field生成,也可以根據(jù)標(biāo)準(zhǔn)的SortField生成,但是作為Sort的字段,必須符合以下的條件:唯一值以及Indexed。可以對Integers, Floats, Strings三種類型排序。
對整數(shù)型的ID檢索結(jié)果排序只要進(jìn)行以下的簡單操作:
Sort sort = new Sort("id");
Hits hits = searcher.search(query, sort);
用戶還可以根據(jù)自己定義更加復(fù)雜的排序,詳細(xì)請參考API。
6.分析器
Lucene使用分析器來處理被索引的文本。在將其存入索引之前,分析器用于將文本標(biāo)記化、摘錄有關(guān)的單詞、丟棄共有的單詞、處理派生詞(把派生詞還原到詞根形式,意思是把bowling、bowler和bowls還原為bowl)和完成其它要做的處理。Lucene提供的通用分析器是:
SimpleAnalyzer:用字符串標(biāo)記一組單詞并且轉(zhuǎn)化為小寫字母。
StandardAnalyzer:用字符串標(biāo)記一組單詞,可識別縮寫詞、email地址、主機(jī)名稱等等。并丟棄基于英語的stop words (a, an, the, to)等、處理派生詞。
ChineseAnalyzer.class,它是一個單字分析法,它把句子中的詞全部分成一個一個的字符,以單個字為單位存儲。
CJKAnalyzer.class,它是雙字分析法,它把中文以雙字為單位拆分得到結(jié)果,從而建立詞條。當(dāng)然這些得到的雙字詞中會有很多不符合中文語義單位的雙字被送進(jìn)索引。
十、需要注意的問題:
1 .IndexWriter在添加新的document后,需要重新建立Index,則需要調(diào)用writer.optimize();方法
2. Lucene沒有update索引的方法,需要刪除后重新建立,參考remove方法
3 .用IndexReader刪除Document后,需要重新用IndexWriter進(jìn)行整理,否則無法在進(jìn)行搜索(不知道是不是我設(shè)置問題)
4.Lucene先在內(nèi)存中進(jìn)行索引操作,并根據(jù)一定的批量進(jìn)行文件的寫入。這個批次的間隔越大,文件的寫入次數(shù)越少,但占用內(nèi)存會很多。反之占用內(nèi)存少,但文件IO操作頻繁,索引速度會很慢。在IndexWriter中有一個MERGE_FACTOR參數(shù)可以幫助你在構(gòu)造索引器后根據(jù)應(yīng)用環(huán)境的情況充分利用內(nèi)存減少文件的操作。根據(jù)我的使用經(jīng)驗(yàn):缺省Indexer是每20條記錄索引后寫入一次,每將MERGE_FACTOR增加50倍,索引速度可以提高1倍左右。
5.并發(fā)操作Lucene
(1)所有只讀操作都可以并發(fā)
(2)在index被修改期間,所有只讀操作都可以并發(fā)
(3)對index修改操作不能并發(fā),一個index只能被一個線程占用
(4)ndex的優(yōu)化,合并,添加都是修改操作
(5)但需要注意的是,在創(chuàng)建搜索的時候用:
searcher = new IndexSearcher(IndexReader.open("E:\\lucene\\test4\\index"));
searcher.close();
這時候是不能關(guān)閉searcher的.
如果想讓searcher能關(guān)閉,就不要用IndexReader了:
searcher = new IndexSearcher("E:\\lucene\\test4\\index");
6.Locking機(jī)制
lucence內(nèi)部使用文件來locking,默認(rèn)的locking文件放在java.io.tmpdir,可以通過-Dorg.apache.lucene.lockDir=xxx指定新的dir,有write.lock commit.lock兩個文件,lock文件用來防止并行操作index,如果并行操作, lucene會拋出異常,可以通過設(shè)置-DdisableLuceneLocks=true來禁止locking,這樣做一般來說很危險(xiǎn),除非你有操作系統(tǒng)或者物理級別的只讀保證,比如把index文件刻盤到CDROM上。
十一、2.0中新增特性
1.新增類: org.apache.lucene.index.IndexModifier ,它合并了 IndexWriter 和 IndexReader,好處是我們可以增加和刪除文檔的時候不同擔(dān)心 synchronisation/locking 的問題了。
2.增加對 contrib/highlighter 的 NullFragmenter , 這對全文本加亮很有用。
3.增加了新類 MatchAllDocsQuery 用來匹配所有文檔。
4.. 增加 ParallelReader,這個一種IndexReader 他合并多個單獨(dú)的索引到一個單獨(dú)的虛擬索引上。
5.增加 Hits.iterator() 方法和相應(yīng)的 HitIterator 和 Hit 對象。
他提供了對 Hits對象標(biāo)準(zhǔn)的 java.util.Iterator 疊代操作。
每個iterator‘s next() 方法返回一個 Hit 對象。
6. 在 term vectors 中增加了位置和偏移信息。(Grant Ingersoll & Christoph)
7. 增加了一個新的 DateTools 。允許用戶格式化日期到一種更可讀的格式,以便于更好的適應(yīng)索引。DateTools 不像 DateFields 類,它允許日期指定到1970年以前,但必須使用指定的日期格式。這樣,在RangeQuerys中使用就更加有效率了。
8. 增加了對壓縮字段存儲的支持。(patch #29370)
實(shí)例:
1.判斷索引文件是否存在:
/**
* 檢查索引是否存在.
* @param indexDir
* @return
*/
public static boolean indexExist(String indexDir)
{
return IndexReader.indexExists(indexDir);
}
private IndexWriter getWriter(String indexFilePath) throws Exception
{
boolean append=true;
File file=new File(indexFilePath+File.separator+"segments");
if(file.exists())
append=false;
return new IndexWriter(indexFilePath,analyzer,append);
}
2.刪除索引
/**
* 刪除索引.
* @param aTerm 索引刪除條件
* @param indexDir 索引目錄
*/
public static void deleteIndex(Term aTerm, String indexDir)
{
List aList = new ArrayList();
aList.add(aTerm);
deleteIndex(aList, indexDir);
}
/**
* 刪除索引.
* @param aTerm 索引刪除條件.
* @param indexDir 索引目錄 *
*/
public static void deleteIndex(List terms, String indexDir)
{
if (null == terms) {
return;
}
if(!indexExist(indexDir)) { return; }
IndexReader reader = null;
try {
reader = IndexReader.open(indexDir);
for (int i = 0; i < terms.size(); i++){
Term aTerm = (Term) terms.get(i);
if (null != aTerm){
reader.delete(aTerm);
}
}
} catch (IOException e){
LogMan.warn("Error in Delete Index", e);
} finally {
try{
if (null != reader){
reader.close();
}
}catch (IOException e){
LogMan.warn("Close reader Error");
}
}
}
刪除索引需要一個條件,類似數(shù)據(jù)庫中的字段條件,例如刪除一條新聞的代碼如下:
public static void deleteNewsInfoIndex(int nid)
{
Term aTerm = new Term("nid", String.valueOf(nid));
deleteIndex(aTerm,indexDir);
}
注:本文有些知識是1.4下的,如果你用的是2.0可能這些例子不能很好的運(yùn)行。不過我覺得看了以上的東西,再結(jié)合一些例子就能對lucene有一定的理解了,最起碼可以開始干活了。在2.0版本中創(chuàng)建索引和進(jìn)行多種搜索的例子我會陸繼寫出來與大家一起學(xué)習(xí)。