爲什麼要學習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入門了,小白一枚,如果以上有什麼不足,請多多指教!