在Solr中該如何使用IK分詞器呢,這是小伙伴們問的頻率比較高的一個問題,今晚特此更新此篇博客。其實之前我在其他博客里已經使用了IK分詞器,只是我沒做詳細說明。
在schema.xml配置中其實有很多關于分詞器的配置示例,我從中摘錄一段配置示例,比如:
fileType是用來定義域類型的,name即表示域名稱,class即表示域類型對應的class類,如果是solr內置的域類型則可以直接使用solr.前綴+域類型的類名即可,如果是你自定義的域類型,則class表示自定義域類型的完整類名(包含完整的包路徑),在fileType元素下有analyzer元素,用來配置當前域類型使用什么分詞器,你肯定很奇怪,為什么要配置兩個analyzer,其實主要是為了區分兩個階段:索引建立階段和Query查詢階段,索引建立階段需要分詞毋庸置疑,查詢階段是否需要分詞,則取決于你的業務需求,用過Google的知道,用戶在查詢輸入框里輸入查詢關鍵字,這時候我們需要對用戶輸入的查詢關鍵字進行分詞器,這時候我們就需要配置查詢階段使用什么分詞器,為什么把分開配置?兩者可以使用統一配置不行嗎,配置兩遍不是顯得很冗余且繁瑣嗎?analyzer的type元素就表示這兩個階段,之所以要分階段配置分詞器,是為了滿足用戶潛在的需求,因為查詢階段的分詞需求和索引階段的分詞需求不一定是相同的。我們都知道分詞器Analyzer是由一個Tokenizer + N個tokenFilter組成,這就是為什么analyzer元素下會有tokenizer元素和filter元素,但tokenizer元素只允許有一個,filter元素可以有N個。之所以這樣設計是為了為用戶提供更細粒度的方式來配置分詞器的行為,即你可以任意組合tokenizer和filter來實現你的特定需求,當然你也可以把這種組合寫在Analyzer類里,然后直接在analyzer元素的class屬性里配置自定義分詞器的完整類名,這樣就不需要這么繁瑣的配置tokenizer和filter,即把實現細節屏蔽在analyzer類內部,但這樣做的話,如果你需要更改實現細節,則需要修改Analyzer源碼,然后重新打包成jar,相對來說,比較麻煩點,而使用analyzer,tokenizer,filter這樣來配置,雖然繁瑣點,但更靈活。而且采用
然后我們在代碼里String useSmart = DOMUtil.getAttr(attrs,"useSmart");就可以獲取到屬性值了,然后就是通過反射把屬性值設置到IKAnalyzer類的useSmart屬性中了,這是基本的Java反射操作,下面我提供幾個反射工具方法:
/**
* 循環向上轉型, 獲取對象的DeclaredField. 若向上轉型到Object仍無法找到, 返回null.
*/
protected static Field getDeclaredField(final Object object, final String fieldName) {
if (null == object || null == fieldName || fieldName.equals("")) {
return null;
}
for (Class> superClass = object.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
try {
return superClass.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
// Field不在當前類定義,繼續向上轉型
continue;
}
}
return null;
}
/**
* 直接設置對象屬性值, 無視private/protected修飾符, 不經過setter函數.
*/
public static void setFieldValue(final Object object, final String fieldName, final Object value) {
Field field = getDeclaredField(object, fieldName);
if (field == null) {
throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]");
}
makeAccessible(field);
try {
field.set(object, value);
} catch (IllegalAccessException e) {
throw new RuntimeException("直接設置對象屬性值出現異常", e);
}
}
/**
* 強行設置Field可訪問
*/
protected static void makeAccessible(final Field field) {
if (!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())) {
field.setAccessible(true);
}
}
直接調用setFieldValue方法即可,比如在 Analyzer analyzer = clazz.newInstance();這句下面添加一句
setFieldValue(analyzer,"useSmart",Boolean.valueOf( useSmart ));
這樣我們在xml中配置的useSmart參數就設置到Analyzer類中了,這樣才能起作用。solr源碼如何導入Eclipse上篇博客里我已經介紹過了,至于如果把修改過后的代碼打包成jar,直接使用eclipse自帶的export功能即可,如圖:
然后一路Next即可。我只是說說思路,剩下留給你們自己去實踐。
但是采用改源碼方式不是很優雅,因為你本地雖然是修改好了,哪天你由Solr5.1.0升級到5.2.0,還要再改一遍,沒升級一次就要改一次,你的代碼copy給別人用,別人運行代碼后看不到效果,增加溝通成本,你還得把你改過源碼的jar包共享給別人,這也就是為什么有那么多人找我要什么IK jar包。
在Solr中可以使用TokenizerFactory方式來解決我剛才提出的問題:IKAnalyzer分詞器的useSmart參數無法通過schema.xml配置文件進行設置。我花了點時間擴展了IKTokenizerFactory類,代碼如下:
package org.apache.lucene.analysis.ik;
import java.util.Map;
import org.apache.lucene.analysis.Tokenizer;
import org.apache.lucene.analysis.util.TokenizerFactory;
import org.apache.lucene.util.AttributeFactory;
import org.wltea.analyzer.lucene.IKTokenizer;
public class IKTokenizerFactory extends TokenizerFactory {
public IKTokenizerFactory(Map
args) {
super(args);
useSmart = getBoolean(args, "useSmart", false);
}
private boolean useSmart;
@Override
public Tokenizer create(AttributeFactory attributeFactory) {
Tokenizer tokenizer = new IKTokenizer(attributeFactory,useSmart);
return tokenizer;
}
}
同時我對 IKTokenizer類也稍作了修改,修改后源碼如下:
/**
* IK分詞器 Lucene Tokenizer適配器類
* 兼容Lucene 4.0版本
*/
public final class IKTokenizer extends Tokenizer {
//IK分詞器實現
private IKSegmenter _IKImplement;
//詞元文本屬性
private final CharTermAttribute termAtt;
//詞元位移屬性
private final OffsetAttribute offsetAtt;
//詞元分類屬性(該屬性分類參考org.wltea.analyzer.core.Lexeme中的分類常量)
private final TypeAttribute typeAtt;
//記錄最后一個詞元的結束位置
private int endPosition;
private Version version = Version.LATEST;
/**
* Lucene 4.0 Tokenizer適配器類構造函數
* @param in
* @param useSmart
*/
public IKTokenizer(Reader in , boolean useSmart){
//super(in);
offsetAtt = addAttribute(OffsetAttribute.class);
termAtt = addAttribute(CharTermAttribute.class);
typeAtt = addAttribute(TypeAttribute.class);
_IKImplement = new IKSegmenter(input , useSmart);
}
public IKTokenizer(AttributeFactory factory, boolean useSmart) {
super(factory);
offsetAtt = addAttribute(OffsetAttribute.class);
termAtt = addAttribute(CharTermAttribute.class);
typeAtt = addAttribute(TypeAttribute.class);
_IKImplement = new IKSegmenter(input , useSmart);
}
/* (non-Javadoc)
* @see org.apache.lucene.analysis.TokenStream#incrementToken()
*/
@Override
public boolean incrementToken() throws IOException {
//清除所有的詞元屬性
clearAttributes();
Lexeme nextLexeme = _IKImplement.next();
if(nextLexeme != null){
//將Lexeme轉成Attributes
//設置詞元文本
termAtt.append(nextLexeme.getLexemeText());
//設置詞元長度
termAtt.setLength(nextLexeme.getLength());
//設置詞元位移
offsetAtt.setOffset(nextLexeme.getBeginPosition(), nextLexeme.getEndPosition());
//記錄分詞的最后位置
endPosition = nextLexeme.getEndPosition();
//記錄詞元分類
typeAtt.setType(nextLexeme.getLexemeTypeString());
//返會true告知還有下個詞元
return true;
}
//返會false告知詞元輸出完畢
return false;
}
/*
* (non-Javadoc)
* @see org.apache.lucene.analysis.Tokenizer#reset(java.io.Reader)
*/
@Override
public void reset() throws IOException {
super.reset();
_IKImplement.reset(input);
}
@Override
public final void end() {
// set final offset
int finalOffset = correctOffset(this.endPosition);
offsetAtt.setOffset(finalOffset, finalOffset);
}
修改后重新打包的IKAnalyzer jar請見底下的附件。
然后我把它打包成了 solr-analyzer-ik-5.1.0.jar,只需要把這個jar包復制到你的core\lib目錄下即可,然后你就可以像配置StandardTokenizerFactory一樣的使用我們自定義的IKTokenizerFactory類了,并且能配置useSmart參數,這正是我想要的,能靈活的控制分詞器參數,so cool。配置示例如下:
然后field域里應用我們配置的這個text_ik域類型,如圖:
然后你還需要把IKAnalyzer jar包以及我們自定義的IKTokenizerFactory的jar包copy到你當前core\lib目錄下,如圖:
IKAnalyzer jar建議使用底下附件里我新上傳的,因為源碼我稍作了修改,上面已經提到過了。然后你需要把IKAnalyzer.cfg.xml配置文件copy到E:\apache-tomcat-7.0.55\webapps\solr\WEB-INF\classes目錄下,其中E
:\apache-tomcat-7.0.55為我的Tomcat安裝根目錄,請類比成你自己的tomcat安裝根目錄,你懂的。如圖:
IKAnalyzer.cfg.xml配置如圖:
ext.dic為IK分詞器的自定義擴展詞典,內容如圖:
我就在里面加了兩個自定義詞語。
然后你就可以啟動你的tomcat,然后如圖進行分詞測試了,
上圖是用來測試useSmart參數設置是否有生效,如果你看到如圖的效果,說明配置成功了。
上圖是用來測試自定義詞典是否有生效,因為我在ext.dic自定義詞典里添加了 勁爆 和 屌絲 這兩個詞,所以IK能分出來,逆襲和白富美沒有在自定義擴展詞典里添加,所以IK分不出來。如果你能看到如圖效果,說明IK的自定義擴展詞典也配置成功了。到此,關于在Solr中使用IK分詞器就介紹到這兒了,如果你還有任何疑問,請通過以下方式聯系到我,謝謝?。?!博客里提到的相關jar包配置文件等等資源文件,我待會兒都會上傳到底下的附件里,特此提醒?。。。?!
大盤預測
國富論
posted on 2015-09-11 17:26
華夢行 閱讀(653)
評論(0) 編輯 收藏