欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

lucene 的分析器(analyzer)与分词器(tokenizer)和过滤器(tokenfilter)

程序员文章站 2024-02-21 22:33:28
...

1〉analyzer主要包含分词器跟过滤器,他的功能就是:将分词器跟分析器进行合理的组合,使之产生对文本分词和过滤效果。因此,分析器使用分词和过滤器构成一个管道,文本在“滤过”这个管道之后,就成为可以进入索引的最小单位。 

2〉tokenizer主要用于对文本资源进行切分,将文本规则切分为一个个可以进入索引的最小单元 

3〉tokenfilter主要对分词器切分的最小单位进入索引进行预处理,如:大写转小写,复数转单数,也可以复杂(根据语义改写拼写错误的单词) 

 

附上lucene的部分类源码以讲解:

public abstract TokenStream tokenStream(String fieldName, Reader reader);//该抽象为子分析器扩展,后面会在说到

  public TokenStream reusableTokenStream(String fieldName, Reader reader) throws IOException {
    return tokenStream(fieldName, reader);
  }//这个是后来发行版本添加的,主要用途是:创建一个TokenStream,为了同一个线程重复使用,节省时间

  private CloseableThreadLocal tokenStreams = new CloseableThreadLocal();// 利用ThreadLocal 来达到在同一个线程重复使用。 这种应用很普遍,例如hibernate的session也是这种情况

  protected Object getPreviousTokenStream() {
    try {
      return tokenStreams.get();
    } catch (NullPointerException npe) {
      if (tokenStreams == null) {
        throw new AlreadyClosedException("this Analyzer is closed");
      } else {
        throw npe;
      }
    }
  }

  protected void setPreviousTokenStream(Object obj) {
    try {
      tokenStreams.set(obj);
    } catch (NullPointerException npe) {
      if (tokenStreams == null) {
        throw new AlreadyClosedException("this Analyzer is closed");
      } else {
        throw npe;
      }
    }
  }

  public int getPositionIncrementGap(String fieldName)
  {
    return 0;
  }

  public void close() {
    tokenStreams.close();
    tokenStreams = null;
  }
 

 

public TokenStream tokenStream(String fieldName, Reader reader) {

 

    StandardTokenizer tokenStream = new StandardTokenizer(reader, replaceInvalidAcronym);
    tokenStream.setMaxTokenLength(maxTokenLength);
    TokenStream result = new StandardFilter(tokenStream);
    result = new LowerCaseFilter(result);
    result = new StopFilter(result, stopSet);
    return result;
  }

 StandardAnalyzer提供的实现,可以看到很简单,就是组合了分词跟过滤器,首先实例了StandardTokenizer(),然后获得了tokenStream,将他传入过滤器,在这样的过程中,result没有发生任何改变,真正发生改变的是在建立索引或者搜索的时候,继续往下看, 

public StandardTokenizer(Reader input, boolean replaceInvalidAcronym) {
    this.replaceInvalidAcronym = replaceInvalidAcronym;
    this.input = input;
    this.scanner = new StandardTokenizerImpl(input);
  }

 StandardTokenizerImpl的构造方法:

StandardTokenizerImpl(java.io.Reader in) {
    this.zzReader = in;
  }

 StartdFilter的构造方法: 

public StandardFilter(TokenStream in) {
    super(in);
  }
 

当建立索引或者搜索的时候,会调用由tokenStream方法返回的TokenStream的next()方法,也是这个时候真正的分词和过滤就开始了。 

接着看,当第一调用next()方法时候,首先应该进入StopFilter的next,因为result = new StopFilter(result, stopSet); 

public final Token next(final Token reusableToken) throws IOException {
    assert reusableToken != null;
    // return the first non-stop word found
    int skippedPositions = 0;
    for (Token nextToken = input.next(reusableToken); nextToken != null; nextToken = input.next(reusableToken)) {
      if (!stopWords.contains(nextToken.termBuffer(), 0, nextToken.termLength())) {
        if (enablePositionIncrements) {
          nextToken.setPositionIncrement(nextToken.getPositionIncrement() + skippedPositions);
        }
        return nextToken;
      }
      skippedPositions += nextToken.getPositionIncrement();
    }
    // reached EOS -- return null
    return null;
  }

 

在这里是调用input的next(),这个input是在初始化stopFilter时进行的,其实input初始化操作也就是也就是把以参数方式传入的result给input,这个result是流经上层的过滤器过来的,回过来看看吧 

StandardTokenizer tokenStream = new StandardTokenizer(reader, replaceInvalidAcronym);
    tokenStream.setMaxTokenLength(maxTokenLength);
    TokenStream result = new StandardFilter(tokenStream);
    result = new LowerCaseFilter(result);
    result = new StopFilter(result, stopSet);
 

是LowerCaseFilter, 其实在LowerCaseFilter中,也是按照同样的道理,这样由底层往上层追溯,然后再向下层流,这也是lucene的架构经典之处。 

 

还有在分词器这里,使用的是JAVACC生成的分词器,他的优越性在于简单性和可扩展性。

 

 

 

 

 

相关标签: lucene Hibernate