Lucene学习二次开发之——分词开发流程

最近没什么事情可做,于是就看了看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 :   | word
2 - 3 : 包 | word
3 - 5 : 北京 | word
5 - 7 : 海淀 | word
挑出地址:key:男 包 address:北京 海淀 guess:
普通分开:男 包 北京 海淀
加扩展:男 or 男士 or 男式 or 男生 包 or 包包 北京 海淀
94

原文地址:https://www.cnblogs.com/tomcattd/p/2881197.html