本文主要面向具體使用,適用于已熟悉java編程的lucene初學(xué)者。
1. Lucene的簡(jiǎn)介
1.1 Lucene 歷史
org.apache.lucene包是純java語(yǔ)言的全文索引檢索工具包。
Lucene的作者是資深的全文索引/檢索專(zhuān)家,最開(kāi)始發(fā)布在他本人的主頁(yè)上,2001年10月貢獻給APACHE,成為APACHE基金jakarta的一個(gè)子項目。
目前,lucene廣泛用于全文索引/檢索的項目中。
lucene也被翻譯成C#版本,目前發(fā)展為L(cháng)ucene.Net(不過(guò)最近好象有流產(chǎn)的消息)。
1.2 Lucene 原理
lucene的檢索算法屬于索引檢索,即用空間來(lái)?yè)Q取時(shí)間,對需要檢索的文件、字符流進(jìn)行全文索引,在檢索的時(shí)候對索引進(jìn)行快速的檢索,得到檢索位置,這個(gè)位置記錄檢索詞出現的文件路徑或者某個(gè)關(guān)鍵詞。
在使用數據庫的項目中,不使用數據庫進(jìn)行檢索的原因主要是:數據庫在非精確查詢(xún)的時(shí)候使用查詢(xún)語(yǔ)言“like %keyword%”,對數據庫進(jìn)行查詢(xún)是對所有記錄遍歷,并對字段進(jìn)行“%keyword%”匹配,在數據庫的數據龐大以及某個(gè)字段存儲的數據量龐大的時(shí)候,這種遍歷是致命的,它需要對所有的記錄進(jìn)行匹配查詢(xún)。因此,lucene主要適用于文檔集的全文檢索,以及海量數據庫的模糊檢索,特別是對數據庫的xml或者大數據的字符類(lèi)型。
2.Lucene的下載和配置
2.1 Lucene的下載
lucene在jakarta項目中的發(fā)布主頁(yè):
http://jakarta.apache.org/lucene/docs/index.html。以下主要針對windows用戶(hù),其它用戶(hù)請在上面的地址中查找相關(guān)下載。
lucene的.jar包的下載(包括.jar和一個(gè)范例demo):
http://apache.oregonstate.edu/jakarta/lucene/binaries/lucene-1.4-final.ziplucene的源代碼下載:
http://www.signal42.com/mirrors/apache/jakarta/lucene/source/lucene-1.4-final-src.ziplucene的api地址:
http://jakarta.apache.org/lucene/docs/api/index.html本文使用lucene版本:lucene-1.4-final.jar。
2.2 lucene的配置
首先請確定你的機子已經(jīng)進(jìn)行了java使用環(huán)境的基本配置,即確保在某個(gè)平臺下能夠運行java源代碼,否則請查閱相關(guān)文檔進(jìn)行配置。
接下來(lái)進(jìn)入lucene的配置:
普通使用者:在環(huán)境變量的CLASSPATH中添加lucene的位置。比如:“D:\java \lucene-1.4-final\lucene-1.4-final.jar;”。
jbuilder使用者:在“Project”--“Project Properties”--“Required Libraries”進(jìn)行添加。
Jsp使用者:也可以直接將lucene-1.4-final.jar文件放到\WEB-INF\classes下。
3. Lucene 的范例(Demo )
3.1 Demo說(shuō)明
可以得到的Demo包括:lucene-demos-1.4-final、XMLIndexingDemo,lucene-demos-1.4-final中包括對普通文件和html文件的兩種索引,XMLIndexingDemo針對xml文件的索引。他們的區別主要在于:對普通文件進(jìn)行索引時(shí)只要對文件的全文進(jìn)行索引,而針對html、xml文件時(shí),對標簽類(lèi)型不能進(jìn)行索引,在實(shí)現上:html、xml的索引需要額外的數據流分析器,以分析哪些內容有用哪些無(wú)用。因此,在后兩者實(shí)現上,索引的時(shí)間額外開(kāi)支,甚至超過(guò)索引本身時(shí)間,而檢索時(shí)間沒(méi)有區別。
以上Demo中,lucene-demos-1.4-final自帶于lucene-1.4-final.zip中,XMLIndexingDemo的下載地址:
http://cvs.apache.org/viewcvs.cgi/jakarta-lucene-sandbox/contributions/XML-Indexing-Demo/3.2 Demo的運行
首先將demo.jar的路徑添加如環(huán)境變量的CLASSPATH中,例如:“D:\java\lucene-1.4-final\lucene-demos-1.4-final.jar;”,同時(shí)確保已經(jīng)添加lucene-1.4-final.jar。
然后進(jìn)行文件的全文索引,在dos控制臺中,輸入命令“java org.apache.lucene.demo.IndexFiles {full-path-to-lucene}/src”,后面的路徑為所要進(jìn)行索引的文件夾,例如:“java org.apache.lucene.demo.IndexFiles c:\test”。
接著(zhù)對索引進(jìn)行檢索,敲入“java org.apache.lucene.demo.SearchFiles”,在提示“Query:”后輸入檢索詞,程序將進(jìn)行檢索列出檢索得到的結果(檢索詞出現的文件路徑)。
其他Demo的運行請參考\docs\demo.html。
在運行Demo后請閱讀Demo的源代碼以便深入學(xué)習。
4. 利用Lucene進(jìn)行索引
進(jìn)行lucene的熟悉后,我們將學(xué)習如何使用Lucene。
一段索引的應用實(shí)例:
//需要捕捉IOException異常
//建立一個(gè)IndexWriter,索引保存目錄為“index”
String[] stopStrs = {
"他奶奶的", "fuck"};
StandardAnalyzer analyzer = new StandardAnalyzer(stopStrs);
IndexWriter writer = new IndexWriter("index", analyzer, true);
//添加一條文檔
Document doc = new Document();
doc.add(Field.UnIndexed("id", "1"));//“id”為字段名,“1”為字段值
doc.add(Field.Text("text", "fuck,他奶奶的,入門(mén)與使用"));
writer.addDocument(doc);
//索引完成后的處理
writer.optimize();
writer.close();
看完這段實(shí)例后,我們開(kāi)始熟悉lucene的使用:
4.1 Lucene的索引接口
在學(xué)習索引的時(shí)候,首先需要熟悉幾個(gè)接口:
4.1.1分析器Analyzer
分析器主要工作是篩選,一段文檔進(jìn)來(lái)以后,經(jīng)過(guò)它,出去的時(shí)候只剩下那些有用的部分,其他則剔除。而這個(gè)分析器也可以自己根據需要而編寫(xiě)。
org.apache.lucene.analysis.Analyzer:這是一個(gè)虛構類(lèi),以下兩個(gè)借口均繼承它而來(lái)。
org.apache.lucene.analysis.SimpleAnalyzer:分析器,支持最簡(jiǎn)單拉丁語(yǔ)言。
org.apache.lucene.analysis.standard.StandardAnalyzer:標準分析器,除了拉丁語(yǔ)言還支持亞洲語(yǔ)言,并在一些匹配功能上進(jìn)行完善。在這個(gè)接口中還有一個(gè)很重要的構造函數:StandardAnalyzer(String[] stopWords),可以對分析器定義一些使用詞語(yǔ),這不僅可以免除檢索一些無(wú)用信息,而且還可以在檢索中定義禁止的政治性、非法性的檢索關(guān)鍵詞。
4.1.2 IndexWriter
IndexWriter的構造函數有三種接口,針對目錄Directory、文件File、文件路徑String三種情況。
例如IndexWriter(String path, Analyzer a, boolean create),path為文件路徑,a為分析器,create標志是否重建索引(true:建立或者覆蓋已存在的索引,false:擴展已存在的索引。)
一些重要的方法:
接口名
備注
addDocument(Document doc)
索引添加一個(gè)文檔
addIndexes(Directory[] dirs)
將目錄中已存在索引添加到這個(gè)索引
addIndexes(IndexReader[] readers)
將提供的索引添加到這個(gè)索引
optimize()
合并索引并優(yōu)化
close()
關(guān)閉
IndexWriter為了減少大量的io維護操作,在每得到一定量的索引后建立新的小索引文件(筆者測試索引批量的最小單位為10),然后再定期將它們整合到一個(gè)索引文件中,因此在索引結束時(shí)必須進(jìn)行wirter. optimize(),以便將所有索引合并優(yōu)化。
4.1.3 org.apache.lucene.document
以下介紹兩種主要的類(lèi):
a)org.apache.lucene.document.Document:
Document文檔類(lèi)似數據庫中的一條記錄,可以由好幾個(gè)字段(Field)組成,并且字段可以套用不同的類(lèi)型(詳細見(jiàn)b)。Document的幾種接口:
接口名
備注
add(Field field)
添加一個(gè)字段(Field)到Document中
String get(String name)
從文檔中獲得一個(gè)字段對應的文本
Field getField(String name)
由字段名獲得字段值
Field[] getFields(String name)
由字段名獲得字段值的集
b)org.apache.lucene.document.Field
即上文所說(shuō)的“字段”,它是Document的片段section。
Field的構造函數:
Field(String name, String string, boolean store, boolean index, boolean token)。
Indexed:如果字段是Indexed的,表示這個(gè)字段是可檢索的。
Stored:如果字段是Stored的,表示這個(gè)字段的值可以從檢索結果中得到。
Tokenized:如果一個(gè)字段是Tokenized的,表示它是有經(jīng)過(guò)Analyzer轉變后成為一個(gè)tokens序列,在這個(gè)轉變過(guò)程tokenization中,Analyzer提取出需要進(jìn)行索引的文本,而剔除一些冗余的詞句(例如:a,the,they等,詳見(jiàn)org.apache.lucene.analysis.StopAnalyzer.ENGLISH_STOP_WORDS和org.apache.lucene.analysis.standard.StandardAnalyzer(String[] stopWords)的API)。Token是索引時(shí)候的基本單元,代表一個(gè)被索引的詞,例如一個(gè)英文單詞,或者一個(gè)漢字。因此,所有包含中文的文本都必須是Tokenized的。
Field的幾種接口:
Name
Stored
Indexed
Tokenized
use
Keyword(String name,
String value)
Y
Y
N
date,url
Text(String name, Reader value)
N
Y
Y
short text fields:
title,subject
Text(String name, String value)
Y
Y
Y
longer text fields,
like “body”
UnIndexed(String name,
String value)
Y
N
N
UnStored(String name,
String value)
N
Y
Y
5. 利用Lucene進(jìn)行檢索
5.1 一段簡(jiǎn)單的檢索代碼
//需要捕捉IOException,ParseException異常
//處理檢索條件
Query query = QueryParser.parse("入門(mén)", "text", analyzer);
//檢索
Searcher searcher = new IndexSearcher("./index");//"index"指定索引文件位置
Hits hits = searcher.search(query);
//打印結果值集
for (int i = 0; i < hits.length(); i++) {
Document doc = hits.doc(i);
String id = doc.get("id");
System.out.println("found " + "入門(mén)" + " on the id:" + id);
}
5.2 利用Lucene的檢索接口
5.2.1 Query與QueryParser
主要使用方法:
QueryParser .parse(String query, String field, Analyzer analyzer),例如:
Query query = QueryParser.parse("入門(mén)", "text", analyzer);
"入門(mén)"為檢索詞, "text"為檢索的字段名, analyzer為分析器
5.2.2 Hits與Searcher
Hits的主要使用接口:
接口名
備注
Doc(int n)
返回第n個(gè)的文檔的所有字段
length()
返回這個(gè)集中的可用個(gè)數
6. Lucene的其他使用
6.1 Lucene 的索引修改
下面給出一段修改索引的代碼,請根據Lucene的API解讀:
/**
* 對已有的索引添加新的一條索引
* @param idStr String:要修改的id
* @param doc Document:要修改的值
*/
public void addIndex(String idStr, String valueStr) {
StandardAnalyzer analyzer = new StandardAnalyzer();
IndexWriter writer = null;
try {
writer = new IndexWriter(indexPath, analyzer, false);
writer.mergeFactor = 2; //修正lucene 1.4.2 bug,否則不能正確反映修改
Document doc = new Document();
doc.add(Field.UnIndexed("id", idStr));//“id”為字段名,“1”為字段值
doc.add(Field.Text("text", valueStr));
writer.addDocument(doc);
writer.optimize();
writer.close();
}
catch (IOException ioe) {
ioe.printStackTrace();
}
}
/**
* 刪除索引
*
* @param idStr String
*/
public void deleteIndex(String idStr) {
try {
Directory dirt = FSDirectory.getDirectory(indexPath, false);
IndexReader reader = IndexReader.open(dirt);
Term term = new Term("text", idStr);
reader.delete(term);
reader.close();
dirt.close();
}
catch (IOException ioe) {
ioe.printStackTrace();
}
}
6.2 Lucene 的檢索結果排序
Lucene的排序主要是對org.apache.lucene.search.Sort的使用。Sort可以直接根據字段Field生成,也可以根據標準的SortField生成,但是作為Sort的字段,必須符合以下的條件:唯一值以及Indexed??梢詫ntegers, Floats, Strings三種類(lèi)型排序。
對整數型的ID檢索結果排序只要進(jìn)行以下的簡(jiǎn)單操作:
Sort sort = new Sort("id");
Hits hits = searcher.search(query, sort);
用戶(hù)還可以根據自己定義更加復雜的排序,詳細請參考API。
7 總結
Lucene給java的全文索引檢索帶來(lái)了非常強大的力量,以上僅對Lucene進(jìn)行簡(jiǎn)單的入門(mén)說(shuō)明。
xio@qq.com
參考資料:
1. Overview (Lucene 1.4-final API)
2. 車(chē)東 《在應用中加入全文檢索功能--基于JAVA的全文索引引擎Lucene簡(jiǎn)介》
3.
http://www.mail-archive.com/lucene-user@jakarta.apache.org/index.html