zoukankan      html  css  js  c++  java
  • Lucene的分析过程

    Lucene的分析过程

    回顾倒排索引的构建

    Image(3)

    1. 收集待建索引的原文档(Document)
    2. 将原文档传给词条化工具(Tokenizer)进行文本词条化
    3. 将第二步得到的词条(Token)传给语言分析工具(Linguistic modules)进行语言学预处理,得到词项(Term)
    4. 将得到的词项(Term)传给索引组件(Indexer),建立倒排索引

    注:详细文档->倒排索引的理论过程见词项词典及倒排记录表

    分析操作的使用场景

    1.如上,倒排索引的构建阶段

    2.针对自由文本的查询阶段

    QueryParser parser = new QueryParser(Version.LUCENE_36, field, analyzer);

    Query query = parser.parse(queryString);

    lucene的Analyzer接收表达式queryString中连续的独立的文本片段,但不会接收整个表达式。

    例如:对查询语句"president obama" + harvard + professor,QueryParser会3次调用分析器,首先是处理文本“president obama”,然后是文本“harvard”,最后处理“professor”。

    3.搜索结果中高亮显示被搜索内容时(即结果摘要-Snippets的生成),也可能会用到分析操作

    剖析lucene分析器

     

    抽象类Analyzer

    Analyzer类是一个抽象类,是所有分析器的基类。

    其主要包含两个接口,用于生成TokenStream(所谓TokenStream,后面我们会讲到,是一个由分词后的Token 结果组成的流,能够不断的得到下一个分成的Token。)。

    接口:

    1.TokenStream tokenStream(String fieldName, Reader reader)

    2.TokenStream reusableTokenStream(String fieldName, Reader reader)

    为了提高性能,使得在同一个线程中无需再生成新的TokenStream 对象,老的可以被重用,所以有reusableTokenStream 一说。

    Analyzer 中有CloseableThreadLocal<Object> tokenStreams = newCloseableThreadLocal<Object>(); 成员变量, 保存当前线程原来创建过的TokenStream , 可用函数setPreviousTokenStream 设定,用函数getPreviousTokenStream 得到。在reusableTokenStream 函数中,往往用getPreviousTokenStream 得到老的TokenStream 对象,然后将TokenStream 对象reset 一下,从而可以重新开始得到Token 流。

    抽象类ReusableAnalyzerBase

    ReusableAnalyzerBase extendsAnalyzer,顾名思义主要为tokenStream的重用。

    其包含一个接口,用于生成TokenStreamComponents。

    接口:

    TokenStreamComponents createComponents(String fieldName,Reader reader);

    reusableTokenStream的实现代码分析:

      public final TokenStream reusableTokenStream(final String fieldName,
          final Reader reader) throws IOException {
        // 得到上一次使用的TokenStream
        TokenStreamComponents streamChain = (TokenStreamComponents)getPreviousTokenStream();
        final Reader r = initReader(reader);
        //如果没有PreviousTokenStream则生成新的, 并且用setPreviousTokenStream放入成员变量,使得下一个可用。
        //如果上一次生成过TokenStream,则reset。reset失败则生成新的。
        if (streamChain == null || !streamChain.reset(r)) {
          streamChain = createComponents(fieldName, r);
          setPreviousTokenStream(streamChain);
        }
        return streamChain.getTokenStream();
      }
    

    内部static类TokenStreamComponents

    简单封装输入Tokenizer和输出TokenStream。

    最简单的一个Analyzer:SimpleAnalyzer

    SimpleAnalyzer extendsReusableAnalyzerBase,实现createComponents方法。TokenStream的处理是将字符串最小化,生成按照空格分隔的Token流

      protected TokenStreamComponents createComponents( final String fieldName,
          final Reader reader) {
        return new TokenStreamComponents(new LowerCaseTokenizer(matchVersion , reader));
      }
     

    抽象类TokenStream

    TokenStream 主要包含以下几个方法:
    1. boolean incrementToken()用于得到下一个Token。IndexWriter调用此方法推动Token流到下一个Token。实现类必须实现此方法并更新Attribute信息到下一个Token。
    2. public void reset() 重设Token流到开始,使得此TokenStrean 可以重新开始返回各个分词。

    和原来的TokenStream返回一个Token 对象不同,Lucene 3.0 开始,TokenStream已经不返回Token对象了,那么如何保存下一个Token 的信息呢?
    在Lucene 3.0 中,TokenStream 是继承于AttributeSource,其包含Map,保存从class 到对象的映射,从而可以保存不同类型的对象的值。在TokenStream 中,经常用到的对象是CharTermAttributeImpl,用来保存Token 字符串;PositionIncrementAttributeImpl 用来保存位置信息;OffsetAttributeImpl 用来保存偏移量信息。所以当生成TokenStream 的时候, 往往调用CharTermAttribute tokenAtt = addAttribute(CharTermAttribute.class)将CharTermAttributeImpl添加到Map 中,并保存一个成员变量。在incrementToken() 中, 将下一个Token 的信息写入当前的tokenAtt , 然后使用CharTermAttributeImpl.buffer()得到Token 的字符串。

    注:Lucene 3.1开始废弃了TermAttribute和TermAttributeImpl,用CharTermAttribute和CharTermAttributeImpl代替。

    Token attributes

    如上述,Token的信息真正存在于各个AttributeImpl中,lucene内建的所有Attribute接口都在org.apache.lucene.analysis.tokenattributes包中。

    Token attributes API的使用

    1. 调用addAttribute(继承于AttributeSource)方法,返回一个对应属性接口的实现类,以获得需要的属性。

    2. 递归TokenStream incrementToken()方法,遍历Token流。当incrementToken返回true时,其中Token的属性信息会将内部状态修改为下个词汇单元。

    3. lucene内建Attribute接口都是可读写的,TokenStream 在遍历Token流时,会调用Attribute接口的set方法,修改属性信息。

    lucene内建常用Attribute接口

    1. CharTermAttribute      保存Token对应的term文本,Lucene 3.1开始用CharTermAttribute代替TermAttribute

    2. FlagsAttribute             自定义标志位

    3. OffsetAttribute            startOffset是指Term的起始字符在原始文本中的位置,endOffset则表示Term文本终止字符的下一个位置。偏移量常用于搜索结果中高亮Snippets的生成

    4. PayloadAttribute          保存有效负载

    5. TypeAttribute              保存Token类型,默认为"word",实际中可根据Term的词性来做自定义操作

    6. PositionIncrementAttribute 

    保存相对于前一个Term的位置信息,默认值设为1,表示所有的Term都是连续的,在位置上是一个接一个的。如果位置增量大于1,则表示Term之间有空隙,可以用这个空隙来表示被删除的Term项(如停用词)。位置增量为0,则表示该Term项与前一个Term项在相同的位置上,0增量常用来表示词项之间是同义词。位置增量因子会直接影响短语查询和跨度查询,因为这些查询需要知道各个Term项之间的距离。

    注:并不是所有的Attribute信息都会保存在索引中,很多Attribute信息只在分析过程使用,Term进索引后部分Attribute信息即丢弃。(如TypeAttribute、FlagsAttribute在索引阶段都会被丢弃)

    Lucene Token流 揭秘

    lucene Token流的生成,主要依赖TokenStream 的两个子类Tokenizer和TokenFilter

    tokenstream

    Tokenizer类的主要作用:接收Read对象,读取字符串进行分词并创建Term项。

    TokenFilter类使用装饰者模式(lucene in action中作者写的是组合模式,本人窃以为应该是装饰者模式),封装另一个TokenStream类,主要负责处理输入的Token项,然后通过新增、删除或修改Attribute的方式来修改Term流。

    Image(25)

    如上图,当Analyzer从它的tokenStream方法或者reusableTokenStream方法返回tokenStream对象后,它就开始用一个Tokenizer对象创建初始Term序列,然后再链接任意数量的TokenFilter来修改这些Token流。这被称为分析器链(analyzer chain)。

    一个简单的Analyzer:StopAnalyzer

      protected TokenStreamComponents createComponents(String fieldName,
          Reader reader) {
        //LowerCaseTokenizer接收Reader,根据Character.isLetter(char)来进行分词,并转换为字符小写
        final Tokenizer source = new LowerCaseTokenizer(matchVersion , reader);
        //只有一个分析器链StopFilter,来去除停用词
        return new TokenStreamComponents(source, new StopFilter(matchVersion ,
              source, stopwords));
      }
     

    StopAnalyzer测试

    String text = "The quick brown fox jumped over the lazy dog";
    System. out.println("Analyzing \"" + text + "\"");
    Analyzer analyzer = new StopAnalyzer(Version.LUCENE_36);
    String name = analyzer.getClass().getSimpleName();
    System. out.println("" + name + ":");
    System. out.print("" );
    AnalyzerUtils. displayTokens(analyzer, text);
    System. out.println("\n" );
     

    结果输出

    Analyzing "The quick brown fox jumped over the lazy dog"

      StopAnalyzer:

        [quick] [brown] [fox] [jumped] [over] [lazy] [dog]

    作者:God bless you
    本文版权归作者,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
  • 相关阅读:
    70.BOM
    69.捕获错误try catch
    68.键盘事件
    523. Continuous Subarray Sum
    901. Online Stock Span
    547. Friend Circles
    162. Find Peak Element
    1008. Construct Binary Search Tree from Preorder Traversal
    889. Construct Binary Tree from Preorder and Postorder Traversal
    106. Construct Binary Tree from Inorder and Postorder Traversal
  • 原文地址:https://www.cnblogs.com/god_bless_you/p/2693122.html
Copyright © 2011-2022 走看看