全文検索エンジンLuceneで自作のAnalyzer/Tokenizerを使用する
端的に言うと、AnalyzerとTokenizerを継承したクラスをそれぞれ作れば良い。
今回作成したTokenizerは以前よりQMACloneで使用している、辞書に含まれている単語を抜き出すというTokenizerである。アルゴリズムはVitabiアルゴリズムを少しだけ変えただけなので省略。ソースコード例はこちら↓
package tv.dyndns.kishibe.server.relevance; import java.io.IOException; import java.io.Reader; import java.text.Normalizer; import java.text.Normalizer.Form; import java.util.ArrayList; import java.util.List; import org.apache.commons.io.IOUtils; import org.apache.lucene.analysis.Tokenizer; import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; import org.apache.lucene.analysis.tokenattributes.TermAttribute; import org.apache.lucene.util.AttributeSource; public class VitabiTokenizer extends Tokenizer { private final TermAttribute termAtt; private final OffsetAttribute offsetAtt; private final List<Integer> offsets = new ArrayList<Integer>(); private final List<Integer> lengths = new ArrayList<Integer>(); private char[] buffer; private int wordIndex = 0; public VitabiTokenizer(Reader input) { super(input); offsetAtt = addAttribute(OffsetAttribute.class); termAtt = addAttribute(TermAttribute.class); initialize(); } public VitabiTokenizer(AttributeFactory factory, Reader input) { super(factory, input); offsetAtt = addAttribute(OffsetAttribute.class); termAtt = addAttribute(TermAttribute.class); initialize(); } public VitabiTokenizer(AttributeSource source, Reader input) { super(source, input); offsetAtt = addAttribute(OffsetAttribute.class); termAtt = addAttribute(TermAttribute.class); initialize(); } private void initialize() { String s = null; try { s = IOUtils.toString(input); } catch (Exception e) { e.printStackTrace(); } offsets.clear(); lengths.clear(); s = Normalizer.normalize(s, Form.NFKC); s = s.toLowerCase(); Trie.getInstance().parse(s, null, offsets, lengths); buffer = s.toCharArray(); wordIndex = 0; } @Override public boolean incrementToken() throws IOException { if (wordIndex == offsets.size()) { return false; } else { final int offset = offsets.get(wordIndex); final int length = lengths.get(wordIndex); ++wordIndex; termAtt.setTermBuffer(buffer, offset, length); offsetAtt.setOffset(offset, offset + length); return true; } } @Override public void end() throws IOException { super.end(); offsetAtt.setOffset(buffer.length, buffer.length); } @Override public void reset(Reader input) throws IOException { super.reset(input); initialize(); } }
Tokenizerを使用するクラスは、getAttribute()でTermAttributeとOffsetAttributeを取得して使用する。この準備のため、それぞれのインスタンスをフィールドに持たせておく。続いて実際のTokenizeを行う。本来はincrementToken()の中で1トークンずつ行うのだが、今回は既に用意してあったライブラリとの兼ね合いで、initialize()の中でTokenizeを行い、その結果を保持してincrementToken()でTokenizeの結果をTermAttributeとOffsetAttributeの中に入れるという形で行っている。もしかしたら速度が遅いかもしれない・・・。