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
    本文版权归作者,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
    分类: 信息检索
  • 相关阅读:
    天梯赛练习 L3-011 直捣黄龙 (30分) dijkstra + dfs
    PAT甲级练习 1087 All Roads Lead to Rome (30分) 字符串hash + dijkstra
    天梯赛练习 L3-010 是否完全二叉搜索树 (30分) 数组建树模拟
    天梯赛练习 L3-008 喊山 (30分) bfs搜索
    天梯赛练习 L3-007 天梯地图 (30分) Dijkstra
    1018 Public Bike Management (30分) PAT甲级真题 dijkstra + dfs
    PAT天梯赛练习 L3-004 肿瘤诊断 (30分) 三维BFS
    课堂实验(计算1!+2!+...+100!)
    39页作业第7题
    39页作业(还款年限—月还款额表)
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2693695.html
Copyright © 2011-2022 走看看