基於lucene與IKAnalyzer的中文搜索

基於lucene與IKAnalyzer的全文檢索

1、全文檢索概念

  全文檢索首先將要查詢的目標數據源中的一部分信息提取出來,組成索引,通過查詢索引達到搜索目標數據源的目的,所以速度較快。這種先建立索引,再對索引進行搜索的過程就叫全文檢索(Full-text Search)
  全文檢索技術是搜索引擎的核心支撐技術。

2、全文檢索的應用領域

  對於數據量大、數據結構不固定的數據可採用全文檢索方式搜索,比如百度、Google等搜索引擎、論壇站內搜索、電商網站站內搜索等。

3、Lucene實現全文檢索

3.1、什麼是Lucene?

Lucene是apache下的一個開放源代碼的全文檢索引擎工具包。提供了完整的查詢引擎和索引引擎,部分文本分析引擎。
Lucene的目的是爲軟件開發人員提供一個簡單易用的工具包,以方便的在目標系統中實現全文檢索的功能。
Lucene不是一個完整的應用程序,而是一個代碼庫和API,可以很容易地用於嚮應用程序添加搜索功能。

3.2、Lucene與搜索引擎的區別

  全文檢索系統是按照全文檢索理論建立起來的用於提供全文檢索服務的軟件系統。全文檢索系統是一個可以運行的系統,包括建立索引、處理查詢返回結果集、增加索引、優化索引結構等功能。例如:百度搜索、eclipse幫助搜索、淘寶網商品搜索。
  搜索引擎是全文檢索技術最主要的一個應用,例如百度。搜索引擎起源於傳統的信息全文檢索理論,即計算機程序通過掃描每一篇文章中的每一個詞,建立以詞爲單位的倒排文件,檢索程序根據檢索詞在每一篇文章中出現的頻率和每一個檢索詞在一篇文章中出現的概率,對包含這些檢索詞的文章進行排序,最後輸出排序的結果。全文檢索技術是搜索引擎的核心支撐技術。
  Lucene和搜索引擎不同,Lucene是一套用java寫的全文檢索的工具包,爲應用程序提供了很多個api接口去調用,可以簡單理解爲是一套實現全文檢索的類庫,搜索引擎是一個全文檢索系統,它是一個單獨運行的軟件。

3.3、安裝Lucene

Lucene是開發全文檢索功能的工具包,從官方網站下載Lucene,並解壓。官方網站:http://lucene.apache.org/

3.4、Lucene主要包結構

包名 功能
org.apache.lucene.analysis 語言分析器,主要用於的切詞,支持中文主要是擴展此類
org.apache.lucene.document 索引存儲時的文檔結構管理,類似於關係型數據庫的表結構
org.apache.lucene.index 索引管理,包括索引建立、刪除等
org.apache.lucene.queryParser 查詢分析器,實現查詢關鍵詞間的運算,如與、或、非等
org.apache.lucene.search 檢索管理,根據查詢條件,檢索得到結果
org.apache.lucene.store 數據存儲管理,主要包括一些底層的I/O操作
org.apache.lucene.util 一些公用類

3.5、Lucene主要組件


被索引的文檔用Document對象 表示。
IndexWriter 通過函數addDocument 將文檔添加到索引中,實現創建索引的過程。
Lucene 的索引是反向索引。
當用戶有請求時,Query 代表用戶的查詢語句。
IndexSearcher 通過函數search 搜索Lucene Index 。
IndexSearcher 計算term weight 和score 並且將結果返回給用戶。
返回給用戶的文檔集合用TopDocsCollector 表示。

3.6、Lucene API 的調用實現索引和搜索過程流程圖

這裏寫圖片描述

索引過程如下:

對文檔索引的過程,將用戶要搜索的文檔內容進行索引,索引存儲在索引庫(index)中。
1、創建一個IndexWriter 用來寫索引文件,它有幾個參數,INDEX_DIR 就是索引文件所存放的位置,Analyzer 便是用來對文檔進行詞法分析和語言處理的。
2、創建一個Document 代表我們要索引的文檔。
3、將不同的Field 加入到文檔中。我們知道,一篇文檔有多種信息,如題目,作者,修改時間,內容等。不同類型的信息用不同的Field 來表示。

搜索流程如下:

搜索就是用戶輸入關鍵字,從索引(index)中進行搜索的過程。根據關鍵字搜索索引,根據索引找到對應的文檔,從而找到要搜索的內容(這裏指磁盤上的文件)。
1、IndexReader 將磁盤上的索引信息讀入到內存,INDEX_DIR 就是索引文件存放的位置。
2、創建IndexSearcher 準備進行搜索。
3、創建Analyzer 用來對查詢語句進行詞法分析和語言處理。
4、創建QueryParser 用來對查詢語句進行語法分析。
5、QueryParser 調用parser 進行語法分析,形成查詢語法樹,放到Query 中。
6、IndexSearcher 調用search 對查詢語法樹Query 進行搜索,得到結果TopScoreDocCollector 。

4、IKAnalyzer中文分詞器

4.1、IKAnalyzer是什麼?

  IKAnalyzer 是一個開源的,基於java語言開發的輕量級的中文分詞工具包。從2006年12月推出1.0版開始,IKAnalyzer已經推出 了3個大版本。最初,它是以開源項目 Lucene爲應用主體的,結合詞典分詞和文法分析算法的中文分詞組件。新版本的IKAnalyzer3.0則發展爲 面向Java的公用分詞組件,獨立於Lucene項目,同時提供了對Lucene的默認優化實現。

5、項目源碼 已託管到github,歡迎fork,star,學習討論,共同進步。

5.1核心代碼如下:

創建索引

   @GetMapping("/createIndex")
   public String createIndex(int limit,int offset) {
       // 拉取數據
       List<Baike> baikes = baikeMapper.getAllBaike(limit,offset);
       Baike baike = new Baike();
       //獲取字段
       for (int i = 0; i < baikes.size(); i++) {
           //獲取每行數據
           baike = baikes.get(i);
           //創建Document對象
           Document doc = new Document();
           //獲取每列數據
           Field id = new Field("id", baike.getId()+"", TextField.TYPE_STORED);
           Field title = new Field("title", baike.getTitle(), TextField.TYPE_STORED);
           Field summary = new Field("summary", baike.getSummary(), TextField.TYPE_STORED);
           //添加到Document中
           doc.add(id);
           doc.add(title);
           doc.add(summary);
           //調用,創建索引庫
           indexDataBase.write(doc);
       }
       return "成功";
   }
@Component//使用@Configuration也可以
@PropertySource(value = "classpath:config.yml")//配置文件路徑
public class IndexDataBase {
    //Lucene索引文件路徑
    @Value("indexPath")
    private String indexPath;

    //定義分詞器
    static Analyzer analyzer = new IKAnalyzer();
    /**
     * 封裝一個方法,用於將數據庫中的數據解析爲一個個關鍵字詞存儲到索引文件中
     * @param doc
     */
    public void write(Document doc){
        try {
            //索引庫的存儲目錄
            Directory dir = FSDirectory.open(Paths.get(indexPath));
            IndexWriterConfig iwc = new IndexWriterConfig(analyzer);
            //傳入目錄和分詞器
            IndexWriter writer = new IndexWriter(dir, iwc);
            //寫入到目錄文件中
            writer.addDocument(doc);
            //提交事務
            writer.commit();
            //關閉流
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

搜索:

   //搜索,實現高亮
   @GetMapping("/getSearchText")
   public ModelAndView getSearchText(String keyWord,String field,ModelAndView mv) throws Exception {
       List<Map> mapList = searchDataBase.search(field, keyWord);
       mv.setViewName("/result");
       mv.addObject("mapList",mapList);
       return mv;
   }
   @Component//使用@Configuration也可以
@PropertySource(value = "classpath:config.yml")//配置文件路徑
public class SearchDataBase {
    //Lucene索引文件路徑
    @Value("${indexPath}")
    private String indexPath;

    //定義分詞器
    static Analyzer analyzer = new IKAnalyzer();

    //搜索
    public List<Map> search(String field, String value) throws Exception{
        //索引庫的存儲目錄
        Directory dir = FSDirectory.open(Paths.get(indexPath));
        //讀取索引庫的存儲目錄
        // 實例化讀取器
        IndexReader reader = DirectoryReader.open(FSDirectory.open(Paths.get(indexPath)));
        //lucence查詢解析器,用於指定查詢的屬性名和分詞器
        // 實例化搜索器
        IndexSearcher searcher = new IndexSearcher(reader);
        //搜索
        // 構造QueryParser查詢分析器
        QueryParser parser = new QueryParser(field, analyzer);
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8));
        String line = value != null ? value : in.readLine();
        // 構造Query對象
        Query query = parser.parse(line);
        //最終被分詞後添加的前綴和後綴處理器,默認是粗體<B></B>
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("<font color=").append("\"").append("red").append("\"").append(">");
        SimpleHTMLFormatter htmlFormatter = new SimpleHTMLFormatter(stringBuffer.toString(),"</font>");
        //高亮搜索的詞添加到高亮處理器中
        Highlighter highlighter = new Highlighter(htmlFormatter, new QueryScorer(query));
        //獲取搜索的結果,指定返回document返回的個數
        // 收集足夠的文檔顯示5頁
        TopDocs results = searcher.search(query, 5);
        ScoreDoc[] hits = results.scoreDocs;
        List<Map> list=new ArrayList<Map>();
        //遍歷,輸出
        for (int i = 0; i < hits.length; i++) {
            int id = hits[i].doc;
            Document hitDoc = searcher.doc(hits[i].doc);
            Map map=new HashMap();
            map.put("id", hitDoc.get("id"));
            //獲取到name
            String name=hitDoc.get("summary");
            //將查詢的詞和搜索詞匹配,匹配到添加前綴和後綴
            TokenStream tokenStream = TokenSources.getAnyTokenStream(searcher.getIndexReader(), id, "summary", analyzer);
            //傳入的第二個參數是查詢的值
            TextFragment[] frag = highlighter.getBestTextFragments(tokenStream, name, false, 10);
            String baikeValue="";
            for (int j = 0; j < frag.length; j++) {
                if ((frag[j] != null)) {
                    //獲取 foodname 的值
                    baikeValue=baikeValue+((frag[j].toString()));
                }
            }
            map.put("title", hitDoc.get("title"));
            map.put("summary", baikeValue);
            list.add(map);
        }
        reader.close();
        dir.close();
        return list;
    }
}

6、寫在最後

本文是基於lucene與IKAnalyzer的中文搜索的demo及學習記錄,目的是對一些關於全文搜索學習資料的整理和建立一個簡單的學習demo,其中很多圖片和內容不是本人原創,如果涉及到侵權,請聯繫我,我馬上撤下這些資源。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章