一、Lucene相關
Lucene是一個高性能、可伸縮的資訊搜尋(IR)庫。它可以為你的應用程式添加索引和搜尋能力。Lucene是用java實作的、成熟的開源項目,是著名的Apache Jakarta大家庭的一員,并且基于Apache軟體許可 [ASF, License]。同樣,Lucene是目前非常流行的、免費的Java資訊搜尋(IR)庫。
突出的優點
Lucene作為一個全文檢索引擎,其具有如下突出的優點:
(1)索引檔案格式獨立于應用平台。Lucene定義了一套以8位位元組為基礎的索引檔案格式,使得相容系統或者不同平台的應用能夠共享建立的索引檔案。
(2)在傳統全文檢索引擎的反向索引的基礎上,實作了分塊索引,能夠針對新的檔案建立小檔案索引,提升索引速度。然後通過與原有索引的合并,達到優化的目的。
(3)優秀的面向對象的系統架構,使得對于Lucene擴充的學習難度降低,友善擴充新功能。
(4)設計了獨立于語言和檔案格式的文本分析接口,索引器通過接受Token流完成索引檔案的創立,使用者擴充新的語言和檔案格式,隻需要實作文本分析的接口。
(5)已經預設實作了一套強大的查詢引擎,使用者無需自己編寫代碼即可使系統可獲得強大的查詢能力,Lucene的查詢實作中預設實作了布爾操作、模糊查詢(Fuzzy Search[11])、分組查詢等等。
二、編寫簡單的demo
1.首先導入相關的jar包

2.編寫工具類
主要分為3步:
第一步建立FSDirectory,指定文檔存放的路徑。
第二步擷取IndexWriter,通過這個來進行文檔的相關操作。
第三步進行增删改查。
package com.feng;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.LockObtainFailedException;
import org.apache.lucene.util.Version;
import org.wltea.analyzer.lucene.IKAnalyzer;
public class LuceneUtil {
public static Version version = Version.LUCENE_35;
public static File indexPath = new File("F:/workspace/document");
public static Analyzer analyzer = new IKAnalyzer();
private static FSDirectory fsDirectory;
private static void initFSDirectory(){
try {
fsDirectory = FSDirectory.open(indexPath);
} catch (IOException e) {
e.printStackTrace();
}
}
public static FSDirectory getFSDirectory(){
if(fsDirectory == null)
initFSDirectory();
if(!LuceneUtil.indexPath.isDirectory())
LuceneUtil.indexPath.mkdirs();
return fsDirectory;
}
public static void addDoc(Document doc){
try {
IndexWriterConfig iwc = new IndexWriterConfig(LuceneUtil.version, LuceneUtil.analyzer);
iwc.setOpenMode(OpenMode.CREATE_OR_APPEND);
IndexWriter indexWriter = new IndexWriter(LuceneUtil.getFSDirectory(), iwc);
indexWriter.addDocument(doc);
indexWriter.close();
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (LockObtainFailedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void updateDoc(String id, Document doc) {
try {
IndexWriterConfig iwc = new IndexWriterConfig(LuceneUtil.version, LuceneUtil.analyzer);
iwc.setOpenMode(OpenMode.CREATE_OR_APPEND);
IndexWriter indexWriter = new IndexWriter(LuceneUtil.getFSDirectory(), iwc);
indexWriter.updateDocument(new Term("id", id), doc);
indexWriter.close();
}catch (Exception e) {
e.printStackTrace();
}
}
public static void deleteDoc(String id){
try {
IndexWriterConfig iwc = new IndexWriterConfig(LuceneUtil.version, LuceneUtil.analyzer);
iwc.setOpenMode(OpenMode.CREATE_OR_APPEND);
IndexWriter indexWriter = new IndexWriter(LuceneUtil.getFSDirectory(), iwc);
indexWriter.deleteDocuments(new Term("id", id));
indexWriter.close();
}catch (Exception e) {
e.printStackTrace();
}
}
public static List<Document> query(String id, String name, String sex){
try {
QueryParser parser = new QueryParser(LuceneUtil.version, "problem", new IKAnalyzer());
parser.setAllowLeadingWildcard(true);
String s = "";
if(id != null){
s += "+(id:*"+id+"*)";
}
if(name != null)
s += "+(name:*"+name+"*)";
if(sex != null)
s += "+(sex:*"+sex+"*)";
Query query1 = parser.parse(s);
IndexReader r = IndexReader.open(LuceneUtil.getFSDirectory());
IndexSearcher indexSearcher = new IndexSearcher(r);
TopDocs docs = indexSearcher.search(query1, 100);
int totalHits = docs.totalHits;
System.out.println("查詢文檔總數 :"+totalHits);
List<Document> result = new ArrayList<Document>();
for (ScoreDoc doc : docs.scoreDocs) {
Document document = indexSearcher.doc(doc.doc);
result.add(document);
}
indexSearcher.close();
r.close();
return result;
} catch (Exception e) {
e.printStackTrace();
}
return new ArrayList<Document>();
}
}
這裡要注意的是上面IndexWriterConfig.setOpenMode(OpenMode);的方法是指定IndexWriter的操作模式,分别有三種
OpenMode.CREATE: 建立或覆寫
OpenMode.APPEND: 追加
OpenMode.CREATE_OR_APPEND: 如果不存在則建立,否則追加
3.測試
Lucene的增删改查都是以DOCUMENT的形式互動的
3.1添加
@org.junit.Test
public void add(){
Document doc = new Document();
/**
* field的構造屬性
* 1.field的名稱,查詢時就是通過這個名稱來查找
* 2.field的值
* 3.是否存儲該field
* 4.是否分詞,是否建立索引
* NOT_ANALYZED_NO_NORMS:不分詞,不建立索引
* NOT_ANALYZED:不分詞,建立索引
*/
doc.add(new Field("id", "1", Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS));
doc.add(new Field("name", "fengzp", Field.Store.YES, Field.Index.NOT_ANALYZED));
doc.add(new Field("sex", "man", Field.Store.YES, Field.Index.NOT_ANALYZED));
LuceneUtil.addDoc(doc);
}
添加後本地就會生成相應的檔案:
3.2查詢
@org.junit.Test
public void query(){
List<Document> list = LuceneUtil.query("1", null, null);
for(Document document : list){
System.out.println("id : "+document.get("id"));
System.out.println("name : "+document.get("name"));
System.out.println("sex : "+document.get("sex"));
}
}
傳回結果
3.3更新
@org.junit.Test
public void update(){
Document doc = new Document();
doc.add(new Field("id", "1", Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS));
doc.add(new Field("name", "fengzp123", Field.Store.YES, Field.Index.NOT_ANALYZED));
doc.add(new Field("sex", "man123", Field.Store.YES, Field.Index.NOT_ANALYZED));
LuceneUtil.updateDoc("1", doc);
}
查詢結果:
3.4删除
@org.junit.Test
public void delete(){
LuceneUtil.deleteDoc("1");
}
這樣看下來,是不是跟資料庫的操作好相似呢。
三、lucene的查詢詳解
lucene 的搜尋相當強大,它提供了很多輔助查詢類,每個類都繼承自Query類,各自完成一種特殊的查詢,你可以像搭積木一樣将它們任意組合使用,完成一些複雜操作;另外lucene還提供了Sort類對結果進行排序,提供了Filter類對查詢條件進行限制。你或許會不自覺地拿它跟SQL語句進行比較: “lucene能執行and、or、order by、where、like ‘%xx%’操作嗎?”回答是:“當然沒問題!”
3.1 各種各樣的Query
下面我們看看lucene到底允許我們進行哪些查詢操作:
3.1.1 TermQuery
首先介紹最基本的查詢,如果你想執行一個這樣的查詢:“在content域中包含‘lucene’的document”,那麼你可以用TermQuery:
Term t = new Term("content", " lucene";
Query query = new TermQuery(t);
3.1.2 BooleanQuery
如果你想這麼查詢:“在content域中包含java或perl的document”,那麼你可以建立兩個TermQuery并把它們用BooleanQuery連接配接起來:
TermQuery termQuery1 = new TermQuery(new Term("content", "java");
TermQuery termQuery 2 = new TermQuery(new Term("content", "perl");
BooleanQuery booleanQuery = new BooleanQuery();
booleanQuery.add(termQuery 1, BooleanClause.Occur.SHOULD);
booleanQuery.add(termQuery 2, BooleanClause.Occur.SHOULD);
3.1.3 WildcardQuery
如果你想對某單詞進行通配符查詢,你可以用WildcardQuery,通配符包括’?’比對一個任意字元和’*’比對零個或多個任意字元,例如你搜尋’use*’,你可能找到’useful’或者’useless’:
Query query = new WildcardQuery(new Term("content", "use*");
3.1.4 PhraseQuery
你可能對中日關系比較感興趣,想查找‘中’和‘日’挨得比較近(5個字的距離内)的文章,超過這個距離的不予考慮,你可以:
PhraseQuery query = new PhraseQuery();
query.setSlop(5);
query.add(new Term("content ", “中”));
query.add(new Term(“content”, “日”));
那麼它可能搜到“中日合作……”、“中方和日方……”,但是搜不到“中國某高層上司說日本欠扁”。
3.1.5 PrefixQuery
如果你想搜以‘中’開頭的詞語,你可以用PrefixQuery:
PrefixQuery query = new PrefixQuery(new Term("content ", "中");
3.1.6 FuzzyQuery
FuzzyQuery用來搜尋相似的term,使用Levenshtein算法。假設你想搜尋跟‘wuzza’相似的詞語,你可以:
Query query = new FuzzyQuery(new Term("content", "wuzza");
你可能得到‘fuzzy’和‘wuzzy’。
3.1.7 RangeQuery
另一個常用的Query是RangeQuery,你也許想搜尋時間域從20060101到20060130之間的document,你可以用RangeQuery:
RangeQuery query = new RangeQuery(new Term(“time”, “20060101”), new Term(“time”, “20060130”), true);
最後的true表示用閉合區間。
3.2 QueryParser
看了這麼多Query,你可能會問:“不會讓我自己組合各種Query吧,太麻煩了!”當然不會,lucene提供了一種類似于SQL語句的查詢語句,我們姑且叫它lucene語句,通過它,你可以把各種查詢一句話搞定,lucene會自動把它們查分成小塊交給相應Query執行。下面我們對應每種 Query示範一下:
TermQuery可以用“field:key”方式,例如“content:lucene”。
BooleanQuery中‘與’用‘+’,‘或’用‘ ’,例如“content:java contenterl”。
WildcardQuery仍然用‘?’和‘*’,例如“content:use*”。
PhraseQuery用‘~’,例如“content:"中日"~5”。
PrefixQuery用‘*’,例如“中*”。
FuzzyQuery用‘~’,例如“content: wuzza ~”。
RangeQuery用‘[]’或‘{}’,前者表示閉區間,後者表示開區間,例如“time:[20060101 TO 20060130]”,注意TO區分大小寫。
你可以任意組合query string,完成複雜操作,例如“标題或正文包括lucene,并且時間在20060101到20060130之間的文章”可以表示為:“+ (title:lucene content:lucene) +time:[20060101 TO 20060130]”。代碼如下:
Directory dir = FSDirectory.getDirectory(PATH, false);
IndexSearcher is = new IndexSearcher(dir);
QueryParser parser = new QueryParser("content", new StandardAnalyzer());
Query query = parser.parse("+(title:lucene content:lucene) +time:[20060101 TO 20060130]";
Hits hits = is.search(query);
for (int i = 0; i < hits.length(); i++)
{
Document doc = hits.doc(i);
System.out.println(doc.get("title");
}
is.close();
首先我們建立一個在指定檔案目錄上的IndexSearcher。
然後建立一個使用StandardAnalyzer作為分析器的QueryParser,它預設搜尋的域是content。
接着我們用QueryParser來parse查詢字串,生成一個Query。
然後利用這個Query去查找結果,結果以Hits的形式傳回。
這個Hits對象包含一個清單,我們挨個把它的内容顯示出來
3.3 排序:Sort
有時你想要一個排好序的結果集,就像SQL語句的“order by”,lucene能做到:通過Sort。
Sort sort = new Sort(“time”); //相當于SQL的“order by time”
Sort sort = new Sort(“time”, true); // 相當于SQL的“order by time desc”