1. 程式人生 > >Lucene--Field域和索引維護

Lucene--Field域和索引維護

Lucene–Field域和索引維護

一、Field域

1.Field屬性

Field是文件中的域,包括Field名和Field值兩部分,一個文件可以包括多個Field,Document只是Field的一個承載體,Field值即為要索引的內容,也是要搜尋的內容。

  • 是否分詞(tokenized)

是:作分詞處理,即將Field值進行分詞,分詞的目的是為了索引。
比如:商品名稱、商品簡介等,這些內容使用者要輸入關鍵字搜尋,由於搜尋的內容格式大、內容多需要分詞後將語彙單元索引。

否:不作分詞處理
比如:商品id、訂單號、身份證號等

  • 是否索引(indexed)

是:進行索引。將Field分詞後的詞或整個Field值進行索引,索引的目的是為了搜尋。
比如:商品名稱、商品簡介分析後進行索引,訂單號、身份證號不用分析但也要索引,這些將來都要作為查詢條件。

否:不索引。該域的內容無法搜尋到
比如:商品id、檔案路徑、圖片路徑等,不用作為查詢條件的不用索引。

  • 是否儲存(stored)

是:將Field值儲存在文件中,儲存在文件中的Field才可以從Document中獲取。
比如:商品名稱、訂單號,凡是將來要從Document中獲取的Field都要儲存。

否:不儲存Field值,不儲存的Field無法通過Document獲取
比如:商品簡介,內容較大不用儲存。如果要向用戶展示商品簡介可以從系統的關係資料庫中獲取商品簡介。

如果需要商品描述,則根據搜尋出的商品ID去資料庫中查詢,然後顯示出商品描述資訊即可。

2.Field常用型別

開發中常用 的Filed型別,注意Field的屬性,根據需求選擇:

Field常用型別.PNG

3.Field改進程式碼

圖書id:

是否分詞:不用分詞,因為不會根據商品id來搜尋商品 
是否索引:不索引,因為不需要根據圖書ID進行搜尋
是否儲存:要儲存,因為查詢結果頁面需要使用id這個值。

圖書名稱:

是否分詞:要分詞,因為要將圖書的名稱內容分詞索引,根據關鍵搜尋圖書名稱抽取的詞。
是否索引:要索引。
是否儲存:要儲存。

圖書價格:

是否分詞:要分詞,lucene對數字型的值只要有搜尋需求的都要分詞和索
引,因為lucene對數字型的內容要特殊分詞處理,本例子可能要根據價格範
圍搜尋,需要分詞和索引。
是否索引:要索引
是否儲存:要儲存

圖書圖片地址:

是否分詞:不分詞
是否索引:不索引
是否儲存:要儲存

圖書描述:

是否分詞:要分詞
是否索引:要索引
是否儲存:因為圖書描述內容量大,不在查詢結果頁面直接顯示,不儲存。
不儲存是來不在lucene的索引檔案中記錄,節省lucene的索引檔案空間,
如果要在詳情頁面顯示描述,思路:
從lucene中取出圖書的id,根據圖書的id查詢關係資料庫中book表
得到描述資訊。

程式碼:

@Test
public void createIndex() throws Exception {
    // 採集資料
    BookDao dao = new BookDaoImpl();
    List<Book> list = dao.queryBooks();

    // 將採集到的資料封裝到Document物件中
    List<Document> docList = new ArrayList<>();
    Document document;
    for (Book book : list) {
        document = new Document();
        // store:如果是yes,則說明儲存到文件域中
        // 圖書ID
        // 不分詞、索引、儲存 StringField
        Field id = new StringField("id", book.getId().toString(), Store.YES);
        // 圖書名稱
        // 分詞、索引、儲存 TextField
        Field name = new TextField("name", book.getName(), Store.YES);
        // 圖書價格
        // 分詞、索引、儲存 但是是數字型別,所以使用FloatField
        Field price = new FloatField("price", book.getPrice(), Store.YES);
        // 圖書圖片地址
        // 不分詞、不索引、儲存 StoredField
        Field pic = new StoredField("pic", book.getPic());
        // 圖書描述
        // 分詞、索引、不儲存 TextField
        Field description = new TextField("description",
                book.getDescription(), Store.NO);

        // 設定boost值
        if (book.getId() == 4)
            description.setBoost(100f);

        // 將field域設定到Document物件中
        document.add(id);
        document.add(name);
        document.add(price);
        document.add(pic);
        document.add(description);

        docList.add(document);
    }

二、索引維護

需求:

管理人員通過電商系統更改圖書資訊,這時更新的是資料庫,如果使用lucene搜尋圖書資訊需要在資料庫表book資訊變化時及時更新lucene索引庫。

1.新增索引

呼叫 indexWriter.addDocument(doc)新增索引。

@Test
public void createIndex() throws Exception {
    // 採集資料
    BookDao dao = new BookDaoImpl();
    List<Book> list = dao.queryBooks();

    // 將採集到的資料封裝到Document物件中
    List<Document> docList = new ArrayList<>();
    Document document;
    for (Book book : list) {
        document = new Document();
        // store:如果是yes,則說明儲存到文件域中
        // 圖書ID
        Field id = new TextField("id", book.getId().toString(), Store.YES);
        // 圖書名稱
        Field name = new TextField("name", book.getName(), Store.YES);
        // 圖書價格
        Field price = new TextField("price", book.getPrice().toString(),
                Store.YES);
        // 圖書圖片地址
        Field pic = new TextField("pic", book.getPic(), Store.YES);
        // 圖書描述
        Field description = new TextField("description",
                book.getDescription(), Store.YES);

        // 將field域設定到Document物件中
        document.add(id);
        document.add(name);
        document.add(price);
        document.add(pic);
        document.add(description);

        docList.add(document);
    }

    // 建立分詞器,標準分詞器
    Analyzer analyzer = new StandardAnalyzer();

    // 建立IndexWriter
    IndexWriterConfig cfg = new IndexWriterConfig(Version.LUCENE_4_10_3,
            analyzer);
    // 指定索引庫的地址
    File indexFile = new File("E:\\11-index\\hm19\\");
    Directory directory = FSDirectory.open(indexFile);
    IndexWriter writer = new IndexWriter(directory, cfg);

    // 通過IndexWriter物件將Document寫入到索引庫中
    for (Document doc : docList) {
        writer.addDocument(doc);
    }

    // 關閉writer
    writer.close();
}
2.刪除索引

2.1刪除指定索引

根據Term項刪除索引,滿足條件的將全部刪除。

Term是索引域中最小的單位。根據條件刪除時,建議根據唯一鍵來進行刪除。在solr中就是根據ID來進行刪除和修改操作的。

@Test
public void deleteIndex() throws Exception {
    // 建立分詞器,標準分詞器
    Analyzer analyzer = new StandardAnalyzer();

    // 建立IndexWriter
    IndexWriterConfig cfg = new IndexWriterConfig(Version.LUCENE_4_10_3,
            analyzer);
    Directory directory = FSDirectory
            .open(new File("E:\\11-index\\hcx\\"));
    // 建立IndexWriter
    IndexWriter writer = new IndexWriter(directory, cfg);

    // Terms
    writer.deleteDocuments(new Term("id", "1"));

    writer.close();
}

2.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之後無法恢復。

3.修改索引

更新索引是先刪除再新增,建議對更新需求採用此方法並且要保證對已存在的索引執行更新,可以先查詢出來,確定更新記錄存在執行更新操作。

@Test
public void updateIndex() throws Exception {
    // 建立分詞器,標準分詞器
    Analyzer analyzer = new StandardAnalyzer();

    // 建立IndexWriter
    IndexWriterConfig cfg = new IndexWriterConfig(Version.LUCENE_4_10_3,
            analyzer);

    Directory directory = FSDirectory
            .open(new File("E:\\11-index\\hcx\\"));
    // 建立IndexWriter
    IndexWriter writer = new IndexWriter(directory, cfg);

    // 第一個引數:指定查詢條件
    // 第二個引數:修改之後的物件
    // 修改時如果根據查詢條件,可以查詢出結果,則將以前的刪掉,然後覆蓋新的Document物件,如果沒有查詢出結果,則新增一個Document
    // 修改流程即:先查詢,再刪除,在新增
    Document doc = new Document();
    doc.add(new TextField("name", "lisi", Store.YES));
    writer.updateDocument(new Term("name", "zhangsan"), doc);

    writer.close();
}