Lucene全文檢索(一)


全文檢索的概念

1.從大量的信息中快速、准確的查找要的信息
2.收索的內容是文本信息
3.不是根據語句的意思進行處理的(不處理語義)
4.全面、快速、准確是衡量全文檢索系統的關鍵指標。
5.搜索時英文不區分大小寫,結果列表有相關度排序。

 

全文檢索與數據庫搜索的區別

1.數據庫搜索
Eg: select * from article where content like ‘%here%’
結果where  here
缺點:
1).搜索效果比較差
2).在搜索的結果中,有大量的數據被搜索出來,有很多數據是沒有用的
3).查詢速度在大量數據的情況下是很難做到快速的
2.全文檢索
1).搜索結果按相關度排序,這意味着只有前幾個頁面對用戶來說是比較有用的,其他的結果與用戶想要的答案可能相差甚遠。數據庫搜索時做不到相關度排序的。
2).因為全文檢索是采用索引的方式,所以在速度上肯定比數據庫方式like要快。
3).所以數據庫不能代替全文檢索。

 

Lucene

Lucene:全文檢索只是一個概念,而具體實現有很多框架,lucene是其中的一種。

Lucene結構圖

說明:
1.索引庫中的索引數據是在磁盤上存在的,我們用Directory這個類來描述。
2.我們可以通過API來實現對索引庫的增、刪、改、查的操作
3.在索引庫中各種數據形式可以抽象出一種數據格式Document
4.Document的結構為:Document(List<Field>)
5.Field里存放一個鍵值對。鍵值對都為字符串的形式。
6.對索引庫中索引的操作實際上也是對Document的操作。

 

Lucene入門案例

1.搭建工程環境,並導入所需的jar包,至少需要以下四個jar包
lucene-core-3.1.0.jar(核心包)
lucene-analyzers-3.1.0.jar(分詞器)
lucene-highlighter-3.1.0.jar(高亮器)
lucene-memory-3.1.0.jar(高亮器)
2.建立索引
步驟:
1)創建IndexWriter對象
2)把JavaBean轉化為Document
3)利用IndexWriter.addDocument方法增加索引
4)關閉資源
Eg:

package cn.lsl.lucene.demo;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Index;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriter.MaxFieldLength;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.junit.Test;

/*
 * 功能1:把一個文章放入索引庫中
 * */
public class LuceneDemo {
    
    @Test
    public void testCreatIndex() throws IOException{
        //1.獲取文章內容
        Article article = new Article();
        article.setId(1);
        article.setTitle("lucene");
        article.setContent("提供了一個簡單卻強大的應用程式接口,能夠做全文索引和搜尋。");
        //把文章放入到索引庫中
        Directory directory = FSDirectory.open(new File("./indexDir"));    //Directory 索引庫
        //分詞器
        Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_30);
        //構造indexWriter對象
        IndexWriter indexWriter = new IndexWriter(directory,analyzer,MaxFieldLength.LIMITED);
        //把Article轉化為Document
        Document doc = new Document();
        Field idField = new Field("id",article.getId().toString(), Store.YES, Index.NOT_ANALYZED);
        Field titleField = new Field("title",article.getTitle(),Store.YES,Index.ANALYZED);
        Field contentField = new Field("content",article.getContent(), Store.YES, Index.ANALYZED);
        doc.add(idField);
        doc.add(titleField);
        doc.add(contentField);
        indexWriter.addDocument(doc);
        indexWriter.close();    
    }
}

原理圖

注:Store這個參數表明是否將內容存放到索引內容中

      Index這個參數表明是否存放關鍵字到索引目錄中。

3.進行搜索
步驟:
1)創建IndexSeacher對象
2)創建Query對象
3)進行搜索
4)獲得總結果數和前N行記錄ID列表
5)根據目錄ID把列表Document轉化為JavaBean並放入集合中
6)循環出要檢索的內容
Eg:

package cn.lsl.lucene.demo;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Index;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriter.MaxFieldLength;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.junit.Test;

/*
 * 功能:從索引庫中把文章檢索出來
 * */
public class LuceneDemo {

    //從索引庫中吧文章檢索出來
    
    @Test
    public void testSearch() throws IOException, ParseException{
        //1.創建IndexSearch對象
        Directory directory = FSDirectory.open(new File("./indexDir"));
        IndexSearcher indexSearcher = new IndexSearcher(directory);
        //2.創建Query對象
        Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_30);
        QueryParser queryParser = new MultiFieldQueryParser(Version.LUCENE_30, new String[]{"title","content"},analyzer);
        //參數為要檢索的關鍵字
        Query query = queryParser.parse("lucene");
        //3.進行搜索
        //query 搜索的條件, 顯示N行記錄,TopDocs 目錄的結果
        TopDocs topDocs = indexSearcher.search(query, 10);
        //4.獲取總記錄數和前N行的目錄ID列表
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        
        int count = topDocs.totalHits;    //總記錄數
        System.out.println("總記錄數:" + count);
        
        //5.根據目錄的行ID獲取每行的document,並吧Article放入集合中
        List<Article> articleList = new ArrayList<Article>();
        for (int i = 0; i < scoreDocs.length; i++) {
            int index = scoreDocs[i].doc;    //索引位置,即目錄列表ID
            float score = scoreDocs[i].score;    //相關度得分
            System.out.println("得分:"+ score);
            Document document = indexSearcher.doc(index);
            //把Document轉化為Article
            Article article = new Article();
            article.setId(Integer.valueOf(document.get("id").toString()));
            article.setTitle(document.get("title"));
            article.setContent(document.get("content"));
            articleList.add(article);
        }
        
        for (Article article : articleList) {
            System.out.println("id:" + article.getId());
            System.out.println("title:" + article.getTitle());
            System.out.println("content:" + article.getContent());
        }
    }
}

原理圖:

 

保持數據庫與索引庫同步

在一個系統中,如果索引功能存在,那么數據庫和索引庫應該是同時存在的。這個時候需要保證索引庫的數據和數據庫中的數據保持一致性。可以在對數據庫進行增刪改查操作的同時對索引庫也進行相應的操作。這樣就可以保持數據庫與索引庫的一致性。

 

Lucene的增刪改查及API詳解

創建工具類:

LuceneConfig.java

package cn.lsl.lucene.util;

import java.io.File;
import java.io.IOException;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;

public class LuceneConfig {
    public static Analyzer analyzer;
    public static Directory directory;
    static{
        try {
            analyzer = new StandardAnalyzer(Version.LUCENE_30);
            directory = FSDirectory.open(new File("./indexDir"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

注意:LuceneConfig這個類對Directory和Analyzer進行了包裝。

因為在創建IndexWriter時,需要用到這兩個類,而管理索引庫的操作也都要用到IndexWriter這個類,所以我們對Directory和Analyzer進行了包裝

 

LuceneUtils.java

package cn.lsl.lucene.util;

import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriter.MaxFieldLength;

public class LuceneUtils {
    public static IndexWriter indexWriter;
    
    private LuceneUtils(){}
    
    public static IndexWriter getIndexWriter() throws Exception {
        if(indexWriter == null){
            indexWriter = new IndexWriter(LuceneConfig.directory,LuceneConfig.analyzer,MaxFieldLength.LIMITED);
        }
        return indexWriter;
    }
}

LuceneUtils類對創建IndexWriter進行了封裝

因為在一個索引庫中只能存在一個IndexWriter對象。(同一個索引庫只能有一個IndexWriter進行操作)

所以我們這里采用了單例的模式進行了封裝。

 

DocumentUtils.java

(把JavaBean封裝成Document和把Document封裝成JavaBean的過程。)

package cn.lsl.lucene.util;

import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Index;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.util.NumericUtils;
import cn.lsl.lucene.demo.Article;

public class DocumentUtils {
    public static Document article2Document(Article article){
        Document document = new Document();
        Field idField = new Field("id",article.getId().toString(), Store.YES, Index.NOT_ANALYZED);
        Field titleField = new Field("title",article.getTitle(), Store.YES, Index.ANALYZED);
        Field contentField = new Field("content",article.getContent(), Store.YES, Index.ANALYZED);
        document.add(idField);
        document.add(titleField);
        document.add(contentField);
        return document;
    }
    
    public static Article document2Article(Document document){
        Article article = new Article();
        article.setId(Integer.valueOf(document.get("id")));
        article.setTitle(document.get("title"));
        article.setContent(document.get("content"));
        return article;
    }
}

什么情況下使用Index.NOT_ANALYZED

           當這個屬性的值代表的是一個不可分割的整體,例如ID

什么情況下使用Index.ANALYZED

           當這個屬性的值代表是一個可分割的整體

 

LuceneManager.java(增刪改查例子)

package cn.lsl.lucene.manager;

import java.util.ArrayList;
import java.util.List;

import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.util.Version;
import org.junit.Test;

import cn.lsl.lucene.demo.Article;
import cn.lsl.lucene.util.DocumentUtils;
import cn.lsl.lucene.util.LuceneConfig;
import cn.lsl.lucene.util.LuceneUtils;

public class LuceneManager {
    
    @Test
    public void testCreateIndex() throws Exception{
        IndexWriter indexWriter = LuceneUtils.getIndexWriter();
        Article article = new Article();
        article.setId(1);
        article.setTitle("lucene");
        article.setContent("全文檢索");
        Document doc = DocumentUtils.article2Document(article);
        indexWriter.addDocument(doc);
        indexWriter.close();
    }
    
    @Test
    public void testUpdateIndex() throws Exception{
        IndexWriter indexWriter = LuceneUtils.getIndexWriter();
        Term term = new Term("id","1");
        Article article = new Article();
        article.setId(1);
        article.setTitle("baidu");
        article.setContent("百度一下,你就知道");
        /*term 關鍵字  用來進行刪除的
         * document 是用來進行增加的
         * */
        indexWriter.updateDocument(term, DocumentUtils.article2Document(article));
        indexWriter.close();
    }
    
    @Test
    public void testDeleteIndex() throws Exception{
        IndexWriter indexWriter = LuceneUtils.getIndexWriter();
        //term 關鍵字
        Term term = new Term("id","1");
        indexWriter.deleteDocuments(term);
        indexWriter.close();
    }
    
    
    @Test
    public void testQueryIndex() throws Exception{
        IndexSearcher indexSearcher = new IndexSearcher(LuceneConfig.directory);
        
        List<Article> articleList = new ArrayList<Article>();
        QueryParser queryParser = new MultiFieldQueryParser(Version.LUCENE_30, new String[]{"title","content"}, LuceneConfig.analyzer);
        Query query = queryParser.parse("lucene");
        TopDocs topDocs = indexSearcher.search(query, 10);    //索引庫
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;//索引庫數組
        int count = topDocs.totalHits;    //總記錄數
        for (int i = 0; i < scoreDocs.length; i++) {
            int index = scoreDocs[i].doc;    //得到相關的索引
            float score = scoreDocs[i].score;    //相關度得分
            Document document = indexSearcher.doc(index);
            Article article = DocumentUtils.document2Article(document);
            articleList.add(article);
        }
        
        for (Article article : articleList) {
            System.out.println(article.getId());
            System.out.println(article.getTitle());
            System.out.println(article.getContent());
        }
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM