1. 程式人生 > >lucene構建同義詞分詞器

lucene構建同義詞分詞器

       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;
	}

}