Lucene的分析过程
转自:http://www.open-open.com/lib/view/open1348033848724.html
Lucene的分析过程
回顾倒排索引的构建
- 收集待建索引的原文档(Document)
- 将原文档传给词条化工具(Tokenizer)进行文本词条化
- 将第二步得到的词条(Token)传给语言分析工具(Linguistic modules)进行语言学预处理,得到词项(Term)
- 将得到的词项(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 tokenStreams = newCloseableThreadLocal(); 成员变量, 保存当前线程原来创建过的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
Tokenizer类的主要作用:接收Read对象,读取字符串进行分词并创建Term项。
TokenFilter类使用装饰者模式(lucene in action中作者写的是组合模式,本人窃以为应该是装饰者模式),封装另一个TokenStream类,主要负责处理输入的Token项,然后通过新 增、删除或修改Attribute的方式来修改Term流。
如上图,当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]
Lucene的分析过程的更多相关文章
- 记一次ORACLE的UNDO表空间爆满分析过程
这篇文章是记录一次ORACLE数据库UNDO表空间爆满的分析过程,主要整理.梳理了同事分析的思路.具体过程如下所示: 早上收到一数据库服务器的UNDO表空间的告警邮件,最早一封是7:55发出的(监控作 ...
- 一次数据库hang住的分析过程
现象: 普通用户和sysdba都无法登陆,业务中断 分析过程: 1.先做hanganalyze和systemstate dump $sqlplus -prelim "/as sysdba&q ...
- LL(1)文法分析表的构造和分析过程示例
在考完编译原理之后才弄懂,悲哀啊.不过懂了就好,知识吗,不能局限于考试. 文法: E→TE' E'→+TE'|ε T→FT ' T'→*FT'|ε F→id| (E) 一.首先判断是不是 LL(1)文 ...
- 一个DOS攻击木马的详细分析过程
一个DOS攻击木马的详细分析过程 0×01 起因 网路流量里发现了大量的的1.exe的文件,而且一直在持续,第一感觉就像是一个木马程序,而且每个1.exe的MD5都不一样,对比发现只有几个字节不一样( ...
- Elasticsearch - 理解字段分析过程(_analyze与_explain)
我们经常会遇到问题.为什么指定的文档没有被搜索到.许多情况下, 这都归因于映射的定义和分析例程配置存在问题. 针对分析过程的调试,ElasticSearch提供了专用的REST API. _analy ...
- 一个杀不死的小强,kill进程无效的原因 记录故障排查过程中kill进程无效的分析过程
今天在处理一个机器异常负载(1000+)的问题,碰到了一个从未碰到过的情况,遇到了一个异常顽固的分子.我使用了所能想到的所有杀进程的方法,却始终无法干掉这个顽固分子,最后终于在谷歌大神的指引下,干掉了 ...
- 使用Django.core.cache操作Memcached导致性能不稳定的分析过程
使用Django.core.cache操作Memcached导致性能不稳定的分析过程 最近测试一项目,用到了Nginx缓存服务,那可真是快啊!2Gb带宽都轻易耗尽. 不过Api接口无法简单使用Ngin ...
- MT6753平台一项目不同手机最低亮度存偏差问题分析过程
现象: MT6753平台一项目不同手机将背光高度调到最低,最低亮度存偏差问题,有一些亮,有一些暗. 现象较明显. 分析过程: 第一天: 和TCL屏天一起验证,有以下结论: 1.TCL和YASSI模组, ...
- Mysql死锁如何排查:insert on duplicate死锁一次排查分析过程
前言 遇到Mysql死锁问题,我们应该怎么排查分析呢?之前线上出现一个insert on duplicate死锁问题,本文将基于这个死锁问题,分享排查分析过程,希望对大家有帮助. 死锁案发还原 表结构 ...
随机推荐
- TCP/IP BOOKS
TCP/IP Fundamentals for Microsoft Windows: Overview https://technet.microsoft.com/en-us/library/bb72 ...
- Unity3D入门
Unity3D是一款应用广泛的3D游戏引擎,本文主要介绍unity3D的简单应用,安装过程略过. 在游戏的整个开发过程中,游戏界面设计占据非常重要的地位.因为游戏启动后,第一个映入眼帘的就是整个游戏U ...
- js实现光标位置置后
//定位div(contenteditable = "true"),上传图片后,光标移到输入框后面 function po_Last_Div(obj) { if (window.g ...
- C++输入输出流格式控制
来源:http://blog.csdn.net/virtualdesk/article/details/5355793 1.使用控制符控制输出格式 控制符 作用 dec 设置整数的基数为10 hex ...
- 如何提高Linux操作系统的安全性 转自https://yq.aliyun.com/articles/24251?spm=5176.100239.blogcont24250.7.CfBYE9
摘要: Linux系统不论在功能上.价格上或性能上都有很多优点,但作为开放式操作系统,它不可避免地存在一些安全隐患.关于如何解决这些隐患,为应用提供一个安全的操作平台,本文会告诉你一些最基本.最常用, ...
- About SSDT BI
Welcome to Microsoft Marketing Speak hell. With the 2012 release of SQL Server, the BIDS, Business I ...
- React - redux, jsx中写js示例
{ this.state.avatarSource === null ? <Text>Select a Photo</Text> : <Image style={styl ...
- 承接Holograms外包 Holograms内容定制 Holograms场景外包开发
HoloLens仿真器与文档现已向开发者们开放 如何为Microsoft HoloLens全息眼镜开发应用? 每款运行Windows 10的设备都使用了相同统一的Windows内核.所以你学习了所有有 ...
- Web性能测试工具JMeter
做Web方面的黑盒测试,也就是功能测试,基本不需要什么测试工具,都是直接打开浏览器访问,点一点界面就行. 现在流行的移动互联网应用,客户端和服务端的开发是分离的,两者开发进度肯定不一样,可能存在服务端 ...
- c++指针与引用问题
本来是回答问题的,到这里做个笔记 *&L是指针的引用,实参是个指针.所以L是实参指针的别名,对别名L的修改,等于对实参的修改.*L是传值,你无法改变传过来的实参指针变量的值程序代码: #inc ...