转自:http://lolorosa.blogbus.com/logs/45646288.html
下面的分析要求了解lucene的document,field等概念
1. 首先来说一个N-Gram的概念,我不知道中文里怎么称呼这个,从Lucene SpellChecker的实现来看,N-Gram是指将一个单词划分成若干等长的字串,每一个成为一个gram,n就是用来控制每个gram的长度的。
2. 对于一个单词我们往往会给不同的N做多次切割,这样便于做搜索建议和拼写检查。
3. 构建词典
a. 对于每个词典中的单词,根据两个值minN~maxN(minN, maxN是根据单词长度由专门的函数产生的)进行分割,介于两个值之间的N都算作一种分割,伪码如下:
for ng = minN to maxN
for i=0 to wordLen-ng +1 (wordLen是指当前这个单词的长度,word是当前单词内容)
gram = word.substr(i,i+ng);
add this gram as a field to the current lucene document
end for
end for
b. 针对每个N,开头结尾的gram也加入索引域
举例来说:
对于three这个词,minN = 2, maxN =3(有一个很简单的函数计算,参看源码);
那么针对three这个词的对应document对象包含的Field有如下
keyword value
----------------------------------------------
F_WORD three(原词)
gram2 th
gram2 hr
gram2 re
gram2 ee
start2 th
end2 ee
gram3 thr
gram3 hre
gram3 ree
start3 thr
end3 ree
通过如上的方式将词典里的每个单词都进行了索引,但请注意上述索引的Field都不进行分词,并且只有F_WORD是存储在索引文件中的(对于这两句不理解的话请参考源码,源码链接我会在下面给出)。
4. 拼写检查
a. 根据输入word构建查询条件
也是通过minN, maxN来讲输入进行gram化,每个对应的分割都构成一个查询Term,同时针对每种分割也有对应的起始和结束查询,过程和构建词典的过程一样。这些条件之间是或的关系。这个用BooleanQuery可以实现。
b. 开始从词典构建的索引中使用上述条件进行查询
I. 从查询结果中过滤掉自身(如果查询结果包含自身的话)
II. 计算查询结果中的每个单词和输入word的编辑距离,过滤掉部分距离过小的单词(源码为: if(sugWord.score<min) continue;, 我觉得应该是过滤掉距离过大的词啊,不太明白这里)
III. 放入以编辑距离为衡量指标的优先队列中,队列长度人为确定,具体根据要查询的建议单词的多少确定
IV. 当队列长度满之后,返回结果,注意此处的结果要是以编辑距离增序的结果(使用优先队列的函数可以达到此要求)。
5. 编辑距离解释:编辑距离是衡量两个字符串相似度的值,介于0和1之间,0表示两个字符串完全相同,1表示两个字符串具有最大的不相似程度。常用的编辑距离有:LevensteinDistance和JaroWinklerDistance.
6. Comments on spellchecker: 从上面的分析来看,拼写检查和搜索建议其实相差非常小(我指如果都用lucene来完成的话),唯一的差别就是多了个编辑距离的检查。
源码下载,请下载:lucene-2.4.1-src.tar.gz 或lucene-2.4.1-src.zip , 解压后在contribute目录中查找spellchecker查看源码。