Solr的分詞器在建索引和查詢時使用,配置schema.xml文件時須要爲字段的類型配置索引(index)和查詢(query)時的分詞器,通常索引和查詢都會使用同一種分詞器,確保按某種方式創建的索引也能以相同的分詞方式檢索出來。Solr支持自定義的分詞器,只須要在schema.xml配置便可(基於Solr1.3版本)。
java
1. 自定義分詞器實現原理app
自定義的分詞器須要繼承自BaseTokenizerFactory或者已有的分詞器,而後實現public TokenStream create(Reader input)方法。參照LetterTokenizerFactory的寫法工具
public class LetterTokenizerFactory extends BaseTokenizerFactory { public LetterTokenizer create(Reader input) { return new LetterTokenizer(input); } }
LetterTokenizer就是一個按字母分詞的分詞器,分詞器最重要的一個方法就是ui
public finalToken next(final Token reusableToken) throws IOException;
該方法負責如何把一個完整的字符串進行分詞,每調用一下這個方法返回一個分詞,因此如何分詞,只須要把分詞方式寫在這個方法裏便可。google
LetterTokenizer沒有直接實現分詞方法,而是繼承了CharTokenizer,CharTokenizer實現了通用字符分詞方法,是個抽象類,子類能夠經過實現protected abstractboolean isTokenChar(char c);和protectedchar normalize(char c)方法實現本身的分詞方式。如下是CharTokenizer的next方法實現方式:spa
private int offset = 0, bufferIndex = 0, dataLen = 0; private static final int MAX_WORD_LEN = 255; private static final int IO_BUFFER_SIZE = 4096; private final char[] ioBuffer = new char[IO_BUFFER_SIZE]; public final Token next(final Token reusableToken) throws IOException { assert reusableToken != null; reusableToken.clear(); int length = 0; int start = bufferIndex; char[] buffer = reusableToken.termBuffer(); while (true) { if (bufferIndex >= dataLen) { offset += dataLen; dataLen = input.read(ioBuffer); //每次讀入4096個字符 if (dataLen == -1) { if (length > 0) break; else return null; } bufferIndex = 0; } final char c = ioBuffer[bufferIndex++];//每次取一個字符出來判斷 if (isTokenChar(c)) {//若是是分詞可包含的字符則繼續,不是則切斷,並把遍歷到的以前的字符當作一個分詞返回,因此子類只要實現本身的isTokenChar(c)方法就能夠實現按本身的方式分詞, LetterTokenizer的實現方式是若是是字母才返回真,因此hello world會被分爲hello和world,由於遇到空格時isTokenChar返回假就會把以前的字符當作一個分詞返回。 if (length == 0) // start of token start = offset + bufferIndex - 1; else if (length == buffer.length) buffer =reusableToken.resizeTermBuffer(1+length); buffer[length++] = normalize(c);//能夠對分詞進行處理,好比LowCaseTokenizer就是經過實現normalize(c),在方法中把每一個字母轉成小寫實現的。 if (length == MAX_WORD_LEN) // buffer overflow! break; } else if (length > 0) // at non-Letter w/ chars break; // return 'em } reusableToken.setTermLength(length); reusableToken.setStartOffset(start); reusableToken.setEndOffset(start+length); return reusableToken; }
2. 能夠按拼音首字母檢索的分詞器實現code
a) 把漢字轉化爲首字母orm
這裏使用了經常使用的方式,google一下不少,ConvertToPY.getFirstLetter(char c)工具類實現把漢字轉化爲首字母。xml
b) 如何把首字母加入到分詞中繼承
要讓用戶經過首字母檢索到內容,那麼就必須在創建索引時爲漢字的拼音首字母也要建索引,這樣才能按首字母搜索,經過分析以上代碼,能夠發現LetterTokenizerFactory類中只有一個方法,該方法傳進一個input也就是未分詞的完整字符串,咱們只須要在他返回以前對這個input加工處理一下就能夠了。
繼續貼代碼:
public class LetterTokenizerFactory extendsBaseTokenizerFactory { public LetterTokenizer create(Readerinput) { return new LetterTokenizer(input); } }
在方法中添加了拼音首字母的代碼以下:
public LetterTokenizer create(Reader input) { LetterTokenizer ts = new LetterTokenizer(input);//先拿到原始的字符串 Token reusableToken = new Token(); StringBuilder sbPrefix = new StringBuilder(); StringBuilder sbSuffix = new StringBuilder(); try { while (ts.next(reusableToken) != null) { //取得每一個分詞,並獲得其首字母保存起來,最後追加到整個漢字字符串的最後 sbPrefix.append(reusableToken.term()); String ch = ConvertToPY.getFirstLetter(reusableToken.term() .charAt(0)); if (!ch.equals("")) { sbSuffix.append( ConvertToPY.getFirstLetter(reusableToken.term() .charAt(0))); } } } catch (IOException e) { e.printStackTrace(); } //最後把拼好的帶漢字和首字母的字符串封裝成一個reader返回。 return new LetterTokenizer(new StringReader(sbPrefix.toString() + sbSuffix.toString())); }
這裏把漢字的首字母組拼後追加到漢字的最後,這樣就把首字母加入到了待分詞的字符串中,接下來再經過分詞器就能夠把組拼好的帶拼音首字母的字符串進行分詞。如:韋小寶—>韋小寶wxb
c) 如何進行分詞
由於solr和Lucene自己都沒有提供對單個字符進行分詞的分詞器,而咱們的需求是把內容按單個字符分詞,這樣用戶輸入一個字符就能檢索出內容,StandardTokenizerFactory能夠把中文按照一個字一個字拆分,可是對英文不行,雖然咱們也能夠把拼音首字母用分割符去組拼,這樣StandardTokenizerFactory也能分出來,但總以爲還不是太好。那麼怎麼去實現按單字符分詞的分詞器呢,若是閱讀過以上CharTokenizer的next方法,而且理解的話,問題就迎刃而解。CharTokenizer經過isTokenChar方法判斷是否須要進行分詞,對於咱們的需求就是不要進行判斷,而是每次讀到一個字符都進行分詞,這樣就實現了。官方爲何不提供這樣的分詞器,我認爲是他們以爲意義不大,由於拆分紅單個字符畢竟會影響效率。如下是按單個字符分詞的next方法,基本就是從CharTokenizer的next方法註釋掉幾行而來的。
public Token next(Token reusableToken) throws IOException { reusableToken.clear(); int length = 0; int start = bufferIndex; char[] buffer =reusableToken.termBuffer(); // while (true) { if (bufferIndex >= dataLen) { offset += dataLen; dataLen = input.read(ioBuffer); if (dataLen == -1) { return null; } bufferIndex = 0; } final char c = ioBuffer[bufferIndex++]; // if (isTokenChar(c)) { // if it's a token char if (length == 0) // start of token start = offset + bufferIndex - 1; else if (length == buffer.length) buffer =reusableToken.resizeTermBuffer(1+length); buffer[length++] =normalize(c); // buffer it, normalized // if (length == 1) // buffer overflow! // break; // } else if (length >0) // at non-Letter w/ chars // break; // return 'em //} reusableToken.setTermLength(length); reusableToken.setStartOffset(start); reusableToken.setEndOffset(start+length); return reusableToken; }
d) 如何使用
把寫好的分詞器打成jar包,放到能夠在solrconfig.xml配置路勁的lib包中,而後在schema.xml中就可使用本身的分詞器了,如
<fieldType name="text"class="solr.TextField" > <analyzer> <tokenizerclass=" com.ckh.solr.analysis.PYOneCharTokenizerFactory "/> <filterclass="solr.LowerCaseFilterFactory"/> </analyzer> </fieldType>
PYOneCharTokenizerFactory是本身寫的可以按拼音首字母搜索的分詞器。
Solr還支持對已經分出來的分詞進行一級級過濾,只須要配置filter便可。