由于系统搜索速度一直不理想,今天决定用Lucene进行 索引。然后全表检索 核心代码如下
package net.java2000.forum.util;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.sql.DataSource;
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.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Searcher;
import org.apache.lucene.search.WildcardQuery;
/**
*
* @author 赵学庆 www.java2000.net
*
*/
public class Lucene {
private static final String indexPath = "d:/index/www.java2000.net";
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// create();
Lucene l = new Lucene();
System.out.println("模糊搜索/n------------------------------");
l.seacherWildcard("域名");
System.out.println("索引搜索/n------------------------------");
l.seacherIndex("域名 AND 系统");
}
public static void rebuildAll() {
synchronized (indexPath) {
Lucene l = new Lucene();
DataSource ds = (DataSource) Factory.getBean("dataSource");
Connection con = null;
Statement stat = null;
ResultSet rs = null;
try {
con = ds.getConnection();
stat = con.createStatement();
rs = stat.executeQuery("select id,subject,content from t_post");
if (rs != null) {
l.Index(rs);
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if (rs != null) {
try {
rs.close();
} catch (Exception ex) {}
}
if (stat != null) {
try {
stat.close();
} catch (Exception ex) {}
}
if (con != null) {
try {
con.close();
} catch (Exception ex) {}
}
}
}
}
public synchronized Analyzer getAnalyzer() {
return new StandardAnalyzer();
}
private synchronized void Index(ResultSet rs) {// 通过结果集就可以获得数据源了
try {
IndexWriter writer = new IndexWriter(indexPath, getAnalyzer(), true);
Date start = new Date();
while (rs.next()) {
Document doc = new Document();// 一个文档相当与表的一条记录
doc.add(new Field("id", rs.getString("id"), Field.Store.YES, Field.Index.UN_TOKENIZED));// 字段id放的是数据库表中的id,lucene的一条记录的一个字段下的数据可以放多个值,这点与数据库表不同
doc.add(new Field("subject", rs.getString("subject"), Field.Store.YES, Field.Index.TOKENIZED));
doc.add(new Field("content", rs.getString("content"), Field.Store.YES, Field.Index.TOKENIZED));
writer.addDocument(doc);
}
writer.optimize();// 优化
writer.close();// 一定要关闭,否则不能把内存中的数据写到文件
Date end = new Date();
System.out.println("重建索引成功!!!!" + "用时" + (end.getTime() - start.getTime()) + "毫秒");
} catch (IOException e) {
System.out.println(e);
} catch (SQLException e) {
System.out.println(e);
}
}
public void IndexSingle(long id, String subject, String content) {// 通过结果集就可以获得数据源了
synchronized (indexPath) {
try {
IndexWriter writer = new IndexWriter(indexPath, getAnalyzer(), false);
Date start = new Date();
Document doc = new Document();// 一个文档相当与表的一条记录
doc.add(new Field("id", Long.toString(id), Field.Store.YES, Field.Index.UN_TOKENIZED));// 字段id放的是数据库表中的id,lucene的一条记录的一个字段下的数据可以放多个值,这点与数据库表不同
doc.add(new Field("subject", subject, Field.Store.YES, Field.Index.TOKENIZED));
doc.add(new Field("content", content, Field.Store.YES, Field.Index.TOKENIZED));
writer.addDocument(doc);
// writer.optimize();// 优化
writer.close();// 一定要关闭,否则不能把内存中的数据写到文件
Date end = new Date();
System.out.println("索引建立成功!!!!" + "用时" + (end.getTime() - start.getTime()) + "毫秒");
} catch (IOException e) {
System.out.println(e);
}
}
}
public Hits seacherWildcard(String queryString) {// 根据关键字搜索
Hits hits = null;
try {
Query query = new WildcardQuery(new Term("subject", "*" + queryString + "*"));// 模糊查询一下
Searcher searcher = new IndexSearcher(indexPath);
hits = searcher.search(query);
for (int i = 0; i < hits.length(); i++) {
Document doc = hits.doc(i);
}
searcher.close();
} catch (Exception e) {
System.out.print(e);
}
return hits;
}
public List
seacherIndex(String queryString) {// 根据关键字搜索
System.out.println(queryString);
Hits hits = null;
try {
IndexSearcher isearcher = new IndexSearcher(indexPath);
// Parse a simple query that searches for "text":
MultiFieldQueryParser parser = new MultiFieldQueryParser(new String[]{"subject","content"},getAnalyzer());
// QueryParser parser = new QueryParser("subject", getAnalyzer());
Query query = parser.parse(queryString);
hits = isearcher.search(query);
List
rtn = new ArrayList
();
for (int i = 0; i < hits.length(); i++) {
Document doc = hits.doc(i);
rtn.add(Long.parseLong(doc.get("id").trim()));
}
isearcher.close();
return rtn;
} catch (Exception e) {
System.out.print(e);
return null;
}
}
}
说明
1 StandardAnalyzer 支持中文,所以不用再找其它的了
2 Index方法里
IndexWriter writer = new IndexWriter(indexPath, getAnalyzer(), true);
最后面的这个true,是重新建立索引。 在第一运行时,必须重新所有所有。
Field.Index.UN_TOKENIZED 是不进行单词拆分 Field.Index.TOKENIZED 是进行拆分,
3 seacherWildcard 里面可以进行模糊搜索
4 seacherIndex 里面 MultiFieldQueryParser 支持多个字段的联合查询
5 我测试的结果,我的网站的11M的索引(应该算很一般的数据量,6000多个帖子),检索竟然几乎不需要时间?或者10毫秒以内。
6 l.seacherIndex("域名 AND 系统"); AND OR NOT 用在单词之间进行组合查询, + 代表 AND -代表 NOT
7 每次有新的数据时,比如发言或者回复则调用
public void IndexSingle(long id, String subject, String content)
进行索引更新,注意里面的参数是false;
IndexWriter writer = new IndexWriter(indexPath, getAnalyzer(), false);
就这些,第一次使用,效果很好。
<script type="text/javascript"> </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>