歡迎訪問筆者個人技術博客:http://rukihuang.xyz/
一、什麼是全文檢索
1.1 數據的分類
1.1.1 結構化數據
- 格式固定、長度固定、數據類型固定
- 如:數據庫中的數據
1.1.2 非結構化數據
- 格式不固定、長度不固定、數據類型不固定
- word文檔、pdf文檔、郵件、html、txt
1.2 數據的查詢
1.2.1 結構化數據的查詢
- SQL語句
- 簡單、數據快
1.2.2 非結構化數據的查詢
- 需求:從文本文件中找出包含特定單詞的文件
- 目測
- 順序掃描。使用程序將文件讀取到內存中,隨後匹配字符串。
- 把非結構化數據轉換爲結構化數據
- 先依據空格進行字符串拆分,得到一個單詞列表,基於單詞列表創建一個索引。
然後查詢索引,分局單詞和文檔的對應關係找到文檔列表,這就是全文檢索。- 索引:一個爲了提高查詢速度,創建某種數據結構的集合。
1.3 全文檢索
- 先創建索引,然後查詢索引的過程叫做全文索引。
- 索引一次創建可以多次使用。
二、全文檢索的應用場景
- 只要是有搜索的地方,就可以使用全文檢索技術。
2.1 搜索引擎
- 百度
- 360
- 谷歌
2.2 站內搜索
- 論壇搜索
- 微博
- 文章搜索
2.3 電商搜索
- 淘寶搜索
- 京東搜索
三、什麼是Lucene
- Lucene是一個基於java開發的全文檢索工具包
- Lucene是apache軟件基金會4 jakarta項目組的一個子項目,是一個開放源代碼的全文檢索引擎工具包,但它不是一個完整的全文檢索引擎,而是一個全文檢索引擎的架構,提供了完整的查詢引擎和索引引擎,部分文本分析引擎(英文與德文兩種西方語言)。Lucene的目的是爲軟件開發人員提供一個簡單易用的工具包,以方便的在目標系統中實現全文檢索的功能,或者是以此爲基礎建立起完整的全文檢索引擎。Lucene是一套用於全文檢索和搜尋的開源程式庫,由Apache軟件基金會支持和提供。Lucene提供了一個簡單卻強大的應用程式接口,能夠做全文索引和搜尋。在Java開發環境裏Lucene是一個成熟的免費開源工具。就其本身而言,Lucene是當前以及最近幾年最受歡迎的免費Java信息檢索程序庫。人們經常提到信息檢索程序庫,雖然與搜索引擎有關,但不應該將信息檢索程序庫與搜索引擎相混淆。
四、 Lucene實現全文檢索的流程
4.1 創建索引
4.1.1 獲得文檔
- 原始文檔:要基於哪些數據來進行搜索,那麼這些搜索對象就是原始文檔。
- 搜索引擎:使用爬蟲獲得原始文檔
- 站內搜索:對數據庫中的數據進行查找。
- 案例:直接使用io流讀取磁盤上的文件。
4.1.2 構建文檔對象
- 對應每個原始文檔創建一個Document
- 每個Document對象中包含多個域(Field),域中保存的就是原始文檔數據。
- 每個文檔都有一個唯一的編號,就是文檔id
4.1.3 分析文檔
- 分詞的過程。(案例)
- 根據空格進行字符串的拆分
- 把單詞統一轉換成小寫
- 去除標點符號
- 去除停用詞(無意義的詞)
- 每個關鍵詞都封裝成一個
Term
對象Term
包含兩部分的內容- 關鍵詞所在的域
- 關鍵詞本身
- 不同的域中拆分出來的相同的關鍵詞是不同的
Term
4.1.4 創建索引
- 基於關鍵詞列表創建一個索引,保存到索引庫中
- 索引庫(三部分內容):
- 索引
- document對象
- 關鍵詞和文檔的對應關係
- 創建索引是對語彙單元索引,通過詞語找文檔,這種索引的結構叫做倒排索引結構
- 倒排索引,也叫反向索引結構,包括索引和文檔兩部分,索引即詞彙表,它的規模較小,而文檔集合較大。
- 傳統方法是根據文件找到文件的該內容,在文件內容中匹配搜索的關鍵字,這種方式是順序掃描方法,數據量大、搜索慢。
4.2 查詢索引
4.2.1 用戶查詢接口
- 用戶輸入查詢條件的地方
- 如:百度的搜索框
4.2.2 把關鍵詞封裝成一個查詢對象
- 要查詢的域
- 要搜索的關鍵詞
4.2.3 執行查詢
- 根據要查詢的關鍵詞到對應的域上進行搜索
- 找到關鍵詞,根據關鍵詞找到對應的文檔
4.2.4 渲染結果
- 根據文檔的結果,找到文檔對象,對關鍵詞進行高亮顯示,分頁處理,頁面展示。
五、入門程序
5.1 創建索引
5.1.1 創建環境
- 下載Lucene,jdk最低要求1.8
5.1.2 工程搭建
- 添加jar包
lucene-analyzers-common-8.3.0.jar
lucene-core-8.3.0.jar
commons-io.jar
5.1.3 步驟
- 創建一個Directory對象,指定索引庫保存的位置。
- 基於Directory對象創建一個
IndexWriter
對象 - 讀取磁盤上的文件,對應每個文件創建一個文檔對象
- 向文檔對象中添加域
- 把文檔對象寫入索引庫
- 關閉
IndexWriter
對象
@Test
public void createIndex() throws Exception {
//1. 創建一個Directory對象,指定索引庫保存的位置。
//把索引庫保存在內存中
//Directory directory = new RAMDirectory();
//把索引庫保存在磁盤中
Directory directory = FSDirectory.open(new File("F:\\lucenetest\\index").toPath());
//2. 基於Directory對象創建一個IndexWriter對象
IndexWriter indexWriter = new IndexWriter(directory, new IndexWriterConfig());
//3. 讀取磁盤上的文件,對應每個we年創建一個文檔對象
File dir = new File("F:\\Java\\12-lucene\\02.參考資料\\searchsource");
File[] files = dir.listFiles();
for (File file : files) {
//獲取文件名
String fileName = file.getName();
//獲取文件路徑
String filePath = file.getPath();
//獲取文件內容
String fileContent = FileUtils.readFileToString(file, "utf-8");
//獲取文集大小
long fileSize = FileUtils.sizeOf(file);
//創建Field
//參數1:域的名稱;參數2:域的內容;參數3:是否存儲
Field fieldName = new TextField("name", fileName, Field.Store.YES);
Field fieldPath = new TextField("path", filePath, Field.Store.YES);
Field fieldContent = new TextField("content", fileContent, Field.Store.YES);
Field fieldSize = new TextField("size", fileSize + "", Field.Store.YES);
//創建文檔對象
Document document = new Document();
//4. 向文檔對象中添加域
document.add(fieldName);
document.add(fieldPath);
document.add(fieldContent);
document.add(fieldSize);
//5. 把文檔對象寫入索引庫
indexWriter.addDocument(document);
}
//6. 關閉indexwriter對象
indexWriter.close();
}
5.2 使用luke查看索引庫中的內容
- luke的版本必須和Lucene的版本相同才能進行可視化查看。
5.3 查詢索引庫
5.3.1 步驟
- 創建一個
Director
對象,指定索引庫的位置 - 創建一個
IndexReader
對象 - 創建一個
IndexSearcher
對象,構造方法中的參數indexReader
對象 - 創建一個
Query
對象,TermQuery
- 執行查詢,得到一個
TopDocs
對象 - 取查詢結果的總記錄數
- 取文檔列表
- 打印文檔中的內容
- 關閉
IndexReader
對象
@Test
public void searchIndex() throws Exception {
//1. 創建一個`Director`對象,指定索引庫的位置
Directory directory = FSDirectory.open(new File("F:\\lucenetest\\index").toPath());
//2. 創建一個`IndexReader`對象
IndexReader indexReader = DirectoryReader.open(directory);
//3. 創建一個`IndexSearcher`對象,構造方法中的參數`indexReader`對象
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
//4. 創建一個`Query`對象,`TermQuery`
Query query = new TermQuery(new Term("content", "spring"));//在content域中,找spring這個關鍵字
//5. 執行查詢,得到一個`TopDocs`對象
//參數1:查詢對象;參數2:查詢結果返回的最大記錄數
TopDocs topDocs = indexSearcher.search(query, 10);
//6. 取查詢結果的總記錄數
System.out.println(topDocs.totalHits);
//7. 取文檔列表
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
//8. 打印文檔中的內容
for (ScoreDoc scoreDoc : scoreDocs) {
//取文檔id
int docId = scoreDoc.doc;
//根據id取文檔對象
Document document = indexSearcher.doc(docId);
System.out.println(document.get("name"));
System.out.println(document.get("path"));
System.out.println(document.get("size"));
System.out.println(document.get("content"));
System.out.println("----------------");
}
//9. 關閉`IndexReader`對象
indexReader.close();
}