Lucene筆記07-IndexReader的設計
一、IndexReader的設計
IndexReader和IndexWriter在建立索引,讀取索引過程中是很常用的兩個類,它們在使用完一定要close(),可是對於它們,開啟和關閉都是非常耗時的操作,特別對於IndexReader,它的操作更加耗時的,所以要設計成單例。
我們需要將IndexReader新增到成員變數中,並在建構函式中對IndexReader物件進行初始化,另外新增一個getIndexSearcher()方法,並對search()進行修改,以後就可以使用這個indexReader了。
private static IndexReader indexReader = null; public IndexUtil() { try { InitDate(); // 設定加權,預設權重是1.0,數值越大權重越高 scores.put("a.com", 1.5f); scores.put("b.com", 1.6f); scores.put("c.com", 1.7f); scores.put("d.com", 1.8f); scores.put("e.com", 1.9f); scores.put("f.com", 2.0f); directory = FSDirectory.open(new File("E:\\Lucene\\IndexLibrary")); // 下面這條語句有可能拋異常,當索引路徑下沒有索引檔案的時候會拋異常,所以如果拋異常了可以暫時先註釋掉 indexReader = IndexReader.open(directory); } catch (IOException e) { e.printStackTrace(); } } public IndexSearcher getIndexSearcher() { return new IndexSearcher(indexReader); } public void search() { IndexSearcher indexSearcher = getIndexSearcher(); try { // 搜尋content域包含“content”的 TermQuery termQuery = new TermQuery(new Term("content", "content")); TopDocs topDocs = indexSearcher.search(termQuery, 10); for (ScoreDoc scoreDoc : topDocs.scoreDocs) { Document document = indexSearcher.doc(scoreDoc.doc); System.out.println(document.get("name") + " " + document.getBoost() + " " + scoreDoc.score); } } catch (IOException e) { e.printStackTrace(); } finally { if (indexSearcher != null) { try { indexSearcher.close(); } catch (IOException e) { e.printStackTrace(); } } } }
現在IndexReader是單例的了,假設在執行過程中,IndexWriter重新寫了索引,那麼此時IndexReader能不能讀取到更新後的索引呢?我們來試一下,附上delete()方法和forceDelete()方法。
public void delete() { IndexWriter indexWriter = null; try { indexWriter = new IndexWriter(directory, new IndexWriterConfig(Version.LUCENE_35, new StandardAnalyzer(Version.LUCENE_35))); // 引數是一個選項,可以是一個Query(查詢的一個範圍),也可以是一個Term(一個精確的查詢值) // 此時文件沒有被徹底刪除,只是放在了回收站中 indexWriter.deleteDocuments(new Term("id", "1")); } catch (IOException e) { e.printStackTrace(); } finally { if (indexWriter != null) { try { indexWriter.close(); } catch (IOException e) { e.printStackTrace(); } } } } public void forceDelete() { IndexWriter indexWriter = null; try { indexWriter = new IndexWriter(directory, new IndexWriterConfig(Version.LUCENE_35, new StandardAnalyzer(Version.LUCENE_35))); indexWriter.forceMergeDeletes(); } catch (IOException e) { e.printStackTrace(); } finally { if (indexWriter != null) { try { indexWriter.close(); } catch (IOException e) { e.printStackTrace(); } } } }
迴圈多次呼叫search(),並新增一條Thread.sleep(),線上程sleep的時候,執行delete()方法,再次看輸出,發現結果並沒有變化,被刪除的那條資料依舊還在,這時候,我們使用forceDelete()來刪除,發現結果還在,於是問題就出來了,這種寫法並不能獲取到索引的變化,它只是在第一次執行的時候將索引寫入記憶體,之後的訪問都是從記憶體獲取索引並輸出的,對於原來索引的變化,並不能感知到。
二、上述問題處理
將IndexUtil構造方法中的“indexReader = IndexReader.open(directory);”語句刪除,將getIndexSearcher()方法做修改。
public IndexSearcher getIndexSearcher() {
try {
if (indexReader == null) {
indexReader = IndexReader.open(directory);
} else {
IndexReader temp = IndexReader.openIfChanged(indexReader);
if (temp != null) {
// 將舊的indexReader關閉
indexReader.close();
indexReader = temp;
}
}
return new IndexSearcher(indexReader);
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
程式碼的改動中有一個IndexReader.openIfChanged()方法,API給出的解釋是“If the index has changed since the provided reader was opened, open and return a new reader; else, return null.”,意思是說從上次開啟indexReader之後,如果索引發生改變,就返回一個新的indexReader,否則返回null。所以說,當索引檔案發生改變之後,openIfChanged()方法肯定會返回一個新的IndexReader物件,下面使用這個新的IndexReader物件創建出來的IndexSearcher物件也是新的,所以就可以輸出索引改動後的內容了。
三、IndexWriter的介紹
在delete()方法中,我們看到最後有一個indexWriter.close(),如果我們的indexWriter也是單例的,我們不希望indexWriter關閉,怎麼保證delete操作提交呢?我們可以使用indexWriter.commit()語句來完成提交。
public void delete() {
IndexWriter indexWriter = null;
try {
indexWriter = new IndexWriter(directory, new IndexWriterConfig(Version.LUCENE_35, new StandardAnalyzer(Version.LUCENE_35)));
// 引數是一個選項,可以是一個Query(查詢的一個範圍),也可以是一個Term(一個精確的查詢值)
// 此時文件沒有被徹底刪除,只是放在了回收站中
indexWriter.deleteDocuments(new Term("id", "1"));
indexWriter.commit();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (indexWriter != null) {
// try {
// indexWriter.close();
// } catch (IOException e) {
// e.printStackTrace();
// }
}
}
}