Lucene實現索引和查詢

来源:http://www.cnblogs.com/zhaoyufei/archive/2017/12/22/8087138.html
-Advertisement-
Play Games

0引言 隨著萬維網的發展和大數據時代的到來,每天都有大量的數字化信息在生產、存儲、傳遞和轉化,如何從大量的信息中以一定的方式找到滿足自己需求的信息,使之有序化並加以利用成為一大難題。全文檢索技術是現如今最普遍的信息查詢應用,生活中利用搜索引擎,在博客論壇中查找信息,這些搜索的核心原理就是本文要實現的 ...


0引言

  隨著萬維網的發展和大數據時代的到來,每天都有大量的數字化信息在生產、存儲、傳遞和轉化,如何從大量的信息中以一定的方式找到滿足自己需求的信息,使之有序化並加以利用成為一大難題。全文檢索技術是現如今最普遍的信息查詢應用,生活中利用搜索引擎,在博客論壇中查找信息,這些搜索的核心原理就是本文要實現的全文檢索技術。隨著文檔信息數字化的實現,將信息有效存儲並及時準確的提取是每一個公司、企業和單位要做好的基礎。針對英文的全文檢索已經有很多成熟的理論和方法,開放源代碼的全文檢索引擎Lucene 是Apache 軟體基金會Jakarta 項目組的一個子項目,它的目的是為軟體開發人員提供一個簡單易用的工具包,方便在目標系統中實現全文檢索的功能。Lucene不支持中文,但是目前已有很多開源的中文分詞器可以對中文內容進行索引,本文在研究Lucene核心原理的基礎上,分別實現了對中英文網頁的爬取和檢索。

1 Lucene介紹

1.1 lucene簡介

Lucene是一個用Java寫的全文檢索引擎工具包,實現構造了索引和搜索兩大核心功能,並且兩者相互獨立,這使得開發人員可以方便擴展,Lucene提供了豐富的API , 可以與存儲在索引中的信息方便的交互。需要說明的是它並不是一個完整的全文檢索應用, 而是為應用程式提供索引和搜索功能。即若想讓Lucene 真正起作用, 還需在其基礎上做一些必要的二次開發。

Lucene的結構設計與資料庫的設計較為相似,但Lucene的索引與資料庫有著極大的不同。資料庫和Lucene建立索引都是為了查找方便,但是資料庫僅僅針對部分欄位進行建立,且需要把數據轉化為格式化信息,並予以保存。而全文檢索是將全部信息按照一定方式進行索引。兩種檢索的不同和相似如表1-1所示。

表1-1:資料庫檢索與Lucene檢索對比

比較項

Lucene檢索

資料庫檢索

數據檢索

從Lucene的索引文件中檢出

由資料庫索引檢索記錄

索引結構

Document(文檔)

Record(記錄)

查詢結果

Hit:滿足關係的文檔組成

查詢結果集:包含關鍵字的記錄組成

全文檢索

支持

不支持

模糊查詢

支持

不支持

結果排序

設置權重,進行相關性排序

不能排序

1.2 lucene總體結構

   Lucene軟體包的發佈形式是一個JAR文件,版本更新較快且版本差距較大,本文使用的是5.3.1的版本,主要使用的子包如表1-2所示。

表1-2:子包和功能

包名

功能

Org .apache.lucene .analysis

分詞

Org .apache.lucene .document

對索引管理的文檔

Org .apache.lucene .index

索引操作,包括增加、刪除等

Org .apache.lucene .queryParser

查詢器,構造檢索表達式

Org .apache.lucene .search

檢索管理

Org .apache.lucene .store

數據存儲管理

Org .apache.lucene .util

公共類

 

1.3 lucene架構設計

Lucene功能非常強大,但從根本上來說,主要包括兩塊:一是從文本內容切分詞後索引入庫;二是根據查詢條件返回結果,即建立索引和進行查詢兩部分。

如圖1-1所示,本文拋出外部介面以及信息來源,重點對網頁爬取的文本內容進行索引和查詢 。  

                              圖1-1:Lucene的架構設計

 

 

2 JDK的安裝和環境變數的配置

  1.jdk的下載:

      在oracle官網下載符合系統版本的壓縮包,網址如下。點擊安裝,根據提示進行安裝,在安裝過程中會提示是否安裝jre,點擊是。                                                         http://www.oracle.com/technetwork/java/javase/downloads/index.html

  2.設置環境變數:

      (1)右鍵電腦=》屬性=》高級系統設置=》環境變數=》系統變數=》新建=》JAVA_HOME:安裝路徑

      (2)Path中新增=》%JAVA_HOME%\bin

   3.測試是否成功:

         開始=》運行=》CMD  回車 在彈出的 DOS 視窗內

      輸入:java -version 會出現版本信息,

      輸入: javac出現 javac 的用法信息

      出現如圖2-1所示為成功。

圖2-1:cmd命令框測試java配置

編寫Java代碼實現對網頁內容的獲取

    因為Lucene針對不同語言要使用不同的分詞器,英文使用標準分詞器,中文選擇使用smartcn分詞器。在獲取網頁的時候,先獲取網頁存為html文件,在html中由於標簽           的干擾,會對檢索效果產生影響,因此需要對html標簽進行剔除,並將文本內容轉為txt文件進行保存。中英文除了分詞器不同,其他基本一致,因此之後的代碼和實驗結果演            示會選擇任一。本文選取五十篇中文故事和英文故事的網頁為例。

              具體代碼設計如下圖:Url2Html.java將輸入網址的網頁轉存為html文件,Html2Txt.java文件實現html文檔標簽的去除,轉存為txt文檔。具體代碼如圖3-1和3-2。

public void way(String filePath,String url) throws Exception{
        File dest = new File(filePath);//建立文件
        InputStream is;//接收位元組輸入流
        FileOutputStream fos = new FileOutputStream(dest);//位元組輸出流
        URL wangzhi = new URL(url);//設定網址URL
        is = wangzhi.openStream();
        BufferedInputStream bis = new BufferedInputStream(is);//為位元組輸入流加緩衝
        BufferedOutputStream bos = new BufferedOutputStream(fos);//為位元組輸出流加緩衝
        /*
         * 對位元組進行讀取
         */
        int length;
        byte[] bytes = new byte[1024*20];
        while((length = bis.read(bytes, 0, bytes.length)) != -1){
            fos.write(bytes, 0, length);
        }
        /*
         * 關閉緩衝流和輸入輸出流
         */
        bos.close();        
        fos.close();
        bis.close();
        is.close();
    }

public String getBody(String val){
  String zyf = val.replaceAll("</?[^>]+>", ""); //剔出<html>的標簽
  return zyf;
}

public void writeTxt(String Str,String writePath) {
  File writename = new File(writePath);
  try {
    writename.createNewFile();
    BufferedWriter out = new BufferedWriter(new FileWriter(writename));
    out.write(Str);
    out.flush();
    out.close();
  } catch (IOException e) {
    e.printStackTrace();
  }
}

以童話故事《笨狼上學》的網頁為例,文檔路徑設為”E:\work \lucene \test \data \html”和”E:\work\lucene\test\data\txt”,在每一次讀取網頁的時候需要設定的兩個參數為文件命名filename和獲取目標網址url。新建一個main函數,實現對兩個方法的調用。具體實現如圖3-3所示:

  public static void main(String[] args) {
    String filename = "jingdizhi";//文件名字
    String url = "http://www.51test.net/show/8072125.html";//需要爬取的網頁url
    String filePath = "E:\\work\\lucene\\test\\data\\html\\"+filename+".html";//寫出html的文件路徑+文件名
    String writePath = "E:\\work\\lucene\\test\\data\\txt\\"+filename+".txt";//寫出txt的文件路徑+文件名

    Url2Html url2html = new Url2Html();
    try {
      url2html.way(filePath,url);
    } catch (Exception e) {
      e.printStackTrace();
    }

    Html2Txt html2txt = new Html2Txt();
    String read=html2txt.readfile(filePath);//讀取html文件
    String txt = html2txt.getBody(read);//去除html標簽
    System.out.println(txt);
    try {
      html2txt.writeTxt(txt,writePath);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

執行程式後,分別在兩個文件夾中建立”笨狼上學.html”和”笨狼上學.txt”。

建立索引

索引和查詢的基本原理如下:

建立索引:搜索引擎的索引其實就是實現“單詞-文檔矩陣”的具體數據結構。也是進行全文檢索的第一步,lucene提供IndexWriter類進行索引的管理,主要包括add()、delete()、update()。還有對權值的設定,通過不同索引權值的設定,可以在搜索的時候根據相關性大小進行返回。

進行搜索:原本的直接搜索是針對文檔進行順序檢索,在建立索引之後,可以通過對索引的查找以找到索引詞在文檔中出現的位置,然後返回索引項所對的文檔中的位置和詞。Lucene提供IndexSearcher類進行對文檔的檢索,檢索形式主要分為兩類,第一類是Term,針對單個詞項的檢索;第二類是Parser,可以自定義構造檢索表達式,有較多的檢索形式,具體的方法會在之後進行實現的演示。

4.1 實驗環境

本PC機採用windows 10x64系統,8G記憶體,256G固態硬碟。開發環境為Myeclipse 10,jdk版本為1.8。在實驗過程中,因為部分語法的轉變,若幹Class採用1.6版本實現。

4.2 建立索引

建立索引庫就是往索引庫添加一條條索引記錄,Lucene為添加一條索引記錄提供了介面,添加索引。

主要用到了“寫索引器”、“文檔”、“域”這3 個類。要建立索引,首先要構造一個Document 文檔對象,確定Document的各個域,這類似於關係型資料庫中表結構的建立,Document相當於表中的一個記錄行,域相當於一行中的列,在Lucene 中針對不同域的屬性和數據輸出的需求,對域還可以選擇不同的索引/存儲欄位規則,在本實驗中,文件名fileName、文件路徑fullPath和文本內容content作為Document 的域。

IndexWriter 負責接收新加入的文檔,並寫入索引庫中。在創建“寫索引器”IndexWriter 時需要指定所使用的語言分析器。建立索引分為兩個類別,第一:不加權索引;第二:加權索引。

public Indexer(String indexDir)throws Exception{
        Directory dir=FSDirectory.open(Paths.get(indexDir));
        Analyzer analyzer=new StandardAnalyzer(); // 標準分詞器
        //SmartChineseAnalyzer analyzer = new SmartChineseAnalyzer();
        IndexWriterConfig iwc=new IndexWriterConfig(analyzer);
        writer=new IndexWriter(dir, iwc);
    }

設置索引欄位,Store表示是否對索引內容存儲:fileName和fullPath占用記憶體較少可以進行存儲,以方便查詢返回。

private Document getDocument(File f)throws Exception {
        Document doc=new Document();
        doc.add(new TextField("contents", new FileReader(f)));
        doc.add(new TextField("fileName", f.getName(),Store.YES));
        doc.add(new TextField("fullPath",f.getCanonicalPath(),Store.YES));//路徑索引
        return doc;
    }

執行主代碼後結果如圖:設計在索引某個文件的時候返迴文件“索引文件:+文件路徑”,且計算輸出索引全部文件花費的時間。

4.3 對索引的刪除和修改

一般對資料庫的操作包括CRUD(增加、刪除、更改、查詢),增加就是對索引項的選擇和建立,查詢作為較為核心的功能會在之後展開論述,這裡主要記錄一下在刪除、更新索引時用到的方法。

刪除分為兩種類型,包括普通的刪除和徹底刪除,因為索引的刪除影響到整個資料庫,而且對於大型的系統而言,刪除索引意味著對系統的底層進行更改,耗時耗力而且無法返回,前面索引的時候看到建立索引後生成若幹小文件,當進行查找的時候會將各個文件進行合併然後查找。普通刪除僅僅是對之前建立的索引做個簡單的標記,致使無法進行查找返回。徹底刪除則是對索引進行銷毀,無法撤銷。以刪除索引項“id”為1的索引為例:

普通的刪除(在合併前刪除):

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

writer.commit();

徹底的刪除(在合併後刪除):

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

   writer.forceMergeDeletes(); // 強制刪除

   writer.commit();

對索引的修改原理比較簡單,就是在原有索引的基礎上實現覆蓋,實現代碼跟上文的增加索引一樣,在此不多做闡述。

4.4 對索引的加權

Lucene預設按照相關度排序,Lucene對Field提供了一個可以設置的Boosting參數,這個參數用來表示記錄的重要性,在滿足搜索條件是,會優先考慮重要性高的記錄,返回結果靠前,如果記錄較多,權值低的記錄會排到首頁之後,因此,對索引的加權操作是影響返回結果滿意度的重要因素,在實際設計信息系統的時候,應該有嚴格的權值計算公式,方便對Field權值的更改,更好的滿足用戶的需求。

例如搜索引擎將點擊率高,鏈入鏈出的網頁給定較高的權重,在返回的時候排到第一頁。實現代碼如圖4-1所示,不加權和加權結果對比如圖4-2所示。

     TextField field = new TextField("fullPath", f.getCanonicalPath(), Store.YES);
        if("A GREAT GRIEF.txt".equals(f.getName())){
            field.setBoost(2.0f);//對文件名為secondry story.txt的fullPath路徑加權;
        }                        //預設權重為1.0,改為1.2即增加權重。
        doc.add(field);

圖4-1:索引加權

圖4-2:加權之前

圖4-2:加權之後

由圖4-2結果可以看出,不加權時,按照字典順序排列返回,因此first在secondry之前,在對secondry命名的文件路徑加權後,返回的時候順序發生變化,實現對權重的測試。

進行查詢

Lucene 的檢索介面主要由QueryParser、IndexSearcher、Hits這3 個類構成,QueryParser 是查詢解析器,負責解析用戶提交的查詢關鍵字,在新建一個解析器時需要指定要解析的域和使用什麼語言分析器,這裡使用的語言分析器必須與索引庫建立時使用的解析器相同,否則查詢結果不正確。IndexSearcher是索引搜索器,在實例化IndexSearcher時需要指定索引庫所在的目錄,IndexSearcher有一個search 方法執行索引的檢索,這個方法接受Query 作為參數,返回Hits,Hists 是一系列排好序的查詢結果的集合,集合的元素是Document。通過Document的get 方法可以得到與這個文檔對應文件的信息,比如:文件名、文件路徑、文件內容等。

5.1 基本查詢

如圖查詢主要有兩種方式,但是推薦使用第一種構造QueryParser表達式,它可以有靈活的組合方式,包括布爾邏輯表達、模糊匹配等,但是第二種Term只能針對辭彙查詢。

1.構造QueryParser查詢式:

    QueryParser parser=new QueryParser("fullPath", analyzer);

       Query query=parser.parse(q);

2.對特定項的查詢:

    Term t = new Term("fileName", q);

       Query query = new TermQuery(t);

查詢結果如圖5-1所示:以查詢文件名fileName包含“大”為例。

圖5-1:“大”查詢結果

5.2 模糊查詢

在構造QueryParser時,通過對詞項q的修改可以實現精確匹配和模糊匹配。模糊匹配通過在“q”之後加“~”進行修改。如圖5-2所示:

圖5-2:模糊匹配

5.3 限定條件查詢

布爾邏輯查詢和模糊查詢只需要對查詢詞q進行更改,而限定條件查詢需要對query表達式進行設定,主要分為以下幾類:

分別為指定項範圍搜索、指定數字範圍、指定字元串開頭和多條件查詢,分別列出應用的查詢,true參數指的:是否包含上限和下限在內。

指定項範圍:

TermRangeQuery query=new TermRangeQuery("desc", new BytesRef("b".getBytes()), new BytesRef("c".getBytes()), true, true);

指定數字範圍:

NumericRangeQuery<Integer> query=NumericRangeQuery.newIntRange("id", 1, 2, true, true);

指定字元串開頭:

PrefixQuery query=new PrefixQuery(new Term("city","a"));

多條件查詢:

NumericRangeQuery<Integer>query1=NumericRangeQuery.newIntRange("id", 1, 2, true, true);

PrefixQuery query2=new PrefixQuery(new Term("city","a"));

BooleanQuery.Builder booleanQuery=new BooleanQuery.Builder();

booleanQuery.add(query1,BooleanClause.Occur.MUST);

booleanQuery.add(query2,BooleanClause.Occur.MUST);

5.4 高亮查詢

在百度、谷歌等搜索引擎中,進行查詢時,返回的網頁包含查詢關鍵字的時候會顯示為紅色,且進行摘要顯示,即對包含關鍵字的部分內容進行截取並返回。高亮查詢即為實現對關鍵字的樣式更改,本實驗在myeclipse中進行,返回結果並不會有樣式的改變,只會對返回內容的關鍵字添加html標簽,如果顯示到網頁即產生樣式的變化。

高亮的設置代碼如圖5-3所示,結果如圖5-4所示,會對南京匹配詞添加<b>和<font>標簽,顯示到網頁上為加粗和變紅。

QueryScorer scorer=new QueryScorer(query);
        Fragmenter fragmenter=new SimpleSpanFragmenter(scorer);
        SimpleHTMLFormatter simpleHTMLFormatter=new SimpleHTMLFormatter("<b><font color='red'>","</font></b>");
        Highlighter highlighter=new Highlighter(simpleHTMLFormatter, scorer);
        highlighter.setTextFragmenter(fragmenter);

圖5-3:高亮設置

圖5-4:高亮顯示結果

實驗過程中遇到的問題和不足

Lucene版本更新較快,在jdk版本、eclipse版本和lucene版本之間需要一個良好的銜接,否則會造成很多的不相容,在調試版本以及jdk1.6和jdk1.8的選擇上出現很多困難,比如網頁抓取中的append方法在1.8版本已經刪除,不能使用。但是對文檔路勁的讀取FSDirectory.open()則需要jdk1.8才支持。

本實驗的不足之處主要表現在:

代碼的靈活性較低,在爬取網頁的時候需要手工進行,且需要對中文和英文分別進行,應該完善代碼使得對網頁的語言有個判定,然後自動選擇執行不同的分詞器。

代碼的復用性較低,沒有較為合理的分類和方法的構建,為了簡便,基本在幾個核心代碼中進行註釋和標記而實現效果,有待改進。

代碼的可移植性較低,對網頁的爬取使用的是jdk1.6的版本,Lucene的實現使用的是jdk1.8的版本,在導出到其他機器上,需要對環境稍加修改和配置,無法實現一鍵式操作。

總結

本文從Lucene的原理出發,瞭解了全文檢索的思路和方法,並對常用的功能進行了實驗和測試。在實驗的過程中,瞭解了搜索引擎的原理,基於信息檢索課程的內容上,有了一個更好的實操體驗。Lucene 是一個優秀的開源全文本搜索技術框架,通過對它的深入研究,對其實現機制更加熟悉,在研究它的過程中學習了很多面向對象的編程方法和思想,它良好的系統框架和擴展性值得學習借鑒。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 版本:3.4.10 問題:啟動 zkServer.cmd時報錯如下, 解決辦法: bin目錄下 zkEnv.cmd配置 修改為: 把雙引號放在外面。 此時運行zkServer.cmd 用zkCli.cmd連接成功: 成功! ...
  • 使用While迴圈時經常會犯的一些小錯誤。以及猜年齡程式的2種編寫方式。 ...
  • 為什麼要用插件 主要還是自動化的考慮,如果額外使用Dockerfile進行鏡像生成,可能會需要自己手動指定jar/war位置,並且打包和生成鏡像間不同步,帶來很多瑣碎的工作。 插件選擇 使用比較多的是spotify的插件:https://github.com/spotify/docker maven ...
  • 關於本文說明,本人原博客地址位於http://blog.csdn.net/qq_37608890,本文來自筆者於2017年12月06日 18:06:30所撰寫內容(http://blog.csdn.net/qq_37608890/article/details/78731169)。 本文根據最近學習 ...
  • Trie樹與AC自動機 作為現階段的學習中個人應有的常識,AC自動機形象的來講就是在Trie樹上跑的一個KMP。由此,我們就先來談一談Trie樹。(有圖) 1. Trie樹 又稱單詞查找樹,字典樹,一般用於字元串的匹配。它利用公共的字元串首碼進行查詢,減少了無謂的操作,是空間換時間的經典演算法。舉例: ...
  • 本文秉承著 你看不懂是你sb,我寫的代碼就要牛逼 的理念來介紹一些js的裝逼技巧。 下麵的技巧,後三個,請謹慎用於團隊項目中(主要考慮到可讀性的問題),不然,leader 乾你沒商量。 [圖片上傳失敗...(image-922e98-1513315809572)] image.png Boolean ...
  • 在2017年末,Face++發了一篇論文[ShuffleNet: An Extremely Efficient Convolutional Neural Network for Mobile Devices ](https://arxiv.org/abs/1707.01083)討論了一個極有效率且可... ...
  • Java基礎 進程和線程的區別; Java的併發、多線程、線程模型; 什麼是線程池,如何使用? 數據一致性如何保證;Synchronized關鍵字,類鎖,方法鎖,重入鎖; Java中實現多態的機制是什麼; 如何將一個Java對象序列化到文件里; 說說你對Java反射的理解; 同步的方法;多進程開發以 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...