Lucene全文檢索引擎工具包使用方法總結
Lucene是apache軟體基金會4 jakarta專案組的一個子專案,是一個開放原始碼的全文檢索引擎工具包,但它不是一個完整的全文檢索引擎,而是一個全文檢索引擎的架構,提供了完整的查詢引擎和索引引擎,部分文字分析引擎(英文與德文兩種西方語言)。Lucene的目的是為軟體開發人員提供一個簡單易用的工具包,以方便的在目標系統中實現全文檢索的功能,或者是以此為基礎建立起完整的全文檢索引擎。Lucene是一套用於全文檢索和搜尋的開源程式庫,由Apache軟體基金會支援和提供。Lucene提供了一個簡單卻強大的應用程式介面,能夠做全文索引和搜尋。在Java開發環境裡Lucene是一個成熟的免費開源工具。就其本身而言,Lucene是當前以及最近幾年最受歡迎的免費Java資訊檢索程式庫。人們經常提到資訊檢索程式庫,雖然與搜尋引擎有關,但不應該將資訊檢索程式庫與
在使用Lucene時有一些注意事項:
1.由於Lucene目前已經更新至6.6.0版本,所以對於JDK版本會有一些要求,建議使用低版本JDK的同學相應下載低版本Lucene。
2.Lucene版本更新迅速,所以各版本之間部分程式碼實現會有差異,所以在實現相關功能時,注意版本問題。
1.Lucene全文檢索流程:
通過對於原資料的採集,並建立索引(Lucene的核心),將索引存放至索引庫中,使用者進行查詢時,直接通過索引查詢,從而實現了Lucene搜尋的快捷,這也是我們學習使用Lucene的原因。相比較傳統方式搜尋,通過索引,無需依次掃描大量檔案,提升了搜尋速度。
2.Lucene下載: 點選開啟連結
3.Lucene環境搭建:
3.1環境準備:
JDK:1.7
lucene:4.10(最新為6.6.0)
MySql:提供原資料
4.Eclipse建立工程,匯入相關jar包:Mysql驅動包,Analysis分詞包,Core核心包,QueryParser包
注意:jUnit包為專案單元測試包,具體使用方法,大家可以百度Eclipse中如何使用jUnit,這裡不做過多介紹。
5.通過java程式實現資料採集與索引建立:
5.1建立Book.java實體類:
public class Book { // 圖書ID private Integer id; // 圖書名稱 private String name; // 圖書價格 private Float price; // 圖書描述 private String description; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Float getPrice() { return price; } public void setPrice(Float price) { this.price = price; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } }
5.2建立資料庫使用物件BookDao.java:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import com.jsoft.vo.Book;
public class BookDao{
public List<Book> queryBookList() throws Exception {
// 資料庫連結
Connection connection = null;
// 預編譯statement
PreparedStatement preparedStatement = null;
// 結果集
ResultSet resultSet = null;
// 圖書列表
List<Book> list = new ArrayList<Book>();
try {
// 載入資料庫驅動
Class.forName("com.mysql.jdbc.Driver");
// 連線資料庫
connection = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/book", "root", "root");
// SQL語句
String sql = "SELECT * FROM book";
// 建立preparedStatement
preparedStatement = connection.prepareStatement(sql);
// 獲取結果集
resultSet = preparedStatement.executeQuery();
// 結果集解析
while (resultSet.next()) {
Book book = new Book();
book.setId(resultSet.getInt("id"));
book.setName(resultSet.getString("name"));
book.setPrice(resultSet.getFloat("price"));
book.setDescription(resultSet.getString("description"));
list.add(book);
}
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
}
5.3建立索引管理類IndexManager,並進行索引建立:
public class IndexManager {
@Test
public void createIndex() throws Exception {
// 採集資料
BookDao dao = new BookDao();
List<Book> list = dao.queryBookList();
// 將採集到的資料封裝到Document物件中
List<Document> docList = new ArrayList<>();
Document document;
for (Book book : list) {
document = new Document();
// store:如果是yes,則說明儲存到文件域中
// 圖書ID
//不分詞、索引、儲存
Field id = new StringField("id", book.getId().toString(), Store.YES);
// 圖書名稱
//分詞、索引、儲存
Field name = new TextField("name", book.getName(), Store.YES);
// 圖書價格
//分詞、索引、儲存
Field price = new FloatField("price", book.getPrice(),Store.YES);
// 圖書描述
//不分詞、索引、不儲存
Field description = new TextField("description",book.getDescription(), Store.YES);
// 將field域設定到Document物件中
document.add(id);
document.add(name);
document.add(price);
document.add(description);
docList.add(document);
}
// 建立分詞器,標準分詞器
Analyzer analyzer = new StandardAnalyzer();
// 建立IndexWriter
IndexWriterConfig cfg = new IndexWriterConfig(Version.LUCENE_4_10_3,
analyzer);
// 指定索引庫的地址
File indexFile = new File("D:/LueneIndex/");
Directory directory = FSDirectory.open(indexFile);
IndexWriter writer = new IndexWriter(directory, cfg);
// 通過IndexWriter物件將Document寫入到索引庫中
for (Document doc : docList) {
writer.addDocument(doc);
}
// 關閉writer
writer.close();
}
}
5.4通過jUnit進行單元測試,測試結果如下:
5.5檢視自定義索引庫地址,發現有新建立檔案:
通過上述步驟,Lucene架構索引即建立成功。
6.建立IndexSearch進行搜尋:
public class IndexSearch {
@Test
public void indexSearch() throws Exception {
// 建立query物件
// 使用QueryParser搜尋時,需要指定分詞器,搜尋時的分詞器要和索引時的分詞器一致
// 第一個引數:預設搜尋的域的名稱
QueryParser parser = new QueryParser("description",new StandardAnalyzer());
// 通過queryparser來建立query物件
// 引數:輸入的lucene的查詢語句(關鍵字一定要大寫)
Query query = parser.parse("description:java OR lucene");
// 建立IndexSearcher
// 指定索引庫的地址
File indexFile = new File("D:/LueneIndex/");
Directory directory = FSDirectory.open(indexFile);
IndexReader reader = DirectoryReader.open(directory);
IndexSearcher searcher = new IndexSearcher(reader);
// 通過searcher來搜尋索引庫
// 第二個引數:指定需要顯示的頂部記錄的N條
TopDocs topDocs = searcher.search(query, 10);
// 根據查詢條件匹配出的記錄總數
int count = topDocs.totalHits;
System.out.println("匹配出的記錄總數:" + count);
// 根據查詢條件匹配出的記錄
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
// 獲取文件的ID
int docId = scoreDoc.doc;
// 通過ID獲取文件
Document doc = searcher.doc(docId);
System.out.println("商品ID:" + doc.get("id"));
System.out.println("商品名稱:" + doc.get("name"));
System.out.println("商品價格:" + doc.get("price"));
// System.out.println("商品描述:" + doc.get("description"));
System.out.println("==========================");
}
// 關閉資源
reader.close();
}
}
結果如下:
7.進行Lecene索引維護:
7.1.根據id刪除索引:
@Test
public void deleteIndex() throws Exception {
// 建立分詞器,標準分詞器
Analyzer analyzer = new StandardAnalyzer();
// 建立IndexWriter
IndexWriterConfig cfg = new IndexWriterConfig(Version.LUCENE_4_10_3,analyzer);
Directory directory = FSDirectory.open(new File("D:/LueneIndex/"));
// 建立IndexWriter
IndexWriter writer = new IndexWriter(directory, cfg);
// Terms
writer.deleteDocuments(new Term("id", "1"));
// 刪除全部
//writer.deleteAll();
writer.close();
}
7.2根據name更新索引:
@Test
public void updateIndex() throws Exception {
// 建立分詞器,標準分詞器
Analyzer analyzer = new StandardAnalyzer();
// 建立IndexWriter
IndexWriterConfig cfg = new IndexWriterConfig(Version.LUCENE_4_10_3, analyzer);
Directory directory = FSDirectory.open(new File("D:/LueneIndex/"));
// 建立IndexWriter
IndexWriter writer = new IndexWriter(directory, cfg);
// 第一個引數:指定查詢條件
// 第二個引數:修改之後的物件
// 修改時如果根據查詢條件,可以查詢出結果,則將以前的刪掉,然後覆蓋新的Document物件,如果沒有查詢出結果,則新增一個Document
// 修改流程即:先查詢,再刪除,在新增
Document doc = new Document();
doc.add(new TextField("name", "Python", Store.YES));
writer.updateDocument(new Term("name", "C"), doc);
writer.close();
}
8.Lucene進行條件搜尋:
8.1建立QuerySearch類
public class QuerySearch {
private void search(Query query) {
// 建立QuerySearcher
// 指定索引庫的地址
try {
File indexFile = new File("D:/LueneIndex/");
Directory directory = FSDirectory.open(indexFile);
IndexReader reader = DirectoryReader.open(directory);
IndexSearcher searcher = new IndexSearcher(reader);
// 通過searcher搜尋索引庫
// 第二個引數:指定需要顯示的頂部記錄的N條
TopDocs topDocs = searcher.search(query, 10);
// 根據查詢條件匹配出的記錄總數
int count = topDocs.totalHits;
System.out.println("匹配出的記錄總數:" + count);
// 根據查詢條件匹配出的記錄
//TopDocs.totalHits:是匹配索引庫中所有記錄的數量
//TopDocs.scoreDocs:匹配相關度高的前邊記錄陣列
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
// 獲取文件的ID
int docId = scoreDoc.doc;
Document doc = searcher.doc(docId);
System.out.println("商品ID:" + doc.get("id"));
System.out.println("商品名稱:" + doc.get("name"));
System.out.println("商品價格:" + doc.get("price"));
// System.out.println("商品描述:" + doc.get("description"));
System.out.println("==========================");
}
// 關閉資源
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
8.2通過以下兩種方式建立查詢物件:
(1)通過Query子類來建立查詢物件
Query子類常用的有:TermQuery、NumericRangeQuery、BooleanQuery
不能輸入lucene的查詢語法,不需要指定分詞器
(2)通過QueryParser來建立查詢物件(常用)
QueryParser、MultiFieldQueryParser
可以輸入lucene的查詢語法、可以指定分詞器
8.2.1 TermQuery:
// 建立TermQuery物件
@Test
public void termQuery() {
Query query = new TermQuery(new Term("description", "java"));
search(query);
}
結果如下:
8.2.2 NumericRangeQuery:
@Test
public void numericRangeQuery() {
// 建立NumericRangeQuery物件
// 引數:域的名稱、最小值、最大值、是否包含最小值、是否包含最大值
Query query = NumericRangeQuery.newFloatRange("price", 55f, 60f, true,false);
search(query);
}
結果如下:
8.2.3 BooleanQuery:
@Test
public void booleanQuery() {
// 建立BooleanQuery
BooleanQuery query = new BooleanQuery();
// 建立TermQuery物件
Query q1 = new TermQuery(new Term("description", "oracle"));
// 建立NumericRangeQuery物件
// 引數:域的名稱、最小值、最大值、是否包含最小值、是否包含最大值
Query q2 = NumericRangeQuery.newFloatRange("price", 15f, 60f, true,false);
// 組合關係代表的意思如下:
// 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表示“或”的概念。
// Occur.MUST 查詢條件必須滿足,相當於and (+)
// Occur.SHOULD 查詢條件可選,相當於or (空)
// Occur.MUST_NOT 查詢條件不能滿足,相當於not (-)
query.add(q1, Occur.MUST);
query.add(q2, Occur.MUST);
search(query);
}
結果如下:
8.2.4 MultiFieldQueryParser:
@Test
public void multiFieldQueryParser() throws Exception {
// 預設搜尋的多個域
String[] fields = { "name", "description" };
Analyzer analyzer = new StandardAnalyzer();
MultiFieldQueryParser parser = new MultiFieldQueryParser(fields,analyzer);
Query query = parser.parse("paradigm");
//等同於
// Query query = parser.parse("name:paradigm OR description:paradigm");
search(query);
}
結果如下:
9.分詞器:
由於Lucene標準分詞器不支援漢語分詞,所以建議使用第三方分詞器。
10.Lucene搜尋相關度排序:boost值設定
10.1 建立索引時設定boost值:
10.2 搜尋時設定Boost值