lucene 分词器Analyzer

由于Luceneanalyisis包下的Standard包下的StandardAnalyzer()功能很强大,英文的处理能力同于StopAnalyzer而且支持CJK分词,我们简要说一下.

此包下的文件是有StandardTokenizer.jj经过javac命令生成的.由于是机器自动生成的代码,可能可读性很差,想了解的话好好看看那个StandardTokenizer.jj文件就会比较明了了.

Lucene常用的Analyzer功能概述.

WhitespaceAnalyzer:仅仅是去除空格,对字符没有lowcase化,不支持中文

SimpleAnalyzer:功能强于WhitespaceAnalyzer,将除去letter之外的符号全部过滤掉,并且将所有的字符lowcase化,不支持中文

StopAnalyzer:StopAnalyzer的功能超越了SimpleAnalyzer,在SimpleAnalyzer的基础上
增加了去除StopWords的功能,不支持中文

StandardAnalyzer:英文的处理能力同于StopAnalyzer.支持中文采用的方法为单字切分.

ChineseAnalyzer:来自于Lucene的sand box.性能类似于StandardAnalyzer,缺点是不支持中英文混和分词.

CJKAnalyzer:chedong写的CJKAnalyzer的功能在英文处理上的功能和StandardAnalyzer相同
但是在汉语的分词上,不能过滤掉标点符号,即使用二元切分

TjuChineseAnalyzer:我写的,功能最为强大.TjuChineseAnlyzer的功能相当强大,在中文分词方面由于其调用的为ICTCLAS的java接口.所以其在中文方面性能上同与ICTCLAS.其在英文分词上采用了Lucene的StopAnalyzer,可以去除 stopWords,而且可以不区分大小写,过滤掉各类标点符号.

各个Analyzer的功能已经比较介绍完毕了,现在咱们应该学写Analyzer,如何diy自己的analyzer呢??

如何DIY一个Analyzer

咱们写一个Analyzer,要求有一下功能

(1)   可以处理中文和英文,对于中文实现的是单字切分,对于英文实现的是以空格切分.

(2)   对于英文部分要进行小写化.

(3)   具有过滤功能,可以人工设定StopWords列表.如果不是人工设定,系统会给出默认的StopWords列表.

(4)   使用P-stemming算法对于英文部分进行词缀处理.

=======================================================

Analyzer(分词器)

分词器能以某种规矩对关键字进行分词,将分好的词放到目次中,以作为检索到的前提,在创建索引时会应用到分词器,在搜刮时也将用到分词器,这两个处所要应用同一个分词器,不然可能找不到成果.分词器一般的工作流程:

  1. 切分关键词
  2. 去除停用词
  3. 对于英文单词,把所有字母转为小写

注:有的分词器对英文支撑的很是好,还能对英文单词进行时态还原.

停用词的概念

有些词在文本中呈现的频率很是高,然则对文本所携带的信息根蒂根基不产生影响,例如英文的“a、an、the、of”,或中文的“的、了、着”,以及各类标点符号等,如许的词称为停用词(stop word).文本经过分词之后,停用词凡是被过滤掉,不会被进行索引.在检索的时辰,用户的查询中若是含有停用词,检索体系也会将其过滤掉(因为用户输入的查询字符串也要进行分词处理惩罚).打消停用词可以加快建树索引的速度,减小索引库文件的大小

常用的中文分词器

中文的分词斗劲错杂,因为不是一个字就是一个词,并且一个词在别的一个处所就可能不是一个词,如在"帽子和服装"中,"和服"就不是一个词.对于中文分词,凡是有三种体式格式:

  • 单字分词
  • 二分法分词
  • 词典分词

单字分词:就是遵守中文一个字一个字地进行分词.如:"我们是中国人",结果:"我"、"们"、"是"、"中"、"国"、"人".(StandardAnalyzer就是如许)

二分法分词:按两个字进行切分.如:"我们是中国人",结果:"我们"、"们是"、"是中"、"中国"、"国人".(CJKAnalyzer就是如许)

词库分词:按某种算法机关词,然后去匹配已建好的词库凑集,若是匹配到就切分出来成为词语.凡是词库分词被认为是最幻想的中文分词算法.如:"我们是中国人",结果为:"我们"、"中国人".(应用极易分词的MMAnalyzer.可以应用极易分词,或者是伙头分词分词器、IKAnalyzer等).

测试分词器

/**
?*?应用指定的分词器对指定的文本进行分词,并打印成果
?*?@param?analyzer
?*?@param?text
?*?@throws?Exception
?*/
private?void?testAnalyzer(Analyzer?analyzer,?String?text)?throws?Exception?{
????System.out.println("当前应用的分词器:"?+?analyzer.getClass());
????TokenStream?tokenStream?=?analyzer.tokenStream("content",?new?StringReader(text));
????tokenStream.addAttribute(TermAttribute.class);
????while?(tokenStream.incrementToken())?{
????????TermAttribute?termAttribute?=?tokenStream.getAttribute(TermAttribute.class);
????????System.out.println(termAttribute.term());
????}
}
?


Highlighter(高亮器)

在搜刮成果中经常可以看到,本身搜刮的关键字加上了一些结果,以凸起显示,Lucene天然供给了如许的设置,我们须要用到Highlighter这个类,用之前须要设备好:

/**
?*?Formatter:设置高亮器的格局,无参机关函数默示应用<b>标签
?*?Scorer:Highlighter须要知道哪些关键词是须要高亮的,须要须要查询前提
?*/
Formatter?formatter?=?new?SimpleHTMLFormatter();
Scorer?scorer?=?new?QueryScorer(query);
Highlighter?highlighter?=?new?Highlighter(formatter,scorer);
//除了上方两个以外,还须要生成一段择要,以便搜刮的时辰显示,指定择要的大小为20个字符
Fragmenter?fragmenter?=?new?SimpleFragmenter(20);
highlighter.setTextFragmenter(fragmenter);

只须要设备这三样就可以了,接着我们要在每一次获取Field对象数据的时辰做一些工作,因为高亮器实际上就是在内容中加了一点样式,我们当然要指定在哪里加:

/**
?*?在轮回获取每一笔记录的时辰,让高亮器找到须要高亮的关键词,
?*?要供给响应的分词器和告诉它高亮哪一个Field中的内容
?*?一次只能高亮一个Field,如须要高亮多个Field,须要写多次
?*?就像如许:
????String?text1?=?highlighter.getBestFragment(
????????Configuration.analyzer,?"title",?
????????doc.get("title"));
?*/
String?text?=?highlighter.getBestFragment(Configuration.analyzer,?"content",?doc.get("content"));
if(text?!=?null){
????doc.getField("content").setValue(text);
}

必然要放在轮回里面,highlighter.getBestFragment()办法本身没有副感化,也就是不改变原有的值,返回一个改变后的成果,所以要手动地改变Field华夏有的值,并且if的前提最好不要省略,有时辰可以会有如许一种景象,呈现关键字的地位在其他Field里面,所以若是当前高亮的属性值中没有呈现搜刮关键字,则返回null.

排序

排序有几种体式格式,最常用的也就是相干度排序,把最有可能是用户须要的数据放在前面.由高到低排序,默认便是应用相干度排序.当然,我们也可以把握相干度排分的比重,比如我们想让一笔记录的得分加倍,就要在建树索引时,加几句操纵:

Document?doc?=?DocumentUtils.docConvert(article,?Document.class);
//setBoost()办法须要一个float类型的参数,默示将相干度得分的因子增长几许倍
doc.setBoost(2F);
indexWriter.addDocument(doc);

如许加进去的索引,就会改变默认的相干度得分因子.若是我们想靠某个Field的值来排序也可以,默认是升序,若是是按某个Field的值来排序,那将不会生成相干度得分,因为没有须要.假设按ID进行升序分列:

Sort?sort?=?new?Sort(new?SortField("id",SortField.INT));
TopDocs?topDocs?=?indexSearcher.search(query,null,?100,sort);

IndexSearcher的search()办法须要改成接管4个参数的重载办法,第二个参数须要一个Filter,这里不须要,就传入null,Sort须要知道按什么排序,由SortField的第一个机关参数指明,第二参数指明类型,接管一个int值,由SortField对象的常量值默示.还可以参加第三个参数:

Sort?sort?=?new?Sort(new?SortField("id",?SortField.INT,?true));

默示是否倒序排序.也可以指定多个排序规矩.由多个SortField构成第1次序、第2次序...

过滤

应用Filter可以对搜刮成果进行过滤,以获得更小局限的成果.应用Filter对机能的影响很大(有可能会使查询慢上百倍).假如我要看ID从5到15的:

//第三个参数:是否包含最小值(5);第四个参数:是否包含最大值(15)
Filter?filter?=?NumericRangeFilter.newIntRange("id",?5,?15,?truetrue);
//这个时辰就不须要第四个参数来指定排序了,search()这个办法有很多重载版本
TopDocs?topDocs?=?indexSearcher.search(query,filter,?100);

光如许写还不可,会看不到成果.这是因为要过滤的值,若是是数字,须要以指定的格局存储才行,不然将按字符串的体式格式来斗劲.

对于这个题目,须要引出一个对象类:NumericUtils,这个对象类专门做数值与数值或字符串间的转换工作.要在对象到Document以及Document到对象的转换的时辰进行,这是对于数值,若是是日期类型,须要应用这个对象类:DateTools.

注:如许做了今后,可能还是不可,对于应用NumericUtils把数字转换为字符串,必然要应用Index.NOT_ANALYZED,以保存可以或许正确的应用Filter

?

其他搜刮

起首,有两种搜刮体式格式:

  1. 应用QueryParser(或MultiFieldQueryParser)解析查询字符串的体式格式
  2. 本身直接创建Query子类的实例体式格式

因为可以应用Query对象的toString()办法打印出对应的查询语法,所以以第二种体式格式做的话,可以看响应的第一种查询办法:

/*???1.匹配所有对应的查询字符串为:???????*:*
?????Query?query?=?new?MatchAllDocsQuery();
?????
?????2.局限查询对应的查询字符串为:??????id:[5?TO?15]
?????Query?query?=?NumericRangeQuery.newIntRange("id",?5,?15,?true,?true);
???????局限查询对应的查询字符串为:??????id:{5?TO?15}
?????Query?query?=?NumericRangeQuery.newIntRange("id",?5,?15,?false,false);
???????局限查询对应的查询字符串为:??????id:{5?TO?15]
?????Query?query?=?NumericRangeQuery.newIntRange("id",?5,?15,?false,?true);
?????
?????3.关键词对应的查询字符串为:????????title:lucene
?????Query?query?=?new?TermQuery(new?Term("title",?"lucene"));
?????
?????4.通配符对应的查询字符串为:????????title:lucen?
?????Query?query?=?new?WildcardQuery(new?Term("title",?"lucen?"));
???????通配符对应的查询字符串为:????????title:lu*ne
?????Query?query?=?new?WildcardQuery(new?Term("title",?"lu*ne"));
?????
?????5.短语对应的查询字符串为:??????????title:"lucene?????工作"
?????PhraseQuery?phraseQuery?=?new?PhraseQuery();
?????phraseQuery.add(new?Term("title",?"lucene"),?0);?//?第一个词的索引是0
?????phraseQuery.add(new?Term("title",?"工作"),?3);
???????短语对应的查询字符串为:??????????title:"lucene?工作"~5
?????phraseQuery.add(new?Term("title",?"lucene"));
?????phraseQuery.add(new?Term("title",?"工作"));
?????phraseQuery.setSlop(5);?//?指定这些词中心的间隔最多不会跨越5个词
???
?????6.布尔查询
?????????对应的查询语法两种:
?????????1.?+?代表MUST,-默示NOT
?????????2.?AND、OR、NOT,重视要满是大写
?????????3.?MUST????????必须满足
?????????????MUST_NOT????非
?????????????SHOULD????????多个SHOULD一路用,是OR的关系
?????????
?????????Query?query1?=?new?TermQuery(new?Term("title",?"lucene"));
?????????Query?query2?=?NumericRangeQuery.newIntRange("id",?5,?15,?false,?true);
?????????对应的查询字符串为:????????? +title:lucene?+id:{5?TO?15]
?????????对应的查询字符串为:????????? title:lucene?AND?id:{5?TO?15]
?????????booleanQuery.add(query1,?Occur.MUST);
?????????booleanQuery.add(query2,?Occur.MUST);???????
?????????
?????????对应的查询字符串为:????????? +title:lucene?-id:{5?TO?15]
?????????对应的查询字符串为:????????? title:lucene?NOT?id:{5?TO?15]
?????????booleanQuery.add(query1,?Occur.MUST);
?????????booleanQuery.add(query2,?Occur.MUST_NOT);
?????????????????
?????????对应的查询字符串为:????????? title:lucene?id:{5?TO?15]
?????????对应的查询字符串为:????????? title:lucene?OR?id:{5?TO?15]
?????????booleanQuery.add(query1,?Occur.SHOULD);
?????????booleanQuery.add(query2,?Occur.SHOULD);
????????----------------------------------------------------
?????????MUST?+?SHOULD,与只有一个MUST结果雷同
?????????SHOULD?+?MUST_NOT,这时SHOULD就相当MUST
?????????MUST_NOT?+?MUST_NOT,没有匹配成果,也不报错。
*/

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值