1.什麼是Lucene?
概念:Lucene是一個基於Java開發全文檢索工具包
1>Lucene實現全文檢索的流程
1).創建索引
獲得文檔:–原始文檔:要基於哪些數據來進行檢索,那麼這些數據就是原始文檔
搜索引擎:使用爬蟲獲得原始文檔
站內搜索:數據庫中的數據
案例:直接使用IO流讀取磁盤中的文件
2).構建文檔對象
獲取原始內容的目的是爲了索引,在索引前需要將原始內容創建成文檔(Doucment),文檔中包括了一個一個的域(Field),域中存儲內容
我們可以把一個磁盤上的一個文件當成一個document,Document中包括一些Field(file_name文件名稱/file_path文件路徑,file_size文件大小,file_context文件內容)
注意:每個Doucument可以有多個Field,不同的Document可以有不同的Field,同一個Document可以有相同的Field(域名和域值都相同)
對應每個原始文檔創建一個Document對象
每個Doucemnt對象中包含多個域(field)
域中保存的就是原始文檔的數據
域的名稱
域的值
每個文檔都有一個文檔的編號爲文本id,
3).分析文檔
將原始內容創建爲包含域(Field)的文檔(Document)需要再對域中的內容進行分析分析的過程是經過對原始文檔提取單詞,將字母轉爲小寫,去除標點符號,去吃停用詞等過程生成最終的單元詞彙,可以將單元理解爲一個一個的單詞
如: Lucene is a java full-text search engine
分析後 lucene is a java full text…
每個單詞叫做一個Term,不同的域中拆分出來的相同的單詞是不同的term,term中包含兩部分一部分是文檔的域名,另一部分是單詞的內容
1.根據空格進行拆分,得到單詞列表
2.把單詞統一轉化成,小寫或者大寫
3.去除標點符號
4.去除停用詞(就是沒有意義的詞)
每個關鍵詞都封裝成一個Term對象中
Term找那個包含兩部分內容
關鍵詞所在的域
關鍵詞本身
在不同的域中拆分出來的相同關鍵詞是不同的Term
4).創建索引
對所有文檔分析得出的詞彙單元進行索,索引的目的是爲了搜索,最終要實現只搜索有索引的詞彙單元從而找到Document(文檔)
注意:創建所以是對詞彙單元索引,通過詞語查找文檔,這種索引的結構叫做倒排索引結構
傳統的方法是根據文件找到該文件的內容,在文件內容中匹配搜索關鍵字,這種方法就是順序掃描方法,數據量大搜索慢
倒敘索引結構也叫反向索引結構,包括索引和文檔兩部分,索引即詞彙表,它的規模較小,而文檔較大
基於關鍵詞鏈表來創建一個索引,保存到磁盤上(索引庫中)
索引庫中包含:
索引
Doucument
關鍵詞和文檔的對應關係
通過詞語找文檔,這種索引的結構叫做倒排索引結構
2>查詢索引
查詢索引也是搜索的過程,搜索就是用戶輸入關鍵字,從索引(index)中進行搜索的過程.根據關鍵字搜索索引,根據索引找到對應的文檔,從而找到要搜索的內容(這裏指的是磁盤上的文件)
1).用戶查詢接口
全文檢索系統提供給用戶搜索界面供用戶提交搜索的關鍵字,搜索完成展示搜索結果
Lucene不提供用戶搜索界面的功能,需要用戶根據自己的需求開發搜索界面
用戶輸入查詢條件的地方
如:百度的搜索框
2).創建查詢
用戶輸入查詢關鍵字執行搜索之前需要先構建一個查詢對象,查詢對象中可以指定查詢要搜索的Field文檔域,查詢關鍵字等,查詢對象會生成具體的查詢方法
如:"fileName:lucene"表示要搜索Field域的內容爲"lucene"的文檔
要查詢的域
要搜索的關鍵詞
3).執行查詢
搜索索引過程:
根據查詢語法在倒排索引詞典表中分別找出對應搜索詞的索引,從而找到索引所連接的穩點鏈表
如:"fileName:lucene"表示搜索出fileName域中包含Lucene的文檔
搜索過程就是在索引上查找域爲fileName,並且關鍵字爲Lucene的tem,並根據term找到文檔id列表
根據要查詢的關鍵詞到對應的域進行搜索
把關鍵詞找到根據關鍵詞來找到對應的文檔
4).渲染結果
以一個友好的界面將查詢結果展示給用戶,用戶根據搜索結果找到自己想要的信息,爲了幫助用戶很快找到自己的結果,提供了很多展示的效果,比如搜索結果中獎關鍵字高亮顯示百度提供的快照等等
根據文檔的ID找到文檔對象需要對關鍵詞進行高亮顯示
需要進行分頁處理
最終展示給用戶看
2.入門程序
1.創建索引
環境:需要下載Lucene/最低要求JDK1.8
添加jar:lucene-analyzers-common-7.4.0.jar
lucene-core-7.4.0.jar
commons-io.jar
步驟:
public static void main(String[]args)throws Exception{
// 1.創建一個Director對象指定索引庫的位置
// C:\Users\One\Documents\Tencent Files\2633655104\FileRecv\type
Directory directory = FSDirectory.open(new File("C:\\Users\\One\\Documents\\Tencent Files\\2633655104\\FileRecv\\type").toPath());
// 2.基於Directory對象創建一個IndexWrite對象
IndexWriter indexWriter=new IndexWriter(directory,new IndexWriterConfig());
// 3.讀取磁盤上的文件,對應每個文件來創建一個文檔對象
File dir=new File("F:\\新建文件夾\\黑馬57期\\講義+筆記+資料\\流行框架\\61.會員版(2.0)-就業課(2.0)-Lucene\\lucene\\02.參考資料\\searchsource");
// 4.向文檔對象中添加域
File[] files=dir.listFiles();
for (File f:files){
//取文件名
String fileName= f.getName();
//文件的路徑
String filePath=f.getPath();
//文件的內容
String fileContext=FileUtils.readFileToString(f,"utf-8");
//文件的大小
long fileSize=FileUtils.sizeOf(f);
//創建Filed
//參數1:域的名稱 參數2:域的內容 參數3:是否存儲
Field fieldName=new TextField("name",fileName,Field.Store.YES);
Field fieldPath=new TextField("path",filePath,Field.Store.YES);
Field fieldContext=new TextField("context",fileContext,Field.Store.YES);
Field fieldSize=new TextField("size",fileSize+"",Field.Store.YES);
//創建文檔對象
Document document=new Document();
document.add(fieldName);
document.add(fieldPath);
document.add(fieldContext);
document.add(fieldSize);
//5.把文檔對象寫入索引庫
indexWriter.addDocument(document);
}
//6.關閉IndexWrite對象
indexWriter.close();
}
2.使用Luke查看索引庫中的內容
3.查詢索引庫
步驟:
public static void main(String[] args) throws IOException {
//創建Directory對象指定索引庫的位置
Directory directory = FSDirectory.open(new File("C:\\Users\\One\\Documents\\Tencent Files\\2633655104\\FileRecv\\type").toPath());
//創建一個indexReader對象
IndexReader indexReader= DirectoryReader.open(directory);
//創建一個indexSearcher對象構造方法中的參數就是indexReader
IndexSearcher indexSearcher=new IndexSearcher(indexReader);
//創建一個Query對象,TermQuery 根據關鍵詞進行查詢
Query query=new TermQuery(new Term("context","spring"));
//執行查詢得到查詢 結果TopDocs對象
TopDocs topDocs=indexSearcher.search(query,11);
//取查詢結果的總記錄數
System.out.println("查詢總記錄數:"+topDocs.totalHits);
//取文檔列表
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc s:scoreDocs
) {
//獲取文檔的id
int id=s.doc;
//根據id取出對象
//打印文檔中的內容
Document document=indexSearcher.doc(id);
System.out.println(document.get("name"));
System.out.println(document.get("path"));
//System.out.println(document.get("context"));
System.out.println(document.get("size"));
System.out.println("-----------分割線--------");
}
//關閉indexReader對象
indexReader.close();
}
}
3.分析器
默認使用的數據標準分析器StandardAnalyzer
1>如何查看分析器的分析效果
使用Analyzer對象的tokenStream方法可以返回一個TokenStream對象,此對象中包含了最終的分詞結果
實現步驟:
1.創建一個Analyzer對象,Standardlyzer對象
2.使用分析器對象的tokenStream方法獲得一個TokenStream對象
3.向TokenStream對象中設置一個引用,相當於是一個指針
4.調用tokenStream對象的rest方法如果不調用拋異常
5.使用while循環遍歷tokenStream對象
6.關閉tokenStream對象
2>IKAnalyze的使用方法
1.把IKAnalyze添加到工程中
2.把配置文件和擴展詞典都添加到工程的classpath下
注意:擴展詞典是嚴禁使用windows記事本編輯的,保證擴展詞典的編碼是utf-8
WINDOWS環境下的記事本編碼默認是UTF-8+BOM
擴展詞典:添加一些新詞
停用詞詞典:無意義的詞或者是敏感詞彙
4.索引庫的維護
1.索引庫的添加
1>.Field域的屬性
是否分析:是否對域的內容進行分詞處理,前提使我們要對域的內容進行查詢
是否索引:將Field分析後的詞整個Field值進行索引,只有索引方可以搜索到
如:商品名稱,商品簡介分析後進行索引,訂單號,身份證號不用分析但也要索引,這些 將來都要作爲查詢的條件
是否存儲:將Field值存儲在文檔中,存儲在文檔中的Field纔可以從Document中獲取
如:商品名稱,訂單號,凡是將來要從Doucument中獲取的Field都要存儲
是否存儲的標準:是否要將內容展示給用戶
2.Field類:
1>.StringField(FieldName,FieldValue,Store.class)
數據類型:字符串
Analyzed是否分析:no
Indexed是否索引:yes
Stored是否存儲:Yes or No
說明:這個Field用來構建一個字符串Field但是不會進行分析,會將整個串存儲在索引中,比如(訂單號,姓名等)是否存儲在文檔中用Store.YES或者Store.NO決定
2>.LongPoint(String name,long...point)
數據類型:Long類型
Analyzed是否分析:yes
Indexed是否索引:yes
Stored是否存儲: No
說明:可以使用LongPoint,IntPoint等類型存儲數值類型的數據.讓數值類型可以進行索引但是不能存儲數據,如果想要存儲數據還需要使用StoredField
3>.StoredField(FieldName,FieldValue)
數據類型:重載方法支持多種類型
Analyzed是否分析:no
Indexed是否索引:no
Stored是否存儲:Yes
說明:這個Field用來構建不同類型Field不分析,不索引,但要Field存儲在文檔中
4>.TextField(FieldName,FieldValue,Store.No)OR TextField(FieldName,reader)
數據類型:字符串OR流
Analyzed是否分析:yes
Indexed是否索引:yes
Stored是否存儲:Yes or No
說明:如果是一個Reader,lucene猜測內容比較多,會採用Unstored的策略
3.添加文檔代碼實現
public void addDocument() throws Exception {
//索引庫存放路徑
Directory directory = FSDirectory.open(new File("D:\\temp\\index").toPath());
IndexWriterConfig config = new IndexWriterConfig(new IKAnalyzer());
//創建一個indexwriter對象
IndexWriter indexWriter = new IndexWriter(directory, config);
//創建一個Document對象
Document document = new Document();
//向document對象中添加域。
//不同的document可以有不同的域,同一個document可以有相同的域。
document.add(new TextField("filename", "新添加的文檔", Field.Store.YES));
document.add(new TextField("content", "新添加的文檔的內容", Field.Store.NO));
//LongPoint創建索引
document.add(new LongPoint("size", 1000l));
//StoreField存儲數據
document.add(new StoredField("size", 1000l));
//不需要創建索引的就使用StoreField存儲
document.add(new StoredField("path", "d:/temp/1.txt"));
//添加文檔到索引庫
indexWriter.addDocument(document);
//關閉indexwriter
indexWriter.close();
}
4.刪除全部索引
public void deleteAllIndex() throws Exception {
IndexWriter indexWriter = getIndexWriter();
//刪除全部索引
indexWriter.deleteAll();
//關閉indexwriter
indexWriter.close();
}
說明:將索引目錄的索引信息全部刪除,直接徹底刪除,無法恢復。
4.1.指定查詢條件刪除
public void deleteIndexByQuery() throws Exception {
IndexWriter indexWriter = getIndexWriter();
//創建一個查詢條件
Query query = new TermQuery(new Term("filename", "apache"));
//根據查詢條件刪除
indexWriter.deleteDocuments(query);
//關閉indexwriter
indexWriter.close();
}
5.索引庫的修改
public void updateIndex() throws Exception {
IndexWriter indexWriter = getIndexWriter();
//創建一個Document對象
Document document = new Document();
//向document對象中添加域。
//不同的document可以有不同的域,同一個document可以有相同的域。
document.add(new TextField("filename", "要更新的文檔", Field.Store.YES));
document.add(new TextField("content", " Lucene 簡介 Lucene 是一個基於 Java 的全文信息檢索工具包," +
"它不是一個完整的搜索應用程序,而是爲你的應用程序提供索引和搜索功能。",
Field.Store.YES));
indexWriter.updateDocument(new Term("content", "java"), document);
//關閉indexWriter
indexWriter.close();
}
6.Lucene索引庫查詢
對要搜索的信息創建Query查詢對象,Lucene會根據Query查詢對象生成最終的查詢語法,類似關係數據庫SqL語法一樣Lucene也有自己的查詢語法
如:"name:lucene"表示查詢Field的name爲"lucene"的文檔信息
可通過兩種方法創建查詢對象
1).使用Lucene提供的Query子類
2).使用QueryParser解析查詢表達式
6.1.TermQuery
TermQuery通過項查詢,TermQuery不適用分析器所以建議匹配不分詞的Field域查詢比如訂單號,分類ID等(制定要查詢的域和要查詢的關鍵詞)
public void testTermQuery() throws Exception {
Directory directory = FSDirectory.open(new File("D:\\temp\\index").toPath());
IndexReader indexReader = DirectoryReader.open(directory);
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
//創建查詢對象
Query query = new TermQuery(new Term("content", "lucene"));
//執行查詢
TopDocs topDocs = indexSearcher.search(query, 10);
//共查詢到的document個數
System.out.println("查詢結果總數量:" + topDocs.totalHits);
//遍歷查詢結果
for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
Document document = indexSearcher.doc(scoreDoc.doc);
System.out.println(document.get("filename"));
//System.out.println(document.get("content"));
System.out.println(document.get("path"));
System.out.println(document.get("size"));
}
//關閉indexreader
indexSearcher.getIndexReader().close();
}
6.2.數值範圍查詢
public void testRangeQuery() throws Exception {
IndexSearcher indexSearcher = getIndexSearcher();
Query query = LongPoint.newRangeQuery("size", 0l, 10000l);
printResult(query, indexSearcher);
}
6.3.使用QueryParser查詢
通過QueryParser也可以創建Query,QueryParser提供了一個Parser方法,此方法可以直接根據語法來查詢,Query對象執行的查詢語法可通過System.out.println(query)
查詢需要使用到分析器,建議創建索引時使用的分析器和查詢索引時使用的分析器要一致
public void testQueryParser() throws Exception {
IndexSearcher indexSearcher = getIndexSearcher();
//創建queryparser對象
//第一個參數默認搜索的域
//第二個參數就是分析器對象
QueryParser queryParser = new QueryParser("content", new IKAnalyzer());
Query query = queryParser.parse("Lucene是java開發的");
//執行查詢
printResult(query, indexSearcher);
}
private void printResult(Query query, IndexSearcher indexSearcher) throws Exception {
//執行查詢
TopDocs topDocs = indexSearcher.search(query, 10);
//共查詢到的document個數
System.out.println("查詢結果總數量:" + topDocs.totalHits);
for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
Document document = indexSearcher.doc(scoreDoc.doc);
System.out.println(document.get("filename"));
//System.out.println(document.get("content"));
System.out.println(document.get("path"));
System.out.println(document.get("size"));
}
//關閉indexreader
indexSearcher.getIndexReader().close();
}