前面介紹了Lucene對於文件怎麼進行索引和搜索,這次就來說說lucene怎麼對數據庫進行索引和搜索。衆所周知,數據庫數據可以通過SQL來查詢,Java EE系統中也可以通過Hbiernate,Ibatis,Spring JdbcTemplate或者直接通過JDBC API來訪問,但是當數據庫很大時,執行SQL語句的代價就很大,速度也會很忙,尤其是在大量用戶高併發量訪問的情況下尤爲明顯,就算是給數據庫創建索引後提高也有限,而且並不是所有的數據庫都支持全文檢索。所以通過lucene創建數據庫索引能夠大大提高系統的性能,其優點主要體現在以下幾個方面:
- 通過創建唯一索引,能保證數據的唯一性;
- 大大提高了數據檢索的速度,這是創建數據庫索引的重要原因;
- 加快了表與表之間的鏈接,對實現數據參考完整性具有重大的意義;
- 通過創建索引,在查詢過程中採用優化策略,能夠提高系統的性能;
- 使用分組和排序字句進行數據檢索時,通過創建索引能夠減少查詢中分組和排序的時間;
- 可特別針對經常需要檢索的列創建索引,能夠減少盲目搜索的時間,大大提高搜索效率;
下面看一下對於數據庫創建索引的例子,我有一個mysql數據庫test,其中有一張University的表,記錄了大學信息:
package com.seiya;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.wltea.analyzer.lucene.IKAnalyzer;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
/**
* Created by dell on 2015/8/10.
* 對數據庫數據做索引
*/
public class DataBaseIndex {
public static final String INDEXPATH = "D://indexFile//dbIndex";
private static String url="jdbc:mysql://localhost:3306/test?user=root&password=root&useUnicode=true&characterEncoding=UTF-8";
public static boolean createIndex() {
try {
Directory dir = FSDirectory.open(Paths.get(INDEXPATH)); // 使用了nio,存儲索引的路徑
Analyzer analyzer = new StandardAnalyzer(); // 無參構造函數
IndexWriterConfig iwc = new IndexWriterConfig(analyzer); // 新的IndexWriter配置類
iwc.setOpenMode(IndexWriterConfig.OpenMode.CREATE); // 創建模式打開
//iwc.setRAMBufferSizeMB(256.0); // 設置內存緩存的大小,提高索引效率,不過如果修改過大的值,需要修改JVM的內存值
IndexWriter writer = new IndexWriter(dir, iwc); // 創建IndexWriter
Class.forName("com.mysql.jdbc.Driver");
Connection con = DriverManager.getConnection(url);
Statement stmt = con.createStatement();
String query = "select * from University";
System.out.println("query==="+query);
ResultSet rs=stmt.executeQuery(query);
while(rs.next()) {
Document doc = new Document();
String cName = rs.getString("UniversityName");
System.out.println("cName--------------->"+cName);
String eName = rs.getString("E_Name");
System.out.println("eName--------------->"+eName);
String type = rs.getString("Type");
String location = rs.getString("Location");
doc.add(new TextField("cName",cName,Field.Store.YES));
doc.add(new TextField("eName",eName,Field.Store.YES)); // 做analyze
doc.add(new StringField("type",type,Field.Store.YES)); // 不做analyze
doc.add(new StringField("location",location,Field.Store.YES));
writer.addDocument(doc);
}
writer.close();
}catch(Exception ex) {
ex.printStackTrace();
}
return true;
}
public static void main(String[] args) {
createIndex();
}
}
這裏要注意的是,對於lucene5,StringField是不做analyze的,TextField是做了分詞的。
再來看一下查詢數據庫數據的例子:
package com.seiya;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.wltea.analyzer.lucene.IKAnalyzer;
import java.io.IOException;
import java.nio.file.Paths;
/**
* Created by dell on 2015/8/11.
*/
public class DataBaseSearcher {
public static final String INDEXPATH = "D://indexFile//dbIndex";
private static int TOP_NUM = 100; // 顯示記錄數
public static void searchData(String queryStr) {
Directory dir = null;
try {
dir = FSDirectory.open(Paths.get(INDEXPATH));
IndexReader reader = DirectoryReader.open(dir);
IndexSearcher searcher = new IndexSearcher(reader);
Analyzer analyzer = new StandardAnalyzer();
String fieldString = "cName";
QueryParser parser = new QueryParser(fieldString, analyzer);
parser.setDefaultOperator(QueryParser.AND_OPERATOR);
Query query = parser.parse(queryStr);
TopDocs hits = searcher.search(query,5); // 查找操作
for(ScoreDoc scoreDoc : hits.scoreDocs) {
Document doc = searcher.doc(scoreDoc.doc); // 根據文檔打分得到文檔的內容
System.out.println(doc.get("cName")); // 找到文件後,輸出路徑
}
} catch (IOException e) {
e.printStackTrace();
} catch (ParseException ex) {
ex.printStackTrace();
}
}
public static void main(String[] args) {
searchData("大學");
}
}
由於IKAnalyzer目前還沒有支持lucene5,所以對於需要用到IK之類的中文分詞器的程序,最好還是使用穩定的Lucene4,當然也可以自己修改IKAnalyzer的源代碼進行適配。