Lucene分词器详解

学习目标:

  • 了解Lucene中提供的分词器实现
  • 熟练使用Lucene分词器API
  • 掌握IKAnaylzer中文分词器的集成

 

 

Lucene-分词器API

1、org.apache.lucene.analysi.Analyzer

  • 分析器,分词器组件的核心API,它的职责:构建真正对文本进行分词处理的TokenStream(分词处理器)。通过调用它的如下两个方法,得到输入文本的分词处理器。
public final TokenStream tokenStream(String fieldName, Reader reader)
public final TokenStream tokenStream(String fieldName, String text)

这两个方法是final方法,不能被覆盖的,在这两个方法中是如何构建分词处理器的呢?
请查看源码,回答后面的问题

  • 问题1:从哪里得到了TokenStream?
  • 问题2:方法传入的字符流Reader 给了谁?
  • 问题3:components是什么?components的获取逻辑是怎样?
  • 问题4:createComponents(fieldName) 方法是个什么方法?
  • 问题5:Analyzer能直接创建对象吗?
  • 问题6:为什么它要这样设计?
  • 问题7:请看一下Analyzer的实现子类有哪些?
  • 问题8:要实现一个自己的Analyzer,必须实现哪个方法?

2、TokenStreamComponents createComponents(String fieldName)

  • 是Analizer中唯一的抽象方法,扩展点。通过提供该方法的实现来实现自己的Analyzer。
  • 参数说明:fieldName,如果我们需要为不同的字段创建不同的分词处理器组件,则可根据这个参数来判断。否则,就用不到这个参数。
  • 返回值为 TokenStreamComponents 分词处理器组件。
  • 我们需要在createComponents方法中创建我们想要的分词处理器组件。

请看它的源码,回答后面的问题

  TokenStreamComponents 是什么?

3、TokenStreamComponents

  • 分词处理器组件:这个类中封装有供外部使用的TokenStream分词处理器。提供了对source(源)和sink(供外部使用分词处理器)两个属性的访问方法。

请看它的源码,回答后面的问题

  • 问题1:这个类的构造方法有几个?区别是什么?从中能发现什么?
  • 问题2:source 和 sink属性分别是什么类型?这两个类型有什么关系?
  • 问题3:在这个类中没有创建source、sink对象的代码(而是由构造方法传人)。也就是说我们在Analyzer.createComponents方法中创建它的对象前,需先创建什么?
  • 问题4:在Analyzer中tokenStream() 方法中把输入流给了谁?得到的TokenStream对象是谁?TokenStream对象sink中是否必须封装有source对象? 如果必须有,这个封装是否也得在Analyzer.createComponents方法中完成?

4、org.apache.lucene.analysis.TokenStream

  • 分词处理器,负责对输入文本完成分词、处理。

请看它的源码,回答后面的问题

  • 问题1:TokenStream中有没有对应的给入方法?
  • 问题2:TokenStream是一个抽象类,有哪些方法,它的抽象方法有哪些?它的构造方法有什么特点?
  • 问题3:TokenStream的具体子类分为哪两类?有什么区别?概念说明:Token: 分项,从字符流中分出一个一个的项
  • 问题4:TokenStream继承了谁?它是干什么用的?概念说明:Token Attribute: 分项属性(分项的信息):如 包含的词、位置等

5、TokenStream 的两类子类

  • Tokenizer:分词器,输入是Reader字符流的TokenStream,完成从流中分出分项
  • TokenFilter:分项过滤器,它的输入是另一个TokenStream,完成对从上一个TokenStream中流出的token的特殊处理。

请看它的源码,回答后面的问题

  • 问题1:请查看Tokenizer类的源码及注释,这个类该如何使用?要实现自己的Tokenizer只需要做什么?
  • 问题2:Tokenizer的子类有哪些?
  • 问题3:请查看TokenFilter类的源码及注释,如何实现自己的TokenFilter?
  • 问题4:TokenFilter的子类有哪些?
  • 问题5:TokenFilter是不是一个典型的装饰器模式?如果我们需要对分词进行各种处理,只需要按我们的处理顺序一层层包裹即可(每一层完成特定的处理)。不同的处理需要,只需不同的包裹顺序、层数。

6、TokenStream 继承了 AttributeSource

  • 问题1:我们在TokenStream及它的两个子类中是否有看到关于分项信息的存储,如该分项的词是什么、这个词的位置索引?概念说明:Attribute 属性 Token Attribute 分项属性(分项信息),如 分项的词、词的索引位置等等。这些属性通过不同的Tokenizer /TokenFilter处理统计得出。不同的Tokenizer/TokenFilter组合,就会有不同的分项信息。它是会动态变化的,你不知道有多少,是什么。那该如何实现分项信息的存储呢?

答案就是 AttributeSource、Attribute 、AttributeImpl、AttributeFactory

  • AttribureSource 负责存放Attribute对象,它提供对应的存、取方法
  • Attribute对象中则可以存储一个或多个属性信息
  • AttributeFactory 则是负责创建Attributre对象的工厂,在TokenStream中默认使用了AttributeFactory.getStaticImplementation 我们不需要提供,遵守它的规则即可。

7、AttributeSource使用规则说明
  1、某个TokenStream实现中如要存储分项属性,通过AttributeSource的两个add方法之一,往AttributeSource中加入属性对象。
    <T extends Attribute> T addAttribute(Class<T> attClass)
    该方法要求传人你需要添加的属性的接口类(继承Attribute),返回对应的实现类实例给你。从接口到实例,这就是为什么需要AttributeFactory的原因。
void addAttributeImpl(AttributeImpl att)
  2、加入的每一个Attribute实现类在AttributeSource中只会有一个实例,分词过程中,分项是重复使用这一实例来存放分项的属性信息。重复调用add方法添加它返回已存储的实例对象。
  3、要获取分项的某属性信息,则需持有某属性的实例对象,通过addAttribute方法或getAttribure方法获得Attribute对象,再调用实例的方法来获取、设置值
  4、在TokenStream中,我们用自己实现的Attribute,默认的工厂。当我们调用这个add方法时,它怎么知道实现类是哪个?这里有一定规则要遵守:

    •   自定义的属性接口 MyAttribute 继承 Attribute
    •   自定义的属性实现类必须继承 Attribute,实现自定义的接口MyAttribute
    •   自定义的属性实现类必须提供无参构造方法
    •   为了让默认工厂能根据自定义接口找到实现类,实现类名需为 接口名+Impl 。

请查看lucene中提供的Attribute实现是否是这样的。

8、TokenStream 的使用步骤。

  • 我们在应用中并不直接使用分词器,只需为索引引擎和搜索引擎创建我们想要的分词器对象。但我们在选择分词器时,会需要测试分词器的效果,就需要知道如何使用得到的分词处理器TokenStream,使用步骤:
    • 1、从tokenStream获得你想要获得分项属性对象(信息是存放在属性对象中的)  
    • 2、调用 tokenStream 的 reset() 方法,进行重置。因为tokenStream是重复利用的。
    • 3、循环调用tokenStream的incrementToken(),一个一个分词,直到它返回false
    • 4、在循环中取出每个分项你想要的属性值。
    • 5、调用tokenStream的end(),执行任务需要的结束处理。
    • 6、调用tokenStream的close()方法,释放占有的资源。

思考:tokenStream是装饰器模式,这个reset / incrementToken / end / close是如何工作的? 请查看 tokenStream / Tokenizer / TokenFilter的源码。

9、简单实现一个我们自己的Analyzer,需求说明

  • Tokenizer:
    •  实现对英文按空白字符进行分词。 
    •  需要记录的属性信息有: 词
  • TokenFilter:
    •  要进行的处理:转为小写

说明:Tokenizer分词时,是从字符流中一个一个字符读取,判断是否是空白字符来进行分词。

思考:Tokenizer是一个 AttributeSource对象,TokenFilter 又是一个AttributeSource对象。在这两个我们自己的实现类中,我们都调用了addAttribute方法,怎么会只有一个 attribute对象? 请查看源码找到答案。

 

小结:

  • 通过API及源码的学习,你是否感受到了作者的一些设计思想
  • 他是如何处理变与不变的,如Analyzer、TokenStream类的设计?
  • 他是如何处理不同分词器有不同的处理逻辑的问题的?

 

Lucene提供的分词器

  • Lucene core模块中的 StandardAnalyzer 英文分词器
    • 看看它都分析存储哪些属性信息  
    • 试试它的英文分词效果
    • 中文分词效果
  • Lucene 的中文分词器 SmartChineseAnalyzer
    • 试试它的中英文分词效果
<!-- Lucene提供的中文分词器模块,lucene-analyzers-smartcn -->
<dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-analyzers-smartcn</artifactId>
    <version>7.3.0</version>
</dependency>
  • Lucene-Analyzers-common包中提供的分词器
    • 看看都有些什么分词器

 


 

IKAnalyzer集成

1、IKAnalyzer 开源、轻量级的中文分词器,应用比较多

  • 最先是作为lucene上使用而开发,后来发展为独立的分词组件。只提供到Lucene 4.0版本的支持。我们在4.0以后版本Lucene中使用就需要简单集成一下。
<!-- ikanalyzer 中文分词器 -->
<dependency>
    <groupId>com.janeluo</groupId>
    <artifactId>ikanalyzer</artifactId>
    <version>2012_u6</version>
    <exclusions>
        <exclusion>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-core</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-queryparser</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-analyzers-common</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<!-- lucene-queryparser 查询分析器模块 -->
<dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-queryparser</artifactId>
    <version>7.3.0</version>
</dependency>    

2、需要做集成,是因为Analyzer的createComponents方法API改变了。

  • 集成步骤:
    • 找到 IkAnalyzer包体提供的Lucene支持类,比较IKAnalyzer的createComponets方法。
    • 照这两个类,创建新版本的,类里面的代码直接复制,修改参数即可。

3、IKAnalyzer提供两种分词模式:细粒度分词和智能分词,看它的构造参数。

4、集成后,请测试细粒度分词、智能分词的效果

5、请比较IKAnalyzer和SmartChineseAnalyzer

6、扩展 IKAnalyzer的停用词

  • Ik中默认的停用词很少,我们往往需要扩展它。可从网址: https://github.com/cseryp/stopwords 下载一份比较全的停用词。
  • Ik中停用词的扩展步骤:
  1. 在类目录下创建IK的配置文件:src/main/resouce/IKAnalyzer.cfg.xml
  2. 在配置文件中增加配置扩展停用词文件的节点(如有多个,以“;”间隔):<entry key=“ext_stopwords”>my_ext_stopword.dic</entry>
  3. 在类目录下创建我们的扩展停用词文件 my_ext_stopword.dic
  4. 编辑该文件加入停用词,一行一个(注意文件要是UTF-8编码)

7、扩展 IKAnalyzer的词典

  • 每年都有很多的新词产生,往分词器的词典中添加新词的步骤:
  1. 在类目录下创建IK的配置文件:src/main/resouce/IKAnalyzer.cfg.xml
  2. 在配置文件中增加配置扩展词文件的节点如有多个,以“;”间隔<entry key="ext_dict">ext.dic</entry>
  3. 在类目录下创建扩展词文件 ext.dic
  4. 编辑该文件加入新词,一行一个(注意文件要是UTF-8编码)

8、IKAnalyzer.cfg.xml 文件示例

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
    <comment>IK Analyzer 扩展配置</comment>
    
    <!--用户可以在这里配置自己的扩展字典 -->
    <entry key="ext_dict">ext.dic</entry>
    
    <!--用户可以在这里配置自己的扩展停止词字典-->
    <entry key="ext_stopwords">my_ext_stopword.dic</entry>
</properties>

 

转载于:https://www.cnblogs.com/morn21/p/9045637.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值