全文檢索課程
Lucene&Solr(1)
1. 計划
第一天:Lucene的基礎知識
1、案例分析:什么是全文檢索,如何實現全文檢索
2、Lucene實現全文檢索的流程
a) 創建索引
b) 查詢索引
3、配置開發環境
4、創建索引庫
5、查詢索引庫
6、分析器的分析過程
a) 測試分析器的分詞效果
b) 第三方中文分析器
7、索引庫的維護
a) 添加文檔
b) 刪除文檔
c) 修改文檔
8、Lucene的高級查詢Lucene的查詢
a) 使用Query的子類查詢
- MatchAllDocsQuery
- TermQuery
- NumericRangeQuery
- BooleanQuery
b) 使用QueryParser
- QueryParser
- MulitFieldQueryParser
2. 案例
實現一個文件的搜索功能,通過關鍵字搜索文件,凡是文件名或文件內容包括關鍵字的文件都需要找出來。還可以根據中文詞語進行查詢,並且需要支持多個條件查詢。
本案例中的原始內容就是磁盤上的文件,如下圖:
3. 需求分析
3.1. 數據庫搜索
數據庫中的搜索很容易實現,通常都是使用sql語句進行查詢,而且能很快的得到查詢結果。
為什么數據庫搜索很容易?
因為數據庫中的數據存儲是有規律的,有行有列而且數據格式、數據長度都是固定的。
3.2. 數據分類
我們生活中的數據總體分為兩種:結構化數據和非結構化數據。
結構化數據:指具有固定格式或有限長度的數據,如數據庫,元數據等。
非結構化數據:指不定長或無固定格式的數據,如郵件,word文檔等磁盤上的文件
3.3. 非結構化數據查詢方法
(1)順序掃描法(Serial Scanning)
所謂順序掃描,比如要找內容包含某一個字符串的文件,就是一個文檔一個文檔的看,對於每一個文檔,從頭看到尾,如果此文檔包含此字符串,則此文檔為我們要找的文件,接着看下一個文件,直到掃描完所有的文件。如利用windows的搜索也可以搜索文件內容,只是相當的慢。
(2)全文檢索(Full-text Search)
將非結構化數據中的一部分信息提取出來,重新組織,使其變得有一定結構,然后對此有一定結構的數據進行搜索,從而達到搜索相對較快的目的。這部分從非結構化數據中提取出的然后重新組織的信息,我們稱之索引。
例如:字典。字典的拼音表和部首檢字表就相當於字典的索引,對每一個字的解釋是非結構化的,如果字典沒有音節表和部首檢字表,在茫茫辭海中找一個字只能順序掃描。然而字的某些信息可以提取出來進行結構化處理,比如讀音,就比較結構化,分聲母和韻母,分別只有幾種可以一一列舉,於是將讀音拿出來按一定的順序排列,每一項讀音都指向此字的詳細解釋的頁數。我們搜索時按結構化的拼音搜到讀音,然后按其指向的頁數,便可找到我們的非結構化數據——也即對字的解釋。
這種先建立索引,再對索引進行搜索的過程就叫全文檢索(Full-text Search)。
雖然創建索引的過程也是非常耗時的,但是索引一旦創建就可以多次使用,全文檢索主要處理的是查詢,所以耗時間創建索引是值得的。
3.4. 如何實現全文檢索
可以使用Lucene實現全文檢索。Lucene是apache下的一個開放源代碼的全文檢索引擎工具包。提供了完整的查詢引擎和索引引擎,部分文本分析引擎。Lucene的目的是為軟件開發人員提供一個簡單易用的工具包,以方便的在目標系統中實現全文檢索的功能。
3.5. 全文檢索的應用場景
對於數據量大、數據結構不固定的數據可采用全文檢索方式搜索,比如百度、Google等搜索引擎、論壇站內搜索、電商網站站內搜索等。
4. Lucene實現全文檢索的流程
4.1. 索引和搜索流程圖
1、綠色表示索引過程,對要搜索的原始內容進行索引構建一個索引庫,索引過程包括:
確定原始內容即要搜索的內容à采集文檔à創建文檔à分析文檔à索引文檔
2、紅色表示搜索過程,從索引庫中搜索內容,搜索過程包括:
用戶通過搜索界面à創建查詢à執行搜索,從索引庫搜索à渲染搜索結果
4.2. 創建索引
對文檔索引的過程,將用戶要搜索的文檔內容進行索引,索引存儲在索引庫(index)中。
這里我們要搜索的文檔是磁盤上的文本文件,根據案例描述:凡是文件名或文件內容包括關鍵字的文件都要找出來,這里要對文件名和文件內容創建索引。
4.2.1. 獲得原始文檔
原始文檔是指要索引和搜索的內容。原始內容包括互聯網上的網頁、數據庫中的數據、磁盤上的文件等。
本案例中的原始內容就是磁盤上的文件,如下圖:
從互聯網上、數據庫、文件系統中等獲取需要搜索的原始信息,這個過程就是信息采集,信息采集的目的是為了對原始內容進行索引。
在Internet上采集信息的軟件通常稱為爬蟲或蜘蛛,也稱為網絡機器人,爬蟲訪問互聯網上的每一個網頁,將獲取到的網頁內容存儲起來。
Lucene不提供信息采集的類庫,需要自己編寫一個爬蟲程序實現信息采集,也可以通過一些開源軟件實現信息采集,如下:
Nutch(http://lucene.apache.org/nutch), Nutch是apache的一個子項目,包括大規模爬蟲工具,能夠抓取和分辨web網站數據。
jsoup(http://jsoup.org/ ),jsoup 是一款Java 的HTML解析器,可直接解析某個URL地址、HTML文本內容。它提供了一套非常省力的API,可通過DOM,CSS以及類似於jQuery的操作方法來取出和操作數據。
heritrix(http://sourceforge.net/projects/archive-crawler/files/),Heritrix 是一個由 java 開發的、開源的網絡爬蟲,用戶可以使用它來從網上抓取想要的資源。其最出色之處在於它良好的可擴展性,方便用戶實現自己的抓取邏輯。
本案例我們要獲取磁盤上文件的內容,可以通過文件流來讀取文本文件的內容,對於pdf、doc、xls等文件可通過第三方提供的解析工具讀取文件內容,比如Apache POI讀取doc和xls的文件內容。
4.2.2. 創建文檔對象
獲取原始內容的目的是為了索引,在索引前需要將原始內容創建成文檔(Document),文檔中包括一個一個的域(Field),域中存儲內容。
這里我們可以將磁盤上的一個文件當成一個document,Document中包括一些Field(file_name文件名稱、file_path文件路徑、file_size文件大小、file_content文件內容),如下圖:
注意:每個Document可以有多個Field,不同的Document可以有不同的Field,同一個Document可以有相同的Field(域名和域值都相同)
每個文檔都有一個唯一的編號,就是文檔id。
4.2.3. 分析文檔
將原始內容創建為包含域(Field)的文檔(document),需要再對域中的內容進行分析,分析的過程是經過對原始文檔提取單詞、將字母轉為小寫、去除標點符號、去除停用詞等過程生成最終的語匯單元,可以將語匯單元理解為一個一個的單詞。
比如下邊的文檔經過分析如下:
原文檔內容:
Lucene is a Java full-text search engine. Lucene is not a complete
application, but rather a code library and API that can easily be used
to add search capabilities to applications.
分析后得到的語匯單元:
lucene、java、full、search、engine。。。。
每個單詞叫做一個Term,不同的域中拆分出來的相同的單詞是不同的term。term中包含兩部分一部分是文檔的域名,另一部分是單詞的內容。
例如:文件名中包含apache和文件內容中包含的apache是不同的term。
4.2.4. 創建索引
對所有文檔分析得出的語匯單元進行索引,索引的目的是為了搜索,最終要實現只搜索被索引的語匯單元從而找到Document(文檔)。
注意:創建索引是對語匯單元索引,通過詞語找文檔,這種索引的結構叫倒排索引結構。
傳統方法是根據文件找到該文件的內容,在文件內容中匹配搜索關鍵字,這種方法是順序掃描方法,數據量大、搜索慢。
倒排索引結構是根據內容(詞語)找文檔,如下圖:
倒排索引結構也叫反向索引結構,包括索引和文檔兩部分,索引即詞匯表,它的規模較小,而文檔集合較大。
4.3. 查詢索引
查詢索引也是搜索的過程。搜索就是用戶輸入關鍵字,從索引(index)中進行搜索的過程。根據關鍵字搜索索引,根據索引找到對應的文檔,從而找到要搜索的內容(這里指磁盤上的文件)。
4.3.1. 用戶查詢接口
全文檢索系統提供用戶搜索的界面供用戶提交搜索的關鍵字,搜索完成展示搜索結果。
比如:
Lucene不提供制作用戶搜索界面的功能,需要根據自己的需求開發搜索界面。
4.3.2. 創建查詢
用戶輸入查詢關鍵字執行搜索之前需要先構建一個查詢對象,查詢對象中可以指定查詢要搜索的Field文檔域、查詢關鍵字等,查詢對象會生成具體的查詢語法,
例如:
語法 “fileName:lucene”表示要搜索Field域的內容為“lucene”的文檔
4.3.3. 執行查詢
搜索索引過程:
根據查詢語法在倒排索引詞典表中分別找出對應搜索詞的索引,從而找到索引所鏈接的文檔鏈表。
比如搜索語法為“fileName:lucene”表示搜索出fileName域中包含Lucene的文檔。
搜索過程就是在索引上查找域為fileName,並且關鍵字為Lucene的term,並根據term找到文檔id列表。
4.3.4. 渲染結果
以一個友好的界面將查詢結果展示給用戶,用戶根據搜索結果找自己想要的信息,為了幫助用戶很快找到自己的結果,提供了很多展示的效果,比如搜索結果中將關鍵字高亮顯示,百度提供的快照等。
5. 配置開發環境
5.1. Lucene下載
Lucene是開發全文檢索功能的工具包,從官方網站下載Lucene4.10.3,並解壓。
官方網站:http://lucene.apache.org/
版本:lucene4.10.3
Jdk要求:1.7以上
IDE:Eclipse
5.2. 使用的jar包
Lucene包:
lucene-core-4.10.3.jar
lucene-analyzers-common-4.10.3.jar
lucene-queryparser-4.10.3.jar
其它:
commons-io-2.4.jar
junit-4.9.jar
6. 功能一:創建索引庫
使用indexwriter對象創建索引
6.1. 實現步驟
第一步:創建一個java工程,並導入jar包。
第二步:創建一個indexwriter對象。
1)指定索引庫的存放位置Directory對象
2)指定一個分析器,對文檔內容進行分析。
第二步:創建document對象。
第三步:創建field對象,將field添加到document對象中。
第四步:使用indexwriter對象將document對象寫入索引庫,此過程進行索引創建。並將索引和document對象寫入索引庫。
第五步:關閉IndexWriter對象。
6.2. Field域的屬性
是否分析:是否對域的內容進行分詞處理。前提是我們要對域的內容進行查詢。
是否索引:將Field分析后的詞或整個Field值進行索引,只有索引方可搜索到。
比如:商品名稱、商品簡介分析后進行索引,訂單號、身份證號不用分析但也要索引,這些將來都要作為查詢條件。
是否存儲:將Field值存儲在文檔中,存儲在文檔中的Field才可以從Document中獲取
比如:商品名稱、訂單號,凡是將來要從Document中獲取的Field都要存儲。
是否存儲的標准:是否要將內容展示給用戶
Field類 |
數據類型 |
Analyzed 是否分析 |
Indexed 是否索引 |
Stored 是否存儲 |
說明 |
StringField(FieldName, FieldValue,Store.YES)) |
字符串 |
N |
Y |
Y或N |
這個Field用來構建一個字符串Field,但是不會進行分析,會將整個串存儲在索引中,比如(訂單號,姓名等) 是否存儲在文檔中用Store.YES或Store.NO決定 |
LongField(FieldName, FieldValue,Store.YES) |
Long型 |
Y |
Y |
Y或N |
這個Field用來構建一個Long數字型Field,進行分析和索引,比如(價格) 是否存儲在文檔中用Store.YES或Store.NO決定 |
StoredField(FieldName, FieldValue) |
重載方法,支持多種類型 |
N |
N |
Y |
這個Field用來構建不同類型Field 不分析,不索引,但要Field存儲在文檔中 |
TextField(FieldName, FieldValue, Store.NO) 或 TextField(FieldName, reader)
|
字符串 或 流 |
Y |
Y |
Y或N |
如果是一個Reader, lucene猜測內容比較多,會采用Unstored的策略. |
6.3. 代碼實現
//創建索引 @Test public void createIndex() throws Exception {
//指定索引庫存放的路徑 //D:\temp\0108\index Directory directory = FSDirectory.open(new File("D:\\temp\\0108\\index")); //索引庫還可以存放到內存中 //Directory directory = new RAMDirectory(); //創建一個標准分析器 Analyzer analyzer = new StandardAnalyzer(); //創建indexwriterCofig對象 //第一個參數: Lucene的版本信息,可以選擇對應的lucene版本也可以使用LATEST //第二根參數:分析器對象 IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, analyzer); //創建indexwriter對象 IndexWriter indexWriter = new IndexWriter(directory, config); //原始文檔的路徑D:\傳智播客\01.課程\04.lucene\01.參考資料\searchsource File dir = new File("D:\\傳智播客\\01.課程\\04.lucene\\01.參考資料\\searchsource"); for (File f : dir.listFiles()) { //文件名 String fileName = f.getName(); //文件內容 String fileContent = FileUtils.readFileToString(f); //文件路徑 String filePath = f.getPath(); //文件的大小 long fileSize = FileUtils.sizeOf(f); //創建文件名域 //第一個參數:域的名稱 //第二個參數:域的內容 //第三個參數:是否存儲 Field fileNameField = new TextField("filename", fileName, Store.YES); //文件內容域 Field fileContentField = new TextField("content", fileContent, Store.YES); //文件路徑域(不分析、不索引、只存儲) Field filePathField = new StoredField("path", filePath); //文件大小域 Field fileSizeField = new LongField("size", fileSize, Store.YES);
//創建document對象 Document document = new Document(); document.add(fileNameField); document.add(fileContentField); document.add(filePathField); document.add(fileSizeField); //創建索引,並寫入索引庫 indexWriter.addDocument(document); } //關閉indexwriter indexWriter.close(); } |
6.4. 使用Luke工具查看索引文件
7. 功能二:查詢索引
7.1. 實現步驟
第一步:創建一個Directory對象,也就是索引庫存放的位置。
第二步:創建一個indexReader對象,需要指定Directory對象。
第三步:創建一個indexsearcher對象,需要指定IndexReader對象
第四步:創建一個TermQuery對象,指定查詢的域和查詢的關鍵詞。
第五步:執行查詢。
第六步:返回查詢結果。遍歷查詢結果並輸出。
第七步:關閉IndexReader對象
7.2. IndexSearcher搜索方法
方法 |
說明 |
indexSearcher.search(query, n) |
根據Query搜索,返回評分最高的n條記錄 |
indexSearcher.search(query, filter, n) |
根據Query搜索,添加過濾策略,返回評分最高的n條記錄 |
indexSearcher.search(query, n, sort) |
根據Query搜索,添加排序策略,返回評分最高的n條記錄 |
indexSearcher.search(booleanQuery, filter, n, sort) |
根據Query搜索,添加過濾策略,添加排序策略,返回評分最高的n條記錄 |
7.3. 代碼實現
//查詢索引庫 @Test public void searchIndex() throws Exception { //指定索引庫存放的路徑 //D:\temp\0108\index Directory directory = FSDirectory.open(new File("D:\\temp\\0108\\index")); //創建indexReader對象 IndexReader indexReader = DirectoryReader.open(directory); //創建indexsearcher對象 IndexSearcher indexSearcher = new IndexSearcher(indexReader); //創建查詢 Query query = new TermQuery(new Term("filename", "apache")); //執行查詢 //第一個參數是查詢對象,第二個參數是查詢結果返回的最大值 TopDocs topDocs = indexSearcher.search(query, 10); //查詢結果的總條數 System.out.println("查詢結果的總條數:"+ topDocs.totalHits); //遍歷查詢結果 //topDocs.scoreDocs存儲了document對象的id for (ScoreDoc scoreDoc : topDocs.scoreDocs) { //scoreDoc.doc屬性就是document對象的id //根據document的id找到document對象 Document document = indexSearcher.doc(scoreDoc.doc); System.out.println(document.get("filename")); //System.out.println(document.get("content")); System.out.println(document.get("path")); System.out.println(document.get("size")); } //關閉indexreader對象 indexReader.close(); } |
7.4. TopDocs
Lucene搜索結果可通過TopDocs遍歷,TopDocs類提供了少量的屬性,如下:
方法或屬性 |
說明 |
totalHits |
匹配搜索條件的總記錄數 |
scoreDocs |
頂部匹配記錄 |
注意:
Search方法需要指定匹配記錄數量n:indexSearcher.search(query, n)
TopDocs.totalHits:是匹配索引庫中所有記錄的數量
TopDocs.scoreDocs:匹配相關度高的前邊記錄數組,scoreDocs的長度小於等於search方法指定的參數n
8. 功能三:支持中文分詞
8.1. 分析器(Analyzer)的執行過程
如下圖是語匯單元的生成過程:
從一個Reader字符流開始,創建一個基於Reader的Tokenizer分詞器,經過三個TokenFilter生成語匯單元Tokens。
要看分析器的分析效果,只需要看Tokenstream中的內容就可以了。每個分析器都有一個方法tokenStream,返回一個tokenStream對象。
8.2. 分析器的分詞效果
//查看標准分析器的分詞效果 public void testTokenStream() throws Exception { //創建一個標准分析器對象 Analyzer analyzer = new StandardAnalyzer(); //獲得tokenStream對象 //第一個參數:域名,可以隨便給一個 //第二個參數:要分析的文本內容 TokenStream tokenStream = analyzer.tokenStream("test", "The Spring Framework provides a comprehensive programming and configuration model."); //添加一個引用,可以獲得每個關鍵詞 CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class); //添加一個偏移量的引用,記錄了關鍵詞的開始位置以及結束位置 OffsetAttribute offsetAttribute = tokenStream.addAttribute(OffsetAttribute.class); //將指針調整到列表的頭部 tokenStream.reset(); //遍歷關鍵詞列表,通過incrementToken方法判斷列表是否結束 while(tokenStream.incrementToken()) { //關鍵詞的起始位置 System.out.println("start->" + offsetAttribute.startOffset()); //取關鍵詞 System.out.println(charTermAttribute); //結束位置 System.out.println("end->" + offsetAttribute.endOffset()); } tokenStream.close(); } |
8.3. 中文分析器
8.3.1. Lucene自帶中文分詞器
l StandardAnalyzer:
單字分詞:就是按照中文一個字一個字地進行分詞。如:“我愛中國”,
效果:“我”、“愛”、“中”、“國”。
l CJKAnalyzer
二分法分詞:按兩個字進行切分。如:“我是中國人”,效果:“我是”、“是中”、“中國”“國人”。
上邊兩個分詞器無法滿足需求。
l SmartChineseAnalyzer
對中文支持較好,但擴展性差,擴展詞庫,禁用詞庫和同義詞庫等不好處理
8.3.2. 第三方中文分析器
- paoding: 庖丁解牛最新版在 https://code.google.com/p/paoding/ 中最多支持Lucene 3.0,且最新提交的代碼在 2008-06-03,在svn中最新也是2010年提交,已經過時,不予考慮。
- mmseg4j:最新版已從 https://code.google.com/p/mmseg4j/ 移至 https://github.com/chenlb/mmseg4j-solr,支持Lucene 4.10,且在github中最新提交代碼是2014年6月,從09年~14年一共有:18個版本,也就是一年幾乎有3個大小版本,有較大的活躍度,用了mmseg算法。
- IK-analyzer: 最新版在https://code.google.com/p/ik-analyzer/上,支持Lucene 4.10從2006年12月推出1.0版開始, IKAnalyzer已經推出了4個大版本。最初,它是以開源項目Luence為應用主體的,結合詞典分詞和文法分析算法的中文分詞組件。從3.0版本開 始,IK發展為面向Java的公用分詞組件,獨立於Lucene項目,同時提供了對Lucene的默認優化實現。在2012版本中,IK實現了簡單的分詞 歧義排除算法,標志着IK分詞器從單純的詞典分詞向模擬語義分詞衍化。 但是也就是2012年12月后沒有在更新。
- ansj_seg:最新版本在 https://github.com/NLPchina/ansj_seg tags僅有1.1版本,從2012年到2014年更新了大小6次,但是作者本人在2014年10月10日說明:“可能我以后沒有精力來維護ansj_seg了”,現在由”nlp_china”管理。2014年11月有更新。並未說明是否支持Lucene,是一個由CRF(條件隨機場)算法所做的分詞算法。
- imdict-chinese-analyzer:最新版在 https://code.google.com/p/imdict-chinese-analyzer/ , 最新更新也在2009年5月,下載源碼,不支持Lucene 4.10 。是利用HMM(隱馬爾科夫鏈)算法。
- Jcseg:最新版本在git.oschina.net/lionsoul/jcseg,支持Lucene 4.10,作者有較高的活躍度。利用mmseg算法。
8.3.3. IKAnalyzer
使用方法:
第一步:把jar包添加到工程中
第二步:把配置文件和擴展詞典和停用詞詞典添加到classpath下
注意:mydict.dic和ext_stopword.dic文件的格式為UTF-8,注意是無BOM 的UTF-8 編碼。
mydict.dic內容是自己定義擴展的詞匯來分詞:
高富帥
白富美
二維表
ext_stopword.dic內容是自己禁止詞匯來分詞:
我
是
用
二
維
表
來
的
a
an
and
are
as
at
be
but
by
for
if
in
into
is
it
no
not
of
on
or
such
that
the
their
then
there
these
they
this
to
was
will
with
使用EditPlus.exe保存為無BOM 的UTF-8 編碼格式,如下圖:
8.4. Analyzer使用時機
8.4.1. 索引時使用Analyzer
輸入關鍵字進行搜索,當需要讓該關鍵字與文檔域內容所包含的詞進行匹配時需要對文檔域內容進行分析,需要經過Analyzer分析器處理生成語匯單元(Token)。分析器分析的對象是文檔中的Field域。當Field的屬性tokenized(是否分詞)為true時會對Field值進行分析,如下圖:
對於一些Field可以不用分析:
1、不作為查詢條件的內容,比如文件路徑
2、不是匹配內容中的詞而匹配Field的整體內容,比如訂單號、身份證號等。
8.4.2. 搜索時使用Analyzer
對搜索關鍵字進行分析和索引分析一樣,使用Analyzer對搜索關鍵字進行分析、分詞處理,使用分析后每個詞語進行搜索。比如:搜索關鍵字:spring web ,經過分析器進行分詞,得出:spring web拿詞去索引詞典表查找 ,找到索引鏈接到Document,解析Document內容。
對於匹配整體Field域的查詢可以在搜索時不分析,比如根據訂單號、身份證號查詢等。
注意:搜索使用的分析器要和索引使用的分析器一致。
9. 功能四:索引庫的維護
9.1. 索引庫的添加
9.1.1. 步驟
向索引庫中添加document對象。
第一步:先創建一個indexwriter對象
第二步:創建一個document對象
第三步:把document對象寫入索引庫
第四步:關閉indexwriter。
9.1.2. 代碼實現
//添加索引 @Test public void addDocument() throws Exception { //索引庫存放路徑 Directory directory = FSDirectory.open(new File("D:\\temp\\0108\\index"));
IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, new IKAnalyzer()); //創建一個indexwriter對象 IndexWriter indexWriter = new IndexWriter(directory, config); //創建一個Document對象 Document document = new Document(); //向document對象中添加域。 //不同的document可以有不同的域,同一個document可以有相同的域。 document.add(new TextField("filename", "新添加的文檔", Store.YES)); document.add(new TextField("content", "新添加的文檔的內容", Store.NO)); document.add(new TextField("content", "新添加的文檔的內容第二個content", Store.YES)); document.add(new TextField("content1", "新添加的文檔的內容要能看到", Store.YES)); //添加文檔到索引庫 indexWriter.addDocument(document); //關閉indexwriter indexWriter.close();
} |
9.2. 索引庫刪除
9.2.1. 刪除全部
//刪除全部索引 @Test public void deleteAllIndex() throws Exception { IndexWriter indexWriter = getIndexWriter(); //刪除全部索引 indexWriter.deleteAll(); //關閉indexwriter indexWriter.close(); } |
說明:將索引目錄的索引信息全部刪除,直接徹底刪除,無法恢復。
此方法慎用!!
9.2.2. 指定查詢條件刪除
//根據查詢條件刪除索引 @Test public void deleteIndexByQuery() throws Exception { IndexWriter indexWriter = getIndexWriter(); //創建一個查詢條件 Query query = new TermQuery(new Term("filename", "apache")); //根據查詢條件刪除 indexWriter.deleteDocuments(query); //關閉indexwriter indexWriter.close(); } |
9.3. 索引庫的修改
原理就是先刪除后添加。
//修改索引庫 @Test public void updateIndex() throws Exception { IndexWriter indexWriter = getIndexWriter(); //創建一個Document對象 Document document = new Document(); //向document對象中添加域。 //不同的document可以有不同的域,同一個document可以有相同的域。 document.add(new TextField("filename", "要更新的文檔", Store.YES)); document.add(new TextField("content", "2013年11月18日 - Lucene 簡介 Lucene 是一個基於 Java 的全文信息檢索工具包,它不是一個完整的搜索應用程序,而是為你的應用程序提供索引和搜索功能。", Store.YES)); indexWriter.updateDocument(new Term("content", "java"), document); //關閉indexWriter indexWriter.close(); } |
10. Lucene索引庫查詢(重點)
對要搜索的信息創建Query查詢對象,Lucene會根據Query查詢對象生成最終的查詢語法,類似關系數據庫Sql語法一樣Lucene也有自己的查詢語法,比如:“name:lucene”表示查詢Field的name為“lucene”的文檔信息。
可通過兩種方法創建查詢對象:
1)使用Lucene提供Query子類
Query是一個抽象類,lucene提供了很多查詢對象,比如TermQuery項精確查詢,NumericRangeQuery數字范圍查詢等。
如下代碼:
Query query = new TermQuery(new Term("name", "lucene"));
2)使用QueryParse解析查詢表達式
QueryParse會將用戶輸入的查詢表達式解析成Query對象實例。
如下代碼:
QueryParser queryParser = new QueryParser("name", new IKAnalyzer());
Query query = queryParser.parse("name:lucene");
10.1. 使用query的子類查詢
10.1.1. MatchAllDocsQuery
使用MatchAllDocsQuery查詢索引目錄中的所有文檔
@Test public void testMatchAllDocsQuery() throws Exception { IndexSearcher indexSearcher = getIndexSearcher(); //創建查詢條件 Query query = new MatchAllDocsQuery(); //執行查詢 printResult(query, indexSearcher); } |
10.1.2. TermQuery
TermQuery,通過項查詢,TermQuery不使用分析器所以建議匹配不分詞的Field域查詢,比如訂單號、分類ID號等。
指定要查詢的域和要查詢的關鍵詞。
//使用Termquery查詢 @Test public void testTermQuery() throws Exception { IndexSearcher indexSearcher = getIndexSearcher(); //創建查詢對象 Query query = new TermQuery(new Term("content", "lucene")); //執行查詢 TopDocs topDocs = indexSearcher.search(query, 10); //共查詢到的document個數 System.out.println("查詢結果總數量:" + topDocs.totalHits); //遍歷查詢結果 for (ScoreDoc scoreDoc : topDocs.scoreDocs) { Document document = indexSearcher.doc(scoreDoc.doc); System.out.println(document.get("filename")); //System.out.println(document.get("content")); System.out.println(document.get("path")); System.out.println(document.get("size")); } //關閉indexreader indexSearcher.getIndexReader().close(); } |
10.1.3. NumericRangeQuery
可以根據數值范圍查詢。
//數值范圍查詢 @Test public void testNumericRangeQuery() throws Exception { IndexSearcher indexSearcher = getIndexSearcher(); //創建查詢 //參數: //1.域名 //2.最小值 //3.最大值 //4.是否包含最小值 //5.是否包含最大值 Query query = NumericRangeQuery.newLongRange("size", 1l, 1000l, true, true); //執行查詢 printResult(query, indexSearcher); } |
10.1.4. BooleanQuery
可以組合查詢條件。
//組合條件查詢 @Test public void testBooleanQuery() throws Exception { IndexSearcher indexSearcher = getIndexSearcher(); //創建一個布爾查詢對象 BooleanQuery query = new BooleanQuery(); //創建第一個查詢條件 Query query1 = new TermQuery(new Term("filename", "apache")); Query query2 = new TermQuery(new Term("content", "apache")); //組合查詢條件 query.add(query1, Occur.MUST); query.add(query2, Occur.MUST); //執行查詢 printResult(query, indexSearcher); } |
Occur.MUST:必須滿足此條件,相當於and
Occur.SHOULD:應該滿足,但是不滿足也可以,相當於or
Occur.MUST_NOT:必須不滿足。相當於not
10.2. 使用queryparser查詢
通過QueryParser也可以創建Query,QueryParser提供一個Parse方法,此方法可以直接根據查詢語法來查詢。Query對象執行的查詢語法可通過System.out.println(query);查詢。
需要使用到分析器。建議創建索引時使用的分析器和查詢索引時使用的分析器要一致。
10.2.1. QueryParser
需要加入queryParser依賴的jar包。
1.1.1.1 程序實現
@Test public void testQueryParser() throws Exception { IndexSearcher indexSearcher = getIndexSearcher(); //創建queryparser對象 //第一個參數默認搜索的域 //第二個參數就是分析器對象 QueryParser queryParser = new QueryParser("content", new IKAnalyzer()); Query query = queryParser.parse("Lucene是java開發的"); //執行查詢 printResult(query, indexSearcher); } |
1.1.1.2 查詢語法
1、基礎的查詢語法,關鍵詞查詢:
域名+“:”+搜索的關鍵字
例如:content:java
2、范圍查詢
域名+“:”+[最小值 TO 最大值]
例如:size:[1 TO 1000]
范圍查詢在lucene中支持數值類型,不支持字符串類型。在solr中支持字符串類型。
3、組合條件查詢
1)+條件1 +條件2:兩個條件之間是並且的關系and
例如:+filename:apache +content:apache
2)+條件1 條件2:必須滿足第一個條件,應該滿足第二個條件
例如:+filename:apache content:apache
3)條件1 條件2:兩個條件滿足其一即可。
例如:filename:apache content:apache
4)-條件1 條件2:必須不滿足條件1,要滿足條件2
例如:-filename:apache content:apache
Occur.MUST 查詢條件必須滿足,相當於and |
+(加號) |
Occur.SHOULD 查詢條件可選,相當於or
|
空(不用符號) |
Occur.MUST_NOT 查詢條件不能滿足,相當於not非 |
-(減號) |
第二種寫法:
條件1 AND 條件2
條件1 OR 條件2
條件1 NOT 條件2
10.2.2. MultiFieldQueryParser
可以指定多個默認搜索域
@Test public void testMultiFiledQueryParser() throws Exception { IndexSearcher indexSearcher = getIndexSearcher(); //可以指定默認搜索的域是多個 String[] fields = {"filename", "content"}; //創建一個MulitFiledQueryParser對象 MultiFieldQueryParser queryParser = new MultiFieldQueryParser(fields, new IKAnalyzer()); Query query = queryParser.parse("java AND apache"); System.out.println(query); //執行查詢 printResult(query, indexSearcher);
} |
txt