ITPub博客

首页 > Linux操作系统 > Linux操作系统 > Lucene源码解析--TokenStream和AttributeSource

Lucene源码解析--TokenStream和AttributeSource

原创 Linux操作系统 作者:百联达 时间:2013-07-07 19:35:11 0 删除 编辑
一:Lucene 的概况


Normal 0 7.8 磅 0 2 false false false EN-US ZH-CN X-NONE 

  • Luceneanalysis模块主要负责词法分析及语言处理而形成Term
  • Luceneindex模块主要负责索引的创建,里面有IndexWriter
  • Lucenestore模块主要负责索引的读写。
  • LuceneQueryParser主要负责语法分析。 
  • Lucenesearch模块主要负责对索引的搜索。
  • Lucenesimilarity模块主要负责对相关性打分的实现。

Lucene包结构功能表

包名

功能

org.apache.lucene.analysis

语言分析器,主要用于的切词,支持中文主要是扩展此类

org.apache.lucene.document

索引存储时的文档结构管理,类似于关系型数据库的表结构

org.apache.lucene.index

索引管理,包括索引建立、删除等

org.apache.lucene.queryParser

查询分析器,实现查询关键词间的运算,如与、或、非等

org.apache.lucene.search

检索管理,根据查询条件,检索得到结果

org.apache.lucene.store

数据存储管理,主要包括一些底层的I/O操作

org.apache.lucene.util

一些公用类


二:TokenStream和AttributeStream
Normal 0 7.8 磅 0 2 false false false EN-US ZH-CN X-NONEAnalyzer通过对文本的分析来建立TokenStreams(分词数据流)TokenStream是由一个个Token(分词组成的数据流)。所以说Analyzer就代表着一个从文本数据中抽取索引词(Term)的一种策略。为了定义具体完成哪些分析工作,Analyzer的子类必须实行自己的createCompontents方法。这里也是我们进行中文扩展的主要切入口。


Normal 0 7.8 磅 0 2 false false false EN-US ZH-CN X-NONE

TokenStream即是从Document的域(field)中或者查询条件中抽取一个个分词而组成的一个数据流。TokenSteam中是一个个的分词,而每个分词又是由一个个的属性(Attribute)组成。对于所有的分词来说,每个属性只有一个实例。这些属性都保存在AttributeSource中,而AttributeSource正是TokenStream的父类。

Normal 0 7.8 磅 0 2 false false false EN-US ZH-CN X-NONE

TokenStream的工作流程:

1.实例化TokenStream, 添加属性到AttributeSource,或从AttributeSource中获取属性.

2.调用reset()方法,设置stream的初始状态

3.调用increamStoken()方法,来获取下一个分词。这个方法会被docuemnt中的每一个分词调用。所以一个有效的实现对于好的性能来说是至关重要的。

4.调用end()方法来完成一些收尾工作

5.调用close()方法来释放stream关联的一些资源。


Normal 0 7.8 磅 0 2 false false false EN-US ZH-CN X-NONE

AttributeSource:

一个AttributeSource中包含着一个由不同AttributeImpl组成的列表,以及添加和获取它们的一些方法。

在同一个AttributeSource实例中 每个属性只有一个单实例。AttributeSource通过AttributeFactory来创建AttributeImpl的实例。通过State来标示每个AttributeImpl的状态。

 private final Map, AttributeImpl> attributes; 

private final Map, AttributeImpl> attributeImpls;

上述两个成员保存了两种映射关系,

1.AttributeImpl实例对应实现的所有Attribute接口,都可以映射到该 AttributeImpl实例;

2.AttributeImpl实例对应实现的所有子类对该 AttributeImpl实例的映射。

设计这两个映射关系的目的是在该AttributeSource实例中对每个AttributeAttributeImpl保证只有一个 AttributeImpl实例,换句话说,当用具体Attribute或者具体AttributeImpl获取其对象实例时,不会每次都新建实例,而是 首次时建立,其后只返回以前建立的。

三:AttributeImpl类介绍

1.AttributeImpl

一个可以往attributeSource中添加属性的基类,属性通常用来以动态的,线程安全的方式往一个流的数据源中添加数据,如tokenStream。

2.CharTermAttributeImpl

保存Token对应的term文本

3.FlagsAttributeImpl

在Tokenizer链中,用以在不同的节点之间传递标识信息。该类同TypeAttribute有着相似的目录但他们之间还是有所不同的,Flags可以用于不同TokenFilter之间分词(Token)信息的加密。

4.TypeAttributeImpl

分词的词汇类型,默认值为“word”

5.KeywordAttributeImpl

该属性用于标识一个分词(token)为关键字。对于TokenStream来说可以用此属性判断分词(Token)是否为关键字来决定是否进行修改,对于TokenFilter来说可以根据分词是否为关键字来进行跳过(skip)处理。

6.OffsetAttributeImpl

Token分词的起始字符,结束字符偏移量

7.PositionIncrementAttribute

它表示tokenStream中的当前token与前一个token在实际的原文本中相隔的词语数量

8.PositionLengthAttributeImpl

Token所占用的位置个数

举例:

原文本:I'm a student. these are apples    

TokenSteam: [1:  I'm ]  [2:a]   [3:student]     [4:these]   [5:are ]   [6:apples]

 

(1) TermAttribute: 表示token的字符串信息。比如"I'm"

(2) TypeAttribute: 表示token的类别信息(在上面讲到)。比如 I'm 就属于,有撇号的类型

(3) OffsetAttribute:表示token的首字母和尾字母在原文本中的位置。比如 I'm 的位置信息就是(0,3)

(4) PositionIncrementAttribute:这个有点特殊,它表示tokenStream中的当前token与前一个token在实际的原文本中相隔的词语数量。

       比如: 在tokenStream中[2:a] 的前一个token是[1:  I'm ] ,它们在原文本中相隔的词语数是1,则token="a"的PositionIncrementAttribute值为1。如果token是原文本中的第一个词,则默认值为1。因此上面例子的PositionIncrementAttribute结果就全是1了。

       如果我们使用停用词表来进行过滤之后的话:TokenSteam就会变成: [1:  I'm ]   [2:student]    [3:apples]这时student的PositionIncrementAttribute值就不会再是1,而是与[1:  I'm ]在原文本中相隔词语数量=2。而apples则变成了3。

       那么这个属性有什么用呢,用处很大的。加入我们想搜索一个短语student apples(假如有这个短语)。很显然,用户是要搜索出student apples紧挨着出现的文档。这个时候我们找到了某一篇文档(比如上面例子的字符串)都含有student apples。但是由于apples的PositionIncrementAttribute值是3,说明肯定没有紧挨着。怎么样,用处很大吧。轻而易举的解决了短语搜索的难题哦。


四:总结

Normal 0 7.8 磅 0 2 false false false EN-US ZH-CN X-NONE

TokenStream的作用是从给入的文本中不断解析出Token,具体的做法是TokenStream有方法incrementToken,每次调用 将产生待分析文本的下一个Token,其实incrementToken做的事情就是填充用户所关心的若干属性,通过这些属性来反馈分析结果,因此自然而然 的一种想法是TokenStream的派生类中有若干的属性成员,每次调用incrementToken都首先清除上一次的属性信息,然后进行分析并填充 属性,这样做无可厚非,但是请考虑TokenStream流的嵌套,也就是说嵌套的内层流获取的属性将作为外层流的分析的输入,如果使用上述方法实现 TokenStream,则必然嵌套流的每层流都将有自己的属性实例,而层次之间可能会出现同样的属性,也就是说同样的属性实例在流层次中可能会有多个, 这样是没有必要的,也就是说对相同的属性在流层次中只有一个实例就可以满足分析的需求了。

 

当将TokenStream所关心的属性抽象的由AttributeSource来管理时,我们在进行流的嵌套时,根据对 AttributeSource的分析可知,外层流定义自己关心的属性,并不需要在构造函数中实例化该属性,而是从AttributeSource中获 取,如果存在的话,则直接返回实例,否则新建,这样在流嵌套式外层流和内存流共享AttributeSource,也就是说当外层流和内层流都关心某个属 性时,内层流首先初始化,此时他将会将该属性注册到AttributeSource中,这样在外层流初始化时将向AttributeSource获取该属 性,从而可以保证在流层次中若干层流都关心的属性只有一份实例。



clip_image008_thumb.jpg

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/28624388/viewspace-765691/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
10年以上互联网经验,先后从事过制造业,证券业,物业行业和物流行业信息系统和互联网产品的研发,6年系统架构经验。最近关注Kubernetes微服务架构和Istio微服务治理框架。

注册时间:2013-02-05

  • 博文量
    317
  • 访问量
    1010981