Lucene.net(4.8.0) 学习问题记录二: 分词器Analyzer中的TokenStream和AttributeSource
前言:目前自己在做使用Lucene.net和PanGu分词实现全文检索的工作,不过自己是把别人做好的项目进行迁移。因为项目整体要迁移到ASP.NET Core 2.0版本,而Lucene使用的版本是3.6.0 ,PanGu分词也是对应Lucene3.6.0版本的。不过好在Lucene.net 已经有了Core 2.0版本,4.8.0 bate版,而PanGu分词,目前有人正在做,貌似已经做完,只是还没有测试~,Lucene升级的改变我都会加粗表示。
Lucene.net 4.8.0
https://github.com/apache/lucenenet
PanGu分词(可以直接使用的)
https://github.com/SilentCC/Lucene.Net.Analysis.PanGu
JIEba分词(可以直接使用的)
https://github.com/SilentCC/JIEba-netcore2.0
Lucene.net 4.8.0 和之前的Lucene.net 3.6.0 改动还是相当多的,这里对自己开发过程遇到的问题,做一个记录吧,希望可以帮到和我一样需要升级Lucene.net的人。我也是第一次接触Lucene ,也希望可以帮助初学Lucene的同学。
一,Analyzer 中的TokenStream
1.TokenSteam的产生
在这篇博文中,其实已经介绍了TokenStream 是怎么产生的:
http://www.cnblogs.com/dacc123/p/8035438.html
在Analyzer 中,同一个线程上的所有Analyzer实例都是共用一个TokenStream,而实现如此都是因为Analyzer类中 storedValue 是全局共用的,获取TokenStream的方法是由reuseStrategy 类提供的,TokenStream 继承自AttributeSource
那么TokenStream的作用什么呢?
2.TokenSteam的使用
TokenStream 实际上是由一系列Token(分词)组合起来的序列,这里仅仅介绍如何通过TokenStream获得分词的信息。TokenStream的工作流程:
1. 创建TokenStream
2.TokenStream.Reset()
3.TokenStream.IncrementToken()
4.TokenStream.End();
5.TokenStream.Dispose() //Lucene 4.8.0中已经取消了Close(),只有Dispose()
在执行:
_indexWriter.AddDocument(doc)
之后,IndexWriter则会调用初始化时创建的Analyzer,也即IndewWriterConfig()中的Analyzer参数。这里以PanGu分词为例子。
调用分词器,首先会执行CreateComponents()函数,创建一个TokenStreamComponents,这也是为什么所有自定义,或者外部的分词器如果继承Analyzer,必须要覆写CreateComponents()函数:
protected override TokenStreamComponents CreateComponents(string fieldName, TextReader reader)
{
var result = new PanGuTokenizer(reader, _originalResult, _options, _parameters);
var finalStream = (TokenStream)new LowerCaseFilter(LVERSION.LUCENE_48, result); finalStream.AddAttribute<ICharTermAttribute>();
finalStream.AddAttribute<IOffsetAttribute>(); return new TokenStreamComponents(result, finalStream);
}
可以看到在这个CreateComponents函数中,我们可以初始化创建自己想要的Tokenizer和TokenStream。TokenStreamComponents是Lucene4.0中才有的,一个TokenStreamComponents是由Tokenizer和TokenStream组成。
在初始化完TokenStream 之后我们可以添加属性Attribute 到TokenStream中:
finalStream.AddAttribute<ICharTermAttribute>();
finalStream.AddAttribute<IOffsetAttribute>();
2.1 AttributeSource的介绍
上面说到TokenStream 继承自AttributeSource , finalStream.AddAttribute<ICharTermAttribute> 真是调用了父类AttributeSource的方法AddAttribute<T>() ,所以AttributeSoucre是用来给TokenStream添加一系列属性的,这是Lucene4.8.0中AttributeSource中AddAttribute的源码:
public T AddAttribute<T>()
where T : IAttribute
{
var attClass = typeof(T);
if (!attributes.ContainsKey(attClass))
{
if (!(attClass.GetTypeInfo().IsInterface && typeof(IAttribute).IsAssignableFrom(attClass)))
{
throw new ArgumentException("AddAttribute() only accepts an interface that extends IAttribute, but " + attClass.FullName + " does not fulfil this contract.");
}
//正真添加Attribute的函数,而创造Attribute实例则是通过AttributeSource中的
//private readonly AttributeFactory factory;
AddAttributeImpl(this.factory.CreateAttributeInstance<T>());
} T returnAttr;
try
{
returnAttr = (T)(IAttribute)attributes[attClass].Value;
}
#pragma warning disable 168
catch (KeyNotFoundException knf)
#pragma warning restore 168
{
return default(T);
}
return returnAttr;
}
2.2 Attribute介绍
上面介绍了AttributeSource 给TokenStream添加属性Attribute ,其实Attribute就是你需要获得的分词的属性。
比如:上面写到的 ICharTermAttribute 继承自CharTermAttribute 表示的是分词内容;
IOffsetAttribute 继承自 OffsetAttribute 表示的是分词起始位置和结束位置;
类似的还有 IFlasAttribute , IKeywordAttribute,IPayloadAttribute,IPositionIncrementAttribute,IPositionLengthAttribute,ITermToBytesRefAttribute,ITypeAttribute
我们再看Token(分词)类的源码:
public class Token : CharTermAttribute, ITypeAttribute, IPositionIncrementAttribute, IFlagsAttribute, IOffsetAttribute, IPayloadAttribute, IPositionLengthAttribute
其实Token(分词),是继承这些Attribute,也就是说分词是由这些属性组成的,所以就可以理解为什么在TokenStream中添加Attributes。
再回到之前,再初始化TokenStream 和添加完属性之后,必须执行TokenStream的Reset(),才可继续执行TokenStream.IncrementToken().
Reset()函数实际上在TokenStream创建和使用之后进行重置,因为我们之前说过,在Analyzer中所有实例是共用一个TokenStream的所以在TokenStream被使用过一次后,需要Reset() 以清除上次使用的信息,重新给下一个需要分词的text使用。
而IncrementToken实际的作用则是在遍历TokenStream 中的Token,类似于一个迭代器。
public sealed override bool IncrementToken()
{
ClearAttributes();
Token word = Next();
if (word != null)
{
var buffer = word.ToString();
termAtt.SetEmpty().Append(buffer);
offsetAtt.SetOffset(word.StartOffset, word.EndOffset);
typeAtt.Type = word.Type;
return true;
}
End();
this.Dispose();
return false;
}
直到返回的false ,表示分词已经遍历完了,这个时候调用End() 和Dispose() 来注销这个TokenStream。在这个过程中,TokenStream是可以被使用多次的,比如我写入索引的时候,加入两个Field :
new Field("title","xxxx")
new Field("content","xxxxx")
对这个两个域进行分词,TokenStream创建之后,会先对title进行分词,遍历。然后执行Reset(),再对content进行分词,遍历。直到所有要分词的域都遍历过了。才会执行End()和Dispose()函数进行销毁。
二,问题:搜索不到内容
在迁移的过程中,突然出现了搜索不到内容的bug,经过调试,发现写索引的时候,对文本的分词都是正确。这里要提一点,分词(Token) 和 Term的区别 ,term是最小的搜索的单位,就是每个词语,比如“我是搞IT的”,那么,经过分词 “我”,“是”,“搞”,“IT” 这些都是term,而这些分词的具体信息,比如起始位置信息,都包含在Token当中,在Lucene2.9中之后,已经不推荐用Token(分词),而直接用Attribute表示这些term的属性
后来发现写索引的时候正常,但是在搜索的时候,获取搜索关键词是,利用自己写的TokenStream获取分词信息出了错。
tokenStream.Reset();
//ItermAttribute在Lucene4.8.0中已经替换为CharTermAttribute
while (tokenStream.IncrementToken())
{ var termAttr = tokenStream.GetAttribute<ICharTermAttribute>();
var str = new string(termAttr.Buffer, , termAttr.Buffer.Length);
var positionAttr = tokenStream.GetAttribute<IOffsetAttribute>();
var start = positionAttr.StartOffset;
var end = positionAttr.EndOffset;
yield return new Token() { EndPosition = end, StartPosition = start, Term = str };
}
termAttr.Buffer 是字节数组,而termAttr.Buffer.Length 是字节数组的长度,是固定。而termAttr.Length 是字节数组中实际元素的长度,是不一样的。我那样写会导致得到term字节信息是 [69,5b,23,/0,/0,/0,/0,/0,/0,/0] 因为长度填错了,所以后面自动填充/0,这样自然搜索不到,改成termAttr.Length就可以了。
这里在提一下在Lcuene.net 4.0中新增了BytesRef 类,表示term的字节信息,以后会介绍道
Lucene.net(4.8.0) 学习问题记录二: 分词器Analyzer中的TokenStream和AttributeSource的更多相关文章
- Lucene.net(4.8.0) 学习问题记录一:分词器Analyzer的构造和内部成员ReuseStategy
前言:目前自己在做使用Lucene.net和PanGu分词实现全文检索的工作,不过自己是把别人做好的项目进行迁移.因为项目整体要迁移到ASP.NET Core 2.0版本,而Lucene使用的版本是3 ...
- Lucene.net(4.8.0) 学习问题记录五: JIEba分词和Lucene的结合,以及对分词器的思考
前言:目前自己在做使用Lucene.net和PanGu分词实现全文检索的工作,不过自己是把别人做好的项目进行迁移.因为项目整体要迁移到ASP.NET Core 2.0版本,而Lucene使用的版本是3 ...
- Lucene.net(4.8.0) 学习问题记录六:Lucene 的索引系统和搜索过程分析
前言:目前自己在做使用Lucene.net和PanGu分词实现全文检索的工作,不过自己是把别人做好的项目进行迁移.因为项目整体要迁移到ASP.NET Core 2.0版本,而Lucene使用的版本是3 ...
- Lucene.net(4.8.0)+PanGu分词器问题记录一:分词器Analyzer的构造和内部成员ReuseStategy
前言:目前自己在做使用Lucene.net和PanGu分词实现全文检索的工作,不过自己是把别人做好的项目进行迁移.因为项目整体要迁移到ASP.NET Core 2.0版本,而Lucene使用的版本是3 ...
- Lucene.net(4.8.0) 学习问题记录三: 索引的创建 IndexWriter 和索引速度的优化
前言:目前自己在做使用Lucene.net和PanGu分词实现全文检索的工作,不过自己是把别人做好的项目进行迁移.因为项目整体要迁移到ASP.NET Core 2.0版本,而Lucene使用的版本是3 ...
- Lucene.net(4.8.0) 学习问题记录四: IndexWriter 索引的优化以及思考
前言:目前自己在做使用Lucene.net和PanGu分词实现全文检索的工作,不过自己是把别人做好的项目进行迁移.因为项目整体要迁移到ASP.NET Core 2.0版本,而Lucene使用的版本是3 ...
- Lucene介绍及简单入门案例(集成ik分词器)
介绍 Lucene是apache软件基金会4 jakarta项目组的一个子项目,是一个开放源代码的全文检索引擎工具包,但它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和 ...
- ElasticSearch7.3 学习之倒排索引揭秘及初识分词器(Analyzer)
一.倒排索引 1. 构建倒排索引 例如说有下面两个句子doc1,doc2 doc1:I really liked my small dogs, and I think my mom also like ...
- lucene版本升级到4.6.0以上之后使用ik分词器遇到的问题
在将lucene core版本从4.5.1升级到4.7.0后,如下代码使用ik分词器报错 IKAnalyzer analyzer = new IKAnalyzer(true); StringReade ...
随机推荐
- php计算多个集合的笛卡尔积实例详解
笛卡尔积 笛卡尔积是指在数学中,两个集合X和Y的笛卡尔积(Cartesian product),又称直积,表示为X*Y,第一个对象是X的成员而第二个对象是Y的所有可能有序对的其中一个成员. 假设集合A ...
- 用echartsjs 实现动态绘制折线、柱状等图形,并实现多图联动效果
echarts对于大数据处理后绘制折线图,柱形图等等的效果和速度都很好.下面我们介绍 怎么把封装的数据列表解析出来,动态绘图,并且实现鼠标联动效果引入js文件: <script type=&qu ...
- 使用Xamarin开发手机聊天程序 -- 基础篇(大量图文讲解 step by step,附源码下载)
如果是.NET开发人员,想学习手机应用开发(Android和iOS),Xamarin 无疑是最好的选择,编写一次,即可发布到Android和iOS平台,真是利器中的利器啊!而且,Xamarin已经被微 ...
- ssm开发使用redis作为缓存,使用步骤
1.关于spring配置文件中对于redis的配置 <!-- redis配置 --> <bean id="jedisPoolConfig" class=" ...
- 使用questionsModel.values()后不能获取模型中的属性对应的外键属性值的解决方式
class QuestionsModel(models.Model): author = models.ForeignKey(FrontUserModel,null=True) content = m ...
- 关于Unicode,字符集,字符编码
基本概念 字符[character] 字符代表了字母表中的字符,标点符号和其他的一些符号.在计算机中,文本是由字符组成的. 字符集合[character set] 由一套用于特定用途的字符组成,例如支 ...
- 如何在你的blog中添加炫酷的飘雪动画效果
将下面的代码复制到你的设置栏下页眉html代码框中即可 <script> (function($){$.fn.snow=function(options){,maxSize:,newOn: ...
- BZOJ 1211 HNOI2004 树的计数 Prufer序列
题目大意:给定一棵树中全部点的度数,求有多少种可能的树 Prufer序列.详细參考[HNOI2008]明明的烦恼 直接乘会爆long long,所以先把每一个数分解质因数.把质因数的次数相加相减.然后 ...
- iOS8 UILocalNotification 添加启动授权
猴子原创.欢迎转载.转载请注明: 转载自Cocos2Der-CSDN,谢谢! 原文地址: http://blog.csdn.net/cocos2der/article/details/46810357 ...
- javascript中的事件Event
一.事件流 1.事件流:描述的是从页面中接受事件的顺序 IE的事件流是事件冒泡流,Netscape的事件流是事件捕获流. 2.事件冒泡 IE的事件流叫做事件冒泡(event bubbing),即事件开 ...