前面介绍了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的源代码进行适配。