由于Lucene的analyisis包下的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(分词器)
分词器能以某种规矩对关键字进行分词,将分好的词放到目次中,以作为检索到的前提,在创建索引时会应用到分词器,在搜刮时也将用到分词器,这两个处所要应用同一个分词器,不然可能找不到成果.分词器一般的工作流程:
- 切分关键词
- 去除停用词
- 对于英文单词,把所有字母转为小写
注:有的分词器对英文支撑的很是好,还能对英文单词进行时态还原.
停用词的概念
有些词在文本中呈现的频率很是高,然则对文本所携带的信息根蒂根基不产生影响,例如英文的“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,?true,true); |
//这个时辰就不须要第四个参数来指定排序了,search()这个办法有很多重载版本 |
TopDocs?topDocs?=?indexSearcher.search(query,filter,?100); |
光如许写还不可,会看不到成果.这是因为要过滤的值,若是是数字,须要以指定的格局存储才行,不然将按字符串的体式格式来斗劲.
对于这个题目,须要引出一个对象类:NumericUtils,这个对象类专门做数值与数值或字符串间的转换工作.要在对象到Document以及Document到对象的转换的时辰进行,这是对于数值,若是是日期类型,须要应用这个对象类:DateTools.
注:如许做了今后,可能还是不可,对于应用NumericUtils把数字转换为字符串,必然要应用Index.NOT_ANALYZED,以保存可以或许正确的应用Filter
?
其他搜刮
起首,有两种搜刮体式格式:
- 应用QueryParser(或MultiFieldQueryParser)解析查询字符串的体式格式
- 本身直接创建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,没有匹配成果,也不报错。 |
*/ |