lucene analyzer 体系结构一览
分词这块内容往往是做搜索的入门级内容,例如我们刚接触搜索的时候,经常会碰到“想要检索的检索不到”的问题,这时得对分词有个大概的了解了。
这里是简要分析,因此我用的是lucene1.9来分析(目前看的源码主要也是1.9版本,虽然都快出4.0了)
一)体系结构
首先我们了解两组类结构,一是Analyzer,二是TokenStream。
图1:analyzer类结构
图2:TokenStream类结构
TokenStream有两个直接子类:TokenFilter和Tokenizer,其最主要的区别是:TokenFilter接收一个TokenStream进行再加工;Tokenizer接收一个Reader进行基础性分词。
然后Analyzer是一个包装器,一般选用一个Tokenizer,外加多个TokenFilter(当然也可以不加TokenFilter)构成一个分词器。
细细想想和java I/O的设计有点类似。
举例:StandardAnalyzer算是最复杂的分词器了(在1.9中),他的组装方式如下:
TokenStream result =
new
StandardTokenizer(reader);
result =
new
StandardFilter(result);
result =
new
LowerCaseFilter(result);
result =
new
StopFilter(result, stopSet);
|
不难看出,StandardAnalyzer首先选用StandardTokenizer进行分词,然后逐一通过StandardFilter、LowerCaseFilter、StopFilter进行再加工处理。
二)Tokenizer
Tokenizer就是按照指定规则将Reader划分成一个个词(Token),例如WhitespaceTokenizer就是按照空格进行划分,LetterTokenizer就是按照是不是字母进行划分,KeywordTokenizer就是将整个reader的内容作为一个token,等等。
对于CharTokenizer(LetterTokenizer、KeywordTokenizer)都是基于字符进行判断的,因此有一个方法比较重要,就是isTokenChar(char c)。
简单看看KeywordTokenizer的源码,如下:
public
Token next()
throws
IOException {
if
(!done) {
done =
true
;
StringBuffer buffer =
new
StringBuffer();
int
length;
while
(
true
) {
length = input.read(
this
.buffer);
if
(length == -
1
)
break
;
buffer.append(
this
.buffer,
0
, length);
}
String text = buffer.toString();
return
new
Token(text,
0
, text.length());
}
return
null
;
}
|
他将输入的内容作为一个整词输出。
三)TokenFilter
TokenFilter是对Tokenizer的再加工,包括字符小写(LowerCaseFilter)、去停用词(StopFilter)等。
LowerCaseFilter源码如下:
public
final
Token next()
throws
IOException {
Token t = input.next();
if
(t ==
null
)
return
null
;
t.termText = t.termText.toLowerCase();
return
t;
}
|
这里不需过多解释,强调一下TokenFilter的输入时一个TokenStream,他们可以穿起来,形成一条加工线。
StopFilter源码如下:
/**
* Returns the next input Token whose termText() is not a stop word.
*/
public
final
Token next()
throws
IOException {
// return the first non-stop word found
for
(Token token = input.next(); token !=
null
; token = input.next())
{
String termText = ignoreCase ? token.termText.toLowerCase() : token.termText;
if
(!stopWords.contains(termText))
return
token;
}
// reached EOS -- return null
return
null
;
}
|
这里有对字符大小写的控制。停用词存放在set中,对TokenStream中的Token进行逐一排查。
四)中文分词
中文分词目前主要是基于词库的方式,然后做最大匹配。例如IKAnalyzer。
也有基于统计的作法,例如CNSmart(Lucene提供)。
这里面又包括:人名、地名、机构名称的识别,未登录词的识别,歧义消除等内容。