前言:
眼前兄弟们对“分词搜索商品代码”大体比较注重,小伙伴们都需要剖析一些“分词搜索商品代码”的相关资讯。那么小编在网摘上收集了一些对于“分词搜索商品代码””的相关知识,希望我们能喜欢,小伙伴们一起来学习一下吧!前面介绍了Lucene,这里看下Lucene分词器的介绍。
org.apache.lucene.analysi.Analyzer,抽象类:Analyzer(分析器)提供了两个方法:
public final TokenStream tokenStream(String fieldName, Reader reader) { Analyzer.TokenStreamComponents components = this.reuseStrategy.getReusableComponents(this, fieldName); Reader r = this.initReader(fieldName, reader); if (components == null) { components = this.createComponents(fieldName); this.reuseStrategy.setReusableComponents(this, fieldName, components); } components.setReader(r); return components.getTokenStream(); }
public final TokenStream tokenStream(String fieldName, String text) { Analyzer.TokenStreamComponents components = this.reuseStrategy.getReusableComponents(this, fieldName); ReusableStringReader strReader = components != null && components.reusableStringReader != null ? components.reusableStringReader : new ReusableStringReader(); strReader.setValue(text); Reader r = this.initReader(fieldName, strReader); if (components == null) { components = this.createComponents(fieldName); this.reuseStrategy.setReusableComponents(this, fieldName, components); } components.setReader(r); components.reusableStringReader = strReader; return components.getTokenStream(); }
两个都为fianl方法说明方法不能扩展
第一个方法:fieldName:字段的名字,reader:字符输入流
第二个方法:fieldName:字段的名字,text:字符串
方法返回的是一个TokenStream。TokenStream:分词处理器
Analyzer.TokenStreamComponents components = this.reuseStrategy.getReusableComponents(this, fieldName);
首先会从重用策略(reuseStrategy)中去获取components ,如果没有则回去创建一个components 然后将其放入到 reuseStrategy 中,其实是一个Map。
Reader r = this.initReader(fieldName, reader);
获取reader,此方法什么也没做,返回还是原来的reader。
components = this.createComponents(fieldName);
没有从重用策略拿到components,重新构建,此方法是抽象方法,需要使用者自己实现。
components.setReader(r);
将reader传给components中的Tokenizer。
return components.getTokenStream();
从分词处理器组件获取到tokenStream,并返回。
看下Analyzer.TokenStreamComponents components的属性方法:
public static class TokenStreamComponents { protected final Tokenizer source; protected final TokenStream sink; transient ReusableStringReader reusableStringReader; public TokenStreamComponents(Tokenizer source, TokenStream result) { this.source = source; this.sink = result; } public TokenStreamComponents(Tokenizer source) { this.source = source; this.sink = source; } //省去get/set方法
TonkenStreamComponents 分词处理器组件:这个类中封装有供外部使用的TonkenStream分词处理器。提供了对Tokenizer source和TokenStream sink两个属性的访问方法。
从上面的分析可以看出,Analyzer抽象类,提供了可以扩展方法createComponents,通过实现这个方法,传入实现自己的Tokenizer和TokenStream,reader构建出分词处理器TokenStream。
那么具体看TokenStream和Tokenizer:
TokenStream是AttributeSource的子类,有一个抽象的方法:
public abstract boolean incrementToken() throws IOException;
这个方法主要是用于不断的迭代,取得下一个分项(Token)的,如果有则返回true没有则返回false。每次调用一次返回一个分项。
TokenStream有两个子类,Tokenizer和TokenFilter。
Tokenizer:分词器,输入是Reader字符流的TokenStream,完成从流中分出分项
TokenFilter:分项过滤器,它的输入是另一个TokenStream,完成对从上一个TokenStream中流出的token的特殊处理。
字符串-->字符流--->Tokenizer(分词)-->分项--->TokenFilter(过滤,修饰)
可能不太准确,有很多的组合嵌套。
这个我们可能可以看出createComponents的sink,source了,传入Tokenizer,TokenFilter进行一番操作,完成分词处理工作。TokenFilter采用装饰着模式。如果我们需要对分词进行各种处理,只需要按我们的处理顺序一层层包裹即可(每一层完成特定的处理)。不同的处理需要,只用不同的包裹顺序、层数。
那通过不同的Tokenizer /TokenFilter处理后的属性信息怎么保存呢,比如位置,次数,偏移量等。
主要通过以下几个类:
AttributeSource、Attribute、AttributeImpl、AttributeFactory
AttributeSource 负责存放Attribute对象,他提供对应的存、取方法Attribute 对象中则可以存储一个或多个属性信息AttributeFactory 则是负责创建Attribute对象的工厂,在TokenStream中默认使用了 AttributeFactory.getStaticImplementation 我们不需要提供,遵守它的规则即可。
TokenStream继承AttributeSource,提供了添加属性,获取属性的方法,
某个TokenStream实现中如要存储分项属性,通过AttributeSource的两个add方法之一,往AttributeSource中加入属性对象,如:
MyCharAttribute charAttr = this.addAttribute(MyCharAttribute.class);
在TokenStream中,我们用自己实现的Attribute,默认的工厂。当我们调用这个add方法时,它怎么知道实现类是哪个?这里有一定规则要遵守:
1、自定义的属性接口 MyAttribute 继承 Attribute
2、自定义的属性实现类必须继承AttributeImpl,实现自定义的接口MyAttribute
3、自定义的属性实现类必须提供无参构造方法
4、为了让默认工厂能根据自定义接口找到实现类,实现类名需为 接口名+Impl 。
我们在应用中并不直接使用分词器,只需为索引引擎和搜索引擎创建我们想要的分词器对象。但我们在选择分词器时,会需要测试分词器的效果,就需要知道如何使用得到的分词处理器TokenStream,使用步骤:
1、从tokenStream获得你想要获得分项属性对象(信息是存放在属性对象中的)
2、调用 tokenStream 的 reset() 方法,进行重置。因为tokenStream是重复利用的。
3、循环调用tokenStream的incrementToken(),一个一个分词,直到它返回false
4、在循环中取出每个分项你想要的属性值。
5、调用tokenStream的end(),执行任务需要的结束处理。
6、调用tokenStream的close()方法,释放占有的资源。
String text = "An AttributeSource contains a list of different AttributeImpls, and methods to add and get them. "; try (Analyzer ana = new MyWhitespaceAnalyzer(); TokenStream ts = ana.tokenStream("aa", text);) { MyCharAttribute ca = ts.getAttribute(MyCharAttribute.class); ts.reset(); while (ts.incrementToken()) { System.out.print(ca.getString() + "|"); } ts.end(); System.out.println(); } catch (IOException e) { e.printStackTrace(); }}
简单实现一个我们自己的Analyzer:
Tokenizer:
实现对英文按空白字符进行分词。
需要记录的属性信息有: 词
TokenFilter:
要进行的处理:转为小写
public class MyWhitespaceAnalyzer extends Analyzer { @Override protected TokenStreamComponents createComponents(String fieldName) { Tokenizer source = new MyWhitespaceTokenizer(); TokenStream filter = new MyLowerCaseTokenFilter(source); return new TokenStreamComponents(source, filter); } static class MyWhitespaceTokenizer extends Tokenizer { // 需要记录的属性 // 词 MyCharAttribute charAttr = this.addAttribute(MyCharAttribute.class); char[] buffer = new char[255]; int length = 0; int c; @Override public boolean incrementToken() throws IOException { // 清除所有的词项属性 clearAttributes(); length = 0; while (true) { c = this.input.read(); if (c == -1) { if (length > 0) { // 复制到charAttr this.charAttr.setChars(buffer, length); return true; } else { return false; } } if (Character.isWhitespace(c)) { if (length > 0) { // 复制到charAttr this.charAttr.setChars(buffer, length); return true; } } buffer[length++] = (char) c; } } } public static class MyLowerCaseTokenFilter extends TokenFilter { public MyLowerCaseTokenFilter(TokenStream input) { super(input); } MyCharAttribute charAttr = this.addAttribute(MyCharAttribute.class); @Override public boolean incrementToken() throws IOException { boolean res = this.input.incrementToken(); if (res) { char[] chars = charAttr.getChars(); int length = charAttr.getLength(); if (length > 0) { for (int i = 0; i < length; i++) { chars[i] = Character.toLowerCase(chars[i]); } } } return res; } } public static interface MyCharAttribute extends Attribute { void setChars(char[] buffer, int length); char[] getChars(); int getLength(); String getString(); } public static class MyCharAttributeImpl extends AttributeImpl implements MyCharAttribute { private char[] chatTerm = new char[255]; private int length = 0; @Override public void setChars(char[] buffer, int length) { this.length = length; if (length > 0) { System.arraycopy(buffer, 0, this.chatTerm, 0, length); } } public char[] getChars() { return this.chatTerm; } public int getLength() { return this.length; } @Override public String getString() { if (this.length > 0) { return new String(this.chatTerm, 0, length); } return null; } @Override public void clear() { this.length = 0; } @Override public void reflectWith(AttributeReflector reflector) { } @Override public void copyTo(AttributeImpl target) { } } public static void main(String[] args) { String text = "An AttributeSource contains a list of different AttributeImpls, and methods to add and get them. "; try (Analyzer ana = new MyWhitespaceAnalyzer(); TokenStream ts = ana.tokenStream("aa", text);) { MyCharAttribute ca = ts.getAttribute(MyCharAttribute.class); ts.reset(); while (ts.incrementToken()) { System.out.print(ca.getString() + "|"); } ts.end(); System.out.println(); } catch (IOException e) { e.printStackTrace(); } }}
标签: #分词搜索商品代码