Lucene于搜索引擎技术(Analysis包详解)

Analysis 包分析

算法和数据结构分析 :

由于 Analysis 包比较简单 , 不详述了 !

算法 : 基于机械分词 1-gram,2-gram,HMM( 如果使用 ICTCLAS 接口的话 )

数据结构 : 部分源码用到了 Set ,HashTable,HashMap

认真理解 Token

Lucene 中的 Analysis 包专门用于完成对于索引文件的分词 .Lucene 中的 Token 是一个非常重要的概念 .

看一下其源码实现:

public final class Token {

  String termText;                        // the text of the term

  int startOffset;                           // start in source text

  int endOffset;                            // end in source text

  String type = "word";                        // lexical type

  private int positionIncrement = 1;

  public Token(String text, int start, int end)

  public Token(String text, int start, int end, String typ)

  public void setPositionIncrement(int positionIncrement)

  public int getPositionIncrement() { return positionIncrement; }

  public final String termText() { return termText; }

  public final int startOffset() { return startOffset; }

public void setStartOffset(int givenStartOffset)

  public final int endOffset() { return endOffset; }

public void setEndOffset(int givenEndOffset)

  public final String type() { return type; }

  public String toString()

  }

下面编一段代码来看一下

TestToken.java

package org.apache.lucene.analysis.test;

import org.apache.lucene.analysis.*;

import org.apache.lucene.analysis.standard.StandardAnalyzer;

import java.io.*;

public class TestToken

{

  public static void main(String[] args)

  {

     String string = new String(" 我爱天大 , 但我更爱中国 ");

//Analyzer analyzer = new StandardAnalyzer();

Analyzer analyzer = new TjuChineseAnalyzer();

//Analyzer analyzer= new StopAnalyzer();

    TokenStream ts = analyzer.tokenStream("dummy",new StringReader(string));

     Token token;

    try

    {

      int n=0;

      while ( (token = ts.next()) != null)

      {

        System.out.println((n++)+"->"+token.toString());

      }

    }

    catch(IOException ioe)

    {

      ioe.printStackTrace();

    }

 


  }

} 注意看其结果如下所示

0->Token's (termText,startOffset,endOffset,type,positionIncrement) is:( ,0,1,<CJK>,1)

1->Token's (termText,startOffset,endOffset,type,positionIncrement) is:( ,1,2,<CJK>,1)

2->Token's (termText,startOffset,endOffset,type,positionIncrement) is:( ,2,3,<CJK>,1)

3->Token's (termText,startOffset,endOffset,type,positionIncrement) is:( ,3,4,<CJK>,1)

4->Token's (termText,startOffset,endOffset,type,positionIncrement) is:( ,5,6,<CJK>,1)

5->Token's (termText,startOffset,endOffset,type,positionIncrement) is:( ,6,7,<CJK>,1)

6->Token's (termText,startOffset,endOffset,type,positionIncrement) is:( ,7,8,<CJK>,1)

7->Token's (termText,startOffset,endOffset,type,positionIncrement) is:( ,8,9,<CJK>,1)

8->Token's (termText,startOffset,endOffset,type,positionIncrement) is:( ,9,10,<CJK>,1)

9->Token's (termText,startOffset,endOffset,type,positionIncrement) is:( ,10,11,<CJK>,1)

注意 : 其中 ”,” StandardAnalyzer 给过滤掉了 , 所以大家注意第 4 Token 直接 startOffset 5 开始 .

如果改用 StopAnalyzer()

0->Token's (termText,startOffset,endOffset,type,positionIncrement) is:( 我爱天大 ,0,4,word,1)

1->Token's (termText,startOffset,endOffset,type,positionIncrement) is:( 但我更爱中国 ,5,11,word,1)

改用 TjuChineseAnalyzer( 我写的 , 下文会讲到如何去写 )

0->Token's (termText,startOffset,endOffset,type,positionIncrement) is:( ,3,4,word,1)

1->Token's (termText,startOffset,endOffset,type,positionIncrement) is:( 天大 ,6,8,word,1)

2->Token's (termText,startOffset,endOffset,type,positionIncrement) is:( ,19,20,word,1)

3->Token's (termText,startOffset,endOffset,type,positionIncrement) is:( ,22,23,word,1)

4->Token's (termText,startOffset,endOffset,type,positionIncrement) is:( 中国 ,25,27,word,1)

讲明白了 Token, 咱们来看以下其他的东西

一个 TokenStream 是用来走访 Token iterator( 迭代器 )

看一下其源代码 :

public abstract class TokenStream {

  public abstract Token next() throws IOException;

  public void close() throws IOException {}

}

一个 Tokenizer is-a TokenStream (派生自 TokenStream ),其输入为 Reader

看一下其源码如下:

public abstract class Tokenizer extends TokenStream {

  protected Reader input;

  protected Tokenizer() {}

  protected Tokenizer(Reader input) {

    this.input = input;

  }

  public void close() throws IOException {

    input.close();

  }

}

一个 TokenFilter is–a TokenStream( 派生自 TokenStream) ,其义如名就是用来完成对 TokenStream 的过滤操作,譬如

StopWords ,将 Token 变为小写等。

源码如下:

public abstract class TokenFilter extends TokenStream {

  protected TokenStream input;

  protected TokenFilter() {}

  protected TokenFilter(TokenStream input) {

    this.input = input;

  }

  public void close() throws IOException {

    input.close();

  }

}

一个 Analyzer 就是一个 TokenStream 工厂

看一下其源码就:

public abstract class Analyzer { 

  public TokenStream tokenStream(String fieldName, Reader reader)

  {

         return tokenStream(reader);

  }

  public TokenStream tokenStream(Reader reader)

  {

         return tokenStream(null, reader);

  }

}

好,现在咱们来看一下 Lucene Analysis 包下面的各个类文件都是用来干什么的。按照字典排序。

Analysis 包中的源码详解

Analyzer.java  上文已经讲过。

CharTokenizer.java  此类为简单一个抽象类,用来对基于字符的进行简单分词( tokenizer

LetterTokenizer.java 两个非字符之间的字符串定义为 token (举例来说英文单词由空白隔开,那个两个空白之间的字符串即被定义为一个 token 。备注:对于绝大多数欧洲语言来说,这个类工作效能很好。当时对于不用空白符分割的亚洲语言 , 效能极差(譬如中日韩)。)

LowerCaseFilter.java is-a TokenFilter 用于将字母小写化

LowerCaseTokenizer is-a Tokenizer 功能上等价于 LetterTokenizer LowerCaseFilter

PerFieldAnalyzerWrapper 是一个 Analyzer ,因为继承自 Analyzer 当不同的域( Field )需要不同的语言分析器( Analyzer )时,这个 Analyzer 就派上了用场。使用成员函数 addAnalyzer 可以增加一个非缺省的基于某个 Field analyzer 。很少使用。

PorterStemFilter.java 使用词干抽取算法对每一个 token 流进行词干抽取。

PorterStemmer.java  有名的 P-stemming 算法

SimpleAnalyzer.java

StopAnalyzer.java   具有过滤停用词的功能

StopFilter.java     StopFilter 为一个 Filter ,主要用于从 token 流中去除 StopWords

Token.java       上面已讲 .

TokenFilter.java   上面已经讲了

Tokenizer.java     上面已经讲了

TokenStream.java   上面已经讲了

WhitespaceAnalyzer.java

WhitespaceTokenizer.java 只是按照 space 区分 Token.

 


由于 Lucene analyisis 包下的 Standard 包下的 StandardAnalyzer() 功能很强大 , 而且支持 CJK 分词 , 我们简要说一下 .

此包下的文件是有 StandardTokenizer.jj 经过 javac 命令生成的 . 由于是机器自动生成的代码 , 可能可读性很差 , 想了解的话好好看看那个 StandardTokenizer.jj 文件就会比较明了了 .

Lucene 常用的 Analyzer 功能概述 .

WhitespaceAnalyzer: 仅仅是去除空格,对字符没有 lowcase , 不支持中文

SimpleAnalyzer: 功能强于 WhitespaceAnalyzer, 将除去 letter 之外的符号全部过滤掉 , 并且将所有的字符 lowcase , 不支持中文

StopAnalyzer:StopAnalyzer 的功能超越了 SimpleAnalyzer ,在 SimpleAnalyzer 的基础上
   
增加了去除 StopWords 的功能 , 不支持中文

StandardAnalyzer: 英文的处理能力同于 StopAnalyzer. 支持中文采用的方法为单字切分 .

ChineseAnalyzer: 来自于 Lucene sand box. 性能类似于 StandardAnalyzer, 缺点是不支持中英文混和分词 .

CJKAnalyzer:chedong 写的 CJKAnalyzer 的功能在英文处理上的功能和 StandardAnalyzer 相同
   
但是在汉语的分词上,不能过滤掉标点符号,即使用二元切分

TjuChineseAnalyzer: 我写的 , 功能最为强大 .TjuChineseAnlyzer 的功能相当强大 , 在中文分词方面由于其调用的为 ICTCLAS java 接口 . 所以其在中文方面性能上同与 ICTCLAS. 其在英文分词上采用了 Lucene StopAnalyzer, 可以去除  stopWords, 而且可以不区分大小写 , 过滤掉各类标点符号 .

各个 Analyzer 的功能已经比较介绍完毕了 , 现在咱们应该学写 Analyzer, 如何 diy 自己的 analyzer ??

如何 DIY 一个 Analyzer

咱们写一个 Analyzer, 要求有一下功能

(1)     可以处理中文和英文 , 对于中文实现的是单字切分 , 对于英文实现的是以空格切分 .

(2)     对于英文部分要进行小写化 .

(3)     具有过滤功能 , 可以人工设定 StopWords 列表 . 如果不是人工设定 , 系统会给出默认的 StopWords 列表 .

(4)     使用 P-stemming 算法对于英文部分进行词缀处理 .

代码如下 :

public final class DiyAnalyzer

    extends Analyzer

{

  private Set stopWords;

  public static final String[] CHINESE_ENGLISH_STOP_WORDS =

      {

      "a", "an", "and", "are", "as", "at", "be", "but", "by",

      "for", &quo

你可能感兴趣的:(apache,数据结构,算法,搜索引擎,Lucene)