Lucene的介紹與使用

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章