Lucene.net 系列二 --- index (上)

本文详细介绍了Lucene建立索引的过程,包括文本抽取、文本分析及写入索引等步骤,并深入探讨了如何添加、删除及更新文档,提供了丰富的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Lucene建立Index的过程:
1.        抽取文本 .
比如将 PDF 以及 Word 中的内容以纯文本的形式提取出来 .Lucene 所支持的类型主要为 String, 为了方便同时也支持 Date 以及 Reader. 其实如果使用这两个类型 lucene 会自动进行类型转换 .
2.        文本分析 .
Lucene 将针对所给的文本进行一些最基本的分析 , 并从中去除一些不必要的信息 , 比如一些常用字 a ,an, the 等等 , 如果搜索的时候不在乎字母的大小写 , 又可以去掉一些不必要的信息 . 总而言之你可以把这个过程想象成一个文本的过滤器 , 所有的文本内容通过分析 , 将过滤掉一些内容 , 剩下最有用的信息 .
3.        写入 index.
google 等常用的索引技术一样 lucene 在写 index 的时候都是采用的倒排索引技术 (inverted index.) 简而言之 , 就是通过某种方法 ( 类似 hash ?) 将常见的 一篇文档中含有哪些词 这个问题转成 哪篇文档中有这些词 ”. 而各个搜索引擎的索引机制的不同主要在于如何为这张倒排表添加更准确的描述 . 比如 google 有名的 PageRank 因素 .Lucene 当然也有自己的技术 , 希望在以后的文章中能为大家加以介绍 .
 
在上一篇文章中 , 使用了最基本的建立索引的方法 . 在这里将对某些问题加以详细的讨论 .
 
1. 添加 Document 至索引
上次添加的每份文档的信息是一样的 , 都是文档的 filename contents.
还有一点 Lucene 支持对 Field 进行 Append , 如下 :
string  baseWord  =  " fast " ;
  string  synonyms[]  =  String  {"quick""rapid""speedy"} ;
Document doc 
=  new  Document();
doc.Add(Field.Text(
" word " , baseWord));
  for  ( int  i  =  0 ; i  <  synonyms.length; i ++ )
     doc.Add(Field.Text(
"word", synonyms[i]));
这点纯粹是为了方便用户的使用 . 在内部 Lucene 自动做了转化 , 效果和将它们拼接好再存是一样 .
 
2. 删除索引中的文档
这一点 Lucene 所采取的方式比较怪 , 它使用 IndexReader 来对要删除的项进行标记 , 然后在 Reader Close 的时候一起删除 .
这里简要介绍几个方法 .
IndexReader reader  =  IndexReader.Open(dir);
reader.Delete(
new  Term( " city " " Amsterdam " ));
reader.Close();
你还可以通过 reader.UndeleteAll() 这个方法取消前面所做的标记 , 即在 read.Close() 调用之前取消所有的删除工作 .
 
3. 更新索引中的文档
这个功能 Lucene 没有支持 , 只有通过删除后在添加来实现 . 看看代码 , 很好理解的 .

需要注意的是以上所有有关索引的操作 , 为了避免频繁的打开和关闭 Writer Reader. 又由于添加和删除是不同的连接 (Writer, Reader) 做的 . 所以应该尽可能的将添加文档的操作放在一起批量执行 , 然后将删除文档的操作也放在一起批量执行 . 避免添加删除交替进行 .
 
以上所有TestCase的源代码下载.
 
 

[TestFixture] 
public  class  DocumentUpdateTest : BaseIndexingTestCase 

     [Test] 
     
public void Update() 
     

         Assert.AreEqual(
1, GetHitCount("city""Amsterdam")); 
 
         IndexReader reader 
= IndexReader.Open(dir); 
 
         reader.Delete(
new Term("city""Amsterdam")); 
 
         reader.Close(); 
 
         Assert.AreEqual(
0, GetHitCount("city""Amsterdam")); 
 
         IndexWriter writer 
= new IndexWriter(dir, GetAnalyzer(),false); 
 
         Document doc 
= new Document(); 
 
         doc.Add(Field.Keyword(
"id""1")); 
 
         doc.Add(Field.UnIndexed(
"country""Netherlands")); 
 
         doc.Add(Field.UnStored(
"contents","Amsterdam has lots of bridges")); 
 
         doc.Add(Field.Text(
"city""Haag")); 
 
         writer.AddDocument(doc); 
 
         writer.Optimize(); 
 
         writer.Close(); 
 
         Assert.AreEqual(
1, GetHitCount("city""Haag")); 
     }
 
 
  
     
protected override Analyzer GetAnalyzer() 
     

         
return new WhitespaceAnalyzer();  //注意此处如果用SimpleAnalyzer搜索会失败 
     }
 
 
  
     
private int GetHitCount(String fieldName, String searchString) 
     

         IndexSearcher searcher 
= new IndexSearcher(dir); 
 
         Term t 
= new Term(fieldName, searchString); 
 
         Query query 
= new TermQuery(t); 
 
         Hits hits 
= searcher.Search(query); 
 
         
int hitCount = hits.Length(); 
 
         searcher.Close(); 
 
         
return hitCount; 
     }
 
}
 
 
 
[TestFixture]

public  class  DocumentDeleteTest : BaseIndexingTestCase    //  BaseIndexingTestCase中的SetUp方法                                                    // 建立了索引其中加入了两个Document
{

     [Test]
     
public void testDeleteBeforeIndexMerge()
     
{     

         IndexReader reader 
= IndexReader.Open(dir);  //当前索引中有两个Document 

         Assert.AreEqual(
2, reader.MaxDoc());   //文档从0开始计数,MaxDoc表示下一个文档的序号

         Assert.AreEqual(
2, reader.NumDocs());  //NumDocs表示当前索引中文档的个数  

         reader.Delete(
1);                    //对标号为1的文档标记为待删除,逻辑删除

         Assert.IsTrue(reader.IsDeleted(
1));         //检测某个序号的文档是否被标记删除

         Assert.IsTrue(reader.HasDeletions());       
//检测索引中是否有Document被标记删除 

         Assert.AreEqual(
2, reader.MaxDoc());        //当前下一个文档序号仍然为2

         Assert.AreEqual(
1, reader.NumDocs());       //当前索引中文档数变成1

         reader.Close();                             
//此时真正从物理上删除之前被标记的文档

         reader 
= IndexReader.Open(dir);

         Assert.AreEqual(
2, reader.MaxDoc());         

         Assert.AreEqual(
1, reader.NumDocs());

         reader.Close();

     }
 

     [Test]
     
public void DeleteAfterIndexMerge()            //在索引重排之后
     
{

         IndexReader reader 
= IndexReader.Open(dir);

         Assert.AreEqual(
2, reader.MaxDoc());

         Assert.AreEqual(
2, reader.NumDocs());

         reader.Delete(
1);

         reader.Close();

         IndexWriter writer 
= new IndexWriter(dir, GetAnalyzer(), false);

         writer.Optimize();                          
//索引重排

         writer.Close();

         reader 
= IndexReader.Open(dir);

         Assert.IsFalse(reader.IsDeleted(
1));

         Assert.IsFalse(reader.HasDeletions());

         Assert.AreEqual(
1, reader.MaxDoc());       //索引重排后,下一个文档序号变为1

         Assert.AreEqual(
1, reader.NumDocs());

         reader.Close();

     }

}
当然你也可以不通过文档序号进行删除工作 . 采用下面的方法 , 可以从索引中删除包含特定的内容文档 .
doc.Add(Field.Keyword( " filename " , file.FullName));

doc.Add(Field.Text(
" contents " new  StreamReader(file.FullName)));
Lucene 中对每个文档的描述是可以不同的 , 比如 , 两份文档都是描述一个人 , 其中一个添加的是 name, age 另一个添加的是 id, sex , 这种不规则的文档描述在 Lucene 中是允许的 .
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值