天天看點

Lucene的介紹與使用

為什麼要學習Lucene?

原來的方式實作搜尋功能,我們的搜尋流程如下圖:

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版本的。

Lucene的介紹與使用

老版本下載下傳位址:http://archive.apache.org/dist/lucene/java/

 Lucene、Solr、Elasticsearch關系

Lucene:底層的API,工具包

Solr:基于Lucene開發的企業級的搜尋引擎産品

Elasticsearch:基于Lucene開發的企業級的搜尋引擎産品

Lucene的入門:

開發者使用Lucene的API就是實作對索引的增(建立索引)、删(删除索引)、改(修改索引)、查(搜尋資料)。

建立索引的流程,如下圖(在網上一篇部落格中找到的,在此謝謝各位大神的總結):

Lucene的介紹與使用

本人用的是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)+" 毫秒");
	}
}
           

運作結果:

Lucene的介紹與使用

生成的索引檔案:

Lucene的介紹與使用

上面案例我們示範了添加索引的功能,那麼既然有添加,肯定就有搜尋了,接下來,我們接着上個案例示範搜尋功能;

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的介紹與使用

以上就算是簡單的lucene入門了,小白一枚,如果以上有什麼不足,請多多指教!

繼續閱讀