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

时间:2020-12-02 03:06:28

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


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至索引

上次添加的每份文档的信息是一样的,都是文档的filenamecontents.

Lucene.net 系列二 --- index (上)doc.Add(Field.Keyword("filename", file.FullName));
Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)doc.Add(Field.Text(
"contents"new StreamReader(file.FullName)));
Lucene.net 系列二 --- index (上)

Lucene中对每个文档的描述是可以不同的,比如,两份文档都是描述一个人,其中一个添加的是name, age 另一个添加的是id, sex ,这种不规则的文档描述在Lucene中是允许的.

还有一点Lucene支持对Field进行Append , 如下:

Lucene.net 系列二 --- index (上)string baseWord = "fast";
  string synonyms[] = String {"quick""rapid""speedy"};Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)Document doc 
= new Document();Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)doc.Add(Field.Text(
"word", baseWord));Lucene.net 系列二 --- index (上)
  for (int i = 0; i < synonyms.length; i++)
     doc.Add(Field.Text(
"word", synonyms[i]));
Lucene.net 系列二 --- index (上)

这点纯粹是为了方便用户的使用.在内部Lucene自动做了转化,效果和将它们拼接好再存是一样.

 

2. 删除索引中的文档

这一点Lucene所采取的方式比较怪,它使用IndexReader来对要删除的项进行标记,然后在Reader Close的时候一起删除.

这里简要介绍几个方法.

Lucene.net 系列二 --- index (上)[TestFixture]
Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)
public class DocumentDeleteTest : BaseIndexingTestCase   // BaseIndexingTestCase中的SetUp方法                                                   //建立了索引其中加入了两个Document
Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)Lucene.net 系列二 --- index (上){
Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)     [Test]Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)     
public void testDeleteBeforeIndexMerge()Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)Lucene.net 系列二 --- index (上)     
{     
Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)         IndexReader reader 
= IndexReader.Open(dir);  //当前索引中有两个Document 
Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)         Assert.AreEqual(
2, reader.MaxDoc());   //文档从0开始计数,MaxDoc表示下一个文档的序号
Lucene.net 系列二 --- index (上)

Lucene.net 系列二 --- index (上)         Assert.AreEqual(
2, reader.NumDocs());  //NumDocs表示当前索引中文档的个数  
Lucene.net 系列二 --- index (上)

Lucene.net 系列二 --- index (上)         reader.Delete(
1);                    //对标号为1的文档标记为待删除,逻辑删除
Lucene.net 系列二 --- index (上)

Lucene.net 系列二 --- index (上)         Assert.IsTrue(reader.IsDeleted(
1));         //检测某个序号的文档是否被标记删除
Lucene.net 系列二 --- index (上)

Lucene.net 系列二 --- index (上)         Assert.IsTrue(reader.HasDeletions());       
//检测索引中是否有Document被标记删除 
Lucene.net 系列二 --- index (上)

Lucene.net 系列二 --- index (上)         Assert.AreEqual(
2, reader.MaxDoc());        //当前下一个文档序号仍然为2
Lucene.net 系列二 --- index (上)

Lucene.net 系列二 --- index (上)         Assert.AreEqual(
1, reader.NumDocs());       //当前索引中文档数变成1
Lucene.net 系列二 --- index (上)

Lucene.net 系列二 --- index (上)         reader.Close();                             
//此时真正从物理上删除之前被标记的文档
Lucene.net 系列二 --- index (上)

Lucene.net 系列二 --- index (上)         reader 
= IndexReader.Open(dir);
Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)         Assert.AreEqual(
2, reader.MaxDoc());         
Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)         Assert.AreEqual(
1, reader.NumDocs());
Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)         reader.Close();
Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)     }
 
Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)     [Test]Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)     
public void DeleteAfterIndexMerge()            //在索引重排之后Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)Lucene.net 系列二 --- index (上)     
{
Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)         IndexReader reader 
= IndexReader.Open(dir);
Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)         Assert.AreEqual(
2, reader.MaxDoc());
Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)         Assert.AreEqual(
2, reader.NumDocs());
Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)         reader.Delete(
1);
Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)         reader.Close();
Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)         IndexWriter writer 
= new IndexWriter(dir, GetAnalyzer(), false);
Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)         writer.Optimize();                          
//索引重排
Lucene.net 系列二 --- index (上)

Lucene.net 系列二 --- index (上)         writer.Close();
Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)         reader 
= IndexReader.Open(dir);
Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)         Assert.IsFalse(reader.IsDeleted(
1));
Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)         Assert.IsFalse(reader.HasDeletions());
Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)         Assert.AreEqual(
1, reader.MaxDoc());       //索引重排后,下一个文档序号变为1
Lucene.net 系列二 --- index (上)

Lucene.net 系列二 --- index (上)         Assert.AreEqual(
1, reader.NumDocs());
Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)         reader.Close();
Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)     }
Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)}
Lucene.net 系列二 --- index (上)

当然你也可以不通过文档序号进行删除工作.采用下面的方法,可以从索引中删除包含特定的内容文档.

Lucene.net 系列二 --- index (上)IndexReader reader = IndexReader.Open(dir);Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)reader.Delete(
new Term("city""Amsterdam"));Lucene.net 系列二 --- index (上)
Lucene.net 系列二 --- index (上)reader.Close();Lucene.net 系列二 --- index (上)

你还可以通过reader.UndeleteAll()这个方法取消前面所做的标记,即在read.Close()调用之前取消所有的删除工作.

 

3. 更新索引中的文档

这个功能Lucene没有支持, 只有通过删除后在添加来实现. 看看代码,很好理解的.

Lucene.net 系列二 --- index (上)[TestFixture] 
Lucene.net 系列二 --- index (上)
public class DocumentUpdateTest : BaseIndexingTestCase 
Lucene.net 系列二 --- index (上)Lucene.net 系列二 --- index (上)

Lucene.net 系列二 --- index (上)     [Test] 
Lucene.net 系列二 --- index (上)     
public void Update() 
Lucene.net 系列二 --- index (上)Lucene.net 系列二 --- index (上)     

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

Lucene.net 系列二 --- index (上)         
return new WhitespaceAnalyzer();  //注意此处如果用SimpleAnalyzer搜索会失败 
Lucene.net 系列二 --- index (上)
     }
 
Lucene.net 系列二 --- index (上) 
Lucene.net 系列二 --- index (上)  
Lucene.net 系列二 --- index (上)     
private int GetHitCount(String fieldName, String searchString) 
Lucene.net 系列二 --- index (上)Lucene.net 系列二 --- index (上)     

Lucene.net 系列二 --- index (上)         IndexSearcher searcher 
= new IndexSearcher(dir); 
Lucene.net 系列二 --- index (上) 
Lucene.net 系列二 --- index (上)         Term t 
= new Term(fieldName, searchString); 
Lucene.net 系列二 --- index (上) 
Lucene.net 系列二 --- index (上)         Query query 
= new TermQuery(t); 
Lucene.net 系列二 --- index (上) 
Lucene.net 系列二 --- index (上)         Hits hits 
= searcher.Search(query); 
Lucene.net 系列二 --- index (上) 
Lucene.net 系列二 --- index (上)         
int hitCount = hits.Length(); 
Lucene.net 系列二 --- index (上) 
Lucene.net 系列二 --- index (上)         searcher.Close(); 
Lucene.net 系列二 --- index (上) 
Lucene.net 系列二 --- index (上)         
return hitCount; 
Lucene.net 系列二 --- index (上)     }
 
Lucene.net 系列二 --- index (上)}
 
Lucene.net 系列二 --- index (上) 

 

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

 
以上所有TestCase的源代码下载.