一、说明
在实际开发中,由于开启IndexReader
和IndexWriter
这两个对象很消耗资源,所以一般会使用单例,或者不关闭而使用一个池,对应于数据库的会话一样。
二、单例 IndexReader
这里我们还是拷贝工程lucene_index02
为lucene_index03
,在之前工程的基础上进行改进。
- 在
IndexUtil.java
类中我们声明一个IndexReader
的属性:
private static IndexReader reader = null;//声明一个IndexReader的属性
- 然后改进方法
IndexUtil()
:
public IndexUtil() {
try {
setDates();//设置日期
scores.put("qq.com", 2.0f);//如果是"qq.com"结尾的索引则让其权值为2.0,注意:默认是1.0
scores.put("sina.edu", 1.5f);
directory = FSDirectory.open(new File("E:/myeclipse/Lucene/index"));
reader = IndexReader.open(directory);//在工具一创建的时候就打开IndexReader
} catch (IOException e) {
e.printStackTrace();
}
}
在构造方法中我们实例化IndexReader
,这样就达到了单例的效果。
- 定义一个方法获取
IndexSearcher
类:
public IndexSearcher getSearcher(){
return new IndexSearcher(reader);
}
- 改进搜索方法:
public void search02(){
IndexReader reader;
try {
IndexSearcher searcher = getSearcher();
TermQuery query = new TermQuery(new Term("content", "like"));//搜索内容中含有like的
TopDocs tds = searcher.search(query, 10);
for(ScoreDoc sd : tds.scoreDocs){
Document doc = searcher.doc(sd.doc);
//这里我们获取权值getBoost()的时候发现都是1.0,这是因为这里是获取的一个document,和原来的没有关系。
//要想看其权值信息,可以使用luke工具
//而这里的日期需要我们转换成日期格式
System.out.println("id号:" + doc.get("id")
+ ",权值:"+ doc.getBoost()
+ ",名字:" + doc.get("name")
+ ",邮箱:" + doc.get("email")
+ ",附件条数:" +doc.get("attach")
+ ",日期:" + doc.get("date"));
searcher.close();//注意:这里不是关闭reader,而是关闭searcher
}
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
注意:这里我们就不要关闭reader
了,而是关闭searcher
,这样reader
就可以一直使用一个。
- 测试
@Test
public void testSearch02(){
IndexUtil util = new IndexUtil();
for(int i = 0; i < 5; i++){
util.search02();//这里我们让其执行5次
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
说明:这里我们在测试的时候先重新生成索引,然后使用上面的测试方法测试,我们每个10秒执行一次搜索,在搜索的过程中,我们再运行删除方法,删除id
为1的索引,但是我们发现即使删除了,索引却并没有变少,这是因为我们一直使用的是同一个reader
。但是当我们关闭此测试方法,再使用之前的搜索方法testSearch01
测试一下,发现索引少了一条,针对此问题,我们继续改进。
- 改进
getSearcher
方法
public IndexSearcher getSearcher(){
try {
if(reader == null){
reader = IndexReader.open(directory);
}else{
//如果reader发生改变则返回旧的reader,否则创建一个新的reader
IndexReader tr = IndexReader.openIfChanged(reader);
if(tr != null){
reader.close();//关闭旧的reader
reader = tr;
}
}
return new IndexSearcher(reader);
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
说明:这里我们使用方法openIfChanged
,此方法在reader
发生变化的时候就会返回新的reader
,否则返回旧的reader
。但是一定注意:我们产生新的reader
之后需要将旧的reader
关闭。之后我们在运行上面的测试方法过程中使用删除方法会发现索引可以实时更新了。
三、单例 IndexWriter
对于也是类似,我们拿删除方法来说明:
public void delete(){
IndexWriter writer = null;
try {
writer = new IndexWriter(directory, new IndexWriterConfig(Version.LUCENE_35, new StandardAnalyzer(Version.LUCENE_35)));
//参数可以是一个选项,可以是一个query,也可以是一个term,term是一个精确查找的值
//这里我们测试此方法之后再次执行搜索方法,发现文档数numDocs还有5个,比之前少了一个,但是maxDoc还是6个
//在我们的索引目录中发现出现了一个delete的文件。这里的删除就像一个回收站一样,是可以恢复的
writer.deleteDocuments(new Term("id", "1"));//这里表示删除索引为1的id
writer.commit();
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (LockObtainFailedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}/*finally{
if(writer != null){
try {
writer.close();
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}*/
}
这里我们可以不关闭IndexWriter
,但是如果这样数据就不能实时更新,此时我们可以像数据库那样,提交一下即可。
最后:这里只是简单演示的了一下单例,但是例子并不完善,比如这里IndexReader
确实是在构造函数中实例化,是单例的,但是我们在对创建索引方法测试的时候也会创建一个IndexReader
对象,但是最后却没有关闭,不知道是否是这个原因导致我们测试创建对象方法的时候出现异常,留待后面解决。
具体的单例实现请参考:http://www.cnblogs.com/likehua/archive/2012/02/20/2359087.html
。