您好,登錄后才能下訂單哦!
1. 全文檢索基礎
1.1. 我們身邊的搜索功能
1. Windows系統中的有搜索功能:打開“我的電腦”,按“F3”就可以使用查找的功能,查找指定的文件或文件夾。搜索的范圍是整個電腦中的文件資源。
2. Eclipse中的幫助子系統:點擊HelpàHelp Contents,可以查找出相關的幫助信息。搜索的范圍是Eclipse的所有幫助文件。
3. 在BBS、BLOG等系統中提供的搜索文章的功能,如這里的貼吧的例子。搜索的范圍是系統內的文章數據(都在數據庫中)。
4. 搜索引擎,如Baidu或Google等,可以查詢到互聯網中的網頁、PDF、DOC、PPT、圖片、音樂、視頻等。下圖是使用百度搜索的效果:
以上的查詢功能都類似。都是查詢的文本內容,都是相同的查詢方式,即找出含有指定字符串的資源,不同的只是查詢范圍(分別為硬盤、所有幫助文件、數據庫、互聯網)。
1.2. 什么是全文檢索
全文檢索是計算機程序通過掃描文章中的每一個詞,對每一個詞建立一個索引,指明該詞在文章中出現的次數和位置。當用戶查詢時根據建立的索引查找,類似于通過字典的檢索字表查字的過程。
對于搜索,按被搜索的資源類型,分為兩種:可以轉為文本的、多媒體類型。我們上一節提到的搜索功能都是搜索的可以轉為文本的資源(第一種)。注意,百度或谷歌提供的音樂或視頻搜索不是多媒體搜索,他們是按文件名搜索。在智能手機上有一款音樂搜索的軟件,可以讓他聽10秒鐘的音樂,然后他就能上網找出這段音樂的名稱、演奏者等信息。這是多媒體搜索。
按搜索的方式,上一節提到的搜索功能都是不處理語義,只是找出包含指定詞的所有資源(只對詞進行匹配)。下圖就是顯示“中國的首都是哪里”這個搜索要求對應的結果,可以看到,是沒有“北京”這個結果的,結果頁面都是出現了這些詞的網頁:
全文檢索(Full-Text Retrieval)是指以文本作為檢索對象,找出含有指定詞匯的文本。全面、準確和快速是衡量全文檢索系統的關鍵指標。
關于全文檢索,我們要知道:1,只處理文本。2,不處理語義。3,搜索時英文不區分大小寫。4,結果列表有相關度排序。
在信息檢索工具中,全文檢索是最具通用性和實用性的。
1.3. 全文檢索的應用場景
我們使用Lucene,主要是做站內搜索,即對一個系統內的資源進行搜索。如BBS、BLOG中的文章搜索,網上商店中的商品搜索等。使用Lucene的項目有Eclipse、Jira等。一般不做互聯網中資源的搜索,因為不易獲取與管理海量資源(專業搜索方向的公司除外)。
所以,學完Lucene后我們就可以為自已的系統增加全文檢索的功能。跟這個學習內容相關的練習為:為“傳智手播客貼吧”增加搜索其中的文章的功能。
1.4. 全文檢索不同于數據庫搜索
全文檢索不同于數據庫的SQL查詢。(他們所解決的問題不一樣,解決的方案也不一樣,所以不應進行對比)。在數據庫中的搜索就是使用SQL,如:SELECT * FROM t WHERE content like ‘%ant%’。這樣會有如下問題:
1. 匹配效果:如搜索ant會搜索出planting。這樣就會搜出很多無關的信息。
2. 相關度排序:查出的結果沒有相關度排序,不知道我想要的結果在哪一頁。我們在使用百度搜索時,一般不需要翻頁,為什么?因為百度做了相關度排序:為每一條結果打一個分數,這條結果越符合搜索條件,得分就越高,叫做相關度得分,結果列表會按照這個分數由高到低排列,所以第1頁的結果就是我們最想要的結果。
3. 全文檢索的速度大大快于SQL的like搜索的速度。這是因為查詢方式不同造成的,以查字典舉例:數據庫的like就是一頁一頁的翻,一行一行的找,而全文檢索是先查目錄,得到結果所在的頁碼,再直接翻到這一頁。
所以數據庫搜索不能替代全文檢索。
1.5. Lucene簡介
全文檢索就如同ORM,是一個概念。ORM的框架有很多種:Hibernate、TopLink、iBatis等,我們之前學習的是Hibernate。同樣的,全文檢索領域中也有多種框架,Lucene就是其中的一個用開源的全文檢索框架。
Lucene的主頁為:http://lucene.apache.org/。本文檔中所使用的Lucene為3.0.1的版本。
2. 第一個Lucene程序
2.1. 準備Lucene的開發環境
搭建Lucene的開發環境只需要加入Lucene的Jar包,要加入的jar包至少要有:
l lucene-core-3.0.1.jar(核心包)
l contrib\analyzers\common\lucene-analyzers-3.0.1.jar(分詞器)
l contrib\highlighter\lucene-highlighter-3.0.1.jar(高亮)
l contrib\memory\lucene-memory-3.0.1.jar(高亮)
2.2. 實現建立索引功能(Indexer類)
2.3. 實現搜索功能(Searcher類)
3. Lucene的核心概念
3.1. 全文檢索程序的工作流程
如果信息檢索系統在用戶發出了檢索請求后再去互聯網上找答案,根本無法在有限的時間內返回結果。所以要先把要檢索的資源集合放到本地,并使用某種特定的結構存儲,稱為索引,這個索引的集合稱為索引庫。由于索引庫的結構是按照專門為快速查詢設計的,所以查詢的速度非常快。我們每次搜索都是在本地的索引庫中進行,如下圖:
從圖片上可以看出,我們不僅要搜索,還要保證數據集合與索引庫的一致性。所以對于全文檢索功能的開發,要做的有兩個方面:索引庫管理(維護索引庫中的數據)、在索引庫中進行搜索。而Lucene就是操作索引庫的工具。
3.2. 使用Lucene的API操作索引庫
索引庫是一個目錄,里面是一些二進制文件,就如同數據庫,所有的數據也是以文件的形式存在文件系統中的。我們不能直接操作這些二進制文件,而是使用Lucene提供的API完成相應的操作,就像操作數據庫應使用SQL語句一樣。
對索引庫的操作可以分為兩種:管理與查詢。管理索引庫使用IndexWriter,從索引庫中查詢使用IndexSearcher。Lucene的數據結構為Document與Field。Document代表一條數據,Field代表數據中的一個屬性。一個Document中有多個Field,Field的值為String型,因為Lucene只處理文本。
我們只需要把在我們的程序中的對象轉成Document,就可以交給Lucene管理了,搜索的結果中的數據列表也是Document的集合。
有了這些概念,可以寫HelloWorld了,其他的概念可以在寫完HelloWorld后再進行說明。
3.3. 索引庫結構——倒排序索引
我們需要對文檔進行預處理,建立一種便于檢索的數據結構,以此來提高信息檢索的速度,這種數據結構就是索引。目前廣泛使用的一種索引方式是倒排序索引。
倒排序索引的原理就如同查字典。要先查目錄,得到數據對應的頁碼,在直接翻到指定的頁碼。不是在文章中找詞,而是從目錄中找詞所在的文章。這需要在索引庫中生成一個詞匯表(目錄),在詞匯表中的每一個條記錄都是類似于“詞à所在文檔的編號列表”的結構,記錄了每一個出現過的單詞,和單詞出現的地方(哪些文檔)。查詢時先查詞匯表,得到文檔的編號,再直接取出相應的文檔。
把數據轉成指定格式放到索引庫中的操作叫做建立索引。建立索引時,在把數據存到索引庫后,再更新詞匯表。進行搜索時,先從檢索詞匯表開始,然后找到相對應的文檔。如果查詢中僅包含一個關鍵詞,則在詞匯表中找到該單詞,并取出他對應的文檔就可以了。如果查詢中包含多個關鍵詞,則需要將各個單詞檢索出的記錄進行合并再取出相應的文檔記錄。
如果詞匯表中有一個詞“傳智播客”對應的文檔編號列表為“1”。現在又有添加了一個包含“傳智播客”的文檔,則詞匯表中的“傳智播客”詞后對應的編號列表變成了“1,2”。因為關鍵詞的數量受實際語言的限制,所以不用擔心詞匯表會變的很大。
3.4. 索引文件的檢索與維護,更新是先刪除后創建
維護倒排索引有三個操作:添加、刪除和更新文檔。但是更新操作需要較高的代價。因為文檔修改后(即使是很小的修改),就可能會造成文檔中的很多的關鍵詞的位置都發生了變化,這就需要頻繁的讀取和修改記錄,這種代價是相當高的。因此,一般不進行真正的更新操作,而是使用“先刪除,再創建”的方式代替更新操作。
3.5. 建立索引的執行過程
在建立索引時,先要把文檔存到索引庫中,還要更新詞匯表。如下圖:
1. 我們做的操作:把數據對象轉成相應的Document,其中的屬性轉為Field。
2. 我們做的操作:調用工具IndexWriter的addDocument(doc),把Document添加到索引庫中。
3. Lucene做的操作:把文檔存到索引庫中,并自動指定一個內部編號,用來唯一標識這條數據。內部編號類似于這條數據的地址,在索引庫內部的數據進行調整后,這個編號就可能會改變,同時詞匯表中引用的編號也會做相應改變,以保證正確。但我們如果在外面引用了這個編號,前后兩次去取,得到的可能不是同一個文檔!所以內部編號最好只在內部用。
4. Lucene做的操作:更新詞匯表。把文本中的詞找出并放到詞匯表中,建立與文檔的對應關系。要把哪些詞放到詞匯表中呢,也就是文本中包含哪些詞呢?這就用到了一個叫做Analyzer(分詞器)的工具。他的作用是把一段文本中的詞按規則取出所包含的所有詞。對應的是Analyzer類,這是一個抽象類,切分詞的具體規則是由子類實現的,所以對于不同的語言(規則),要用不同的分詞器。如下圖:
在把對象的屬性轉為Field時,相關代碼為:doc.add(new Field("title", article.getTitle(), Store.YES, Index.ANALYZED))。第三與第四個參數的意思為:
枚舉類型
枚舉常量
說明
Store
NO
不存儲屬性的值
YES
存儲屬性的值
Index
NO
不建立索引
ANALYZED
分詞后建立索引
NOT_ANALYZED
不分詞,把整個內容作為一個詞建立索引
說明:Store是影響搜索出的結果中是否有指定屬性的原始內容。Index是影響是否可以從這個屬性中查詢(No),或是查詢時可以查其中的某些詞(ANALYZED),還是要把整個內容作為一個詞進行查詢(NOT_ANALYZED)。
3.6. 從索引庫中搜索的執行過程
在進行搜索時,先在詞匯表中查找,得到符合條件的文檔編號列表。再根據文檔編號真正的去取出數據(Document)。如下圖:
1, 把要查詢字符串轉為Query對象。這就像在Hibernate中使用HQL查詢時,也要先調用Session.createQuery(hql)轉成Hibernate的Query對象一樣。把查詢字符串轉換成Query是使用QueryParser,或使用MultiFieldQueryParser。查詢字符串也要先經過Analyzer(分詞器)。要求搜索時使用的Analyzer要與建立索引時使用的Analzyer要一致,否則可能搜不出正確的結果。
2, 調用IndexSearcher.search(),進行查詢,得到結果。此方法返回值為TopDocs,是包含結果的多個信息的一個對象。其中有totalHits 代表決記錄數,ScoreDoc的數組。ScoreDoc是代表一個結果的相關度得分與文檔編號等信息的對象。
3, 取出要用到的數據列表。調用IndexSearcher.doc(scoreDoc.doc)以取出指定編號對應的Document數據。在分頁時要用到:一次只取一頁的數據。
4. Lucene的核心API介紹
4.1. 與建立索引有關的API
4.1.1. IndexWriter
構造方法1:IndexWriter(Directory d, Analyzer a, MaxFieldLength mfl)
構造方法2:IndexWriter(Directory d, Analyzer a, boolean create, MaxFieldLength mfl),第三個參數指定,true表示建立新的索引庫或覆蓋現有的索引庫(刪除后重建);false表示使用已有的索引庫,如果不存在,就報錯。
commit()
rollback()
close()
4.1.2. Directory--FSDirectory、RAMDirectory
4.1.3. Analyzer--分詞器
4.1.4. Document
4.1.5. Field
4.2. 與搜索有關的API
4.2.1. IndexSearcher
在索引庫中進行搜索是使用類IndexSearcher。創建其實例的構造方法為:IndexSearcher (Directory path)。用完后要調用IndexSearcher.close()方法釋放資源。
4.2.2. Term
4.2.3. Query類及其子類
Query:抽象類,必須通過一系列子類來表述檢索的具體需求。
4.2.4. QueryParser與MultiFieldQueryParser
1, QueryParser與MultiFieldQueryParser
查詢分析器,處理用戶輸入的查詢條件。把用戶輸入的非格式化檢索詞轉化成后臺檢索可以理解的Query對象。使用的構造方法為:QueryParser(Version matchVersion, String f, Analyzer a)
2, MultiFieldQueryParser
是QueryParser的子類。與父類相比,MultiFieldQueryParser可以在多個屬性中搜索。使用的構造方法為:MultiFieldQueryParser(Version matchVersion, String[] fields, Analyzer analyzer)
4.2.5. TopDocs
4.2.6. ScoreDoc
5. 維護索引庫
5.1. 數據與Document、Field的轉換
我們在應用程序中使用對象表示數據。在數據庫中使用的是表記錄,所以存在來回轉換的問題。同樣,要索引庫中使用的是Document,也存在來回轉換的問題。如下圖:
對于一個要進行搜索的實體對象,我們會寫一個對應的工具類,其中有兩個方法:
Document Object2Document(Object object); // 對象àDocument
Object Document2Object(Document doc); // Documentà對象
在轉換時,對象中的屬性對應Document中的Field。由于Lucene只處理文本,所有所有的屬性值在存儲前都要先轉成字符串。使用構造方法:Field(String name, String value, Store store, Index index)。
Store與Index都是枚舉類型。Store:指定是否把當前屬性值的原始內容存儲到索引庫中。如果存儲(YES),在搜索出相應數據時這個屬性就有原始的值;如果不存儲(NO),得到的數據的這個屬性的值為null。Index:指定是否建立索引(詞匯表)。建立索引才能被搜索到。不可以不存儲也不建立索引(沒有意義)。
// Store 指定當前字段的數據要不要存到索引庫中
// Index 指定當前字段的數據是否可以被搜索(是否更新詞匯表)
索引設置的一些建議:
1) 盡量減少不必要的存儲
2) 不需要檢索的內容不要建立索引
3) 非文本格式需要提前轉化
4)需要整體存放的內容不要分詞
NumericUtils與DateTools
如果屬性的類型不是字符串,則要先進轉換:如果是數字類型,使用NumericUtils。如果是日期類型,則使用DataTools。
5.2. 創建索引
5.3. 刪除索引
5.4. 更新索引
5.5. 索引庫文件的優化
5.5.1. 合并索引庫文件
IndexWriter.optimize()
indexWriter.setMergeFactor(int)
5.5.2. 使用RAMDirectory
Lucene的API接口設計的比較通用,輸入輸出結構都很像數據庫的表==>記錄==>字段,所以很多傳統的應用的文件、數據庫等都可以比較方便的映射到Lucene的存儲結構/接口中。總體上看:可以先把Lucene當成一個支持全文索引的數據庫系統。
Lucene的索引存儲位置使用的是一個接口(抽象類),也就可以實現各種各樣的實際存儲方式(實現類、子類),比如存到文件系統中,存在內存中、存在數據庫中等等。Lucene提供了兩個子類:FSDirectory與RAMDirectory。
1, FSDirectory:在文件系統中,是真實的文件夾與文件。
2, RAMDirectory:在內存中,是模擬的文件夾與文件。與FSDirectory相比:1因為沒有IO操作,所以速度快。2,因為在內存中,所以在程序退出后索引庫數據就不存在了。
索引庫的相關操作:
1, 合并索引庫:Directory.addIndexes()
2, 索引的優化:IndexWriter.optimize()
6. 從索引庫中搜索
6.1. 兩種查詢方式說明
6.2. 使用查詢字符串+QueryParser的查詢方式
QueryParser:只在一個字段中查詢
MultiFieldQueryParser:可以在多個字段查詢
6.3. 使用構建Query子類的查詢方式
6.3.1. TermQuery
關鍵詞查詢
6.3.2. NumericRangeQuery
范圍查詢。使用靜態方法構造實例:
newIntRange(final String field,
Integer min, Integer max,
final boolean minInclusive, final boolean maxInclusive)
newLongRange(final String field,
Long min, Long max,
final boolean minInclusive, final boolean maxInclusive)
newFloatRange(final String field,
Float min, Float max,
final boolean minInclusive, final boolean maxInclusive)
newDoubleRange(final String field,
Double min, Double max,
final boolean minInclusive, final boolean maxInclusive)
6.3.3. WildcardQuery
通配符查詢
6.3.4. PhraseQuery
短語查詢
public void add(Term term, int position)
public void setSlop(int s)
例:add( new Term(“name”, “lucene”, 1);
add(new Term(“name”, “教程”, 3);
代表搜索的是“Lucene ? 教程”,?表示中間隔一個詞。
setSlop(2);
代表這兩個詞中間可以最多隔2個詞
6.3.5. BooleanQuery
public void add(Query query, Occur occur)
Occur 用于表示布爾查詢子句關系的類,包括:
Occur.MUST,Occur.MUST_NOT,Occur.SHOULD。
1, MUST和MUST:取得連個查詢子句的交集。
2, MUST和MUST_NOT:包含MUST并且查詢結果中不包含MUST_NOT的檢索結果。
3, SHOULD與SHOULD,表示“或”關系,最終檢索結果為所有檢索子句的并集。
一般不單獨使用,因為單獨就不應使用BooleanQuery了。
使用時注意:
1, 單獨使用MUST_NOT:無意義,檢索無結果。(也不報錯)
2, MUST_NOT和MUST_NOT:無意義,檢索無結果。(也不報錯)
3, 單獨使用SHOULD:結果相當于MUST。
4, SHOULD和MUST_NOT: 此時SHOULD相當于MUST,結果同MUST和MUST_NOT。
5, MUST和SHOULD:此時SHOULD無意義,結果為MUST子句的檢索結果。
6.3.6. 測試各種查詢的代碼模板
// 查詢出所有文檔
@Test
public void testMatchAllDocsQuery () { }
// 關鍵詞查詢
@Test
public void testTermQuery() { }
// 范圍查詢
@Test
public void testRangeQuery() { }
// 通配符查詢
@Test
public void testWildcardQuery() { }
// 短語查詢
@Test
public void testPhraseQuery() { }
// 布爾查詢
@Test
public void testBooleanQuery() { }
6.4. 排序
通過改變文檔Boost值來改變排序結果。Boost是指索引建立過程中,給整篇文檔或者文檔的某一特定屬性設定的權值因子,在檢索時,優先返回分數高的。通過Document對象的setBoost()方法和Field對象的setBoost()方法,可以分別為Document和Field指定Boost參數。不同在于前者對文檔中每一個域都修改了參數,而后者只針對指定域進行修改。默認情值為1F,一般不做修改。
使用Sort對象定制排序。Sort支持的排序功能以文檔當中的域為單位,通過這種方法,可以實現一個或者多個不同域的多形式的值排序。時間類型的屬性采用STRING常量。
6.4.1. 按相關度排序
1,相關度得分是在查詢時根據查詢條件實進計算出來的
2,如果索引庫據不變,查詢條件不變,查出的文檔得分也不變
6.4.2. 按指定的字段排序
If you want to be able to sort results by a Field value, you must add it as a Field that is indexed but not analyzed, using Field.Index.NOT_ANALYZED.
6.5. 過濾
使用Filter可以對搜索結果進行過濾以獲得更小范圍的結果。使用Filter對性能的影響很大(有可能會使查詢慢上百倍)。
6.6. 高亮
6.6.1. 高亮所需的jar包
contrib\highlighter\lucene-highlighter-3.0.1.jar
contrib\memory\lucene-memory-3.0.1.jar
6.6.2. 實現高亮效果的代碼
// 生成高亮器
Formatter formatter = new SimpleHTMLFormatter("<span class='kw'>", "</span>");
Scorer scorer = new QueryScorer(query);
Highlighter highlighter = new Highlighter(formatter, scorer);
highlighter.setTextFragmenter(new SimpleFragmenter(20));
// 使用高亮器:對content屬性值進行摘要并高亮
String text = highlighter.getBestFragment(LuceneUtils.getAnalyzer(), "content", doc.get("content"));
// 如果進行高亮的屬性值中沒有要搜索的關鍵字,則返回null
if (text != null) {
doc.getField("content").setValue(text);
}
7. 分詞器
7.1. 分詞器的作用
在創建索引時會用到分詞器,在使用字符串搜索時也會用到分詞器,這兩個地方要使用同一個分詞器,否則可能會搜索不出結果。
Analyzer(分詞器)的作用是把一段文本中的詞按規則取出所包含的所有詞。對應的是Analyzer類,這是一個抽象類,切分詞的具體規則是由子類實現的,所以對于不同的語言(規則),要用不同的分詞器。如下圖:
7.2. 分詞器的工作流程
(英文)分詞器的一般工作流程:
1, 切分關鍵詞
2, 去除停用詞
3,對于英文單詞,把所有字母轉為小寫(搜索時不區分大小寫)
說明:有的分詞器還對英文進行形態還原,就是去除單詞詞尾的形態變化,將其還原為詞的原形。這樣做可以搜索出更多有意義的結果。如搜索sutdent時,也可以搜索出students,這是很有用的。
7.3. 停用詞
有些詞在文本中出現的頻率非常高,但是對文本所攜帶的信息基本不產生影響,例如英文的“a、an、the、of”,或中文的“的、了、著”,以及各種標點符號等,這樣的詞稱為停用詞(stop word)。文本經過分詞之后,停用詞通常被過濾掉,不會被進行索引。在檢索的時候,用戶的查詢中如果含有停用詞,檢索系統也會將其過濾掉(因為用戶輸入的查詢字符串也要進行分詞處理)。排除停用詞可以加快建立索引的速度,減小索引庫文件的大小。
7.4. 常用的中文分詞器
中文的分詞比較復雜,因為不是一個字就是一個詞,而且一個詞在另外一個地方就可能不是一個詞,如在“帽子和服裝”中,“和服”就不是一個詞。對于中文分詞,通常有三種方式:單字分詞、二分法分詞、詞典分詞。
l 單字分詞:就是按照中文一個字一個字地進行分詞。如:“我們是中國人”,效果:“我”、“們”、“是”、“中”、“國”、“人”。(StandardAnalyzer就是這樣)。
l 二分法分詞:按兩個字進行切分。如:“我們是中國人”,效果:“我們”、“們是”、“是中”、“中國”、“國人”。(CJKAnalyzer就是這樣)。
l 詞庫分詞:按某種算法構造詞,然后去匹配已建好的詞庫集合,如果匹配到就切分出來成為詞語。通常詞庫分詞被認為是最理想的中文分詞算法。如:“我們是中國人”,效果為:“我們”、“中國人”。(使用極易分詞的MMAnalyzer。可以使用“極易分詞”,或者是“庖丁分詞”分詞器、IKAnalyzer)。
其他的中文分詞器有:
1, 極易分詞:MMAnalyzer,最后版本是1.5.3,更新時間是2007-12-05,不支持Lucene3.0
2, 庖丁分詞:PaodingAnalzyer,最后版本是2.0.4,更新時間是2008-06-03,不支持Lucene3.0
中文分詞器使用IKAnalyzer,主頁:http://www.oschina.net/p/ikanalyzer。
實現了以詞典為基礎的正反向全切分,以及正反向最大匹配切分兩種方法。IKAnalyzer是第三方實現的分詞器,繼承自Lucene的Analyzer類,針對中文文本進行處理。具體的使用方式參見其文檔。
注意:擴展的詞庫與停止詞文件要是UTF-8的編碼,并且在要文件頭部加一空行。
7.5. 測試分詞器的代碼
/**
* 使用指定的分詞器對指定的文本進行分詞,并打印出分出的詞
* @param analyzer
* @param text
* @throws Exception
*/
private void testAnalyzer(Analyzer analyzer, String text) throws Exception {
System.out.println("當前使用的分詞器:" + analyzer.getClass());
TokenStream tokenStream = analyzer.tokenStream("content", new StringReader(text));
tokenStream.addAttribute(TermAttribute.class);
while (tokenStream.incrementToken()) {
TermAttribute termAttribute = tokenStream.getAttribute(TermAttribute.class);
System.out.println(termAttribute.term());
}
System.out.println();
}
8. Lucene應用
8.1. 使用IndexDao封裝對索引庫的增刪改查
8.1.1. IndexDao說明
提出問題:所有的數據(對象),我們都要存到數據庫中。對于要進行搜索的數據,還要存到索引庫中,以供搜索。一份數據同時存到數據庫與索引庫中(格式不同),就要想辦法保證他們的狀態一致。否則,就會影響搜索結果。
解決思路:對于上一段提出的問題:保證索引庫中與數據庫中的數據一致(只要針對要進行搜索的數據)。我們采用的方法是,在數據庫中做了相應的操作后,在索引庫中也做相應的操作。具體的索引庫操作,是通過調用相應的IndexDao方法完成的。IndexDao類似于數據庫層的Dao。
我們目前只關注IndexDao中的每個方法的作用(怎么用起來方便就怎么設計)。現在不需要關心IndexDao的每個方法怎么實現,因為那是下一步的事情。設計IndexDao如下:
IndexDao的使用:
PublishAction.execute(){ // 發表文章
actionForm à article對象
articleDao.save( article ); // 保存到數據庫
articleIndexDao.save( article ); // 保存到索引庫
}
DeleteAction.execute(){ // 刪除文章
articleDao.delete( id ); // 從數據庫中刪除
articleIndex.delete( id ); // 從索引庫中刪除
}
UpdateAction.execute(){ // 更新文章
actionForm à article對象
articleDao.update( article ); // 更新數據庫中的相應數據
articleIndexDao.update( article ); // 更新索引庫中的相應數據
}
8.1.2. 實現IndexDao中的方法:建立、刪除、更新索引
索引庫的管理操作操作是通過類IndexWriter完成的。創建實例是使用構造方法:IndexWriter(Directory d, Analyzer a, MaxFieldLength mfl)。用完后要調用IndexWriter.close()方法釋放資源。
1, 建立索引:保存文檔到索引庫中。
a) 把數據轉成Document對象的形式。
b) 調用方法IndexWriter.addDocument(Document doc)
2, 刪除索引:刪除所有包含指定Term的文檔。
a) 生成用于確定要刪除的文檔的Term
b) 調用方法IndexWriter.deleteDocuments(Term term)
說明:在生成Term時,一般。如果有多個文檔含有指定的Term,則都會被刪掉。
3, 更新索引:實際執行的是先刪除,后創建的操作。(參見前面的 索引文件的檢索與維護)
a) 把要更新后的對象轉為Document對象
b) 生成用于確定要更新的文檔的Term
c) 調用方法IndexWriter.updateDocument(Term term, Document doc)
說明:如果有多個文檔含有指定的Term,更新后就只有一條記錄(刪掉所有,再創建一個)。如果沒有文檔含有指定的記錄,不會報錯,更新后有一條(新創建的)記錄。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。