目錄
1.什么是Lucene
Apache Lucene 是完全用Java編寫的高性能,功能齊全的,全文檢索引擎工具包,通過lucene可以讓程序員快速開發一個全文檢索功能。
1.1什么是全文檢索
在我們的生活中數據總體分為兩種:結構化數據 和非結構化數據 。
結構化數據: 指具有固定格式或有限長度的數據,如數據庫,元數據等。
非結構化數據: 指不定長或無固定格式的數據,如郵件,word文檔等。非結構化數據又叫全文數據。
當然有的地方還會提到第三種,半結構化數據,如XML,HTML等,半結構化數據可以根據需要按結構化數據來處理,也可以抽取出純文本按非結構化數據來處理。
按照數據的分類,搜索也分為兩種:
搜索結構化數據 :如對數據庫的搜索,用SQL語句。再如對元數據的搜索,如利用windows搜索對文件名,類型,修改時間進行搜索等。
搜索非結構化數據 :如利用windows的搜索也可以搜索文件內容,Linux下的grep命令,再如用Google和百度可以搜索大量內容數據。
對非結構化數據的搜索,即對全文數據的搜索主要有兩種方法:
一種是順序掃描法 (Serial Scanning): 比如要找內容包含某一個字符串的文件,就是一個文檔一個文檔的看,對於每一個文檔,從頭看到尾,如果此文檔包含此字符串,則此文檔為我們要找的文件,接着看下一個文件,直到掃描完所有的文件。如利用windows的搜索也可以搜索文件內容,只是相當的慢。如果你有一個80G硬盤,如果想在上面找到一個內容包含某字符串的文件,需要花費很長的時間。Linux下的grep命令也是這樣一種方式。對於小數據量的文件,這種方法還是最直接,最方便的,但是對於大量的文件,這種方法就很慢了。
對非結構化數據順序掃描很慢,對結構化數據的搜索卻相對較快(由於結構化數據有一定的結構可以采取一定的搜索算法加快速度),那么我們把非結構化數據轉化得有一定結構不就行了嗎?
其實這就是全文檢索的基本思路,即將非結構化數據中的一部分信息提取出來,重新組織,使其變得有一定結構,然后對有一定結構的數據進行搜索,從而達到快速搜索非結構化數據的目的。
這部分從非結構化數據中提取出來,然后重新組織的信息,我們稱之索引 。例如字典,字典的拼音表和部首檢字表就相當於字典的索引,由於對每一個字的解釋都是非結構化的,如果字典沒有音節表和部首檢字表,在茫茫辭海中找一個字只能順序掃描,即一頁一頁進行查找。然而字的某些信息可以提取出來進行結構化處理,比如讀音,就比較結構化,分聲母和韻母,分別只有幾種可以一一列舉,於是將讀音拿出來按一定的順序排列,每一項讀音都指向此字的詳細解釋的頁數。我們搜索時按結構化的拼音搜到讀音,然后按其指向的頁數,便可找到我們的非結構化數據——即對字的解釋說明。
總結:全文檢索首先將要查詢的目標數據中的詞提取出來,組成索引,通過查詢索引達到搜索目標數據的目的。這種先建立索引,再對索引進行搜索的過程就叫全文檢索(Full-text Search)。
1.2 全文檢索的應用場景
對於數據量大、數據結構不固定的數據可采用全文檢索方式搜索,比如百度、Google等搜索引擎、論壇站內搜索、電商網站站內搜索等。
1.3. 如何實現全文檢索
可以使用Lucene實現全文檢索。Lucene是apache下的一個開放源代碼的全文檢索引擎工具包,它可以為應用程序提供多個api接口去調用,可以簡單理解為是一套實現全文檢索的類庫。
2.Lucene實現全文檢索的流程
2.1. 創建索引和搜索流程圖
全文檢索的流程分為兩大部分:索引流程、搜索流程。
索引流程:確定原始內容即要搜索的內容->采集原始內容數據->創建文檔->分析文檔(分詞)->創建索引
搜索流程:用戶通過搜索界面->創建查詢->執行搜索->從索引庫搜索->渲染搜索結果
2.2. 創建索引
將用戶要搜索的數據內容進行索引,索引存儲在索引庫(index)中的過程。
采集數據技術有哪些?
1、對於互聯網上的網頁,通過http協議抓取html網頁內容到本地。
2、針對電商站內搜索功能,全文檢索的數據在數據庫中,需要通過jdbc訪問數據庫表中的內容。
3、如果數據是文件系統中的某個文件,就通過I/O讀取文件的內容。
這里我們以搜索磁盤上的文本文件為例,凡是文件名或文件內容包括關鍵字(albert)的文件都要找出來,這里要對文件名和文件內容創建索引。
2.2.1. 創建文檔對象
獲取原始數據的目的是為了創建索引,在創建索引前需要將原始數據創建成文檔(Document),文檔中包括一個一個的域(Field),域中存儲原始數據的內容。這里可以把Document理解為數據庫表中的一條記錄,可以把域理解為數據庫中的字段。
可以將磁盤上的一個文件當成一個document,Document中包括一些Field(file_name文件名稱、file_path文件路徑、file_size文件大小、file_content文件內容),如下圖:
2.2.2. 索引文件的邏輯結構
文檔:
對非結構化的數據統一格式為document文檔格式,一個文檔有多個field域,不同的文檔其field的個數可以不同,建議相同類型的文檔包括相同的field。 本例子一個Document對應一個磁盤上的文件。
索引域:
用於搜索程序從索引域中搜索一個一個詞,根據詞找到對應的文檔。將Document中的Field的內容進行分詞,將分好的詞創建索引,索引=Field域名:詞。
2.2.2. 分析文檔(分詞)
將原始內容創建為包含域(Field)的文檔(document),需要再對域中的內容進行分析,分析的過程是經過對原始文檔提取單詞、將字母轉為小寫、去除標點符號、去除停用詞等過程生成最終的語匯單元,可以將語匯單元理解為一個一個的單詞。
分詞 主要過程就是分詞和過慮兩步:
分詞:就是將采集到的文檔內容切分成一個一個的詞,具體應該說是將Document中Field的value值切分成一個一個的詞。
過慮:包括去除標點符號、去除停用詞(的、是、a、an、the等)、大寫轉小寫、詞的形還原(復數形式轉成單數形參、過去式轉成現在式。。。)等。
什么是停用詞?停用詞是為節省存儲空間和提高搜索效率,搜索引擎在索引頁面或處理搜索請求時會自動忽略某些字或詞,這些字或詞即被稱為Stop Words(停用詞)。比如語氣助詞、副詞、介詞、連接詞等,通常自身並無明確的意義,只有將其放入一個完整的句子中才有一定作用,如常見的“的”、“在”、“是”、“啊”等。
比如下邊的文檔經過分析器分析如下:
原文檔內容:
Lucene is a Java full-text search engine. |
分析后得到的語匯單元(Token):
lucene、java、full、text、search、engine |
2.2.3. 創建索引
對所有文檔分析得出的語匯單元進行索引,索引的目的是為了搜索,最終要實現只搜索被索引的語匯單元從而找到Document(文檔)。
注意:創建索引是對語匯單元索引,通過詞語找文檔,這種索引的結構叫倒排索引結構。
倒排索引表
傳統方法是先找到文件,然后在文件中找內容,在文件內容中匹配搜索關鍵字,這種方法是順序掃描方法,數據量大比較大的時候。搜索很慢。
倒排索引結構是根據內容(詞語)找文檔,倒排索引結構也叫反向索引結構,包括索引和文檔兩部分,索引即詞匯表,它是在索引中匹配搜索關鍵字,由於索引內容量有限並且采用固定優化算法搜索速度很快,找到了索引中的詞匯,詞匯與文檔關聯,從而最終找到了文檔。
倒排索引結構是根據內容(詞語)找文檔,如下圖:
2.3. 查詢索引
查詢索引也是搜索的過程。搜索就是用戶輸入關鍵字,從索引(index)中進行搜索的過程。根據關鍵字搜索索引,根據索引找到對應的文檔。
和索引過程的分詞一樣,搜索時也要對用戶輸入的關鍵字進行分詞,一般情況索引和搜索使用的分詞器一致。比如:輸入搜索關鍵字“Lucene教程”,分詞后為Lucene和教程兩個詞,與Lucene和教程有關的內容都會被搜索出來。
3.Lucene搜索案例
3.1 需求分析
為磁盤上的文本文件創建索引,然后進行查找,凡是文件名或文件內容包括關鍵字(albert)的文件都要找出來,這里要對文件名和文件內容創建索引。
3.2 開發准備
3.2.1 Lucene工具包下載
Lucene是開發全文檢索功能的工具包,使用時從官方網站下載,並解壓。
官方網站:http://lucene.apache.org/ 目前最新版本:8.5.2
下載地址:http://archive.apache.org/dist/lucene/java/
API: https://lucene.apache.org/core/6_6_0/core/index.html
下載版本:6.6.0
JDK要求:1.8(以上)
下載並解壓Lucene:
lucene-analyzers-common-6.6.0.jar:lucene-6.6.0/analysis/common
lucene-analyzers-smartcn-6.6.0.jar:lucene-6.6.0/analysis/smartcn/
lucene-core-6.6.0.jar:lucene-6.6.0/core/
lucene-highlighter-6.6.0.jar:lucene-6.6.0/highlighter/
lucene-memory-6.6.0.jar:lucene-6.6.0/memory/
lucene-queries-6.6.0.jar:lucene-6.6.0/queries/
lucene-queryparser-6.6.0.jar:lucene-6.6.0/queryparser/
3.2.2 創建工程並添加jar包
整體思想 :1.通過IO采集文件系統中的文檔數據,放入Lucene的Document中
2.寫入索引庫()對文檔(Document)進行分詞並創建索引(利用IndexWriter對象 )
第一步:創建一個java工程,並導入jar包。
-
Analysis的包
-
Core包
-
QueryParser包
-
Junit包(非必須)(下載地址:https://github.com/junit-team/junit4/wiki/Download-and-Install)
-
commons-io(非必須)(下載地址:http://commons.apache.org/proper/commons-io/download_io.cgi)
3.2.3 代碼實現
package com.albertyy.com;
import org.apache.commons.io.FileUtils;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.*;
import org.apache.lucene.index.*;
import org.apache.lucene.search.*;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.junit.Test;
import java.io.File;
import java.nio.file.Paths;
/**
* 為磁盤上的文本文件創建索引,然后進行查找,
* 凡是文件名或文件內容包括關鍵字(albert)的文件都要找出來
* @author albert
*/
public class LuceneTest1 {
// 創建索引
@Test
public void testIndex() throws Exception {
/*
* 第一步:創建一個indexwriter對象
* 1指定索引庫的存放位置Directory對象
* 2指定一個分析器,對文檔內容進行分析。
*/
Directory directory = FSDirectory.open(Paths.get("D:\\temp\\index"));
// Directory directory = new RAMDirectory();//保存索引到內存中 (內存索引庫)
// 官方推薦分詞器,對中文不友好
Analyzer analyzer = new StandardAnalyzer();
IndexWriterConfig config = new IndexWriterConfig(analyzer);
IndexWriter indexWriter = new IndexWriter(directory, config);
// 第二步:通過IO讀取磁盤上的文件信息
File f = new File("D:\\學習\\Lucene");
File[] listFiles = f.listFiles();
if(listFiles != null){
for (File file : listFiles) {
if(file.isFile()){
// 第三步:創建document對象, 並把文件信息添加到document對象中
Document document = new Document();
// 文件名稱
String file_name = file.getName();
Field fileNameField = new TextField("fileName", file_name, Field.Store.YES);
// 文件路徑
String file_path = file.getPath();
Field filePathField = new StoredField("filePath", file_path);
// 文件大小
long file_size = FileUtils.sizeOf(file);
//索引
Field fileSizeField1 = new LongPoint("fileSize", file_size);
//存儲
Field fileSizeField2 = new StoredField("fileSize", file_size);
// 文件內容
String file_content = FileUtils.readFileToString(file, "UTF-8");
Field fileContentField = new TextField("fileContent", file_content, Field.Store.NO);
document.add(fileNameField);
document.add(fileSizeField1);
document.add(fileSizeField2);
document.add(filePathField);
document.add(fileContentField);
// 第四步:使用indexwriter對象將document對象寫入索引庫,此過程進行索引創建。並將索引和document對象寫入索引庫。
indexWriter.addDocument(document);
}
}
// 第五步:關閉IndexWriter對象。
indexWriter.close();
}
}
// 搜索索引
@Test
public void testSearch() throws Exception {
// 第一步:創建一個Directory對象,也就是索引庫存放的位置。
Directory directory = FSDirectory.open(Paths.get("D:\\temp\\index"));
// 第二步:創建一個indexReader對象,需要指定Directory對象。
IndexReader indexReader = DirectoryReader.open(directory);
// 第三步:創建一個indexsearcher對象,需要指定IndexReader對象
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
// 第四步:創建一個TermQuery對象,指定查詢的域和查詢的關鍵詞。
Query query = new TermQuery(new Term("fileName", "albert"));
// 第五步:執行查詢。
TopDocs topDocs = indexSearcher.search(query, 10);
// 第六步:返回查詢結果。遍歷查詢結果並輸出。
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
int doc = scoreDoc.doc;
Document document = indexSearcher.doc(doc);
// 文件名稱
String fileName = document.get("fileName");
System.out.println(fileName);
// 文件內容
String fileContent = document.get("fileContent");
System.out.println(fileContent);
// 文件大小
String fileSize = document.get("fileSize");
System.out.println(fileSize);
// 文件路徑
String filePath = document.get("filePath");
System.out.println(filePath);
System.out.println("------------");
}
// 第七步:關閉IndexReader對象
indexReader.close();
}
}
搜索結果:
3.2.4 使用工具查看索引
Luke是用於內省Lucene / Solr / Elasticsearch索引的GUI工具。它允許:
- 瀏覽您的文檔,索引詞和過帳列表
- 搜索索引
- 執行索引維護:索引運行狀況檢查,索引優化(在運行此文件之前先備份一下!)
- 測試您的自定義Lucene分析器(Tokenizer / CharFilter / TokenFilter)
記住你的lukeall的版本和lucene的版本要一致不然可能會出問題。
luke各個版本下載地址: https://github.com/DmitryKey/luke/releases
根據你的系統來選擇執行不同的腳本:
3.4.5 Field的常用類型
Field是文檔中的域,包括Field名和Field值兩部分,一個文檔可以包括多個Field,Document只是Field的一個承載體,Field值即為要索引的內容,也是要搜索的內容。
是否分詞(tokenized)
是:作分詞處理,即將Field值進行分詞,分詞的目的是為了索引。
比如:商品名稱、商品簡介等,這些內容用戶要輸入關鍵字搜索,由於搜索的內容格式大、內容多需要分詞后將語匯單元索引。
否:不作分詞處理
比如:商品id、訂單號、身份證號等
是否索引(indexed)
是:進行索引。將Field分詞后的詞或整個Field值進行索引,索引的目的是為了搜索。
比如:商品名稱、商品簡介分析后進行索引,訂單號、身份證號不用分析但也要索引,這些將來都要作為查詢條件。
否:不索引。該域的內容無法搜索到
比如:商品id、文件路徑、圖片路徑等,不用作為查詢條件的不用索引。
是否存儲(stored)
是:將Field值存儲在文檔中,存儲在文檔中的Field才可以從Document中獲取。
比如:商品名稱、訂單號,凡是將來要從Document中獲取的Field都要存儲。
否:不存儲Field值,不存儲的Field無法通過Document獲取
比如:商品簡介,內容較大不用存儲。如果要向用戶展示商品簡介可以從系統的關系數據庫中獲取商品簡介。
4.索引維護
4.1添加索引
參考上邊案例代碼
4.2 刪除索引
增刪改操作,都是需要通過IndexWriter對象來操作,Term是索引域中最小的單位。根據條件刪除時,建議根據唯一鍵來進行刪除。在solr中就是根據ID來進行刪除和修改操作的。
4.2.1 根據條件刪除
//刪除fileName為albert的索引
@Test
public void testDelete() throws Exception {
IndexWriter indexWriter = getIndexWriter();
Query query = new TermQuery(new Term("fileName","albert"));
indexWriter.deleteDocuments(query);
indexWriter.close();
}
4.2.2 刪除全部
//刪除全部索引
@Test
public void testAllDelete() throws Exception {
// 第一步:創建一個indexwriter對象。
Directory directory = FSDirectory.open(Paths.get("D:\\temp\\index"));
// 官方推薦
Analyzer analyzer = new StandardAnalyzer();
IndexWriterConfig config = new IndexWriterConfig(analyzer);
IndexWriter indexWriter = new IndexWriter(directory, config);
indexWriter.deleteAll();
indexWriter.close();
}
4.3 修改索引
//修改索引,把fileName為AlbertLucene.text的索引進行修改
@Test
public void testUpdate() throws Exception {
Directory directory = FSDirectory.open(Paths.get("D:\\temp\\index"));
Analyzer analyzer = new StandardAnalyzer();
IndexWriterConfig config = new IndexWriterConfig(analyzer);
IndexWriter indexWriter = new IndexWriter(directory, config);
Document doc = new Document();
doc.add(new TextField("fileN", "testFileName111", Field.Store.YES));
doc.add(new TextField("fileC", "testFileContent111", Field.Store.YES));
indexWriter.updateDocument(new Term("fileName","AlbertLucene.text"), doc);
indexWriter.close();
}
5.搜索
5.1創建查詢對象的兩種方式
對要搜索的信息創建Query查詢對象,Lucene會根據Query查詢對象生成最終的查詢語法。類似關系數據庫Sql語法一樣,Lucene也有自己的查詢語法,比如:“name:lucene”表示查詢Field的name為“lucene”的文檔信息。
可通過兩種方法創建查詢對象:
1)使用Lucene提供Query子類
Query是一個抽象類,lucene提供了很多查詢對象,比如TermQuery項精確查詢,TermRangeQuery,范圍查詢, BooleanQuery 組合查詢等。
如下代碼:
Query query = new TermQuery(new Term("name", "albert"));
2)使用QueryParse解析查詢表達式
QueryParser會將用戶輸入的查詢表達式解析成Query對象實例。(QueryParser、MultiFieldQueryParser)
如下代碼:
QueryParser queryParser = new QueryParser("name", new IKAnalyzer());
Query query = queryParser.parse("name:albert");
5.2 通過Query子類來創建查詢對象
5.2.1 TermQuery
精確的詞項查詢,查看上邊的案例。
5.2.2 查詢所有
//創建IndexReader和IndexSearcher
public IndexSearcher getIndexSearcher() throws Exception{
// 第一步:創建一個Directory對象,也就是索引庫存放的位置。
Directory directory = FSDirectory.open(Paths.get("D:\\temp\\index"));
// 第二步:創建一個indexReader對象,需要指定Directory對象。
IndexReader indexReader = DirectoryReader.open(directory);
// 第三步:創建一個indexsearcher對象,需要指定IndexReader對象
return new IndexSearcher(indexReader);
}
//執行查詢的結果
public void printResult(IndexSearcher indexSearcher,Query query)throws Exception{
// 第五步:執行查詢。
TopDocs topDocs = indexSearcher.search(query, 10);
// 第六步:返回查詢結果。遍歷查詢結果並輸出。
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
int doc = scoreDoc.doc;
Document document = indexSearcher.doc(doc);
// 文件名稱
String fileName = document.get("fileName");
System.out.println(fileName);
// 文件內容
String fileContent = document.get("fileContent");
System.out.println(fileContent);
// 文件路徑
String filePath = document.get("filePath");
System.out.println(filePath);
System.out.println("------------");
}
}
//查詢所有
@Test
public void testMatchAllDocsQuery() throws Exception {
IndexSearcher indexSearcher = getIndexSearcher();
Query query = new MatchAllDocsQuery();
System.out.println(query);
printResult(indexSearcher, query);
//關閉資源
indexSearcher.getIndexReader().close();
}
5.2.3 根據文件大小范圍進行查詢
//根據文件大小范圍查詢
@Test
public void testRangeQuery() throws Exception {
IndexSearcher indexSearcher = getIndexSearcher();
Query query = LongPoint.newRangeQuery("fileSize", 0L, 200L);
System.out.println(query);
printResult(indexSearcher, query);
//關閉資源
indexSearcher.getIndexReader().close();
}
5.2.4 組合查詢(BooleanQuery)
//組合查詢條件
@Test
public void testBooleanQuery() throws Exception {
IndexSearcher indexSearcher = getIndexSearcher();
BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder();
Query query1 = new TermQuery(new Term("fileName","albert"));
Query query2 = new TermQuery(new Term("fileName","yang"));
booleanQuery.add(query1, BooleanClause.Occur.MUST);
booleanQuery.add(query2, BooleanClause.Occur.SHOULD);
System.out.println(booleanQuery);
printResult(indexSearcher, booleanQuery.build());
//關閉資源
indexSearcher.getIndexReader().close();
}
組合關系代表的意思如下:
1、MUST和MUST表示“與”的關系,即“並集”。
2、MUST和MUST_NOT前者包含后者不包含。
3、MUST_NOT和MUST_NOT沒意義
4、SHOULD與MUST表示MUST,SHOULD失去意義;
5、SHOUlD與MUST_NOT相當於MUST與MUST_NOT。
6、SHOULD與SHOULD表示“或”的概念。
5.3 通過QueryParser創建查詢對象
5.3.1 QueryParser
通過QueryParser來創建query對象,可以指定分詞器,搜索時的分詞器和創建該索引的分詞器一定要一致。
//條件解釋的對象查詢
@Test
public void testQueryParser() throws Exception {
IndexSearcher indexSearcher = getIndexSearcher();
//參數1: 默認查詢的域
//參數2:采用的分析器
QueryParser queryParser = new QueryParser("fileName",new StandardAnalyzer());
// *:* 域:值
Query query = queryParser.parse("fileName:lucene.txt OR fileContent:lucene is apache");
printResult(indexSearcher, query);
//關閉資源
indexSearcher.getIndexReader().close();
}
5.3.2 MultiFieldQueryParser
//多域查詢
@Test
public void testMultiFieldQueryParser() throws Exception {
IndexSearcher indexSearcher = getIndexSearcher();
String[] fields = {"fileName","fileContent"};
//參數1: 默認查詢的域
//參數2:采用的分析器
MultiFieldQueryParser queryParser = new MultiFieldQueryParser(fields,new StandardAnalyzer());
// *:* 域:值
Query query = queryParser.parse("lucene is apache");
System.out.println(query);
printResult(indexSearcher, query);
//關閉資源
indexSearcher.getIndexReader().close();
}
5.3.3 查詢語法
1、基礎的查詢語法,關鍵詞查詢:
域名+":"+搜索的關鍵字
例如:content:java
范圍查詢
域名+":"+[最小值 TO 最大值]
例如:size:[1 TO 1000]
組合條件查詢
Occur.MUST 查詢條件必須滿足,相當於and |
+(加號) |
Occur.SHOULD 查詢條件可選,相當於or
|
空(不用符號) |
Occur.MUST_NOT 查詢條件不能滿足,相當於not非 |
-(減號) |
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 AND 條件2
條件1 OR 條件2
條件1 NOT 條件2
5.3.4 TopDocs
Lucene搜索結果可通過TopDocs遍歷,TopDocs類提供了少量的屬性,如下:
方法或屬性 |
說明 |
totalHits |
匹配搜索條件的總記錄數 |
scoreDocs |
頂部匹配記錄 |
注意:
Search方法需要指定匹配記錄數量n:indexSearcher.search(query, n)
TopDocs.totalHits:是匹配索引庫中所有記錄的數量
TopDocs.scoreDocs:匹配相關度高的前邊記錄數組,scoreDocs的長度小於等於search方法指定的參數n
6. 相關度排序
6.1 什么是相關度排序
相關度排序就是查詢關鍵字與查詢結果的匹配相關度。匹配越高的越靠前。Lucene是通過打分來進行相關度排序的。
打分分兩步:
- 根據詞計算詞的權重
- 根據詞的權重進行打分
詞的權重:詞指的就是term。也就是說一個term對一個文檔的重要性,就叫詞的權重。
影響詞的權重的方式有兩種:Tf和Df
詞在同一個文檔中出現的頻率, Tf越高,說明詞的權重越高
詞在多個文檔中出現的頻率,Df越高,說明詞的權重越低
以上是自然打分的規則。
6.2 設置boost值影響打分
Boost:加權值,默認是1.0f。設置加權值可以在創建索引時設置,也可以在查詢時設置。
Boost值是設置到Field域上的。
6.2.1 創建索引時設置boost值
// 文件內容
String file_content = FileUtils.readFileToString(file, "UTF-8");
Field fileContentField = new TextField("fileContent", "測試設置BOOST值 lucene", Field.Store.NO);
// 設置boost
fileContentField.setBoost(10.0f);
6.2.2 搜索時設置boost值
在MultiFieldQueryParser創建時設置boost值。
// 搜索時設置boost值
@Test
public void testMultiFieldQueryParser2() throws Exception {
IndexSearcher indexSearcher = getIndexSearcher();
String[] fields = {"fileName","fileContent"};
Map<String, Float> boosts = new HashMap<String, Float>();
boosts.put("fileName", 100f);
//參數1: 默認查詢的域
//參數2:采用的分析器
MultiFieldQueryParser queryParser = new MultiFieldQueryParser(fields,new StandardAnalyzer(), boosts);
// *:* 域:值
Query query = queryParser.parse("apache1.0 lucene");
System.out.println(query);
printResult(indexSearcher, query);
//關閉資源
indexSearcher.getIndexReader().close();
}
7 中文分詞器
7.1 什么是中文分詞器
對於英文,是按照空格和標點符號進行分詞的, 但是對於中文來說,這是不正確的,中文應該按照具體的詞來分,中文分詞就是將詞,切分成一個個有意義的詞。
比如:“我是中國人”,分詞:我、是、中國、中國人、國人。
7.2 Lucene自帶的中文分詞器
StandardAnalyzer:
單字分詞:就是按照中文一個字一個字地進行分詞。如:“我是中國人”,
效果:“我”、“是”、“中”、“國”、“人”。
CJKAnalyzer
二分法分詞:按兩個字進行切分。如:“我是中國人”,效果:“我是”、“是中”、“中國”“國人”。
顯然以上兩個分詞器很難滿足我們對於中文分詞的需求。
7.3 第三方中文分詞器
- mmseg4j:最新版已從 https://code.google.com/p/mmseg4j/ 移至 https://github.com/chenlb/mmseg4j-solr,在github中最新提交代碼是2014年6月,從09年~14年一共有:18個版本,也就是一年幾乎有3個大小版本,有較大的活躍度,用了mmseg算法。
- IK-analyzer: 最新版在https://code.google.com/p/ik-analyzer/上,從2006年12月推出1.0版開始, IKAnalyzer已經推出了4個大版本。最初,它是以開源項目Luence為應用主體的,結合詞典分詞和文法分析算法的中文分詞組件。從3.0版本開 始,IK發展為面向Java的公用分詞組件,獨立於Lucene項目,同時提供了對Lucene的默認優化實現。在2012版本中,IK實現了簡單的分詞 歧義排除算法,標志着IK分詞器從單純的詞典分詞向模擬語義分詞衍化。 2012年12月后沒有再更新。
- ansj_seg:最新版本在 https://github.com/NLPchina/ansj_seg tags僅有1.1版本,從2012年到2014年更新了大小6次,但是作者本人在2014年10月10日說明:“可能我以后沒有精力來維護ansj_seg了”,現在由”nlp_china”管理。2014年11月有更新。並未說明是否支持Lucene,是一個由CRF(條件隨機場)算法所做的分詞算法。
- Jcseg:最新版本在git.oschina.net/lionsoul/jcseg,支持Lucene 4.10,作者有較高的活躍度。利用mmseg算法。
7.4 Ikanalyzer
7.4.1 在項目中添加ikanalyzer的jar包
7.4.2 修改分詞器代碼
//Analyzer analyzer = new StandardAnalyzer();
Analyzer analyzer = new IKAnalyzer();
7.4.3 擴展中文詞庫
將以下文件拷貝到當前項目目錄下
從ikanalyzer包中拷貝配置文件到classpath下
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>IK Analyzer 擴展配置</comment>
<!-- 用戶可以在這里配置自己的擴展字典 -->
<entry key="ext_dict">dicdata/mydict.dic</entry>
<!-- 用戶可以在這里配置自己的擴展停用詞字典 -->
<entry key="ext_stopwords">dicdata/ext_stopword.dic</entry>
</properties>
如果想配置擴展詞和停用詞,就創建擴展詞的文件和停用詞的文件,文件的編碼要是utf-8。
注意:不要用記事本保存擴展詞文件和停用詞文件,這樣會導致格式中是含有bom。
本文內容較長,寫的時候花了不少時間,如果覺得不錯的話,歡迎點贊,評論,轉發支持。感謝