Lucene5.5.2開發入門詳解

 

1、 全文檢索的概念

常見的全文檢索

1) 在window系統中,能夠指定磁盤中的某一個位置來搜索你想要獲得的東西。這個功能是windows比較經常使用的功能。java

2) 在eclipse中,幫助文檔搜索:heltp 》help content,文件搜索ctrl+h,ctrl+shift+R.sql

 

其實eclipse的搜索功能就是用lucene寫的數據庫

 

3) 在百度和google 中,能夠搜索互聯網中的信息,有:網頁、pdf、word音頻、視頻等內容。apache

4) Taobao搜索商品。windows

全文檢索的應用場景

一、站內搜索

l 一般用於在大量數據出現的系統中,找出你想要的資料。常見的有緩存

l baidu貼吧框架

l 商品網站的搜索等eclipse

l 中關村在線     商品的名稱、電腦硬件名稱 (CPU)iphone

l 文件管理系統工具

l 對文件的搜索功能。Window的文件搜索

二、垂直搜索

l 是針對 某個行業的搜索引擎

l 是搜索引擎的細分和延伸

l 是針對網頁庫中的專門信息的整合

l 其特色是專、深、精,並具備行業色彩

l 能夠應用於購物搜索、房產搜索、人才搜索

說明

l 從大量的信息中快速、準確地查找出要的信息

l 搜索的內容是文本信息(不是多媒體)

l 全文檢索只是一個概念,而具體實現有不少框架,lucene是其中的一種,常見的還有solr,es等。其實solr和es底層也是lucene,只是作了一些封裝。

 

2、Lucene與sql性能比較

lsql:好比我要查找某個商品,根據商品名,好比select * from product where doctname like %keywords%,這樣查詢的話對於數據量少是能夠的,但是一旦你的數據量巨大幾萬幾十萬的時候,你的性能將會極大的減弱。

l lucene:在索引庫裏面會把全部的商品名根據分詞器創建索引,就比如新華字典,索引對應document,好比輸入襯衫,那麼就會根據索引迅速的翻到襯衫對應的商品名,時間迅速,性能很好。

 

3、 第一個lucene程序

開發前的準備

1. 版本說明

因爲lucene的版本很是多,目前從apach官網上能下載到完整jar包的版本目前只有5.5.2和6.2.0,所以這裏選擇5.5.2版本,5.5.2版本所須要的jdk須要1.7。

2. 下載地址

http://apache.fayea.com/lucene/java/5.5.2/

3. 本例使用到的jar包

一、 lucene-core-5.5.2.jar(核心包)

二、 lucene-analyzers-common-5.5.2.jar(分詞器)

三、 lucene-backward-codecs-5.5.2.jar(兼容老版本)

四、 lucene-queryparser-5.5.2.jar(查詢解析)

創建索引

 

步驟:

1. 建立IndexWriter對象

a) 須要指定索引庫的位置,索引庫的位置多是磁盤,也有多是內存。

b) 須要指定用什麼分詞器,由於建立索引的時候,會按照必定的切份內容的方式去建立索引,這樣才能經過關鍵字查找到對應的內容。

2. 把JavaBean轉化爲Document

a) Javabean的每個字段就表明一個field,一個javabean就表明一個document。

3. 利用IndexWriter.addDocument方法增長索引

a) 說明:關於這個方法,實際上作了兩件事情,先是建立好內容,再根據指定的分詞器分詞出關鍵字,建立索引目錄。

 

4. 關閉IndexWriter

說明:使用完以後,要關閉掉資源,由於這裏有io流資源相關的操做。另一個緣由關閉indexwriter,同時會提交操做,這樣才能建立索引成功。

關鍵類說明:

IndexWriter是增、刪、改索引過程的核心組件。這個類負責建立新索引或者打開已有索引,以及向索引中添加、刪除或者更新被索引文檔的信息。

IndexWriterConfig獲取indexwriter所須要的對象,能夠理解爲獲取indexwriter的一個設置對象,能夠設定使用什麼分詞建立索引,也能夠設定建立索引的方式,是追加仍是覆蓋。

Directory:在數據庫中,數據庫中的數據文件存儲在磁盤上。索引庫也是一樣,索引庫中的索引數據也在磁盤上存在,咱們用Directory這個類來描述。

Analyzer指定錄入的內容按照切分紅若干個關鍵詞放入到目錄庫中。

Field:存放一個鍵值對。這麼理解:相似於數據庫的一個字段。Filed的實現有不少種,如:StringField、TextField、IntField。Field包含了三個屬性:類型、name、value。不一樣的實現體現了不一樣的類型,name、value表明的是一對鍵值對的名稱和值。

Document:這麼理解:相似於數據庫的一條記錄。Document的結構爲:Document(List<Field>)。

Store:一個枚舉類,兩個值yes和no,yes表明內容存放在索引庫中,no表明內容不存放在索引庫中,這裏要注意是不存放內容,可是是否會做爲索引關鍵詞是另外一回事。

編碼實現:

 

注:關於上面狀況下使用IntField,StringField,TextField的問題。

IntField:主要在保存相似數量,例如庫存數量,須要常常用到範圍查詢的時候。使用這個字段保存的數據,沒法經過正常的關鍵字查找到。

StringField:不須要分詞,須要精確匹配的這段,如數據庫的id,地理名等。

TextField:須要分詞的字段

索引查看工具luke

搜索

步驟:

1. 建立IndexSearch

a) 須要拿到indexReader對象

2. 建立Query對象

3. 進行搜索

4. 根據獲取到的TopDocs,得到總結果數和前N行目錄ScoreDoc。

5. 根據目錄ID列表獲取到document。indexSearcher.doc(int)。

關鍵類說明:

IndexSearch:是查索引過程的核心組件。

QueryParser 解析用戶的查詢字符串進行搜索,是一個解析用戶輸入的工具,能夠經過掃描用戶輸入的字符串,生成Query對象。

Query查詢對象。封裝要查詢的相關信息。

TopDocs索引庫目錄的一個引用。能夠理解爲搜索到的目錄結果集的引用。主要包含了兩個信息,總的記錄數和目錄結果集。

ScoreDoc一條目錄。包含了得分和索引下標。

編碼:

刪除索引

編碼:

說明:獲取到的indexwriter必定要關閉,緣由和建立索引的緣由同樣。

更新索引

lucene的更新操做與數據庫的更新操做是不同的。由於在更新的時候,有可能變換了關鍵字的位置,這樣分詞器對關鍵字還得從新查找,並且還得在目錄和內容中替換,這樣作的效率比較低,因此lucene的更新操做是刪除增長兩步驟來完成的。

注:關於索引庫優化。

舊版本:每執行一次就生成一個cfs文件。若是增長、刪除反覆操做不少次,就會形成文件大量增長,這樣檢索的速度也會降低,因此咱們有必要去優化索引結構。使文件的結構發生改變從而提升效率。

新版本當達到一個數量後會自動優化。

優化lucene能過forceMerge方法來將當小文件達到多少個時,就自動合併多個小文件爲一個大文件,由於它的使用代價較高不意見使用此方法,默認狀況下lucene會本身合併。

編碼:

內存索引庫

特色

在內存中開闢一塊空間,專門爲索引庫存放。這樣有如下幾個特徵:

1) 由於索引庫在內存中,因此訪問速度更快。

2) 在程序退出時,索引庫中的文件也相應的消失了。

3) 若是索引庫比較大,必須得保證足夠多的內存空間。

根據Directory不一樣的子類獲取到內存索引仍是文件索引庫。建立索引的操做基本相似。

編碼:

分詞器

英文分詞器

步驟:切分關鍵詞

去除停用詞

轉爲小寫(搜索時不區分大小寫,由於分詞器會幫你轉化)

如:I am a programmer,live in shenzhen,what about you?

分詞後的結果爲:

i

am

programmer

live

shenzhen

what

about

you

分詞代碼(無需研究沒有什麼意義,看分詞後的結果就行了):

/**
	 * 英文分詞器
	 */
	@Test
	public void testEnAnalyzer() throws Exception{
		
		Analyzer analyzer = new StandardAnalyzer();
		
		String text = "I am a programmer,live in shenzhen,what about you?";
		
		this.testAnalyzer(text, analyzer);
	}
	
	/**
	 * 中文分詞器ik
	 */
	@Test
	public void testIKAnalyzer() throws Exception{
		
		Analyzer analyzer = new IKAnalyzer();
		
//		String text = "我是一名程序猿,居住在深圳,你呢?";
		
		String text = "立創商城";
		
		this.testAnalyzer(text, analyzer);
	}

	private void testAnalyzer(String text , Analyzer analyzer) throws Exception {

		TokenStream ts = null;
		
		try {
			
			ts = analyzer.tokenStream("myfield", new StringReader(text));

			// 獲取詞元文本屬性
			CharTermAttribute term = ts.addAttribute(CharTermAttribute.class);

			// 重置TokenStream(重置StringReader)
			ts.reset();
			
			// 迭代獲取分詞結果
			while (ts.incrementToken()) {
				
				System.out.println( term.toString());
			}
			// 關閉TokenStream(關閉StringReader)
			ts.end(); 

		} catch (IOException e) {
			
			e.printStackTrace();
		} finally {
			
			// 釋放TokenStream的全部資源
			if (ts != null) {
				try {
					ts.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

中文分詞器

1) 經常使用的分詞器有:IKAnalyzer、paoding、mmseg4j、imdict。 這裏選用:IKAnalyzer

2) 如何自定義擴展詞彙:將IKAnalyzer.cfg.xml拷貝到src下,而後新建一個本身的自定義詞典,配置IKAnalyzer.cfg.xml便可。

源碼分析入口:

初始化最主要的工做就是讀入詞典,並將這些詞放入內存字典樹

1.main2012.dic(關鍵詞)2.quantifier.dic(量詞)3.stopword.dic(停用詞,可擴展)4.ext.dic(擴展詞,可選)   

注意:自定義的詞彙文件要注意編碼格式:UTF-8無BOM格式,不然分詞會沒有做用。

關於中文分詞器,一定會有一個默認的詞庫文件。

IK Analyzer2012支持 細粒度切分 和 智能切分。

樣例:

  1)文本原文:

  IKAnalyzer是一個開源的,基於java語言開發的輕量級的中文分詞工具包。從2006年12月推出1.0版本開始,IKAnalyzer已經推出了3個大版本。

  智能分詞結果:

  ikanalyzer | 是 | 一個 | 開源 | 的 | 基於 | java | 語言 | 開發 | 的 | 輕量級 | 的 | 中文 | 分詞 | 工具包 | 從 | 2006年 | 12月 | 推出 | 1.0版 | 開始 | ikanalyzer | 已經 | 推 | 出了 | 3個 | 大 | 版本

  最細粒度分詞結果:

  ikanalyzer | 是 | 一個 | 一 | 個 | 開源 | 的 | 基於 | java | 語言 | 開發 | 的 | 輕量級| 量級 | 的 | 中文 | 分詞 | 工具包 | 工具 | 包 | 從 | 2006 | 年 | 12 | 月 | 推出 | 1.0 | 版 | 開始 | ikanalyzer | 已經 | 推出 | 出了 | 3 | 個 | 大 | 版本

Lucene分頁

Lucene的分頁,總的來講有兩種形式。 

方式一:在ScoresDocs裏進行分頁

原理:在lucene裏面,每個索引內容都會對應一個不重複的docid,而這一點跟Oralce數據庫的僞列rownum同樣,偏偏正是因爲這個docid的 存在,因此讓lucene在海量數據檢索時從而擁有更好的性能,咱們都知道Oracle數據庫在分頁時,使用的就是僞列進行分頁,那麼個人lucene也是同樣,既然有一個docid的存在,那麼就能夠把數據所有拿到內存中,根據docid進行分頁。 

編碼實現:

方式二:利用SearchAfter,再次查詢分頁

原理:先拿到上一頁最後一個ScoreDoc,利用lucene提供的SearchAfter和獲得的ScoreDoc查詢當前頁的索引結果。

編碼實現:

兩種方式的優缺點對比

編號

方式

優勢

缺點

1

在ScoresDocs裏進行分頁

無需再次查詢索引,速度很快

在海量數據時,會內存溢出

2

利用SearchAfter,再次查詢分頁

適合大批量數據的分頁

再次查詢,速度相對慢一點,但能夠利用緩存彌補

 

搜索方式Query

一、TermQuery

關鍵字查詢,不分詞。

Query query = new TermQuery(new Term("id", "1"));

二、MatchAllDocsQuery

查詢全部。

Query query = new MatchAllDocsQuery();

三、WildcardQuery

通配符查詢。?:匹配任意一個字符,*匹配多個任意字符

Query query = new WildcardQuery(new Term("name","i?"));

四、PhraseQuery

同個字段多個關鍵字組合查詢,而且的關係。不分詞,關鍵字區分大小寫

Query query = new PhraseQuery("name", "iphone","6plus");

五、BooleanQuery

多個字段關鍵字組合查詢

BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder();

booleanQuery.add(new TermQuery(new Term("name", "iphone")), Occur.MUST);

booleanQuery.add(new TermQuery(new Term("description", "grey")), Occur.MUST);

Query query = booleanQuery.build();

六、NumericRangeQuery

範圍查詢,只針對IntField、FloatField這些數字類型的Field才能進行範圍查詢。

Query query = NumericRangeQuery.newIntRange("id", 1, 2, true, true);

說明:第四個參數和第五個參數,表明是否大於等於的意思,true即爲包含。

排序

觀察源碼,關於IndexSearcher.search有不少重載的方法,其中有方法能夠傳遞Sort對象進去實現排序,以下圖:

構造Sort字段實現排序,其中,這裏要注意Lucene5.x的排序相比lucene4.x以前改動較大。

主要在排序字段須要在創建索引的時候單獨指定,即不能根據索引的內容字段或者關鍵字字段去排序。以下圖,若是某個字段須要支持排序,須要額外構建下面類型的Field字段,不然沒法排序。

編碼實現:

4、相關文件

IK分詞器:http://pan.baidu.com/s/1dFMcxnb

luke:http://pan.baidu.com/s/1dE0N2d7

相關文章
相關標籤/搜索