成果物
CJKAnalyzer とは?
Lucene で 日本語韓国語中国語 を Bi-gram にしてくれるやつ。
ただ、日本語だとカタカナはバイグラムじゃなくても良かったりする。
CJKAnalyzer を使う簡単なソースコードだけ、先にあげときます。
import java.util.ArrayList;
import java.util.List;
import java.io.IOException;
import org.apache.lucene.analysis.cjk.CJKAnalyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;
public class Sample {
public static void main(String[] args) {
String text = "岸田文雄首相は衆院本会議で所信表明演説し、個人のリスキリング(学び直し)の支援に5年で1兆円を投じると表明した。";
CJKAnalyzer analyzer = new CJKAnalyzer();
// 英語でよく使う StandardAnalyzer では、
// StandardAnalyzer analyzer = new StandardAnalyzer();
TokenStream tokenStream = analyzer.tokenStream("testField", text);
try {
tokenStream.reset();
CharTermAttribute termAtt = tokenStream.addAttribute(CharTermAttribute.class);
// OffsetAttribute offsetAtt = tokenStream.addAttribute(OffsetAttribute.class);
while (tokenStream.incrementToken()) {
System.out.println(termAtt.toString());
}
tokenStream.end();
} catch (IOException e) {
System.out.println(e);
}
}
}
lucene/lucene/analysis/common/src/java/org/apache/lucene/analysis/cjk
にある cjk と util を /lucene/lucene/core/src/java/org/apache/lucene/analysis
に移動させて、上の Sample.java を lucene/lucene/core/src/java
に置けば動きます。
結果は、Bi-gram された結果が出てきたのではないでしょうか?
カタカナだけ Bigram でなくする。
日本語では特に Bigram にする必要もない カタカナだけ、連続して表示させてみましょう。
まずは、CJKAnalyzer.java のソースコード を見ます。一番下の方です。
@Override
protected TokenStreamComponents createComponents(String fieldName) {
final Tokenizer source = new StandardTokenizer();
// run the widthfilter first before bigramming, it sometimes combines characters.
TokenStream result = new CJKWidthFilter(source);
result = new LowerCaseFilter(result);
result = new CJKBigramFilter(result);
return new TokenStreamComponents(source, new StopFilter(result, stopwords));
}
ここの StandardTokenizer で 日本語を 1文字区切りにしていますが、これをカタカナだけ一まとまりにしてくれたら、カタカナだけ Bi-gram にしないようにできそうじゃないでしょうか?
なので、この StandardTokenizer の中身を見てみましょう。
詳細は、この記事 に譲るとして、今回は この JFlex ファイルをいじれば、StandardTokenizer (元ファイル) の結果だけでは、カタカナを一まとまりにすることができます。それが下のコードです。
KatakanaEx = [\p{WB:Katakana}]
[
と ]
をつけただけですが、これで StandardTokenizer はカタカナを1まとまりにしてはき出してくれます。
ですが、このまま CJKAnalyzer を使っても、カタカナは Bi-gram のまま表示されます。ここで見ておく必要が出てくるのが、CJKBigramFilter です。
今回は、結果だけ載せますが、
public static final String EXTRA_TYPE = "<EXTRA>";
// 省略
private void flushExtragram(int extra_length) {
//
refill();
// 【 や 】でズレた時 用の patch
char bufferChar = (char)buffer[index + 1];
int bufferOffset = 0;
if (bufferChar != originalBuffer[0]) {
int[] checkBufferRange = Arrays.copyOfRange(buffer, index + 1, index + extraLength + 1);
char[] checkOriginalBufferRange = Arrays.copyOfRange(originalBuffer, index, index + extraLength);
for (int i = 0; i < checkOriginalBufferRange.length; i++) {
if ( checkOriginalBufferRange[i] == (char)checkBufferRange[0] ) {
bufferOffset = i;
break;
}
}
}
int originalIndex = 1;
int originalExtraLength = extraLength + 1;
originalIndex -= bufferOffset;
originalExtraLength -= bufferOffset;
//
clearAttributes();
char[] termBuffer = termAtt.resizeBuffer(extraLength*2);
int len_extra = 0;
for (int i = originalIndex; i < originalExtraLength; i++){
int len_i = Character.toChars(buffer[index + i], termBuffer, len_extra);
len_extra += len_i;
}
termAtt.setLength(len_extra);
offsetAtt.setOffset(startOffset[originalIndex], endOffset[originalIndex + originalExtraLength - bufferOffset]);
typeAtt.setType(EXTRA_TYPE);
index += extraLength + 1 - bufferOffset;
}
の定数と関数が新しく追加した部分で、この関数を
} else if (doNext()) {
// case 2: look at the token type. should we form any n-grams?
String type = typeAtt.type();
if (type == doHan || type == doHiragana || type == doKatakana || type == doHangul) {
+ if (termAtt.length() > 1) {
+ //
+ flushExtragram(termAtt.length());
+ return true;
+ }
188行目あたりで呼び出せば、カタカナだけ連続して表示されるはずです。
簡単に説明すると、doNext()
で StandardTokenizer で分割された 次の トークン を読み出す時に、その長さが 1 より大きい場合は カタカナになるので、その場合だけ flushExtragram という関数で独自に termAtt に一まとまりのカタカナを登録する流れになります。
最後の index += extra_length + 1 - bufferOffset
で トークンにあるカタカナの数だけ index を飛ばしています。
StandardTokenizer で出来たから試したけど、意外とできた。
修正点などあったら、ご指摘お願いします。