l 通过 IndexWriter 类读取索引的相关信息,将其显示出来
l 通过 IndexReader 及其子类读取读取索引的相关信息,将其显示出来
l 通过可视化工具(如 Luke 和 Limo )查看索引相关信息
1.1 使用 IndexWriter 类读取索引相关信息
该类不是用于查看索引的类,使用它不是最佳方法
String indexPath = "index";
//IndexWriter
IndexW riter writer = new IndexWriter(indexPath,new StandardAnalyzer());
String d = writer.getDirectory().toString();
System.out.println(d);
double t = writer.getDefaultWriteLockTimeout();
System.out.println(t);
int n = writer.numRamDocs();
System.out.println(n);
int c = writer.docCount();
System.out.println(c);
1.2 使用 IndexReader 及其子类读取索引的相关信息
IndexReader 类是专门用于读取索引的类,它是一个抽象类,含有 FilterIndexReader 、 MultiReader 、 ParallelReader 等子类
IndexReader 类通过静态方法 open(String indexPath) 可以得到其子类。
使用 IndexReader 类可以获得某个索引的详细信息,如指定路径的索引是否存在,索引中包含文档个数,索引中包含词条情况,索引是否经过优化等。
1.3 通过可视化工具(如 Luke 和 Limo )查看索引相关信息
示例一:是否存在、最后修改时间、路径信息、含有文档数量
String indexPath = "index";
// 建立 IndexReader
IndexReader ir = IndexReader.open(indexPath);
//indexExists
if(ir.indexExists(indexPath))
{
System.out.println(" 该索引存在! ");
}
else
{
System.out.println(" 该索引不存在! ");
}
//lastModified
Date d = new Date(ir.lastModified(indexPath));
System.out.println(d);
//isCurrent()
System.out.println(ir.isCurrent());
//isOptimized() 索引是否优化
System.out.println(ir.isOptimized());
//directory() 索引路径
System.out.println(ir.directory());
//numDocs() 索引中的文档数量
int num = ir.numDocs();
System.out.println(num);
// 关闭 IndexReader
ir.close();
示例二:读取指定文档、指定字段
String indexPath = "index";
// 建立 IndexReader
IndexReader ir = IndexReader.open(indexPath);
//numDocs() 索引中的文档数量
int num = ir.numDocs();
//Document 文档对象信息,各个字段
Document doc = ir.document(44);
System.out.println(doc);
if(num>10)
{
for(int i=0;i<10;i++)
{
doc = ir.document(i);
Field field = doc.getField("filename"); // 特定字段
System.out.println(field.stringValue());
}
}
// 关闭 IndexReader
ir.close();
示例三:词条、过滤词条 ReaderIndex3
String indexPath = "index";
// 建立 IndexReader
IndexReader ir = IndexReader.open(indexPath);
//terms() 索引中的所有词条
int i = 0;
TermEnum te = ir.terms(); // 不带过滤
while(te.next())
{
Term t = te.term();
i++;
if(i<10)
{
System.out.println(t);
}
}
te.close();
// 词条总数
System.out.println(i);
// 符合 "text: 鼻祖 " 的词条
i = 0;
Term t = new Term("text"," 鼻祖 "); // 过滤
te = ir.terms(t);
while(te.next())
{
t = te.term();
i++;
}
te.close();
System.out.println(i);
//"text: 鼻祖 " 词条总数
System.out.println(ir.docFreq(t));
TermDocs td = ir.termDocs(t); // 输出含有词条的文档
while(td.next())
{
int id = td.doc();
Document doc = ir.document(id);
Field field = doc.getField("filename");
System.out.println(field.stringValue());
}
// 关闭 IndexReader
ir.close();
通过 IndexReader 和 IndexModifier 两个类删除文档。
2.1 IndexReader
删除文档需要根据文档序号或词项进行
a) 删除指定序号的文档
IndexReader 的 deleteDocument(int id) 方法
IndexReader ir = IndexReader.open(indexPath);
ir.deleteDocument(0);
执行后,会发现索引目录下生成了一个扩展名 del 的新文件,存储着被删除文档的信息。这时候,文档并没有真正从索引中删除,只是做了已经删除的标记,从而不能参与检索。
b) 恢复被删除的文档
undeleteAll 方法可以回复所有逻辑删除的文档。
ir.undeleteAll();
c) 物理删除文档
两个步骤:
(1) 使用 IndexReader 作删除标记
(2) 执行 IndexWriter 的 optimize 方法
/ / undeleteIndex1.java
String indexPath = "index";
IndexReader ir = IndexReader.open(indexPath); // 建立 IndexReader
ir.deleteDocument(0); // 逻辑删除
ir.close(); // 关闭 IndexReader
// 建立 IndexWriter
IndexWriter iw = new IndexWriter(indexPath, new StandardAnalyzer());
iw.optimize(); // 执行 优化方法
iw.close(); // 关闭 IndexWriter
d) 批量删除文档
( 1 ) IndexReader 按序号批量删除
( 2 ) IndexReader 按词项批量删除
在倒排索引中,索引是以文档编号和短语为标志排列的,所以 Lucene 实现了按照 Term 删除文档,也是逻辑删除。 deleteIndex3.java
IndexReader ir = IndexReader.open(indexPath);
Term t = new Term("filename","lucene"); // 词项
int i = ir.deleteDocuments(t); // 批量删除的文档个数
2.2 IndexModifier
IndexModifier 是专门用于全面操作索引的类,集成了 IndexWriter 和 IndexReader 两个类的方法,既可以建立索引、查看索引、修改索引。
String indexPath = "im";
IndexModifier indexModifier = new IndexModifier("im", new StandardAnalyzer(),true);
Document doc = new Document();
doc.add(new Field("id", "1", Field.Store.YES, Field.Index.UN_TOKENIZED)); doc.add(new Field("body", "aaaaaaaaa", Field.Store.YES, Field.Index.TOKENIZED)); indexModifier.addDocument(doc);
doc.add(new Field("id", "2", Field.Store.YES, Field.Index.UN_TOKENIZED)); doc.add(new Field("body", "bbbbbbbbb", Field.Store.YES, Field.Index.TOKENIZED)); indexModifier.addDocument(doc);
System.out.println("1:" + indexModifier.docCount());
int deleted = indexModifier.deleteDocuments(new Term("id", "1"));
System.out.println("Deleted " + deleted + " document");
System.out.println("2:" + indexModifier.docCount());
// 将修改内容写入磁盘
indexModifier.flush();
System.out.println("3:" + indexModifier.docCount());
indexModifier.close();
先删除,再添加
3.1 更新单个文档
// 建立索引
String indexPath = "di";
IndexWriter iw = new IndexWriter(indexPath,new StandardAnalyzer());
Document doc = new Document();
doc.add(new Field("id", "1", Field.Store.YES, Field.Index.UN_TOKENIZED));
doc.add(new Field("body", "aa", Field.Store.YES, Field.Index.TOKENIZED));
iw.addDocument(doc);
doc = new Document();
doc.add(new Field("id", "2", Field.Store.YES, Field.Index.UN_TOKENIZED));
doc.add(new Field("body", "bb", Field.Store.YES, Field.Index.TOKENIZED));
iw.addDocument(doc);
iw.close();
/******************************************************************/
// 删除第一个文档
IndexReader ir = IndexReader.open(indexPath);
System.out.println("before updatinging:");
System.out.println("total: " + ir.numDocs());
doc = ir.document(0);
System.out.println("body: " + doc.getField("body").stringValue());
//delete
ir.deleteDocument(0);
System.out.println("after deleting:");
System.out.println("total: " + ir.numDocs());
ir.close();
/******************************************************************/
// 添加第一个文档
iw = new IndexWriter(indexPath,new StandardAnalyzer());
doc = new Document();
doc.add(new Field("id", "1", Field.Store.YES, Field.Index.UN_TOKENIZED));
doc.add(new Field("body", "up", Field.Store.YES, Field.Index.TOKENIZED));
iw.addDocument(doc);
iw.optimize();
iw.close();
/******************************************************************/
// 查看更新结果
ir = IndexReader.open(indexPath);
System.out.println("after updatinging:");
System.out.println("total: " + ir.numDocs());
doc = ir.document(0);
System.out.println("body: " + doc.getField("body").stringValue());
doc = ir.document(1);
System.out.println("body: " + doc.getField("body").stringValue());
ir.close();
结果:原来的 0 号文档被删除, 1 号文档变为 0 号文档,新添加的文档成为 1 号文档。
3.2 批量更新文档
先批量删除,再批量添加
UpdateIndex2.java
// 建立索引
// 批量删除文档
ir.deleteDocuments(new Term(“body”, “aa”));
// 逐个添加文档
IndexWriter 的 updateDocument 方法用于实现索引的更新,该方法以 Term 为条件,先批量删除对应的文档,然后批量增加文档。
void updateDocument(Term term, Document doc, Analyzer analyzer);
void updateDocument(Term term, Document doc);
UpdateIndex3.java
// 建立索引
String indexPath = "u3";
IndexWriter iw = new IndexWriter(indexPath,new StandardAnalyzer());
Document doc = new Document();
doc.add(new Field("id", "1", Field.Store.YES, Field.Index.UN_TOKENIZED));
doc.add(new Field("body", "aa", Field.Store.YES, Field.Index.TOKENIZED));
iw.addDocument(doc);
doc = new Document();
doc.add(new Field("id", "2", Field.Store.YES, Field.Index.UN_TOKENIZED));
doc.add(new Field("body", "bb", Field.Store.YES, Field.Index.TOKENIZED));
iw.addDocument(doc);
iw.close();
/******************************************************************/
// 添加第一个文档
iw = new IndexWriter(indexPath,new StandardAnalyzer());
doc = new Document();
doc.add(new Field("id", "1", Field.Store.YES, Field.Index.UN_TOKENIZED));
doc.add(new Field("body", "up", Field.Store.YES, Field.Index.TOKENIZED));
iw.updateDocument(new Term("body","aa"),doc);
// 个人认为该方法只能使用这一次,下面还是要一个个添加的,与示例二的方法差别不大。
iw.close();
同步,即并发访问,当许多用户同时访问某个索引文件的时候就会遇到该问题。
4.1 Lucene 并发访问机制
( 1 )任意数量的只读操作都可以同时执行
( 2 )在某一时刻,只允许一个写操作。即同一时刻,只能被一个 IndexWriter 、 IndexReader 或 IndexModifier 对象打开。一个类操作完后要立即关闭。
( 3 )只读的搜索操作可以在索引被修改的时候进行。
4.2 线程安全性
Lucene 保证索引操作线程安全性的原则是: IndexWriter 、 IndexReader 或 IndexModifier 三个类不能同时操作同一索引。
但是,这三个类本身是线程安全的,可以被多线程共享, Lucene 会对各个线程中所有修改索引的方法的调用进行恰当的同步处理,以保证修改操作能一个接一个的有序进行。
应用程序不需要进行额外的同步处理,只需保证这三个类的对象对索引的修改操作不重叠。一个类操作完成要马上调用 close 方法,然后再使用下一个类。
4.3 索引锁机制
为了处理索引同步问题, Lucene 内置了一种锁机制。锁,体现为一种文件。
锁有两种: write.lock 和 commit.lock
write.lock 文件用于阻止进程同时修改一个索引。三个类对索引进行添加、删除或修改的时候, write.lock 文件就产生了。
在读取和合并索引块的时候,会用到 commit.lock 锁,这是一种事务锁。
这两种锁都是自动建立的,不需要手动建立和修改。 IndexWriter 类可以通过四种方法设定或取得锁的时间。
iw.setWriteLockTimeout(long writeLockTimeout);
iw.getWriteLockTimeout(long writeLockTimeout);
iw.setDefaultWriteLockTimeout(long writeLockTimeout);
iw.getDefaultWriteLockTimeout(long writeLockTimeout);
Luke 是一套可视化管理 Lucene 索引的软件。通过 Luke ,可以根据文档的编号浏览文档内容、复制文档内容、执行搜索、,查看结果等。
http://getopt.org/luke/
http://code.google.com/p/luke/
Luke 是一个桌面应用程序, Limo 则是一个基于 Web 的应用程序,需要 JSP 服务器的支持。