1. 程式人生 > >Lucene全文檢索引擎工具包使用方法總結

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值


總結:以上是Lucene使用方法的簡單總結,歡迎批評指正。