lucene構建同義詞分詞器
阿新 • • 發佈:2019-01-10
lucene4.0版本以後 已經用TokenStreamComponents 取代了TokenStream流。裡面包括了filter和tokenizer
在較複雜的lucene搜尋業務場景下,直接網上下載一個作為專案的分詞器,是不夠的。那麼怎麼去評定一箇中文分詞器的好與差:一般來講,有兩個點;詞庫和搜尋效率,也就是演算法。
lucene的倒排列表中,不同的分詞單元有不同的PositionIncrementAttribute,如果兩個詞之間PositionIncrementAttribute距離為0,則為同義詞;比如:我定義美國和中國這兩個詞在倒排列表中是同一個位置及距離為0,那麼搜尋美國的話,中國也能出來。這就是同義詞搜尋原理。
以下程式碼(用mmseg的 Tokenizer 去切詞之後,然後再做同義詞):
先自定義分詞器:
package hhc; import java.io.Reader; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.TokenStream; import com.chenlb.mmseg4j.Dictionary; import com.chenlb.mmseg4j.MaxWordSeg; import com.chenlb.mmseg4j.analysis.MMSegTokenizer; /** * 寫一個分詞器,一般可以參照原來分詞器是怎麼寫法的 * @author hhc * */ public class MySameAnalyzer extends Analyzer{ //同義詞 private SamewordContext samewordContext=null; public MySameAnalyzer(SamewordContext samewordContext){ this.samewordContext=samewordContext; } @Override public TokenStream tokenStream(String fieldName, Reader reader) { // Dictionary dic=Dictionary.getInstance(); return new MySameTokenFilter(new MMSegTokenizer(new MaxWordSeg(dic), reader),samewordContext); } }
然後再對TokenStream流做同義詞處理
package hhc; import java.io.IOException; import java.util.Stack; import org.apache.lucene.analysis.TokenFilter; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; import org.apache.lucene.util.AttributeSource; public class MySameTokenFilter extends TokenFilter { // 分詞單元資訊 private CharTermAttribute cta = null; // 位置資訊 private PositionIncrementAttribute pia = null; // 狀態 private AttributeSource.State current; // 同義詞集合 private Stack<String> sames = null; private SamewordContext samewordContext=null; protected MySameTokenFilter(TokenStream input,SamewordContext samewordContext) { super(input); cta = input.addAttribute(CharTermAttribute.class); pia = input.addAttribute(PositionIncrementAttribute.class); sames=new Stack<String>(); this.samewordContext=samewordContext; } @Override public boolean incrementToken() throws IOException { try { if (sames!=null&&sames.size()> 0) { // 刪除物件在堆疊,然後返回的物件上的函式值,並且獲取這個同義詞 String str = sames.pop(); // 還原狀態 restoreState(current); cta.setEmpty(); cta.append(str); pia.setPositionIncrement(0); return true; } // 如果流中沒有資料了。 if (!input.incrementToken())return false; /** * 流中有資料的話,進行相應的同義詞 */ // 處理切分出來的詞的資訊 if (existAddSameword(cta.toString())) { // 把當前狀態先儲存 current = captureState(); } } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } return true; } /** * 判斷是否該分詞單元存在 * * @param word * @return */ private boolean existAddSameword(String word) { String[] words=samewordContext.getSameword(word); if (words != null) { for (String s : words) { sames.push(s); } return true; } return false; } }