為什麼要學習Lucene?
原來的方式實作搜尋功能,我們的搜尋流程如下圖:
如果使用者比較少而且資料庫的資料量比較小,那麼這種方式實作搜尋功能在企業中是比較常見的。但是資料量過多時,資料庫的壓力就會變得很大,查詢速度會變得非常慢。我們需要使用更好的解決方案來分擔資料庫的壓力。現在的方案(使用Lucene),如下圖
為了解決資料庫壓力和速度的問題,我們的資料庫就變成了索引庫,我們使用Lucene的API來操作伺服器上的索引庫。這樣完全和資料庫進行了隔離。
資料查詢方法:
1.順序掃描法
所謂順序掃描,例如要找内容包含一個字元串的檔案,就是一個文檔一個文檔的看,對于每一個文檔,從頭看到尾,如果此文檔包含此字元串,則此文檔為我們要找的檔案,接着看下一個檔案,直到掃描完所有的檔案。
這種方法是順序掃描方法,資料量大就搜尋慢。
2.反向索引
例如我們使用新華字典查詢漢字,新華字典有偏旁部首的目錄(索引),我們查字首先查這個目錄,找到這個目錄中對應的偏旁部首,就可以通過這個目錄中的偏旁部首找到這個字所在的位置(文檔)。
Lucene的概述
什麼是lucene?
Lucene是apache軟體基金會4 jakarta項目組的一個子項目,是一個開放源代碼的全文檢索引擎工具包,但它不是一個完整的全文檢索引擎,而是一個全文檢索引擎的架構,提供了完整的查詢引擎和索引引擎,部分文本分析引擎(英文與德文兩種西方語言)。
Lucene提供了一個簡單卻強大的應用程式接口,能夠做全文索引和搜尋, 在Java開發環境裡Lucene是一個成熟的免費開放源代碼工具
Lucene并不是現成的搜尋引擎産品,但可以用來制作搜尋引擎産品
Lucene和搜尋引擎不同,Lucene是一套用java或其它語言寫的全文檢索的工具包,為應用程式提供了很多個api接口去調用,可以簡單了解為是一套實作全文檢索的類庫,搜尋引擎是一個全文檢索系統,它是一個單獨運作的軟體系統
官網位址:http://lucene.apache.org/
什麼是全文索引(全文檢索)
計算機索引程式通過掃描文章中的每一個詞,對每一個詞建立一個索引,指明該詞在文章中出現的次數和位置,當使用者查詢時,檢索程式就根據事先建立的索引進行查找,并将查找的結果回報給使用者的檢索方式。
Lucene版本下載下傳
目前官網最新的版本是8.x系列,現在大部分公司還是用4.x比較多,本人用的也是4.x版本的。
老版本下載下傳位址:http://archive.apache.org/dist/lucene/java/
Lucene、Solr、Elasticsearch關系
Lucene:底層的API,工具包
Solr:基于Lucene開發的企業級的搜尋引擎産品
Elasticsearch:基于Lucene開發的企業級的搜尋引擎産品
Lucene的入門:
開發者使用Lucene的API就是實作對索引的增(建立索引)、删(删除索引)、改(修改索引)、查(搜尋資料)。
建立索引的流程,如下圖(在網上一篇部落格中找到的,在此謝謝各位大神的總結):
本人用的是lucene4.10.3版本進行學習示範的,大家可以在上面連結進行自由下載下傳選擇:
本項目是基于maven來進行jar的管理:
<!-- lucene的核心庫 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>4.10.3</version>
</dependency>
<!-- lucene的查詢解析器 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queryparser</artifactId>
<version>4.10.3</version>
</dependency>
<!-- lucene的預設分詞-->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-common</artifactId>
<version>4.10.3</version>
</dependency>
<!-- lucene的高亮-->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-highlighter</artifactId>
<version>4.10.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
索引實作步驟:
1 建立文檔對象
2 建立存儲目錄
3 建立分詞器
4 建立索引寫入器的配置對象
5 建立索引寫入器對象
6 将文檔交給索引寫入器
7 送出
8 關閉
案例:把指定檔案夾中的檔案名稱-路徑索引到索引庫中,并檢視添加時耗時多久
在E:\\lucene\\file檔案夾下有7個檔案,我們利用lucene進行索引:
public class IndexTest01 {
// 建立寫索引執行個體
private IndexWriter writer;
/**
* 構造方法 執行個體化IndexWriter
* @param indexDir
* @throws Exception
*/
public IndexTest01(String indexDir)throws Exception{
//索引目錄類 指定索引在硬碟中的位置
Directory dir=FSDirectory.open(new File(indexDir));
// 建立分詞器對象 标準分詞器
Analyzer analyzer=new StandardAnalyzer();
//索引寫出工具的配置對象
IndexWriterConfig iwc=new IndexWriterConfig(Version.LATEST,analyzer);
//建立索引的寫出工具類
writer=new IndexWriter(dir, iwc);
}
/**
* 關閉寫索引
* @throws Exception
*/
public void close()throws Exception{
//關閉
writer.close();
}
/**
* 索引指定目錄的所有檔案
* @param dataDir
* @throws Exception
*/
public int index(String dataDir)throws Exception{
//列出指定檔案夾中所有的檔案
File []files=new File(dataDir).listFiles();
for(File f:files){
indexFile(f);
}
//傳回多少個檔案
return writer.numDocs();
}
/**
* 索引指定檔案
* @param f
*/
private void indexFile(File f) throws Exception{
System.out.println("索引檔案:"+f.getCanonicalPath());
//得到每一個文檔對象
Document doc=getDocument(f);
//吧文檔交給indexWriter
writer.addDocument(doc);
writer.commit();//送出
}
/**
* 擷取文檔,文檔裡再設定每個字段
* @param f
*/
private Document getDocument(File f)throws Exception {
// 建立文檔對象
Document doc=new Document();
//這裡用TextField,即建立索引又會被分詞。StringField會建立索引,但是不會被分詞
//建立并添加字段資訊 參數 字段名稱 字段的值 是否存儲Field.Store.YES 存儲 no 不存儲
doc.add(new TextField("contents",new FileReader(f)));
doc.add(new TextField("fileName", f.getName(),Field.Store.YES));
doc.add(new TextField("fullPath",f.getCanonicalPath(),Field.Store.YES));
return doc;
}
public static void main(String[] args) {
String indexDir="E:\\lucene";
String dataDir="E:\\lucene\\file";
IndexTest01 indexer=null;
int numIndexed=0;
//開始時間
long start=System.currentTimeMillis();
try {
//new對象
indexer = new IndexTest01(indexDir);
//調用index方法
numIndexed=indexer.index(dataDir);
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
indexer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//結束時間
long end=System.currentTimeMillis();
System.out.println("索引:"+numIndexed+" 個檔案 花費了"+(end-start)+" 毫秒");
}
}
運作結果:
生成的索引檔案:
上面案例我們示範了添加索引的功能,那麼既然有添加,肯定就有搜尋了,接下來,我們接着上個案例示範搜尋功能;
public class SearchTest {
public static void search(String indexDir,String q)throws Exception{
Directory dir=FSDirectory.open(new File(indexDir));
IndexReader reader=DirectoryReader.open(dir);
//索引搜尋工具
IndexSearcher is=new IndexSearcher(reader);
// 标準分詞器
Analyzer analyzer=new StandardAnalyzer();
//建立查詢解析器 預設要查詢的字段的名稱 分詞器
QueryParser parser=new QueryParser("contents", analyzer);
//建立查詢對象
Query query=parser.parse(q);
//開始計時
long start=System.currentTimeMillis();
搜尋資料,兩個參數:查詢條件對象要查詢的最大結果條數
// 傳回的結果是 按照比對度排名得分前N名的文檔資訊(包含查詢到的總條數資訊、所有符合條件的文檔的編号資訊)。
TopDocs hits=is.search(query, 10);
//計時結束
long end=System.currentTimeMillis();
System.out.println("比對 "+q+" ,總共花費"+(end-start)+"毫秒"+"查詢到"+hits.totalHits+"個記錄");
// 擷取得分文檔對象(ScoreDoc)數組.SocreDoc中包含:文檔的編号、文檔的得分
ScoreDoc[] docs = hits.scoreDocs;
for(ScoreDoc scoreDoc:docs){
//取得文檔編号
int docId = scoreDoc.doc;
//根據編号去找文檔
Document doc=is.doc(docId);
//根據字段查找指定的内容
System.out.println("符合送出的路徑:"+doc.get("fullPath"));
System.out.println("得分:"+scoreDoc.score);
}
reader.close();
}
public static void main(String[] args) {
String indexDir="E:\\lucene";
String q="Zygmunt Saloni";
try {
search(indexDir,q);
} catch (Exception e) {
e.printStackTrace();
}
}
}
結果:
以上就算是簡單的lucene入門了,小白一枚,如果以上有什麼不足,請多多指教!