一:什么是全文檢索
數據分類
結構化數據:有固定的格式和有限的長度,比如Oracle和mysql數據庫中的數據,可以利用sql語句查詢,如果查詢的數據量大時,可以在數據庫中創建索引,但是此時不支持模糊查詢
非結構化數據:沒有固定的的格式和長度,比如磁盤上的文件如txt,pdf等,)順序掃描法(Serial Scanning),全文檢索(Full-text Search)
對數據源創建索引,在索引庫中搜索
二:如何實現全文檢索
使用Lucene
三:什么是Lucene
Lucene是apache軟件基金會4 jakarta項目組的一個子項目,是一個開放源代碼的全文檢索引擎工具包
四:Lucene實現流程

獲得文檔對象:
應用場景:站內搜索,通過IO流
構建文檔對象:
獲取原始內容的目的是為了索引,在索引前需要將原始內容創建成文檔(Document),文檔中包括一個一個的域(Field),域中存儲內容。
這里我們可以將磁盤上的一個文件當成一個document,Document中包括一些Field(file_name文件名稱、file_path文件路徑、file_size文件大小、file_content文件內容),如下圖:

注意: (1)每個Document可以有多個Field
(2)不同的Document可以有不同的Field
(3)同一個Document可以有相同的Field(域名和域值都相同)
(4)每個文檔都有一個唯一的編號,就是文檔id。
分析文檔:
將原始內容創建為包含域(Field)的文檔(document),需要再對域中的內容進行分析,分析的過程是經過對原始文檔提取單詞、將字母轉為小寫、去除標點符號、去除停用詞等過程生成最終的語匯單元,可以將語匯單元理解為一個一個的單詞。
比如下邊的文檔經過分析如下:
原文檔內容:
Lucene is a Java full-text search engine.
分析后得到的語匯單元:
lucene、java、full、search、engine
每個單詞叫做一個Term,不同的域中拆分出來的相同的單詞是不同的term。term中包含兩部分一部分是文檔的域名,另一部分是單詞的內容。
例如:文件名中包含apache和文件內容中包含的apache是不同的term。
創建索引:

根據不同的term找到對應的Document
注意: (1)創建索引是對語匯單元索引,通過詞語找文檔,這種索引的結構叫倒排索引結構。
(2)傳統方法是根據文件找到該文件的內容,在文件內容中匹配搜索關鍵字,這種方法是順序掃描方法,數據量大、搜索慢。
用戶查詢接口:
搜索框輸入關鍵字
五:入門案例
導入相關jar包

IndexWriterTest.java
1 package com.it.lucene;
2
3 import java.io.File;
4 import java.io.IOException;
5
6 import org.apache.commons.io.FileUtils;
7 import org.apache.lucene.analysis.Analyzer;
8 import org.apache.lucene.analysis.standard.StandardAnalyzer;
9 import org.apache.lucene.document.Document;
10 import org.apache.lucene.document.Field;
11 import org.apache.lucene.document.Field.Store;
12 import org.apache.lucene.document.TextField;
13 import org.apache.lucene.index.IndexWriter;
14 import org.apache.lucene.index.IndexWriterConfig;
15 import org.apache.lucene.store.Directory;
16 import org.apache.lucene.store.FSDirectory;
17
18 public class lucene_first {
19 public static void main(String[] args) throws Exception {
20 //1,指定索引庫位置
21 Directory directory =FSDirectory.open(new File("D:\\BaiduNetdiskDownload\\lucene\\indexDatebase").toPath());
22 //指定分詞器
23 Analyzer analyzer=new StandardAnalyzer();
24 IndexWriterConfig config=new IndexWriterConfig(analyzer);
25
26 //2,創建寫入索引的對象
27 IndexWriter indexWriter=new IndexWriter(directory, config);
28
29 //3獲取原文檔
30 File scrFile=new File("D:\\BaiduNetdiskDownload\\lucene\\searchSource");
31 //遍歷
32 File[] listFiles = scrFile.listFiles();
33 for (File file : listFiles) {
34 Document doc=new Document();
35 //將域寫入到文檔中
36 //1),文件名稱
37 String name = file.getName();
38 Field fileName=new TextField("name",name, Store.YES);
39 doc.add(fileName);
40 //2),文件大小
41 long size = FileUtils.sizeOf(file);
42 Field fileSize=new TextField("size",size+"", Store.YES);
43 doc.add(fileSize);
44 //3),文件路徑
45 String path = file.getPath();
46 Field filePath=new TextField("path",path+"", Store.YES);
47 doc.add(filePath);
48 //4),文件內容
49 String content = FileUtils.readFileToString(file);
50 Field fileContent=new TextField("content",content, Store.YES);
51 doc.add(fileContent);
52
53 //4,將文檔寫入索引庫
54 indexWriter.addDocument(doc);
55 }
56 //5關閉資源
57 indexWriter.close();
58 }
59 }
運行程序后,在索引庫中可以查看到索引文件,通過luke可視化工具查看到
IndexReaderTest.java
1 package com.it.lucene;
2
3 import java.io.File;
4 import java.io.IOException;
5
6 import org.apache.commons.io.FileUtils;
7 import org.apache.lucene.analysis.Analyzer;
8 import org.apache.lucene.analysis.standard.StandardAnalyzer;
9 import org.apache.lucene.document.Document;
10 import org.apache.lucene.document.Field;
11 import org.apache.lucene.document.Field.Store;
12 import org.apache.lucene.document.TextField;
13 import org.apache.lucene.index.DirectoryReader;
14 import org.apache.lucene.index.IndexReader;
15 import org.apache.lucene.index.IndexWriter;
16 import org.apache.lucene.index.IndexWriterConfig;
17 import org.apache.lucene.index.Term;
18 import org.apache.lucene.search.IndexSearcher;
19 import org.apache.lucene.search.Query;
20 import org.apache.lucene.search.ScoreDoc;
21 import org.apache.lucene.search.TermQuery;
22 import org.apache.lucene.search.TopDocs;
23 import org.apache.lucene.store.Directory;
24 import org.apache.lucene.store.FSDirectory;
25
26 public class IndexReaderTest {
27 public static void main(String[] args) throws Exception {
28 //1,指定索引庫位置
29 Directory directory =FSDirectory.open(new File("D:\\BaiduNetdiskDownload\\lucene\\indexDatebase").toPath());
30 //2,創建索引讀取對象
31 IndexReader indexReader=DirectoryReader.open(directory);
32 //3,創建索引查詢對象
33 IndexSearcher indexSearcher=new IndexSearcher(indexReader);
34 //4,查詢條件
35 Query query=new TermQuery(new Term("content","spring"));
36 //5,返回查詢結果
37 TopDocs result = indexSearcher.search(query, 100);//100指最多返回100個Document
38 System.out.println("總記錄數:"+result.totalHits);
39 ScoreDoc[] scoreDocs = result.scoreDocs;
40 for (ScoreDoc scoreDoc : scoreDocs) {
41 int docId = scoreDoc.doc;
42 //獲取文件
43 Document doc = indexSearcher.doc(docId);
44 System.out.println("文件名"+doc.get("name"));
45 System.out.println("文件路徑"+doc.get("path"));
46 }
47 //6,關閉資源
48 indexReader.close();
49 }
50 }
六:分詞器(Aanlyzer)
每個分詞器都有tokenStream()方法
中文一般使用第三方分詞器IK-Aanlyzer(需要導入相應的包)
下載地址: https://pan.baidu.com/s/1BAujr36FozHuwt6JyVFpHQ 提取碼: m3mt
注意:搜索使用的分析器要和索引使用的分析器一致,不然搜索出來結果可能會錯亂。
七:Field域的屬性概述
是否分析:即是否分詞
是否索引:即是否添加到索引庫中用來檢索
是否存儲:即是否用來展示出來
如下圖:
Field類 |
數據類型 |
Analyzed 是否分析 |
Indexed 是否索引 |
Stored 是否存儲 |
說明 |
StringField(FieldName, FieldValue,Store.YES)) |
字符串 |
N |
Y |
Y或N |
這個Field用來構建一個字符串Field,但是不會進行分析,會將整個串存儲在索引中,比如(訂單號,姓名等) 是否存儲在文檔中用Store.YES或Store.NO決定 |
LongField(FieldName, FieldValue,Store.YES) |
Long型 |
Y |
Y |
Y或N |
這個Field用來構建一個Long數字型Field,進行分析和索引,比如(價格) 是否存儲在文檔中用Store.YES或Store.NO決定 |
StoredField(FieldName, FieldValue) |
重載方法,支持多種類型 |
N |
N |
Y |
這個Field用來構建不同類型Field 不分析,不索引,但要Field存儲在文檔中 |
TextField(FieldName, FieldValue, Store.NO) 或 TextField(FieldName, reader) |
字符串 或 流 |
Y |
Y |
Y或N |
如果是一個Reader, lucene猜測內容比較多,會采用Unstored的策略. |
|
八:索引查詢
1,MatchAllDocsQuery(查詢索引庫中的全部Document)
2,TermQuery(精准查詢)
3,NumericRangeQuery(根據數值范圍查詢)
示例代碼:
1 //數值范圍查詢
2 @Test
3 public void testNumericRangeQuery() throws Exception {
4 //創建一個Directory對象,指定索引庫存放的路徑
5 Directory directory = FSDirectory.open(new File("E:\\programme\\test"));
6 //創建IndexReader對象,需要指定Directory對象
7 IndexReader indexReader = DirectoryReader.open(directory);
8 //創建Indexsearcher對象,需要指定IndexReader對象
9 IndexSearcher indexSearcher = new IndexSearcher(indexReader);
10
11 //創建查詢
12 //參數:
13 //1.域名
14 //2.最小值
15 //3.最大值
16 //4.是否包含最小值
17 //5.是否包含最大值
18 Query query = NumericRangeQuery.newLongRange("fileSize", 41L, 2055L, true, true);
19 //執行查詢
20
21 //第一個參數是查詢對象,第二個參數是查詢結果返回的最大值
22 TopDocs topDocs = indexSearcher.search(query, 10);
23
24 //查詢結果的總條數
25 System.out.println("查詢結果的總條數:"+ topDocs.totalHits);
26 //遍歷查詢結果
27 //topDocs.scoreDocs存儲了document對象的id
28 //ScoreDoc[] scoreDocs = topDocs.scoreDocs;
29 for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
30 //scoreDoc.doc屬性就是document對象的id
31 //int doc = scoreDoc.doc;
32 //根據document的id找到document對象
33 Document document = indexSearcher.doc(scoreDoc.doc);
34 //文件名稱
35 System.out.println(document.get("fileName"));
36 //文件內容
37 System.out.println(document.get("fileContent"));
38 //文件大小
39 System.out.println(document.get("fileSize"));
40 //文件路徑
41 System.out.println(document.get("filePath"));
42 System.out.println("----------------------------------");
43 }
44 //關閉indexreader對象
45 indexReader.close();
46 }
4,BooleanQuery(組合條件查詢)
示例代碼:
1 //組合條件查詢
2 2 @Test
3 3 public void testBooleanQuery() throws Exception {
4 4 //創建一個Directory對象,指定索引庫存放的路徑
5 5 Directory directory = FSDirectory.open(new File("E:\\programme\\test"));
6 6 //創建IndexReader對象,需要指定Directory對象
7 7 IndexReader indexReader = DirectoryReader.open(directory);
8 8 //創建Indexsearcher對象,需要指定IndexReader對象
9 9 IndexSearcher indexSearcher = new IndexSearcher(indexReader);
10 10
11 11 //創建一個布爾查詢對象
12 12 BooleanQuery query = new BooleanQuery();
13 13 //創建第一個查詢條件
14 14 Query query1 = new TermQuery(new Term("fileName", "apache"));
15 15 Query query2 = new TermQuery(new Term("fileName", "lucene"));
16 16 //組合查詢條件
17 17 /*
18 18 Occur.MUST:必須滿足此條件,相當於and
19 19
20 20 Occur.SHOULD:應該滿足,但是不滿足也可以,相當於or
21 21
22 22 Occur.MUST_NOT:必須不滿足。相當於not*/
23 23
24 17 query.add(query1, Occur.MUST);
25 18 query.add(query2, Occur.MUST);
26 19 //執行查詢
27 20
28 21 //第一個參數是查詢對象,第二個參數是查詢結果返回的最大值
29 22 TopDocs topDocs = indexSearcher.search(query, 10);
30 23
31 24 //查詢結果的總條數
32 25 System.out.println("查詢結果的總條數:"+ topDocs.totalHits);
33 26 //遍歷查詢結果
34 27 //topDocs.scoreDocs存儲了document對象的id
35 28 //ScoreDoc[] scoreDocs = topDocs.scoreDocs;
36 29 for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
37 30 //scoreDoc.doc屬性就是document對象的id
38 31 //int doc = scoreDoc.doc;
39 32 //根據document的id找到document對象
40 33 Document document = indexSearcher.doc(scoreDoc.doc);
41 34 //文件名稱
42 35 System.out.println(document.get("fileName"));
43 36 //文件內容
44 37 System.out.println(document.get("fileContent"));
45 38 //文件大小
46 39 System.out.println(document.get("fileSize"));
47 40 //文件路徑
48 41 System.out.println(document.get("filePath"));
49 42 System.out.println("----------------------------------");
50 43 }
51 44 //關閉indexreader對象
52 45 indexReader.close();
53 46 }
5,queryparser(更具查詢語法查詢)
查詢語法
1、基礎的查詢語法,關鍵詞查詢:
域名+“:”+搜索的關鍵字
例如:content:java
2、范圍查詢
域名+“:”+[最小值 TO 最大值]
例如:size:[1 TO 1000]
范圍查詢在lucene中支持數值類型,不支持字符串類型。在solr中支持字符串類型。
3、組合條件查詢
1)+條件1 +條件2:兩個條件之間是並且的關系and
例如:+filename:apache +content:apache
2)+條件1 條件2:必須滿足第一個條件,應該滿足第二個條件
例如:+filename:apache content:apache
3)條件1 條件2:兩個條件滿足其一即可。
例如:filename:apache content:apache
4)-條件1 條件2:必須不滿足條件1,要滿足條件2
例如:-filename:apache content:apache
示例代碼:
1 @Test
2 public void testQueryParser() throws Exception {
3 //創建一個Directory對象,指定索引庫存放的路徑
4 Directory directory = FSDirectory.open(new File("E:\\programme\\test"));
5 //創建IndexReader對象,需要指定Directory對象
6 IndexReader indexReader = DirectoryReader.open(directory);
7 //創建Indexsearcher對象,需要指定IndexReader對象
8 IndexSearcher indexSearcher = new IndexSearcher(indexReader);
9
10 //創建queryparser對象
11 //第一個參數默認搜索的域
12 //第二個參數就是分析器對象
13 QueryParser queryParser = new QueryParser("fileName", new IKAnalyzer());
14 //使用默認的域,這里用的是語法,下面會詳細講解一下
15 Query query = queryParser.parse("apache");
16 //不使用默認的域,可以自己指定域
17 //Query query = queryParser.parse("fileContent:apache");
18 //執行查詢
19
20
21 //第一個參數是查詢對象,第二個參數是查詢結果返回的最大值
22 TopDocs topDocs = indexSearcher.search(query, 10);
23
24 //查詢結果的總條數
25 System.out.println("查詢結果的總條數:"+ topDocs.totalHits);
26 //遍歷查詢結果
27 //topDocs.scoreDocs存儲了document對象的id
28 //ScoreDoc[] scoreDocs = topDocs.scoreDocs;
29 for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
30 //scoreDoc.doc屬性就是document對象的id
31 //int doc = scoreDoc.doc;
32 //根據document的id找到document對象
33 Document document = indexSearcher.doc(scoreDoc.doc);
34 //文件名稱
35 System.out.println(document.get("fileName"));
36 //文件內容
37 System.out.println(document.get("fileContent"));
38 //文件大小
39 System.out.println(document.get("fileSize"));
40 //文件路徑
41 System.out.println(document.get("filePath"));
42 System.out.println("----------------------------------");
43 }
44 //關閉indexreader對象
45 indexReader.close();
46 }
6,MultiFieldQueryParser(指定多個默認域)
示例代碼:
1 @Test
2 public void testMultiFiledQueryParser() throws Exception {
3 //創建一個Directory對象,指定索引庫存放的路徑
4 Directory directory = FSDirectory.open(new File("E:\\programme\\test"));
5 //創建IndexReader對象,需要指定Directory對象
6 IndexReader indexReader = DirectoryReader.open(directory);
7 //創建Indexsearcher對象,需要指定IndexReader對象
8 IndexSearcher indexSearcher = new IndexSearcher(indexReader);
9
10 //可以指定默認搜索的域是多個
11 String[] fields = {"fileName", "fileContent"};
12 //創建一個MulitFiledQueryParser對象
13 MultiFieldQueryParser queryParser = new MultiFieldQueryParser(fields, new IKAnalyzer());
14 Query query = queryParser.parse("apache");
15 System.out.println(query);
16 //執行查詢
17
18
19 //第一個參數是查詢對象,第二個參數是查詢結果返回的最大值
20 TopDocs topDocs = indexSearcher.search(query, 10);
21
22 //查詢結果的總條數
23 System.out.println("查詢結果的總條數:"+ topDocs.totalHits);
24 //遍歷查詢結果
25 //topDocs.scoreDocs存儲了document對象的id
26 //ScoreDoc[] scoreDocs = topDocs.scoreDocs;
27 for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
28 //scoreDoc.doc屬性就是document對象的id
29 //int doc = scoreDoc.doc;
30 //根據document的id找到document對象
31 Document document = indexSearcher.doc(scoreDoc.doc);
32 //文件名稱
33 System.out.println(document.get("fileName"));
34 //文件內容
35 System.out.println(document.get("fileContent"));
36 //文件大小
37 System.out.println(document.get("fileSize"));
38 //文件路徑
39 System.out.println(document.get("filePath"));
40 System.out.println("----------------------------------");
41 }
42 //關閉indexreader對象
43 indexReader.close();
44 }
7:IndexSearcher.search()查詢方法
方法 |
說明 |
indexSearcher.search(query, n) |
根據Query搜索,返回評分最高的n條記錄 |
indexSearcher.search(query, filter, n) |
根據Query搜索,添加過濾策略,返回評分最高的n條記錄 |
indexSearcher.search(query, n, sort) |
根據Query搜索,添加排序策略,返回評分最高的n條記錄 |
indexSearcher.search(booleanQuery, filter, n, sort) |
根據Query搜索,添加過濾策略,添加排序策略,返回評分最高的n條記錄 |
8:TopDocs(返回的查詢結果)
TopDocs topDocs.totalHits 查詢到的總
條數