1. 程式人生 > >Lucene全文檢索工具包學習筆記總結

Lucene全文檢索工具包學習筆記總結

Lucene—-全文檢索的工具包
隸屬於apache(solr也是屬於apache,solr底層的實現是Lucene)

一、資料的分類:
結構化資料
具有固定型別和長度的資料
比如:資料庫(mysql/oracl)中的資料,元資料(windows中的檔案)

非結構化資料
    沒有固定型別和長度的資料
    比如:郵件/word裡面的資料

二、資料查詢的方式
結構化資料
資料庫中資料通過sql語句可以搜尋
元資料(windows中的)通過windows提供的搜尋欄進行搜尋
非結構化資料
Word文件使用ctrl+F來搜尋
順序查詢法(效率低,只要文件中有一定就能夠找到)
全文檢索(倒排查詢),類似於字典查詢方式
三、全文檢索
含義:
把檔案中的內容提取出來,把檔案一個一個劃分成片語(分),
把片語組裝成索引,在進行搜尋的時候先對索引進行搜尋,
通過索引在去查詢文件,這個過程叫全文搜尋
優點:搜尋快,效率高
缺點:使用空間去換取時間。
全文檢索模仿的是字典查詢

四、Lucene
1.含義:
Lucene是一個全文檢索的工具包(jar);
通過Lucene可以構建一個全文檢索系統。
全文檢索系統:就是能在tomcat下獨立執行的war包,
對外提供全文檢索的服務。

2.應用領域:
    (1)網際網路全文檢索(比如baidu/goole等搜尋引擎);
    (2)站內全文檢索(比如:淘寶、jd站內搜尋);
    (3)優化資料庫(like模糊查詢,使用的是順序查詢,查詢慢);

3.Lucene結構:
    (類似於字典)
    Lucene結構=索引+Document文件(可以有多個);

4.Document文件物件
    先要獲取文件,然後建立文件物件Document;
    Document物件裡面包含了[域名name;域值value]形式的鍵值對,我們成為Field(域);
    Field可以儲存檔名稱、檔案大小、檔案型別、檔案儲存的路徑、檔案裡面的內容等;
    比如:一個document就是資料庫中的一條資料,一個Field對應資料庫中的一行一列
    注意:
        (1)建立好文件物件之後,我們需要對文件物件進行分詞,
           這裡使用什麼分詞器,在查詢的時候也要使用同樣的分詞器
        (2)每個Document可以有多個Field,不同的Document可以有不同的Field,
           同一個Document可以有相同的Field(域名和域值都相同)

5.分詞
    就是把提取的文件物件進行一個一個片語拆分;
    拆分的時候需要去掉停用詞(a, an, the ,的, 地, 得, 啊, 嗯 ,呵呵),
    因為搜尋的時候搜尋這些詞沒有意義,將句子拆分成詞,去掉標點符號和空格
    得到詞叫做詞元(term)

五、Document文件物件中的Field域

六、建立索引的流程
獲取需要建立索引的檔案—->穿件Document物件—->進行分詞
—->建立索引寫物件
—->將文件加入到索引和文件的寫物件中
—->索引寫物件提交和關閉索引寫物件流
@Test
public void testIndexManager() throws Exception {
List documents = new ArrayList<>();//建立文件物件集合
//讀取需要建立索引的檔案
File f = new File(“D:\Indexsearchsource”);
for (File file : f.listFiles()) {
//檔名
String fileName = file.getName();
//檔案內容
String fileContent = FileUtils.readFileToString(file);
//檔案大小
Long fileSize = FileUtils.sizeOf(file);

         //把檔名 檔案內容 檔案大小 放入到Field域中
         TextField nameField = new TextField("fileName", fileName, Store.YES);
         TextField contentField = new TextField("fileContent", fileContent, Store.YES);
         LongField sizeField = new LongField("fileSize", fileSize, Store.YES);

         //把域放入到文件Document物件中
         Document document = new Document();
         document.add(nameField);
         document.add(contentField);
         document.add(sizeField);

         //放入到一個document集合中
         documents.add(document);
    }
     //建立分詞器
     //Analyzer analyzer = new StandardAnalyzer();//標準分詞器
     Analyzer analyzer = new IKAnalyzer();//IK中文分詞器

     //索引放的位置  FS----磁碟  RAM----記憶體
     Directory directory = FSDirectory.open(new File("d:\\indexDir"));

     //寫物件的配置  使用什麼分詞器  lucene版本
     IndexWriterConfig conf = new IndexWriterConfig(Version.LUCENE_4_10_3, analyzer);

     //建立索引寫物件
     IndexWriter indexWriter = new IndexWriter(directory, conf);

     for (Document document : documents) {
        indexWriter.addDocument(document);
    }

     indexWriter.commit();
     indexWriter.close();
}

七、全文檢索刪除
刪除索引使用的IndexWriter物件
所以分詞器需要和建立索引的時候保持一致

刪除所有indexWriter.deleteAll();
根據某個詞元進行刪除indexWriter.deleteDocuments(new Term("fileName", "apache"));


@Test
public void testIndexDel() throws Exception{
    //建立分詞器,StandardAnalyzer標準分詞器,標準分詞器對英文分詞效果很好,對中文是單字分詞
    Analyzer analyzer = new IKAnalyzer();
    //指定索引和文件儲存的目錄
    Directory directory = FSDirectory.open(new File("E:\\dic"));
    //建立寫物件的初始化物件
    IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_4_10_3, analyzer);
    //建立索引和文件寫物件
    IndexWriter indexWriter = new IndexWriter(directory, config);

    //刪除所有
    //indexWriter.deleteAll();

    //根據名稱進行刪除
    //Term詞元,就是一個詞, 第一個引數:域名, 第二個引數:要刪除含有此關鍵詞的資料
    indexWriter.deleteDocuments(new Term("fileName", "apache"));

    //提交
    indexWriter.commit();
    //關閉
    indexWriter.close();
}

八、全文檢索修改

/**
 * 更新就是按照傳入的Term進行搜尋,如果找到結果那麼刪除,將更新的內容重新生成一個Document物件
 * 如果沒有搜尋到結果,那麼將更新的內容直接新增一個新的Document物件
 * @throws Exception
 */
@Test
public void testIndexUpdate() throws Exception{
    //建立分詞器,StandardAnalyzer標準分詞器,標準分詞器對英文分詞效果很好,對中文是單字分詞
    Analyzer analyzer = new IKAnalyzer();
    //指定索引和文件儲存的目錄
    Directory directory = FSDirectory.open(new File("E:\\dic"));
    //建立寫物件的初始化物件
    IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_4_10_3, analyzer);
    //建立索引和文件寫物件
    IndexWriter indexWriter = new IndexWriter(directory, config);


    //根據檔名稱進行更新
    Term term = new Term("fileName", "web");
    //更新的物件
    Document doc = new Document();
    doc.add(new TextField("fileName", "xxxxxx", Store.YES));
    doc.add(new TextField("fileContext", "think in java xxxxxxx", Store.NO));
    doc.add(new LongField("fileSize", 100L, Store.YES));

    //更新
    indexWriter.updateDocument(term, doc);

    //提交
    indexWriter.commit();
    //關閉
    indexWriter.close();
}

九、全文檢索查詢(重點)
TermQuery:
根據詞進行搜尋(只能從文字中進行搜尋)
QueryParser:
根據域名進行搜尋,可以設定預設搜尋域,推薦使用. (只能從文字中進行搜尋)
NumericRangeQuery:
從數值範圍進行搜尋
BooleanQuery:
組合查詢,可以設定組合條件,not and or.從多個域中進行查詢
must相當於and關鍵字,是並且的意思
should,相當於or關鍵字或者的意思
must_not相當於not關鍵字, 非的意思
注意:單獨使用must_not 或者 獨自使用must_not沒有任何意義
MatchAllDocsQuery:
查詢出所有文件
MultiFieldQueryParser:

可以從多個域中進行查詢,只有這些域中有關鍵詞的存在就查詢出來.

@Test
public void testIndexSearch() throws Exception {
    // 建立查詢的分詞器要和建立索引的分詞器保持一致
    Analyzer analyzer = new IKAnalyzer();
    // 目錄物件
    Directory directory = FSDirectory.open(new File("d:\\indexDir"));
    // 建立讀的索引物件
    IndexReader indexReader = IndexReader.open(directory);

    // 建立索引搜尋物件
    IndexSearcher indexSearcher = new IndexSearcher(indexReader);

    // 建立查詢物件,第一個引數:預設搜尋域, 第二個引數:分詞器
    // 預設搜尋域作用:如果搜尋語法中指定域名從指定域中搜索,
    // 如果搜尋時只寫了查詢關鍵字,則從預設搜尋域中進行搜尋
    QueryParser queryParser = new QueryParser("fileContent", analyzer);
    // 查詢語法=域名:搜尋的關鍵字
    Query query = queryParser.parse("fileName:apache");

    TopDocs topDocs = indexSearcher.search(query, 5);
    System.out.println("一共搜尋到多少條記錄====" + topDocs.totalHits);

    // 從搜尋結果物件中獲取結果集
    ScoreDoc[] scoreDocs = topDocs.scoreDocs;
    for (ScoreDoc scoreDoc : scoreDocs) {
        int docId = scoreDoc.doc;// 索引的id
        // 通過文件ID從硬碟中讀取出對應的文件
        Document document = indexReader.document(docId);
        String fileName = document.get("fileName");
        String fileContent = document.get("fileContent");
        String fileSize = document.get("fileSize");
        System.out.println(fileName);
        //System.out.println(fileContent);
        System.out.println(fileSize);
        System.out.println("==============");
    }
}

@Test
public void testIndexTermQuery() throws Exception{
    //建立分詞器(建立索引和所有時所用的分詞器必須一致)
    Analyzer analyzer = new IKAnalyzer();

    //建立詞元:就是詞,   
    Term term = new Term("fileName", "apache");
    //使用TermQuery查詢,根據term物件進行查詢
    TermQuery termQuery = new TermQuery(term);


    //指定索引和文件的目錄
    Directory dir = FSDirectory.open(new File("E:\\dic"));
    //索引和文件的讀取物件
    IndexReader indexReader = IndexReader.open(dir);
    //建立索引的搜尋物件
    IndexSearcher indexSearcher = new IndexSearcher(indexReader);
    //搜尋:第一個引數為查詢語句物件, 第二個引數:指定顯示多少條
    TopDocs topdocs = indexSearcher.search(termQuery, 5);
    //一共搜尋到多少條記錄
    System.out.println("=====count=====" + topdocs.totalHits);
    //從搜尋結果物件中獲取結果集
    ScoreDoc[] scoreDocs = topdocs.scoreDocs;

    for(ScoreDoc scoreDoc : scoreDocs){
        //獲取docID
        int docID = scoreDoc.doc;
        //通過文件ID從硬碟中讀取出對應的文件
        Document document = indexReader.document(docID);
        //get域名可以取出值 列印
        System.out.println("fileName:" + document.get("fileName"));
        System.out.println("fileSize:" + document.get("fileSize"));
        System.out.println("===================================");
    }
}
@Test
public void testNumericRangeQuery() throws Exception{
    //建立分詞器(建立索引和所有時所用的分詞器必須一致)
    Analyzer analyzer = new IKAnalyzer();

    //根據數字範圍查詢
    //查詢檔案大小,大於100 小於1000的文章
    //第一個引數:域名      
    //第二個引數:最小值,  
    //第三個引數:最大值, 
    //第四個引數:是否包含最小值,   
    //第五個引數:是否包含最大值
    Query query = NumericRangeQuery.newLongRange("fileSize", 100L, 1000L, true, true);      

    //指定索引和文件的目錄
    Directory dir = FSDirectory.open(new File("E:\\dic"));
    //索引和文件的讀取物件
    IndexReader indexReader = IndexReader.open(dir);
    //建立索引的搜尋物件
    IndexSearcher indexSearcher = new IndexSearcher(indexReader);
    //搜尋:第一個引數為查詢語句物件, 第二個引數:指定顯示多少條
    TopDocs topdocs = indexSearcher.search(query, 5);
    //一共搜尋到多少條記錄
    System.out.println("=====count=====" + topdocs.totalHits);
    //從搜尋結果物件中獲取結果集
    ScoreDoc[] scoreDocs = topdocs.scoreDocs;

    for(ScoreDoc scoreDoc : scoreDocs){
        //獲取docID
        int docID = scoreDoc.doc;
        //通過文件ID從硬碟中讀取出對應的文件
        Document document = indexReader.document(docID);
        //get域名可以取出值 列印
        System.out.println("fileName:" + document.get("fileName"));
        System.out.println("fileSize:" + document.get("fileSize"));
        System.out.println("====================================");
    }
}

@Test
public void testBooleanQuery() throws Exception{
    //建立分詞器(建立索引和所有時所用的分詞器必須一致)
    Analyzer analyzer = new IKAnalyzer();

    //布林查詢,就是可以根據多個條件組合進行查詢
    //檔名稱包含apache的,並且檔案大小大於等於100 小於等於1000位元組的文章
    BooleanQuery query = new BooleanQuery();

    //根據數字範圍查詢
    //查詢檔案大小,大於100 小於1000的文章
    //第一個引數:域名      
    //第二個引數:最小值,  
    //第三個引數:最大值, 
    //第四個引數:是否包含最小值,   
    //第五個引數:是否包含最大值
    Query numericQuery = NumericRangeQuery.newLongRange("fileSize", 100L, 1000L, true, true);

    //建立詞元:就是詞,   
    Term term = new Term("fileName", "apache");
    //使用TermQuery查詢,根據term物件進行查詢
    TermQuery termQuery = new TermQuery(term);

    //Occur是邏輯條件
    //must相當於and關鍵字,是並且的意思
    //should,相當於or關鍵字或者的意思
    //must_not相當於not關鍵字, 非的意思
    //注意:單獨使用must_not  或者 獨自使用must_not沒有任何意義
    query.add(termQuery, Occur.MUST);
    query.add(numericQuery, Occur.MUST);

    //指定索引和文件的目錄
    Directory dir = FSDirectory.open(new File("E:\\dic"));
    //索引和文件的讀取物件
    IndexReader indexReader = IndexReader.open(dir);
    //建立索引的搜尋物件
    IndexSearcher indexSearcher = new IndexSearcher(indexReader);
    //搜尋:第一個引數為查詢語句物件, 第二個引數:指定顯示多少條
    TopDocs topdocs = indexSearcher.search(query, 5);
    //一共搜尋到多少條記錄
    System.out.println("=====count=====" + topdocs.totalHits);
    //從搜尋結果物件中獲取結果集
    ScoreDoc[] scoreDocs = topdocs.scoreDocs;

    for(ScoreDoc scoreDoc : scoreDocs){
        //獲取docID
        int docID = scoreDoc.doc;
        //通過文件ID從硬碟中讀取出對應的文件
        Document document = indexReader.document(docID);
        //get域名可以取出值 列印
        System.out.println("fileName:" + document.get("fileName"));
        System.out.println("fileSize:" + document.get("fileSize"));
        System.out.println("===================================");
    }
}

@Test
public void testMathAllQuery() throws Exception{
    //建立分詞器(建立索引和所有時所用的分詞器必須一致)
    Analyzer analyzer = new IKAnalyzer();

    //查詢所有文件
    MatchAllDocsQuery query = new MatchAllDocsQuery();

    //指定索引和文件的目錄
    Directory dir = FSDirectory.open(new File("E:\\dic"));
    //索引和文件的讀取物件
    IndexReader indexReader = IndexReader.open(dir);
    //建立索引的搜尋物件
    IndexSearcher indexSearcher = new IndexSearcher(indexReader);
    //搜尋:第一個引數為查詢語句物件, 第二個引數:指定顯示多少條
    TopDocs topdocs = indexSearcher.search(query, 5);
    //一共搜尋到多少條記錄
    System.out.println("=====count=====" + topdocs.totalHits);
    //從搜尋結果物件中獲取結果集
    ScoreDoc[] scoreDocs = topdocs.scoreDocs;

    for(ScoreDoc scoreDoc : scoreDocs){
        //獲取docID
        int docID = scoreDoc.doc;
        //通過文件ID從硬碟中讀取出對應的文件
        Document document = indexReader.document(docID);
        //get域名可以取出值 列印
        System.out.println("fileName:" + document.get("fileName"));
        System.out.println("fileSize:" + document.get("fileSize"));
        System.out.println("======================================");
    }
}

@Test
public void testMultiFieldQueryParser() throws Exception{
    //建立分詞器(建立索引和所有時所用的分詞器必須一致)
    Analyzer analyzer = new IKAnalyzer();

    String [] fields = {"fileName","fileContext"};
    //從檔名稱和檔案內容中查詢,只有含有apache的就查出來
    MultiFieldQueryParser multiQuery = new MultiFieldQueryParser(fields, analyzer);
    //輸入需要搜尋的關鍵字
    Query query = multiQuery.parse("apache");

    //指定索引和文件的目錄
    Directory dir = FSDirectory.open(new File("E:\\dic"));
    //索引和文件的讀取物件
    IndexReader indexReader = IndexReader.open(dir);
    //建立索引的搜尋物件
    IndexSearcher indexSearcher = new IndexSearcher(indexReader);
    //搜尋:第一個引數為查詢語句物件, 第二個引數:指定顯示多少條
    TopDocs topdocs = indexSearcher.search(query, 5);
    //一共搜尋到多少條記錄
    System.out.println("=====count=====" + topdocs.totalHits);
    //從搜尋結果物件中獲取結果集
    ScoreDoc[] scoreDocs = topdocs.scoreDocs;

    for(ScoreDoc scoreDoc : scoreDocs){
        //獲取docID
        int docID = scoreDoc.doc;
        //通過文件ID從硬碟中讀取出對應的文件
        Document document = indexReader.document(docID);
        //get域名可以取出值 列印
        System.out.println("fileName:" + document.get("fileName"));
        System.out.println("fileSize:" + document.get("fileSize"));
        System.out.println("===================================");
    }
}

}