Lucene介紹和入門

Lucene簡介
  Lucene是apache軟件基金會4 jakarta項目組的一個子項目,是一個開放源代碼的全文檢索引擎工具包,但它不是一個完整的全文檢索引擎,而是一個全文檢索引擎的架構,提供了完整的查詢引擎和索引引擎,部分文本分析引擎(英文與德文兩種西方語言)。Lucene的目的是爲軟件開發人員提供一個簡單易用的工具包,以方便的在目標系統中實現全文檢索的功能,或者是以此爲基礎創建起完整的全文檢索引擎。Lucene是一套用於全文檢索和搜尋的開源程式庫,由Apache軟件基金會支持和提供。Lucene提供了一個簡單卻強大的應用程式接口,可以作全文索引和搜尋。在Java開發環境裏Lucene是一個成熟的免費開源工具。就其自己而言,Lucene是當前以及最近幾年最受歡迎的免費Java信息檢索程序庫。人們常常提到信息檢索程序庫,雖然與搜索引擎有關,但不該該將信息檢索程序庫與搜索引擎相混淆。
  那麼先來講一說什麼是全文搜索
  說以前先說一說數據的分類: 
    咱們生活中的數據整體分爲兩種:結構化數據和非結構化數據。
    (1)結構化數據:指具備固定格式或有限長度的數據,如數據庫,元數據等。
    (2)非結構化數據:指不定長或無固定格式的數據,如郵件,word文檔等磁盤上的文件
  結構化數據查詢方法  數據庫搜索
  數據庫中的搜索很容易實現,一般都是使用sql語句進行查詢,並且能很快的獲得查詢結果。
  爲何數據庫搜索很容易?
  由於數據庫中的數據存儲是有規律的,有行有列並且數據格式、數據長度都是固定的。
  非結構化數據查詢方法
  (1)順序掃描法(Serial Scanning)
  所謂順序掃描,好比要找內容包含某一個字符串的文件,就是一個文檔一個文檔的看,對於每個文檔,從頭看到尾,若是此文檔包含此字符串,則此文檔爲咱們要找的文件,接着看下一個文件,直到掃描完全部的文件。如利用windows的搜索也能夠搜索文件內容,只是至關的慢。
  (2)全文檢索(Full-text Search)
  將非結構化數據中的一部分信息提取出來,從新組織,使其變得有必定結構,而後對此有必定結構的數據進行搜索,從而達到搜索相對較快的目的。這部分從非結構化數據中提取出的而後從新組織的信息,咱們稱之索引。
例如:字典。字典的拼音表和部首檢字表就至關於字典的索引,對每個字的解釋是非結構化的,若是字典沒有音節表和部首檢字表,在茫茫辭海中找一個字只能順序掃描。然而字的某些信息能夠提取出來進行結構化處理,好比讀音,就比較結構化,分聲母和韻母,分別只有幾種能夠一一列舉,因而將讀音拿出來按必定的順序排列,每一項讀音都指向此字的詳細解釋的頁數。咱們搜索時按結構化的拼音搜到讀音,而後按其指向的頁數,即可找到咱們的非結構化數據——也即對字的解釋。
  這種先創建索引,再對索引進行搜索的過程就叫全文檢索(Full-text Search)。
  雖然建立索引的過程也是很是耗時的,可是索引一旦建立就能夠屢次使用,全文檢索主要處理的是查詢,因此耗時間建立索引是值得的。
  全文檢索的應用場景
  對於數據量大、數據結構不固定的數據可採用全文檢索方式搜索,好比百度、Google等搜索引擎、論壇站內搜索、電商網站站內搜索等。
Lucene實現全文檢索的流程
一、綠色表示索引過程,對要搜索的原始內容進行索引構建一個索引庫,索引過程包括:
    肯定原始內容即要搜索的內容→採集文檔→建立文檔→分析文檔→索引文檔
  二、紅色表示搜索過程,從索引庫中搜索內容,搜索過程包括:
    用戶經過搜索界面→建立查詢→執行搜索,從索引庫搜索→渲染搜索結果
1,建立索引
  對文檔索引的過程,將用戶要搜索的文檔內容進行索引,索引存儲在索引庫(index)中。
這裏咱們要搜索的文檔是磁盤上的文本文件,根據案例描述:凡是文件名或文件內容包括關鍵字的文件都要找出來,這裏要對文件名和文件內容建立索引。
  1.1.1.   得到原始文檔
  原始文檔是指要索引和搜索的內容。原始內容包括互聯網上的網頁、數據庫中的數據、磁盤上的文件等。
  從互聯網上、數據庫、文件系統中等獲取須要搜索的原始信息,這個過程就是信息採集,信息採集的目的是爲了對原始內容進行索引。在Internet上採集信息的軟件一般稱爲爬蟲或蜘蛛,也稱爲網絡機器人,爬蟲訪問互聯網上的每個網頁,將獲取到的網頁內容存儲起來。
        Lucene不提供信息採集的類庫,須要本身編寫一個爬蟲程序實現信息採集,也能夠經過一些開源軟件實現信息採集,以下:
         (1)Nutch(http://lucene.apache.org/nutch), Nutch是apache的一個子項目,包括大規模爬蟲工具,可以抓取和分辨web網站數據。
         (2)jsoup(http://jsoup.org/ ),jsoup 是一款Java 的HTML解析器,可直接解析某個URL地址、HTML文本內容。它提供了一套很是省力的API,可經過DOM,CSS以及相似於jQuery的操做方法來取出和操做數據。
         (3)heritrix(http://sourceforge.net/projec...),Heritrix 是一個由 java 開發的、開源的網絡爬蟲,用戶可使用它來從網上抓取想要的資源。其最出色之處在於它良好的可擴展性,方便用戶實現本身的抓取邏輯。
  本案例咱們要獲取磁盤上文件的內容,能夠經過文件流來讀取文本文件的內容,對於pdf、doc、xls等文件可經過第三方提供的解析工具讀取文件內容,好比Apache POI讀取doc和xls的文件內容。
  1.1.2.   建立文檔對象
  獲取原始內容的目的是爲了索引,在索引前須要將原始內容建立成文檔(Document),文檔中包括一個一個的域(Field),域中存儲內容。
這裏咱們能夠將磁盤上的一個文件當成一個document,Document中包括一些Field(file_name文件名稱、file_path文件路徑、file_size文件大小、file_content文件內容)
注意:(1)每一個Document能夠有多個Field
   (2)不一樣的Document能夠有不一樣的Field
   (3)同一個Document能夠有相同的Field(域名和域值都相同)
   (4)每一個文檔都有一個惟一的編號,就是文檔id。
1.1.3.   分析文檔
  將原始內容建立爲包含域(Field)的文檔(document),須要再對域中的內容進行分析,分析的過程是通過對原始文檔提取單詞、將字母轉爲小寫、去除標點符號、去除停用詞等過程生成最終的語彙單元,能夠將語彙單元理解爲一個一個的單詞。
  好比下邊的文檔通過分析以下:
  原文檔內容:
  Lucene is a Java full-text search engine.  
  分析後獲得的語彙單元:
  lucene、java、full、search、engine
  每一個單詞叫作一個Term,不一樣的域中拆分出來的相同的單詞是不一樣的term。term中包含兩部分一部分是文檔的域名,另外一部分是單詞的內容。
  例如:文件名中包含apache和文件內容中包含的apache是不一樣的term。
1.1.4.   建立索引
  對全部文檔分析得出的語彙單元進行索引,索引的目的是爲了搜索,最終要實現只搜索被索引的語彙單元從而找到Document(文檔)。
注意:(1)建立索引是對語彙單元索引,經過詞語找文檔,這種索引的結構叫倒排索引結構。
   (2)傳統方法是根據文件找到該文件的內容,在文件內容中匹配搜索關鍵字,這種方法是順序掃描方法,數據量大、搜索慢。
     (3)倒排索引結構是根據內容(詞語)找文檔
倒排索引結構也叫反向索引結構,包括索引和文檔兩部分,索引即詞彙表,它的規模較小,而文檔集合較大。
編寫建立索引代碼
  使用indexwriter對象建立索引
  具體步驟:
  第一步:建立一個indexwriter對象。
    1)指定索引庫的存放位置Directory對象
    2)指定一個分析器,對文檔內容進行分析。
  第二步:建立document對象。
  第三步:建立field對象,將field添加到document對象中。
  第四步:使用indexwriter對象將document對象寫入索引庫,此過程進行索引建立。並將索引和document對象寫入索引庫。
  第五步:關閉IndexWriter對象。
[Java] 純文本查看 複製代碼
?
//建立索引
 2     public void testCreateIndex() throws IOException{
 3         //指定索引庫的存放位置Directory對象
 4         Directory directory = FSDirectory.open(new File("E:\programme\test"));
 5         //索引庫還能夠存放到內存中
 6         //Directory directory = new RAMDirectory();
 7
 8         //指定一個標準分析器,對文檔內容進行分析
 9         Analyzer analyzer = new StandardAnalyzer();
10        
11         //建立indexwriterCofig對象
12         //第一個參數: Lucene的版本信息,能夠選擇對應的lucene版本也可使用LATEST
13         //第二根參數:分析器對象
14         IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, analyzer);
15        
16         //建立一個indexwriter對象
17         IndexWriter indexWriter = new IndexWriter(directory, config);
18        
19         //原始文檔的路徑
20         File file = new File("E:\programme\searchsource");
21         File[] fileList = file.listFiles();
22         for (File file2 : fileList) {
23             //建立document對象
24             Document document = new Document();
25            
26             //建立field對象,將field添加到document對象中
27            
28             //文件名稱
29             String fileName = file2.getName();
30             //建立文件名域
31             //第一個參數:域的名稱
32             //第二個參數:域的內容
33             //第三個參數:是否存儲
34             Field fileNameField = new TextField("fileName", fileName, Store.YES);
35            
36             //文件的大小
37             long fileSize  = FileUtils.sizeOf(file2);
38             //文件大小域
39             Field fileSizeField = new LongField("fileSize", fileSize, Store.YES);
40            
41             //文件路徑
42             String filePath = file2.getPath();
43             //文件路徑域(不分析、不索引、只存儲)
44             Field filePathField = new StoredField("filePath", filePath);
45            
46             //文件內容
47             String fileContent = FileUtils.readFileToString(file2);
48             //String fileContent = FileUtils.readFileToString(file2, "utf-8");
49             //文件內容域
50             Field fileContentField = new TextField("fileContent", fileContent, Store.YES);
51            
52             document.add(fileNameField);
53             document.add(fileSizeField);
54             document.add(filePathField);
55             document.add(fileContentField);
56             //使用indexwriter對象將document對象寫入索引庫,此過程進行索引建立。並將索引和document對象寫入索引庫。
57             indexWriter.addDocument(document);
58         }
59         //關閉IndexWriter對象。
60         indexWriter.close();
61     }
Field域的屬性概述
  是否分析:是否對域的內容進行分詞處理。前提是咱們要對域的內容進行查詢。
  是否索引:將Field分析後的詞或整個Field值進行索引,只有索引方可搜索到。
  好比:商品名稱、商品簡介分析後進行索引,訂單號、身份證號不用分析但也要索引,這些未來都要做爲查詢條件。
  是否存儲:將Field值存儲在文檔中,存儲在文檔中的Field才能夠從Document中獲取
  好比:商品名稱、訂單號,凡是未來要從Document中獲取的Field都要存儲。java

  1. 查詢索引

  查詢索引也是搜索的過程。搜索就是用戶輸入關鍵字,從索引(index)中進行搜索的過程。根據關鍵字搜索索引,根據索引找到對應的文檔,從而找到要搜索的內容(這裏指磁盤上的文件)。
       對要搜索的信息建立Query查詢對象,Lucene會根據Query查詢對象生成最終的查詢語法,相似關係數據庫Sql語法同樣Lucene也有本身的查詢語法,好比:「name:lucene」表示查詢Field的name爲「lucene」的文檔信息。
  2.1.   用戶查詢接口
  全文檢索系統提供用戶搜索的界面供用戶提交搜索的關鍵字,搜索完成展現搜索結果。
  好比: 百度搜索
  Lucene不提供製做用戶搜索界面的功能,須要根據本身的需求開發搜索界面。
  2.2.   建立查詢
  用戶輸入查詢關鍵字執行搜索以前須要先構建一個查詢對象,查詢對象中能夠指定查詢要搜索的Field文檔域、查詢關鍵字等,查詢對象會生成具體的查詢語法,
  例如: 語法 「fileName:lucene」表示要搜索Field域的內容爲「lucene」的文檔
  2.3.   執行查詢
  搜索索引過程:
  根據查詢語法在倒排索引詞典表中分別找出對應搜索詞的索引,從而找到索引所連接的文檔鏈表。
  好比搜索語法爲「fileName:lucene」表示搜索出fileName域中包含Lucene的文檔。
  搜索過程就是在索引上查找域爲fileName,而且關鍵字爲Lucene的term,並根據term找到文檔id列表。
 
       可經過兩種方法建立查詢對象:
         1)使用Lucene提供Query子類
         Query是一個抽象類,lucene提供了不少查詢對象,好比TermQuery項精確查詢,NumericRangeQuery數字範圍查詢等。
         以下代碼:
      Query query = new TermQuery(new Term("name", "lucene"));
 
         2)使用QueryParse解析查詢表達式
         QueryParse會將用戶輸入的查詢表達式解析成Query對象實例。
         以下代碼:
           QueryParser queryParser = new QueryParser("name", new IKAnalyzer());
           Query query = queryParser.parse("name:lucene");
  首先,演示第一種方法,使用query的子類查詢
  實現步驟
  第一步:建立一個Directory對象,也就是索引庫存放的位置。
  第二步:建立一個indexReader對象,須要指定Directory對象。
  第三步:建立一個indexsearcher對象,須要指定IndexReader對象
  第四步:建立一個Query的子類對象,指定查詢的域和查詢的關鍵詞。
  第五步:執行查詢。
  第六步:返回查詢結果。遍歷查詢結果並輸出。
  第七步:關閉IndexReader對象
  MatchAllDocsQuery
  使用MatchAllDocsQuery查詢索引目錄中的全部文檔
[Java] 純文本查看 複製代碼
?web

@Test
   public void testMatchAllDocsQuery() throws Exception {
       //建立一個Directory對象,指定索引庫存放的路徑
       Directory directory = FSDirectory.open(new File("E:\programme\test"));
       //建立IndexReader對象,須要指定Directory對象
       IndexReader indexReader = DirectoryReader.open(directory);
       //建立Indexsearcher對象,須要指定IndexReader對象
       IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        
       //建立查詢條件
       //使用MatchAllDocsQuery查詢索引目錄中的全部文檔
       Query query = new MatchAllDocsQuery();
       //執行查詢
       //第一個參數是查詢對象,第二個參數是查詢結果返回的最大值
       TopDocs topDocs = indexSearcher.search(query, 10);
        
       //查詢結果的總條數
       System.out.println("查詢結果的總條數:"+ topDocs.totalHits);
       //遍歷查詢結果
       //topDocs.scoreDocs存儲了document對象的id
       //ScoreDoc[] scoreDocs = topDocs.scoreDocs;
       for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
           //scoreDoc.doc屬性就是document對象的id
           //int doc = scoreDoc.doc;
           //根據document的id找到document對象
           Document document = indexSearcher.doc(scoreDoc.doc);
           //文件名稱
           System.out.println(document.get("fileName"));
           //文件內容
           System.out.println(document.get("fileContent"));
           //文件大小
           System.out.println(document.get("fileSize"));
           //文件路徑
           System.out.println(document.get("filePath"));
           System.out.println("----------------------------------");
       }
       //關閉indexreader對象
       indexReader.close();
   }
TermQuery(精準查詢)
  TermQuery,經過項查詢,TermQuery不使用分析器因此建議匹配不分詞的Field域查詢,好比訂單號、分類ID號等。
指定要查詢的域和要查詢的關鍵詞。
[Java] 純文本查看 複製代碼
?
//搜索索引
 2     @Test
 3     public void testSearchIndex() throws IOException{
 4         //建立一個Directory對象,指定索引庫存放的路徑
 5         Directory directory = FSDirectory.open(new File("E:\programme\test"));
 6         //建立IndexReader對象,須要指定Directory對象
 7         IndexReader indexReader = DirectoryReader.open(directory);
 8         //建立Indexsearcher對象,須要指定IndexReader對象
 9         IndexSearcher indexSearcher = new IndexSearcher(indexReader);
10         //建立一個TermQuery(精準查詢)對象,指定查詢的域與查詢的關鍵詞
11         //建立查詢
12         Query query = new TermQuery(new Term("fileName", "apache"));
13         //執行查詢
14         //第一個參數是查詢對象,第二個參數是查詢結果返回的最大值
15         TopDocs topDocs = indexSearcher.search(query, 10);
16         //查詢結果的總條數
17         System.out.println("查詢結果的總條數:"+ topDocs.totalHits);
18         //遍歷查詢結果
19         //topDocs.scoreDocs存儲了document對象的id
20         //ScoreDoc[] scoreDocs = topDocs.scoreDocs;
21         for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
22             //scoreDoc.doc屬性就是document對象的id
23             //int doc = scoreDoc.doc;
24             //根據document的id找到document對象
25             Document document = indexSearcher.doc(scoreDoc.doc);
26             //文件名稱
27             System.out.println(document.get("fileName"));
28             //文件內容
29             System.out.println(document.get("fileContent"));
30             //文件大小
31             System.out.println(document.get("fileSize"));
32             //文件路徑
33             System.out.println(document.get("filePath"));
34             System.out.println("----------------------------------");
35         }
36         //關閉indexreader對象
37         indexReader.close();
38     }
39 }
NumericRangeQuery
  能夠根據數值範圍查詢。
[Java] 純文本查看 複製代碼
?sql

//數值範圍查詢
    @Test
    public void testNumericRangeQuery() throws Exception {
        //建立一個Directory對象,指定索引庫存放的路徑
        Directory directory = FSDirectory.open(new File("E:\programme\test"));
        //建立IndexReader對象,須要指定Directory對象
        IndexReader indexReader = DirectoryReader.open(directory);
        //建立Indexsearcher對象,須要指定IndexReader對象
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);
         
        //建立查詢
        //參數:
        //1.域名
        //2.最小值
        //3.最大值
        //4.是否包含最小值
        //5.是否包含最大值
        Query query = NumericRangeQuery.newLongRange("fileSize", 41L, 2055L, true, true);
        //執行查詢
 
        //第一個參數是查詢對象,第二個參數是查詢結果返回的最大值
        TopDocs topDocs = indexSearcher.search(query, 10);
         
        //查詢結果的總條數
        System.out.println("查詢結果的總條數:"+ topDocs.totalHits);
        //遍歷查詢結果
        //topDocs.scoreDocs存儲了document對象的id
        //ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
            //scoreDoc.doc屬性就是document對象的id
            //int doc = scoreDoc.doc;
            //根據document的id找到document對象
            Document document = indexSearcher.doc(scoreDoc.doc);
            //文件名稱
            System.out.println(document.get("fileName"));
            //文件內容
            System.out.println(document.get("fileContent"));
            //文件大小
            System.out.println(document.get("fileSize"));
            //文件路徑
            System.out.println(document.get("filePath"));
            System.out.println("----------------------------------");
        }
        //關閉indexreader對象
        indexReader.close();
    }數據庫

相關文章
相關標籤/搜索