1 Field域
1.1 Field屬性
Field是文檔中的域,包括Field名和Field值兩部分,一個文檔可以包括多個Field,Document只是Field的一個承載體,Field值即爲要索引的內容,也是要搜索的內容。
l 是否分詞(tokenized)
是:作分詞處理,即將Field值進行分詞,分詞的目的是爲了索引。
比如:商品名稱、商品簡介等,這些內容用戶要輸入關鍵字搜索,由於搜索的內容格式大、內容多需要分詞後將語彙單元索引。
否:不作分詞處理
比如:商品id、訂單號、身份證號等
l 是否索引(indexed)
是:進行索引。將Field分詞後的詞或整個Field值進行索引,索引的目的是爲了搜索。
比如:商品名稱、商品簡介分析後進行索引,訂單號、身份證號不用分析但也要索引,這些將來都要作爲查詢條件。
否:不索引。該域的內容無法搜索到
比如:商品id、文件路徑、圖片路徑等,不用作爲查詢條件的不用索引。
l 是否存儲(stored)
是:將Field值存儲在文檔中,存儲在文檔中的Field纔可以從Document中獲取。
比如:商品名稱、訂單號,凡是將來要從Document中獲取的Field都要存儲。
否:不存儲Field值,不存儲的Field無法通過Document獲取
比如:商品簡介,內容較大不用存儲。如果要向用戶展示商品簡介可以從系統的關係數據庫中獲取商品簡介。
1.2 Field常用類型
下邊列出了開發中常用 的Filed類型,注意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的策略. |
1.3 Field代碼修改如下
圖書id:
是否分詞:不用分詞,因爲不會根據商品id來搜索商品
是否索引:不索引,因爲不需要根據圖書ID進行搜索
是否存儲:要存儲,因爲查詢結果頁面需要使用id這個值。
圖書名稱:
是否分詞:要分詞,因爲要將圖書的名稱內容分詞索引,根據關鍵搜索圖書名稱抽取的詞。
是否索引:要索引。
是否存儲:要存儲。
圖書價格:
是否分詞:要分詞,lucene對數字型的值只要有搜索需求的都要分詞和索引,因爲lucene對數字型的內容要特殊分詞處理,本例子可能要根據價格範圍搜索,需要分詞和索引。
是否索引:要索引
是否存儲:要存儲
圖書圖片地址:
是否分詞:不分詞
是否索引:不索引
是否存儲:要存儲
圖書描述:
是否分詞:要分詞
是否索引:要索引
是否存儲:因爲圖書描述內容量大,不在查詢結果頁面直接顯示,不存儲。
不存儲是來不在lucene的索引文件中記錄,節省lucene的索引文件空間,如果要在詳情頁面顯示描述,思路:
從lucene中取出圖書的id,根據圖書的id查詢關係數據庫中book表得到描述信息。
// 圖書ID // 參數:域名、域中存儲的內容、是否存儲 // 不分詞、索引、要存儲 // Field id = new TextField("id", book.getId().toString(), // Store.YES); Field id = new StoredField("id", book.getId().toString(), Store.YES); // 圖書名稱 // 分詞、索引、存儲 Field bookname = new TextField("bookname", book.getName(), Store.YES); // 圖書價格 // 分詞、索引、存儲 Field price = new FloatField("price", book.getPrice(), Store.YES); // 圖書圖片 // 不分詞、不索引、要存儲 Field pic = new StoredField("pic", book.getPic()); // 圖書描述 // 分詞、索引、不存儲 Field description = new TextField("description", book.getDescription(), Store.NO); |
2 索引維護
2.1 需求
管理人員通過電商系統更改圖書信息,這時更新的是數據庫,如果使用lucene搜索圖書信息需要在數據庫表book信息變化時及時更新lucene索引庫。
2.2 添加索引
調用 indexWriter.addDocument(doc)添加索引。
參考創建索引。
2.3 刪除索引
2.3.1 刪除指定索引
根據Term項刪除索引,滿足條件的將全部刪除。
// 刪除索引 @Test public void deleteIndex() throws Exception { // 1、指定索引庫目錄 Directory directory = FSDirectory.open(new File("E:\\11-index\\0720")); // 2、創建IndexWriterConfig IndexWriterConfig cfg = new IndexWriterConfig(Version.LATEST, new StandardAnalyzer()); // 3、 創建IndexWriter IndexWriter writer = new IndexWriter(directory, cfg); // 4、通過IndexWriter來刪除索引 // b)、刪除指定索引 writer.deleteDocuments(new Term("filename", "apache")); // 5、關閉IndexWriter writer.close(); } |
2.3.2 刪除全部索引(慎用)
將索引目錄的索引信息全部刪除,直接徹底刪除,無法恢復。慎用!!!
// 刪除索引 @Test public void deleteIndex() throws Exception { // 1、指定索引庫目錄 Directory directory = FSDirectory.open(new File("E:\\11-index\\0720")); // 2、創建IndexWriterConfig IndexWriterConfig cfg = new IndexWriterConfig(Version.LATEST, new StandardAnalyzer()); // 3、 創建IndexWriter IndexWriter writer = new IndexWriter(directory, cfg); // 4、通過IndexWriter來刪除索引 // a)、刪除全部索引 writer.deleteAll(); // 5、關閉IndexWriter writer.close(); } |
建議參照關係數據庫基於主鍵刪除方式,所以在創建索引時需要創建一個主鍵Field,刪除時根據此主鍵Field刪除。
索引刪除後將放在Lucene的回收站中,Lucene3.X版本可以恢復刪除的文檔,3.X之後無法恢復。
2.4 修改索引
更新索引是先刪除再添加,建議對更新需求採用此方法並且要保證對已存在的索引執行更新,可以先查詢出來,確定更新記錄存在執行更新操作。
// 修改索引 @Test public void updateIndex() throws Exception { // 1、指定索引庫目錄 Directory directory = FSDirectory.open(new File("E:\\11-index\\0720")); // 2、創建IndexWriterConfig IndexWriterConfig cfg = new IndexWriterConfig(Version.LATEST, new StandardAnalyzer()); // 3、 創建IndexWriter IndexWriter writer = new IndexWriter(directory, cfg); // 4、通過IndexWriter來修改索引 // a)、創建修改後的文檔對象 Document document = new Document();
// 文件名稱 Field filenameField = new StringField("filename", "updateIndex", Store.YES); document.add(filenameField);
// 修改指定索引爲新的索引 writer.updateDocument(new Term("filename", "apache"), document);
// 5、關閉IndexWriter writer.close(); } |
3 搜索
3.1 創建查詢的兩種方法
對要搜索的信息創建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解析查詢表達式
QueryParser會將用戶輸入的查詢表達式解析成Query對象實例。
如下代碼:
QueryParser queryParser = new QueryParser("name", new IKAnalyzer());
Query query = queryParser.parse("name:lucene");
3.2 通過Query子類搜索
3.2.1 TermQuery
TermQuery項查詢,TermQuery不使用分析器,搜索關鍵詞作爲整體來匹配Field域中的詞進行查詢,比如訂單號、分類ID號等。
private void doSearch(Query query) { IndexReader reader = null; try { // a) 指定索引庫目錄 Directory indexdirectory = FSDirectory.open(new File( "E:\\11-index\\0720")); // b) 創建IndexReader對象 reader = DirectoryReader.open(indexdirectory); // c) 創建IndexSearcher對象 IndexSearcher searcher = new IndexSearcher(reader); // d) 通過IndexSearcher對象執行查詢索引庫,返回TopDocs對象 // 第一個參數:查詢對象 // 第二個參數:最大的n條記錄 TopDocs topDocs = searcher.search(query, 10); // e) 提取TopDocs對象中的文檔ID,如何找出對應的文檔 ScoreDoc[] scoreDocs = topDocs.scoreDocs; System.out.println("總共查詢出的結果總數爲:" + topDocs.totalHits); Document doc; for (ScoreDoc scoreDoc : scoreDocs) { // 文檔對象ID int docId = scoreDoc.doc; doc = searcher.doc(docId); // f) 輸出文檔內容 System.out.println(doc.get("filename")); System.out.println(doc.get("path")); System.out.println(doc.get("size")); } } catch (IOException e) { e.printStackTrace(); } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } }
@Test public void testTermQuery() throws Exception { // 1、 創建查詢(Query對象) Query query = new TermQuery(new Term("filename", "apache")); // 2、 執行搜索 doSearch(query); } |
3.2.2 NumbericRangeQuery
NumericRangeQuery,指定數字範圍查詢.
@Test public void testNumbericRangeQuery() throws Exception { // 創建查詢 // 第一個參數:域名 // 第二個參數:最小值 // 第三個參數:最大值 // 第四個參數:是否包含最小值 // 第五個參數:是否包含最大值 Query query = NumericRangeQuery.newLongRange("size", 1l, 100l, true,true); // 2、 執行搜索 doSearch(query); }
|
3.2.3 BooleanQuery
BooleanQuery,布爾查詢,實現組合條件查詢。
@Test public void booleanQuery() throws Exception { BooleanQuery query = new BooleanQuery(); Query query1 = new TermQuery(new Term("id", "3")); Query query2 = NumericRangeQuery.newFloatRange("price", 10f, 200f, true, true);
//MUST:查詢條件必須滿足,相當於AND //SHOULD:查詢條件可選,相當於OR //MUST_NOT:查詢條件不能滿足,相當於NOT非 query.add(query1, Occur.MUST); query.add(query2, Occur.SHOULD); System.out.println(query);
search(query); } |
組合關係代表的意思如下:
1、MUST和MUST表示“與”的關係,即“並集”。
2、MUST和MUST_NOT前者包含後者不包含。
3、MUST_NOT和MUST_NOT沒意義
4、SHOULD與MUST表示MUST,SHOULD失去意義;
5、SHOUlD與MUST_NOT相當於MUST與MUST_NOT。
6、SHOULD與SHOULD表示“或”的概念。
3.3 通過QueryParser搜索
通過QueryParser也可以創建Query,QueryParser提供一個Parse方法,此方法可以直接根據查詢語法來查詢。Query對象執行的查詢語法可通過System.out.println(query);查詢。
3.3.1 QueryParser
3.3.1.1 代碼實現
@Test public void testQueryParser() throws Exception { // 創建QueryParser // 第一個參數:默認域名 // 第二個參數:分詞器 QueryParser queryParser = new QueryParser("name", new IKAnalyzer()); // 指定查詢語法 ,如果不指定域,就搜索默認的域 Query query = queryParser.parse("lucene"); System.out.println(query); // 2、 執行搜索 doSearch(query);
} |
3.3.1.2 查詢語法
1、基礎的查詢語法,關鍵詞查詢:
域名+“:”+搜索的關鍵字
例如:content:java
2、範圍查詢
域名+“:”+[最小值 TO 最大值]
例如:size:[1 TO 1000]
注意:QueryParser不支持對數字範圍的搜索,它支持字符串範圍。數字範圍搜索建議使用NumericRangeQuery。
3、組合條件查詢
Occur.MUST 查詢條件必須滿足,相當於and |
+(加號) |
Occur.SHOULD 查詢條件可選,相當於or
|
空(不用符號) |
Occur.MUST_NOT 查詢條件不能滿足,相當於not非 |
-(減號) |
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
第二種寫法:
條件1 AND 條件2
條件1 OR 條件2
條件1 NOT 條件2
3.3.2 MultiFieldQueryParser
通過MuliFieldQueryParse對多個域查詢。
@Test public void testMultiFieldQueryParser() throws Exception { // 可以指定默認搜索的域是多個 String[] fields = { "name", "description" }; // 創建一個MulitFiledQueryParser對象 QueryParser parser = new MultiFieldQueryParser(fields, new IKAnalyzer()); // 指定查詢語法 ,如果不指定域,就搜索默認的域 Query query = parser.parse("lucene"); // 2、 執行搜索 doSearch(query); } |
3.4 TopDocs
Lucene搜索結果可通過TopDocs遍歷,TopDocs類提供了少量的屬性,如下:
方法或屬性 |
說明 |
totalHits |
匹配搜索條件的總記錄數 |
|
|
|
|
|
|
scoreDocs |
頂部匹配記錄 |
注意:
Search方法需要指定匹配記錄數量n:indexSearcher.search(query, n)
TopDocs.totalHits:是匹配索引庫中所有記錄的數量
TopDocs.scoreDocs:匹配相關度高的前邊記錄數組,scoreDocs的長度小於等於search方法指定的參數n