1. 程式人生 > >Lucene筆記07-IndexReader的設計

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();
            // }
        }
    }
}