Lucene分词基本概念

Lucene分词基本概念

Lucene接收纯文本,分词之后写入索引。
分词就是将一段文本拆分成多个词(Token),并产生与每个词相关联的一些属性(Attribute)的过程。

TokenStream、PositionIncrement和PositionLength

Lucene用TokenStream来表示分词的结果。一般情况下,TokenStream可以看做是一个顺序的Token流。
例如,有一段文本:

fast wi fi network has down

分词之后产生的TokenStream如下图:
Lucene分词基本概念

但是,有些情况下的分词结果是一张图。例如,添加以下同义词配置:

wi fi network, hotspot
speedy, fast

分词之后产生的TokenStream如下图:
Lucene分词基本概念

一个正确的分词结果应该满足这样的条件:从任何一条路径顺序遍历这张图,得到的结果都与原句语义相同。

图中的每一个节点代表一个position,每一条边代表一个Token,每一个Token有startPosition和endPosition两个属性。

token序号 token startPosition endPosition
1 speedy 0 1
2 fast 0 1
3 hotspot 1 4
4 wi 1 2
5 fi 2 3
6 network 3 4
7 has 4 5
8 down 5 6

StartPosition和endPosition已经足以描述一个token的位置信息,但是Lucene采用的是另外两种属性:

  • positionIncrement : 表示当前token相对于上一个token的位置增量
    positionIncrement = 当前token的startPosition - 上一个token的startPosition
  • positionLength : 表示当前token的position跨度
    positionLength = 当前token的endPosition - 当前token的startPosition
token序号 token startPosition endPosition positionIncrement positionLength
1 speedy 0 1 1 1
2 fast 0 1 0 1
3 hotspot 1 4 1 3
4 wi 1 2 0 1
5 fi 2 3 1 1
6 network 3 4 1 1
7 has 4 5 1 1
8 down 5 6 1 1
xxx 999998 999999 1 1
yyy 999999 1000000 1 1

对于比较大的文档,positionIncrement和positionLength的值会比startPosition和endPosition小很多(例如上表中的最后两行),
这可以显著的减少Lucene索引文件的大小。

Lucene分词的核心组件

  • CharFilter : 在分词之前,对文本进行一些转换。通常是一些字符层面的操作。例如:
    • MappingCharFilter可以将à转换成a
    • HTMLStripCharFilter可以去除文本中的HTML标签
  • Tokenizer : 基于某种规则进行分词。例如:
    • WhitespaceTokenizer可以按空格来分词。
  • TokenFilter : 对Tokenizer产生的结果进一步的处理。例如:
    • PorterStemFilter可以对token进行词干提取
    • LowerCaseFilter可以将token转换成小写形式
    • SynonymFilter可以完成同义词的映射
  • Analyzer : Analyzer本身不负责任何分词相关的工作,但它负责组装和构造CharFilter, Tokenizer和TokenFilter。
    Lucene在索引和查询时都只与Analyzer交互。

CharFilter是java.io.Reader的子类,它装饰另外一个java.io.Reader,做一些字符层面的转换工作,
并记录每个字符在原文本中的偏移量offset, 一个Analyzer中可以有多个CharFilter链式的工作

Tokenizer本身就是TokenStream的子类,它就已经可以看作是分词的结果。
一个Analyzer中只能一个Tokenizer

TokenFilter也是TokenStream的子类,一个Analyzer中可以有0个或多个TokenFilter,用来修饰Tokenizer的结果。

Lucene分词基本概念

与Token关联的属性

Lucene用Attribute来表示与Token关联的各种属性。例如:

  • CharTermAttribute表示Token的文本内容
  • OffsetAttribute表示Token在原文本中的字符 起始偏移量和结束偏移量(startOffset & endOffset)
  • PositionIncrementAttribute 和 PositionLengthAttribute 表示Token位置相关的属性

Lucene分词API示例

@Test
public void testAnalyzer() throws IOException {
    Analyzer analyzer = new Analyzer() {

        @Override
        protected Reader initReader(String fieldName, Reader reader) {
            return new HTMLStripCharFilter(reader); // 去除HTML标签
        }

        @Override
        protected TokenStreamComponents createComponents(String fieldName) {
            Tokenizer tokenizer = new WhitespaceTokenizer();  // 空格分词
            TokenStream ts = new LowerCaseFilter(tokenizer);  // 转小写
            ts = new PorterStemFilter(ts);  // 词干提取
            return new TokenStreamComponents(tokenizer, ts);
        }
    };
    final String text = "<b>CAT running</b>";
    TokenStream ts = analyzer.tokenStream("", text);
    CharTermAttribute charTermAtt = ts.getAttribute(CharTermAttribute.class);
    ts.reset();
    while (ts.incrementToken()) {
        String term = charTermAtt.toString();
        System.out.println(term); // 结果: cat   \n  run 
    }
}