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.
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]));
IndexReader reader
=
IndexReader.Open(dir);
reader.Delete(
new
Term(
"
city
"
,
"
Amsterdam
"
));
reader.Close();
以上所有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();
}
}
还有一点
Lucene
支持对
Field
进行
Append ,
如下
:

string synonyms[] = String {"quick", "rapid", "speedy"} ;





for ( int i = 0 ; i < synonyms.length; i ++ )
doc.Add(Field.Text("word", synonyms[i]));

这点纯粹是为了方便用户的使用
.
在内部
Lucene
自动做了转化
,
效果和将它们拼接好再存是一样
.
2.
删除索引中的文档
这一点
Lucene
所采取的方式比较怪
,
它使用
IndexReader
来对要删除的项进行标记
,
然后在
Reader Close
的时候一起删除
.
这里简要介绍几个方法
.






你还可以通过
reader.UndeleteAll()
这个方法取消前面所做的标记
,
即在
read.Close()
调用之前取消所有的删除工作
.
3.
更新索引中的文档
这个功能
Lucene
没有支持
,
只有通过删除后在添加来实现
.
看看代码
,
很好理解的
.
需要注意的是以上所有有关索引的操作
,
为了避免频繁的打开和关闭
Writer
和
Reader.
又由于添加和删除是不同的连接
(Writer, Reader)
做的
.
所以应该尽可能的将添加文档的操作放在一起批量执行
,
然后将删除文档的操作也放在一起批量执行
.
避免添加删除交替进行
.
以上所有TestCase的源代码下载.


























































































































































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




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