最近没什么事情可做,于是就看了看Lucene源码,以往版本Lucene的各个功能都是合在一个jar 包的,
最近发布4.0,4.1 就将各个功能都分开了
首先对分词(分析)部分进行了学习
说是分词,更准确的应该叫分析, 主要指将域(Field)文本转换为最基本的索引表示单元 ——项(Term) 的过程。
这些操作包括:提取单词,去掉标点,将字母转换为小写,去除常用词(停用词),将单词还原为词干…………
Token(单个词信息) 和他所在域(Filed)结合后就是项——term
多个Term 组成了Field
分词流程:
在Lucene中,对分词主要依靠Analyzer类解析实现。Analyzer内部主要通过TokenStream类实现。
Tonkenizer类、TokenFilter类是TokenStream的两个子类。
Tokenizer处理单个字符组成的字符流,读取Reader对象中的数据,处理后转换成词汇单元。
TokneFilter完成文本过滤器的功能,但在使用过程中必须注意不同的过滤器的使用的顺序。
Analyzer
他下面有几个重要的方法
(3.x版本中):
(1) public abstract TokenStream tokenStream(String fieldName, Reader reader);
该方法需要自定义的分词器去实现,并返回TokenStream,即将对象以Reader的方式输入分词为fieldName字段。
TokenStream:分词流,即将对象分词后所得的Token在内存中以流的方式存在,也说是说如果在取得Token必须从TokenStream中获取,而分词对象可以是文档文本,也可以是查询文本。
参数说明:
fieldName——字段名,也就是你建索引的时候对应的字段名,比如:Field f = new Field("title","hello",Field.Store.YES, Field.Index.TOKENIZED);这句中的"title";
reader——java.io.Reader对象;
(2) public TokenStream reusableTokenStream(String fieldName, Reader reader)
设置为可复用TokenStream,将同一线程中前面时间的TokenStream设置为可复用。那些无必要同一时刻使用多个TokenStream的调用者使用这个方法,可以提升性能。
(4.0—1 版本)
(1)TokenStreamComponents createComponents(String fieldName, Reader reader)
实现他的方法有(这是从3.x 的api上摘的,但在源码里好像没有这些类 像SmartChineseAnalyzer 看了下api 貌似是中科院imdict继承的, 4.x 版本中证实了想法,把这个都放在了lucene-analyzers-common-4.1.0.jar包里了 ):
CollationKeyAnalyzer, ICUCollationKeyAnalyzer, LimitTokenCountAnalyzer, MockAnalyzer, PerFieldAnalyzerWrapper,QueryAutoStopWordAnalyzer,
QueryParserTestBase.QPTestAnalyzer, ReusableAnalyzerBase, ShingleAnalyzerWrapper, SmartChineseAnalyzer,SnowballAnalyzer
在(4.x 的版本中实现他的方法就只有AnalyzerWrapper)
Lucene 自带的几个分词器
继承自ReusableAnalyzerBase
WhitespaceAnalyzer 这是根据空格分割的
SimpleAnalyzer 他是先根据非字母字符来分割,并且将字符转换为小写,这个分词器会去掉数字类型的字符
TokenStream
是一个抽象类,枚举词序列,要么是从一个文档的域得来,要么是从一个查询文本中得到。主要任务有:
(1)获取下一Token;
public abstract boolean incrementToken() throws IOException;
(2)重设流(可选);public void reset() throws IOException
(3)关闭流,释放资源;public void close() throws IOException
实现他的方法Lucene自带的有
CannedTokenStream, CategoryAttributesStream, EmptyTokenStream, NumericTokenStream, PrefixAndSuffixAwareTokenFilter,PrefixAwareTokenFilter,
ShingleMatrixFilter, SingleTokenTokenStream, TeeSinkTokenFilter.SinkTokenStream, TokenFilter, Tokenizer,TokenStreamFromTermPositionVector
Token
该类继承了一个类
TermAttributeImpl
实现了
TypeAttribute 表示token的字符串信息
PositionIncrementAttribute 表示token词典类别信息,默认为“Word”,
FlagsAttribute 用于在Tokenizer链之前传递标记(因为前面一个操作可能会影响后面的操作)。那么这个属性有什么用呢,用处很大的。加入我们想搜索一个短语student apples(假如有这个短语)。很显然,用户是要搜索出student apples紧挨着出现的文档。这个时候我们找到了某一篇文档(比如上面例子的字符串)都含有student apples。但是由于apples的PositionIncrementAttribute值是5,说明肯定没有紧挨着。
OffsetAttribute 表示token的首字母和尾字母在原文本中的位置,需要注意的是startOffset与endOffset的差值并不一定就是termText.length(),因为可能term已经用过滤器处理过
PayloadAttribute 即负载量意思,是每个term出现一次则存储一次的元数据,它存储于特定term的posting list内部。
PositionLengthAttribute 它表示tokenStream中的当前token与前一个token在实际的原文本中相隔的词语数量,用于短语查询。
Tokenizer
继承了TokenStream类 该类主要是接收输入流并根据输入流进行词切分。虽然她本身是个抽象类,但是定制分词器的核心之一。
实现的子类有很多
Lucene自带的就有:
CharTokenizer, ChineseTokenizer, CJKTokenizer, ClassicTokenizer, EdgeNGramTokenizer, EmptyTokenizer, ICUTokenizer,JapaneseTokenizer, KeywordTokenizer,
MockTokenizer, NGramTokenizer, PathHierarchyTokenizer, ReversePathHierarchyTokenizer,SentenceTokenizer, StandardTokenizer, UAX29URLEmailTokenizer, WikipediaTokenizer
TokenFilter
TokenFilter类继承于TokenStream,其输入是另一个TokenStream,主要职责是对TokenStream进行过滤,例如去掉一些索引词、替代同义索引词等操作。
实现他的子类很多不过一般实用的类有 对空格过滤,停止词过滤,
实现他的子类有:
ArabicNormalizationFilter, ArabicStemFilter, ASCIIFoldingFilter, BeiderMorseFilter, BrazilianStemFilter, BulgarianStemFilter,CachingTokenFilter,
CategoryParentsStream, CategoryTokenizerBase, ChineseFilter, CJKBigramFilter, CJKWidthFilter, ClassicFilter,CollationKeyFilter, CompoundWordTokenFilterBase,
CzechStemFilter, DelimitedPayloadTokenFilter, DoubleMetaphoneFilter,DutchStemFilter, EdgeNGramTokenFilter, ElisionFilter,
EnglishMinimalStemFilter, EnglishPossessiveFilter, FilteringTokenFilter,FinnishLightStemFilter, FrenchLightStemFilter, FrenchMinimalStemFilter,
FrenchStemFilter, GalicianMinimalStemFilter,GalicianStemFilter, GermanLightStemFilter, GermanMinimalStemFilter, GermanNormalizationFilter,
GermanStemFilter,GreekLowerCaseFilter, GreekStemFilter, HindiNormalizationFilter, HindiStemFilter, HungarianLightStemFilter,
HunspellStemFilter,ICUCollationKeyFilter, ICUNormalizer2Filter, ICUTransformFilter, IndicNormalizationFilter,
IndonesianStemFilter,IrishLowerCaseFilter, ISOLatin1AccentFilter, ItalianLightStemFilter, JapaneseBaseFormFilter,
JapaneseKatakanaStemFilter,JapaneseReadingFormFilter, KeywordMarkerFilter, KStemFilter, LatvianStemFilter,
LimitTokenCountFilter, LookaheadTokenFilter,LowerCaseFilter, MockFixedLengthPayloadFilter, MockHoleInjectingTokenFilter,
MockVariableLengthPayloadFilter, NGramTokenFilter,NorwegianLightStemFilter, NorwegianMinimalStemFilter, NumericPayloadTokenFilter,
OffsetLimitTokenFilter, PersianNormalizationFilter,PhoneticFilter, PorterStemFilter, PortugueseLightStemFilter, PortugueseMinimalStemFilter,
PortugueseStemFilter, PositionFilter,QueryParserTestBase.QPTestFilter, ReverseStringFilter, RussianLightStemFilter, RussianLowerCaseFilter,
RussianStemFilter,ShingleFilter, SnowballFilter, SpanishLightStemFilter, StandardFilter, StemmerOverrideFilter, StempelFilter,
SwedishLightStemFilter,SynonymFilter, TeeSinkTokenFilter, ThaiWordFilter, TokenOffsetPayloadTokenFilter, TurkishLowerCaseFilter,
TypeAsPayloadTokenFilter,ValidatingTokenFilter, WordTokenFilter
知道了Lucene分词的核心方法分词扩展就比较容易了
我自己将以前的分词器继承了4.x
下面是输出代码:
public String ordinaryAnalyzer(String string) { Analyzer analyzer = new MyAnalyzer(Version.LUCENE_41); TokenStream ts = null; StringBuffer sb = new StringBuffer(); try { ts = analyzer.tokenStream("sentence", new StringReader(string)); CharTermAttribute term = ts.addAttribute(CharTermAttribute.class); ts.reset(); while (ts.incrementToken()) { if (term.toString().equals(" ")) { } else { sb.append(term.toString() + " "); } } ts.end(); } catch (IOException e) { e.printStackTrace(); } finally { if (ts != null) { try { ts.close(); } catch (IOException e) { e.printStackTrace(); } } } return sb.toString().trim(); }
你还可以加入别的信息 开始位置,结束位置,还有type
public class TestFenci { public static void main(String[] args) { ChineseAnalyzer as = new ChineseAnalyzer(); long startTime = System.currentTimeMillis(); String sentence = "男 包北京海淀"; as.wordAnalyzer(sentence); System.out.println("挑出地址:" + as.pickCityAnalyzer(sentence)); System.out.println("普通分开:" + as.ordinaryAnalyzer(sentence)); System.out.println("加扩展:" + as.expandAnalyzer(sentence)); System.out.print(System.currentTimeMillis() - startTime); }}
结果:
0 - 1 : 男 | word
1 - 2 : | word2 - 3 : 包 | word3 - 5 : 北京 | word5 - 7 : 海淀 | word挑出地址:key:男 包 address:北京 海淀 guess:普通分开:男 包 北京 海淀加扩展:男 or 男士 or 男式 or 男生 包 or 包包 北京 海淀94