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


    作者:1987(lianj_lee)
    www.dzxiaoshuo.com

    好久没有写博客了,最近确实是有点忙。这几天在给IK做测试,很高兴,我能为IK做贡献,希望以后能继续。

    正是由于给做测试工作,所以我就把关于lucene的分析器,过滤器和分词器这些东西温习了下,然后希望这个博客能给大家带来一定帮助。

    还有我想从今天声明一下,欢迎大家转载我的博客,我希望给越来越多的人带来帮助,但是请务必注明作者, 1987(lianj_lee)

    入正题:

    1〉analyzer主要包含分词器跟过滤器,他的功能就是:将分词器跟分析器进行合理的组合,使之产生对文本分词和过滤效果。因此,分析器使用分词和过滤器构成一个管道,文本在“滤过”这个管道之后,就成为可以进入索引的最小单位。
    2〉tokenizer主要用于对文本资源进行切分,将文本规则切分为一个个可以进入索引的最小单元
    3〉tokenfilter主要对分词器切分的最小单位进入索引进行预处理,如:大写转小写,复数转单数,也可以复杂(根据语义改写拼写错误的单词)

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

     

    Java代码  收藏代码
    1. public abstract TokenStream tokenStream(String fieldName, Reader reader);//该抽象为子分析器扩展,后面会在说到  
    2.   
    3.   public TokenStream reusableTokenStream(String fieldName, Reader reader) throws IOException {  
    4.     return tokenStream(fieldName, reader);  
    5.   }//这个是后来发行版本添加的,主要用途是:创建一个TokenStream,为了同一个线程重复使用,节省时间  
    6.   
    7.   private CloseableThreadLocal tokenStreams = new CloseableThreadLocal();// 利用ThreadLocal 来达到在同一个线程重复使用。 这种应用很普遍,例如hibernate的session也是这种情况  
    8.   
    9.   protected Object getPreviousTokenStream() {  
    10.     try {  
    11.       return tokenStreams.get();  
    12.     } catch (NullPointerException npe) {  
    13.       if (tokenStreams == null) {  
    14.         throw new AlreadyClosedException("this Analyzer is closed");  
    15.       } else {  
    16.         throw npe;  
    17.       }  
    18.     }  
    19.   }  
    20.   
    21.   protected void setPreviousTokenStream(Object obj) {  
    22.     try {  
    23.       tokenStreams.set(obj);  
    24.     } catch (NullPointerException npe) {  
    25.       if (tokenStreams == null) {  
    26.         throw new AlreadyClosedException("this Analyzer is closed");  
    27.       } else {  
    28.         throw npe;  
    29.       }  
    30.     }  
    31.   }  
    32.   
    33.   public int getPositionIncrementGap(String fieldName)  
    34.   {  
    35.     return 0;  
    36.   }  
    37.   
    38.   public void close() {  
    39.     tokenStreams.close();  
    40.     tokenStreams = null;  
    41.   }  


    Java代码  收藏代码
    1. public TokenStream tokenStream(String fieldName, Reader reader) {  
    2.     StandardTokenizer tokenStream = new StandardTokenizer(reader, replaceInvalidAcronym);  
    3.     tokenStream.setMaxTokenLength(maxTokenLength);  
    4.     TokenStream result = new StandardFilter(tokenStream);  
    5.     result = new LowerCaseFilter(result);  
    6.     result = new StopFilter(result, stopSet);  
    7.     return result;  
    8.   }  


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

    Java代码  收藏代码
    1. public StandardTokenizer(Reader input, boolean replaceInvalidAcronym) {  
    2.     this.replaceInvalidAcronym = replaceInvalidAcronym;  
    3.     this.input = input;  
    4.     this.scanner = new StandardTokenizerImpl(input);  
    5.   }  



    StandardTokenizerImpl的构造方法:

    Java代码  收藏代码
    1. StandardTokenizerImpl(java.io.Reader in) {  
    2.     this.zzReader = in;  
    3.   }  



    StartdFilter的构造方法:

    Java代码  收藏代码
    1. public StandardFilter(TokenStream in) {  
    2.     super(in);  
    3.   }  



    当建立索引或者搜索的时候,会调用由tokenStream方法返回的TokenStream的next()方法,也是这个时候真正的分词和过滤就开始了。
    接着看,当第一调用next()方法时候,首先应该进入StopFilter的next,因为result = new StopFilter(result, stopSet);

    Java代码  收藏代码
    1. public final Token next(final Token reusableToken) throws IOException {  
    2.     assert reusableToken != null;  
    3.     // return the first non-stop word found  
    4.     int skippedPositions = 0;  
    5.     for (Token nextToken = input.next(reusableToken); nextToken != null; nextToken = input.next(reusableToken)) {  
    6.       if (!stopWords.contains(nextToken.termBuffer(), 0, nextToken.termLength())) {  
    7.         if (enablePositionIncrements) {  
    8.           nextToken.setPositionIncrement(nextToken.getPositionIncrement() + skippedPositions);  
    9.         }  
    10.         return nextToken;  
    11.       }  
    12.       skippedPositions += nextToken.getPositionIncrement();  
    13.     }  
    14.     // reached EOS -- return null  
    15.     return null;  
    16.   }  


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

    Java代码  收藏代码
    1. StandardTokenizer tokenStream = new StandardTokenizer(reader, replaceInvalidAcronym);  
    2.     tokenStream.setMaxTokenLength(maxTokenLength);  
    3.     TokenStream result = new StandardFilter(tokenStream);  
    4.     result = new LowerCaseFilter(result);  
    5.     result = new StopFilter(result, stopSet);  


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

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

  • 相关阅读:
    用于重启Greenbrowser的插件[V1.3]
    奥运开幕式没有八卦
    头像和Karma汽车
    ScribeFire试用
    人生三宝
    月光:一个漂亮的flash
    google chromium浏览器绿色版+升级器+启动器
    firefox 缓存问题导致“载入页面时到服务器的连接被重置”
    生命的使命和程序(V2)
    性,野心和梦
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2739313.html
Copyright © 2020-2023  润新知