Lucene全文檢索(一)

全文檢索的概念java

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

 

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

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

 

Lucene框架

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

Lucene結構圖spa

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

 

Lucene入門案例code

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());
        }
    }
}
相關文章
相關標籤/搜索