1. 程式人生 > >Lucene全文檢索隨筆

Lucene全文檢索隨筆

一,什麼是全文檢索

全文檢索是計算機程式通過掃描文章中的每一個詞,對每一個詞建立一個索引,指明該詞在文章中出現的次數和位置。當用戶查詢時根據建立的索引查詢,類似於通過字典的檢索字表查字的過程。

全文檢索(Full-Text Retrieval)以文字作為檢索物件,找出含有指定詞彙的文字。全面、準確和快速是衡量全文檢索系統的關鍵指標。

關於全文檢索,我們要知道:

   1,只處理文字。

   2,不處理語義。

   3,搜尋時英文不區分大小寫。

   4,結果列表有相關度排序

 

二、 全文檢索與資料庫檢索的區別

 

全文檢索不同於資料庫的SQL查詢。(他們所解決的問題不一樣,解決的方案也不一樣,所以不應進行對比)。在資料庫中的搜尋就是使用SQL,如:SELECT * FROM t WHERE content like ‘%ant%’。這樣會有如下問題:

 

1匹配效果:如搜尋ant會搜尋出planting。這樣就會搜出很多無關的資訊。

 

2相關度排序:查出的結果沒有相關度排序,不知道我想要的結果在哪一頁。我們在使用百度搜索時,一般不需要翻頁,為什麼?因為百度做了相關度排序:為每一條結果打一個分數,這條結果越符合搜尋條件,得分就越高,叫做相關度得分,結果列表會按照這個分數由高到低排列,所以第

1頁的結果就是我們最想要的結果。

 

3全文檢索的速度大大快於SQLlike搜尋的速度。這是因為查詢方式不同造成的,以查字典舉例:資料庫的like就是一頁一頁的翻,一行一行的找,而全文檢索是先查目錄,得到結果所在的頁碼,再直接翻到這一頁。

 

 

 

三、 全文檢索的使用場景

 

   我們使用Lucene,主要是做站內搜尋,即對一個系統內的資源進行搜尋。如BBS(論壇)BLOG(部落格)中的文章搜尋,網上商店中的商品搜尋等。使用Lucene的專案有Eclipse,智聯招聘,天,京東等

。一般不做網際網路中資源的搜尋,因為不易獲取與管理海量資源(專業搜尋方向的公司除外)

四。Lucene建立索引

(1).maven 依賴

1   <dependency>
2       <groupId>org.apache.lucene</groupId>
3       <artifactId>lucene-core</artifactId>
4       <version>4.4.0</version>
5     </dependency>

(2)建立索引

 @Test
    public void create() throws IOException {
        //索引庫
        Directory dir = FSDirectory.open(new File("e:/index"));
        Analyzer analyzer = new IKAnalyzer();//分詞器,常用為IK分詞器
        //索引寫入器的相關配置
        IndexWriterConfig conf = new IndexWriterConfig(Version.LUCENE_44, analyzer);
        //建立索引寫入器
        IndexWriter indexWriter = new IndexWriter(dir, conf);
        //article---->document
        //field。store yes:儲存元資料,通常不想顯示詳細資訊時為no,入:商品詳情(一般存入資料                     庫),文章正文,需要查詢資料庫而不是查索引庫
        for (int i=0;i<10;i++)
        {
            Document document = new Document();
            document.add(new StringField("id",     String.valueOf(i), Field.Store.YES));
            document.add(new StringField("title", "背影", Field.Store.YES));
            document.add(new StringField("author", "朱自清", Field.Store.YES));
            document.add(new TextField("content", "你站在這裡不要動,我去給你買幾個橘子百知教育", Field.Store.YES));
            document.add(new StringField("date", "2019-1-2", Field.Store.YES));
            indexWriter.addDocument(document);
        }
        indexWriter.commit();
        indexWriter.close();
    }

(3)搜尋索引

 @Test
    public void search() throws IOException {
        Directory directory = FSDirectory.open(new File("e:/index"));
        IndexReader reader = DirectoryReader.open(directory);
        IndexSearcher indexSearcher = new IndexSearcher(reader);
        //第一個引數 搜尋的條件   查詢出的條數
        Query query=new TermQuery(new Term("content","橘子"));
        TopDocs topDocs = indexSearcher.search(query, 100); //相關度排序
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        for (int i = 0; i < scoreDocs.length; i++) {
            ScoreDoc scoreDoc = scoreDocs[i];
            int doc = scoreDoc.doc;
            Document document = indexSearcher.doc(doc);
            System.out.println("this is score"+scoreDoc.score);
            System.out.println("this is id"+document.get("id"));
            System.out.println("this is title"+document.get("title"));
            System.out.println("this is author"+document.get("author"));
            System.out.println("this is content"+document.get("content"));
            System.out.println("this is date"+document.get("date"));
        }
    }

(4)刪除索引

 @Test
    public void test3() throws IOException {
        Directory dir = FSDirectory.open(new File("e:/index"));
        Analyzer analyzer = new IKAnalyzer();
        IndexWriterConfig conf = new IndexWriterConfig(Version.LUCENE_44, analyzer);
        IndexWriter indexWriter = new IndexWriter(dir, conf);
//        indexWriter.deleteAll();
        indexWriter.deleteDocuments(new Term("id", "9"));
    }

(5)整合web環境

service層呼叫資料庫mapper後,還應為商品建立索引,為了讓使用者更快的得到響應,可以先執行資料庫操作,響應後再建立索引

注意:springboot環境中,配置類中可注入LuceneDao,service層可以直接@Autowired直接注入Dao。如下:

@Configuration
public class LuceneConfig {
    @Bean
    public LuceneProductDao getLuceneProductDao() {
        return new LuceneProductDao();
    }
    @Bean
    public LuceneChapterDao getLuceneChapterDao(){return new LuceneChapterDao();}

}

 

(6)多屬性查詢

依賴

 <!-- https://mvnrepository.com/artifact/com.janeluo/ikanalyzer -->
        <dependency>
            <groupId>com.janeluo</groupId>
            <artifactId>ikanalyzer</artifactId>
            <version>2012_u6</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.lucene/lucene-queryparser -->
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-queryparser</artifactId>
            <version>4.4.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.lucene/lucene-highlighter -->
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-highlighter</artifactId>
            <version>4.4.0</version>
        </dependency>

在一個專案的章節模組中,前臺輸入查詢內容,後臺去索引庫執行查詢,並將關鍵字以高亮飄紅形式展示在前臺的搜尋介面,可用以下實現

首先在新增章節時已經建立索引,才可查到

package com.baizhi.lucenedao;

import com.baizhi.entity.Chapter;
import com.baizhi.util.LuceneUtil;
import org.apache.lucene.document.*;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.search.*;
import org.apache.lucene.search.highlight.*;
import org.apache.lucene.search.highlight.Scorer;
import org.apache.lucene.util.Version;
import org.wltea.analyzer.lucene.IKAnalyzer;

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

public class LuceneChapterDao {
    //建立索引,新增資源時建立
    public void createIndex(Chapter chapter) {
        IndexWriter indexWriter = LuceneUtil.getIndexWriter();
        Document docFromPro = getDocFromPro(chapter);
        try {
            indexWriter.addDocument(docFromPro);
            LuceneUtil.commit(indexWriter);
        } catch (IOException e) {
            e.printStackTrace();
            LuceneUtil.rollback(indexWriter);
        }
    }

    //搜尋索引
    public List<Chapter> SearcherIndex(String params) {
        int pageSize = 2;//此處可從前臺傳入,不應寫死
        int pageNum = 1;
        IndexSearcher indexSearcher = LuceneUtil.getIndexSearcher();
        List<Chapter> list = null;
        try {
            String[] strs = {"title"};
            //常用查詢
            MultiFieldQueryParser multiFieldQueryParser = new MultiFieldQueryParser(Version.LUCENE_44, strs, new IKAnalyzer());
            Query query = multiFieldQueryParser.parse(params);

            Formatter formatter = new SimpleHTMLFormatter("<font color='red'>", "</font>");
            Scorer scorer = new QueryScorer(query);
            Highlighter highlighter = new Highlighter(formatter, scorer);

            TopDocs topDocs = indexSearcher.search(query, pageNum * pageSize);
            ScoreDoc[] scoreDocs = topDocs.scoreDocs;
            list = new ArrayList<>();
            
            for (int i=(pageNum - 1) * pageSize; i < scoreDocs.length; i++) {
                ScoreDoc scoreDoc = scoreDocs[i];
                int doc = scoreDoc.doc;
                Document document = indexSearcher.doc(doc);
                //高亮開始
                try {
                    String bestFragment = highlighter.getBestFragment(new IKAnalyzer(), "title", document.get("title"));
                    if (bestFragment == null) {
                        System.out.println("this is  title8" + document.get("id"));
                    } else {
                        System.out.println("this is  title9" + bestFragment);

                        //Chapter chapter = getProFromDoc(document);
                        Chapter chapter = new Chapter();
                        chapter.setTitle(bestFragment);
                        System.out.println(chapter);
                        //存入list
                        list.add(chapter);
                    }
                  //  System.out.println("this is  id3" + highlighter.getBestFragment(new IKAnalyzer(), "content", document.get("content")));
                } catch (InvalidTokenOffsetsException e) {
                    e.printStackTrace();
                }
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }catch (IOException ee) {
            ee.printStackTrace();
        }
        return list;
    }
    public Document getDocFromPro(Chapter chapter) {
        Document document = new Document();
        // document.add(new StringField("id", chapter.getId()+"", Field.Store.YES));
        document.add(new TextField("title", chapter.getTitle(), Field.Store.YES));
        return document;
    }
    public Chapter getProFromDoc(Document document) {
        Chapter chapter = new Chapter();
        // chapter.setId(document.get("id"));
        chapter.setTitle(document.get("title"));
        return chapter;
    }
}