1. 程式人生 > >重新更新索引的方法

重新更新索引的方法

由於分詞工具用的使用者字典有了更新,比如加入了一些出現頻度較高的專有名詞。
這時候希望通過重建索引,使查詢結果更加準確。
但是,由於一些資訊是當初建索引時加入的,而且這些資訊是不能從原始檔案中提取的,如當初的上傳者是誰、上傳日期 等。所以,不能夠直接刪除索引檔案,重新來過。

為此,必須遍歷原有索引,將其中進行了分析的Field重新分析並更新,不需要分析的Field則保持不變。

/**
 * 為現有檔案重新建立索引
 * 例如當更新了使用者字典時使用
 */
public void rebuildIndex(){
    IndexReader ireader = null;
    IndexWriter iwriter = null;
    Directory directory = null;
    try {
        long start = new Date().getTime();
        //前期準備工作
        File indexPath = new File(SystemProperties.getIndexPath());
        directory = FSDirectory.open(indexPath);
        //例項化IKAnalyzer分詞器
        Analyzer analyzer = new IKAnalyzer();
        //Analyzer analyzer = new CJKAnalyzer(Version.LUCENE_CURRENT);
        //建立記憶體索引物件
        ireader = IndexReader.open(directory);
        if (indexPath.list().length > 0) {
            // 已有以往索引
            iwriter = new IndexWriter(directory, analyzer, false, IndexWriter.MaxFieldLength.LIMITED);
        } else {
            // 首次建立索引
            iwriter = new IndexWriter(directory, analyzer, true, IndexWriter.MaxFieldLength.LIMITED);
        }
        //遍歷每一已有Document
        for (int i = 0; i < ireader.maxDoc(); i++) {
            try {
                // 提取原Document
                Document oldDoc = ireader.document(i);
                // 建立新Document
                Document newDoc = new Document();
                // 不變的Field直接從原Document中取
                // KEY
                newDoc.add(oldDoc.getField(Constants.FEILD_NAME_KEY));
                // 檔名
                newDoc.add(oldDoc.getField(Constants.FEILD_NAME_CLIENTNAME)); 
                newDoc.add(oldDoc.getField(Constants.FEILD_NAME_SERVERNAME));
                // 檔案型別
                newDoc.add(oldDoc.getField(Constants.FEILD_NAME_FILETYPE));
                // 加入時間
                newDoc.add(oldDoc.getField(Constants.FEILD_NAME_ADDTIME));
                // 所屬部門,使用者查詢時的許可權控制
                newDoc.add(oldDoc.getField(Constants.FEILD_NAME_OWNER));
                // 不變的Field從原Document中取得後,重新Analyse
                // 標題
                String title = oldDoc.getField(Constants.FEILD_NAME_TITLE).stringValue();
                newDoc.add(new Field(Constants.FEILD_NAME_TITLE, title, Field.Store.YES, Field.Index.ANALYZED));
                // 內容
                String text = oldDoc.getField(Constants.FEILD_NAME_CONTENTS).stringValue();
                newDoc.add(new Field(Constants.FEILD_NAME_CONTENTS, text, Field.Store.YES, Field.Index.ANALYZED));
                String key = oldDoc.getField(Constants.FEILD_NAME_KEY).stringValue();
                // 用KEY做查詢條件
                Term term = new Term(Constants.FEILD_NAME_KEY, key);
                // 替換原有的Document
                iwriter.updateDocument(term, newDoc);   
            } catch (Throwable t) {
                if (log.isErrorEnabled()) {
                    log.error(t.getMessage());
                }
            }
        }
        long end = new Date().getTime();

        if (log.isDebugEnabled()) {
            log.debug("Rebuild Index: " + ireader.maxDoc() + " documents, in " + (end - start) + " milliseconds.");
        }
    } catch (Throwable t) {
        if (log.isErrorEnabled()) {
            log.error(t.getMessage());
        }
    } finally {
        if (ireader != null) {
            try {
                ireader.close();
            } catch (AlreadyClosedException e) {
                log.error(e.getMessage());
            } catch (IOException e) {
                log.error(e.getMessage());
            }
        }
        if (iwriter != null) {
            try {
                // 注意這一句非常重要,否則雖然效果已經達到,但Documents數和儲存空間都會翻倍!
                // 但使用此方法的前提是,磁碟剩餘空間必須有已用索引空間的2倍
                // 此時由於重建,索引空間已經是翻倍的了,所以剩餘空間應該有之前索引空間的4被!
                iwriter.optimize();
                iwriter.close();
            } catch (AlreadyClosedException e) {
                log.error(e.getMessage());
            } catch (IOException e) {
                log.error(e.getMessage());
            }
        }
        if (directory != null) {
            try {
                directory.close();
            } catch (IOException e) {
                log.error(e.getMessage());
            }
        }
    }
}

剛剛接觸Lucene時間不長,不知道以上自己“杜撰”的程式碼是否可行,請大家多多指點。