全文檢索的引入
全文資料的搜尋通常我們采取以下兩種方式:順序掃描和全文檢索。
在了解順序掃描和全文檢索之前,我們先要了解幾個概念:
結構化資料:指具有“固定格式”或“有限長度”的資料,如資料庫,中繼資料等;
非結構化資料:指不定長或無固定格式的資料,如郵件,word文檔等;
半結構化資料,如XML,HTML等,當根據需要可按結構化資料來處理,也可抽取出純文字按非結構化資料來處理。
順序掃描:所謂順序掃描,比如要找内容包含某一個字元串的檔案,就是一個文檔一個文檔的看,對于每一個文檔,從頭看到尾,如果此文檔包含此字元串,則此文檔為我們要找的檔案,接着看下一個檔案,直到掃描完所有的檔案。比如Window自帶的搜尋,在資料庫中掃描不帶索引文本字段等。
全文檢索:從非結構化資料中提取出的然後重新組織的資訊,我們稱之索引。即為文本資料建立類似字典目錄,進而提高檢索速度。全文檢索還有一層含義,将傳回資料按照相關度的大小傳回。
從上文我們可以看出,采用順序掃描可以達到我們檢索全文資料的效果,但是當資料量較大時,我們的檢索效率将變得非常的低,嚴重影響的使用體驗。是以我們引入了全文檢索的方式來進行,已達到提高搜尋效率的需求。
全文檢索的工作流程
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38CXlZHbvN3cpR2Lc1TPB10QGtWUCpEMJ9CXsxWam9CXwADNvwVZ6l2c052bm9CXUJDT1wkNhVzLcRnbvZ2Lc1TUE10dBRUT3FEVj9GZuV2coNDTwYVbiVHNHpleO1GTulzRilWO5x0LcRHelR3LcJzLctmch1mclRXY39jM4czMwYTMwIDMyMDM4EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
全文檢索的工作流程如圖所示,全文檢索主要執行兩個操作,①是收集資料,收集到的資料經過一定的處理,建立自己的索引庫;②就是處理使用者的搜尋請求,因為在①中已經按照一定的規則建立了自己的索引庫,此時搜尋引擎經過一定的檢索方式,将資料回報給使用者的搜尋請求。
全文檢索的工作原理
全文檢索之是以能夠提高檢索效率,關鍵就在于它對索引庫的建立,那麼接下來我們就講一下它的工作原理。
在①中我們準備了三條收集的資料,②我們将收集到的資料通過詞法分析将資料分成一個個的單詞,在分詞完畢後還需剔除掉一些語氣助詞等沒有語義的詞語,第二步我們通常是找與收集的資料的語言相對應的分詞器完成這步操作,③統一單詞的大小寫和使用時态,④将單詞進行排序,⑤将單詞進行合并,保留單詞所在資料的編号。
經過以上的幾步操作,我們将非結構化資料就能轉換成有序的結構化的索引庫,有序的結構化資料能夠大幅度的提升我們的檢索效率。
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);
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])。