全文檢索-Lucene簡介
全文檢索的引入
全文資料的搜尋通常我們採取以下兩種方式:順序掃描和全文檢索。
在瞭解順序掃描和全文檢索之前,我們先要了解幾個概念:
結構化資料:指具有“固定格式”或“有限長度”的資料,如資料庫,元資料等;
非結構化資料:指不定長或無固定格式的資料,如郵件,word文件等;
半結構化資料,如XML,HTML等,當根據需要可按結構化資料來處理,也可抽取出純文字按非結構化資料來處理。
順序掃描:所謂順序掃描,比如要找內容包含某一個字串的檔案,就是一個文件一個文件的看,對於每一個文件,從頭看到尾,如果此文件包含此字串,則此文件為我們要找的檔案,接著看下一個檔案,直到掃描完所有的檔案。比如Window自帶的搜尋,在資料庫中掃描不帶索引文字欄位等。
全文檢索:從非結構化資料中提取出的然後重新組織的資訊,我們稱之索引。即為文字資料建立類似字典目錄,從而提高檢索速度。全文檢索還有一層含義,將返回資料按照相關度的大小返回。
從上文我們可以看出,採用順序掃描可以達到我們檢索全文資料的效果,但是當資料量較大時,我們的檢索效率將變得非常的低,嚴重影響的使用體驗。因此我們引入了全文檢索的方式來進行,已達到提高搜尋效率的需求。
全文檢索的工作流程
全文檢索的工作流程如圖所示,全文檢索主要執行兩個操作,①是收集資料,收集到的資料經過一定的處理,建立自己的索引庫;②就是處理使用者的搜尋請求,因為在①中已經按照一定的規則建立了自己的索引庫,此時搜尋引擎經過一定的檢索方式,將資料反饋給使用者的搜尋請求。
全文檢索的工作原理
全文檢索之所以能夠提高檢索效率,關鍵就在於它對索引庫的建立,那麼接下來我們就講一下它的工作原理。
在①中我們準備了三條收集的資料,②我們將收集到的資料通過詞法分析將資料分成一個個的單詞,在分詞完畢後還需剔除掉一些語氣助詞等沒有語義的詞語,第二步我們通常是找與收集的資料的語言相對應的分詞器完成這步操作,③統一單詞的大小寫和使用時態,④將單詞進行排序,⑤將單詞進行合併,保留單詞所在資料的編號。
經過以上的幾步操作,我們將非結構化資料就能轉換成有序的結構化的索引庫,有序的結構化資料能夠大幅度的提升我們的檢索效率。
Lucene的基本API操作(java)
Lucene就是針對以上的操作需求開發並封裝的執行程式,儘管它從誕生到現在已經很多年了,但是,現在搭建的許多的全文檢索框架仍然是基於Lucene進行的封裝,包括現在使用較多的ElasticSearch、Solr、Katta等。
在操作Lucene的Api之前,我們需要匯入以下的jar包:
各個jar包簡介
IKAnalyzer:針對中文分詞的包
common:通用的解析資料的包(分詞等功能,拆分英文還行)
smartcn:lucene自帶的解析中文的包(效果不好,推薦解析中文采用IKAnalyzer)
core:lucene核心包
highlighter:控制關鍵字高亮的包
queries:檢索使用的包
queryparser:檢索解析使用的包
索引庫的簡單建立
public void testWriteDoc() throws Exception {
//目錄物件(索引庫相關的資料存放的地方)
Directorydirectory =FSDirectory.open(Paths.get("E:/workspace/lucene-hello/helloIndex"));
//詞法分析器物件(標準詞法分析器,只對英文有效)
Analyzeranalyzer = new StandardAnalyzer();
//配置物件
IndexWriterConfigconf = new IndexWriterConfig(analyzer);
/*
* 配置索引庫的生成策略,主要有以下三種建立方式
* 1、CREATE:每次都重新建立索引庫
* 2、CREATE_OR_APPEND:當索引庫存在時直接在該索引庫追加資料,否則重新建立
* 3、APPEND:在當前指定索引庫追加資料
*/
conf.setOpenMode(OpenMode.CREATE);
//獲取核心物件IndexWritter
IndexWriterindexWriter = new IndexWriter(directory, conf);
System.out.println(indexWriter);
//通過IndexWritter的addDocument方法完成文件的新增
//模擬新增第一條資料
Documentdocument1 = new Document();
//增加多個文件的欄位及其值
IndexableFieldfield = new TextField("docId", "doc1", Store.YES);
document1.add(field);
document1.add(newTextField("content", doc1, Store.YES));
//完成文件的新增和索引的建立
indexWriter.addDocument(document1);
//模擬新增第二條資料
Documentdocument2 = new Document();
document2.add(newTextField("docId", "doc2", Store.YES));
document2.add(newTextField("content", doc2, Store.YES));
indexWriter.addDocument(document2);
//模擬新增第三條資料
Documentdocument3 = new Document();
document3.add(newTextField("docId", "doc3", Store.YES));
document3.add(newTextField("content", doc3, Store.YES));
indexWriter.addDocument(document3);
//提交修改到索引庫和關閉核心寫物件
indexWriter.commit();
indexWriter.close();
//查詢所有資料,檢視是否將資料新增成功
testSearch("*:*");
}
簡單的檢索測試
privatevoid testSearch(String queryStr) throws Exception {
//核心目錄物件
Directorydirectory = FSDirectory.open(Paths.get("E:/workspace/lucene-hello/helloIndex"));
//核心索引檔案的讀物件
IndexReaderreader = DirectoryReader.open(directory);
//核心物件IndexSearcher的建立
IndexSearcherindexSearcher = new IndexSearcher(reader);
//在文件的哪個欄位上查詢
Stringfield = "content";
//搜尋的時候的詞法分析器
Analyzeranalyer = new StandardAnalyzer();
QueryParserqueryParser = new QueryParser(field, analyer);
//通過查詢分析器直接解析查詢內容生成
Queryquery = queryParser.parse(queryStr);
//通過query物件指定的條件,最終返回最相關(相關度最高)的前n個文件
TopDocstopDocs = indexSearcher.search(query, 5);
//通過結果封裝物件topDocs獲取返回的具體結果
//獲取最高的相關度的值
floatmaxScore = topDocs.getMaxScore();
//查詢的相關的文件總數
inttotalHits = topDocs.totalHits;
//獲取具體文件資料
ScoreDoc[]scoreDocs = topDocs.scoreDocs;
//遍歷檢視資料
for(ScoreDoc scoreDoc : scoreDocs) {
//相關度
floatscore = scoreDoc.score;
//文件的ID
intdocumentId = scoreDoc.doc;
Documentdocument = indexSearcher.doc(documentId);
//獲取欄位的內容
StringdocId = document.get("docId");
Stringcontent = document.get("content");
System.out.println("id="+ documentId + ",score=" + score + ",docId=" + docId +",content=" + content);
}
}
以上內容純屬個人見解,如有疏漏,歡迎指正([email protected])。