目次
久しぶりに、技術的なブログ書いてます。
Ingest Processorのプラグインを作ってみたくなったので、書いてみました。 ただ書いてみるんじゃ3番煎じになりそうなので、cookiecutterを使ってみました。
と言っても、同僚のAlexがcookiecutter-elasticsearch-ingest-processorと言うテンプレートを作ってくれているのを使っただけですが。(https://discuss.elastic.co に投稿された記事で、使い方がアニメgifで説明されててわかりやすいです)
cookiecutterとは、コマンドラインで質問に答えると、テンプレートからプロジェクトが生成できるツールです。 Elasticでは、カスタムBeatを作る時に利用する例がいつかの日本語ブログや発表資料で話題になっていました。 これのIngest Processorのプラグインバージョンです。
今回は、NEologdも使ってみたかったので、Lucene Kuromoji for NEologdを利用して 指定した品詞の単語だけを抽出するProcessorを作ってみました。
GitHubのプロジェクト:https://github.com/johtani/elasticsearch-ingest-kuromoji-pos-extract
Cookiecutterの使い方
Cookiecutterのインストールはサイトをご覧ください。
cookiecutter gh:spinscale/cookiecutter-elasticsearch-ingest-processor
あとは、出てくる以下の項目を指定するだけです。
processor_type
: Ingest Processorのタイプ名です。kuromoji_part_of_speech_extract
としました。(Alexのだと_
を使うとちょっと問題があるので後述)description
: readme.mdに利用されます。developer_name
: 名前を記載。Javaのファイルのヘッダに利用elasticsearch_version
: デフォルトで5.0.0-alpha4
が指定されているので、特に指定せず
以上の質問に答えたら、プロジェクトのディレクトリ構造が出来上がってます。 プロジェクトのビルドなどにはGradleを利用します。
プロジェクトのIntelliJ IDEA用のファイルを生成
build.gradleファイルでGradleのideaプラグインがapplyされているので、以下のコマンドを叩けばIntelliJ IDEAのプロジェクトファイル(?)が生成され、IntelliJで開けばすぐに開発ができる状態にできます。
gradle idea
コーディング
あとは、必要処理をコーディングします。
実際にコーディングするクラスはorg.elasticsearch.plugin.ingest.kuromoji_part_of_speech_extract
のパッケージにある以下の2つです。(パッケージ名にはprocessor_typeの名前が指定されている)
- IngestKuromojiPartOfSpeechExtractPlugin
- KuromojiPartOfSpeechExtractProcessor
IngestKuromojiPartOfSpeechExtractPlugin
Pluginというクラスは、プラグインをNodeのModuleとして登録する処理を書くクラスとなります。 生成してすぐは、次のような形になっています。(※importやクラス定義の部分は省略しています。)
...
public static final Setting<String> YOUR_SETTING =
new Setting<>("ingest.kuromoji_part_of_speech_extract.setting", "foo", (value) -> value, Setting.Property.NodeScope);
@Override
public List<Setting<?>> getSettings() {
return Arrays.asList(YOUR_SETTING);
}
public void onModule(NodeModule nodeModule) throws IOException {
nodeModule.registerProcessor(KuromojiPartOfSpeechExtractProcessor.TYPE,
(registry) -> new KuromojiPartOfSpeechExtractProcessor.Factory());
}
...
YOUR_SETTING
プロパティとgetSettings()
メソッドはelasticsearch.yml
で指定したい設定を記述する場合の例になります。今回は特に必要ないので両方削除しました。
最終系はGitHubのコードをご覧ください。
KuromojiPartOfSpeechExtractProcessor
Processorは実際にIngest Nodeで行う処理を書くところです。
public static final String TYPE = "kuromoji_part_of_speech_extract";
private final String field;
private final String targetField;
public KuromojiPartOfSpeechExtractProcessor(String tag, String field, String targetField) throws IOException {
super(tag);
this.field = field;
this.targetField = targetField;
}
@Override
public void execute(IngestDocument ingestDocument) throws Exception {
String content = ingestDocument.getFieldValue(field, String.class);
// TODO implement me!
ingestDocument.setFieldValue(targetField, content);
}
@Override
public String getType() {
return TYPE;
}
public static final class Factory extends AbstractProcessorFactory<KuromojiPartOfSpeechExtractProcessor> {
@Override
public KuromojiPartOfSpeechExtractProcessor doCreate(String processorTag, Map<String, Object> config) throws Exception {
String field = readStringProperty(TYPE, processorTag, config, "field");
String targetField = readStringProperty(TYPE, processorTag, config, "target_field", "default_field_name");
return new KuromojiPartOfSpeechExtractProcessor(processorTag, field, targetField);
}
}
TYPE
がIngest APIのPipelineでProcessorを指定するときに使う名前になります。ここは、cookiecutterの時にprocessor_typeに入力した文字列になっています。
kuromoji_part_of_speech_extract
だと長いので、kuromoji_pos_extract
に変えました。
execute()
メソッドに// TODO implement me!
とあります。
この部分に実際の処理を記述していきます。
あとは、Factory
クラスでIngest APIで指定された設定項目を読み込みます。
今回作成したelasticsearch-ingest-kuromoji-pos-extract
では品詞を指定する必要があるので、pos_tags
を指定できるように処理を追加しました。
私が実装したものの説明をするとちょっと長くなりそうなので、GitHubのコードをご覧ください。
テストのコーディング
テストのクラスもテンプレートで生成されています。
- KuromojiPartOfSpeechExtractProcessorTests
- KuromojiPartOfSpeechExtractRestIT
KuromojiPartOfSpeechExtractProcessorTests
Processorクラスのテストになります。生成直後は次のような感じです。
public void testThatProcessorWorks() throws Exception {
Map<String, Object> document = new HashMap<>();
document.put("source_field", "fancy source field content");
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), document);
KuromojiPartOfSpeechExtractProcessor processor = new KuromojiPartOfSpeechExtractProcessor(randomAsciiOfLength(10), "source_field", "target_field");
processor.execute(ingestDocument);
Map<String, Object> data = ingestDocument.getSourceAndMetadata();
assertThat(data, hasKey("target_field"));
assertThat(data.get("target_field"), is("fancy source field content"));
// TODO add fancy assertions here
}
テストメソッドも実装されていますが、パラメータの追加の設定処理やアサーションが書かれてません。 実装に合わせて、アサーションや設定処理を追加しましょう。
KuromojiPartOfSpeechExtractRestIT
こちらはIntegration Testになります。
実際にElasticsearchに対して外部からAPIを叩くような感じです。
APIを叩くときに利用するJSONの設定やアサーションはsrc/test/resources
にyamlファイルがあります。
- 10_basic.yaml
- 20_kuromoji_part_of_speech_extract_processor.yaml
10_basic.yaml
はプラグインがインストールされているかの確認のテストです。特に変更する必要はないです。
20_kuromoji_part_of_speech_extract_processor.yaml
は実際にコーディングしたProcessorが動くかどうかのテストです。
テストの内容については、GitHubのコードをご覧ください。
テストの実行とZipの生成
テストの実行とZipの生成は次のコマンドを実行すればOKです。
gradle check
テストに問題があった場合は、コケますし、問題なければSUCCESS
と表示が出ます。
成功した場合はbuild/distributions/
というディレクトリにzipファイルができています。
これをElasticsearchのpluginコマンドでインストールすれば動きます。
bin/plugin install file:///path/to/elasticsearch-ingest-kuromoji-pos-extract/build/distribution/ingest-kuromoji_part_of_speech_extract-0.0.1-SNAPSHOT.zip
kuromoji_pos_extractの利用方法
Ingest APIには便利なSimulate Pipeline APIがあります。
ということで、mecab-ipadic-NEologdにあったサンプルの文章を使って、使い方の説明です。
POST _ingest/pipeline/_simulate
{
"pipeline" : {
"description" : "kuromoji neologd extract test",
"processors" : [
{
"kuromoji_pos_extract" : {
"field" : "body",
"target_field" : "noun_field",
"pos_tags" : [
"名詞-固有名詞-組織",
"名詞-固有名詞-一般",
"名詞-固有名詞-人名-一般",
"名詞-固有名詞-地域-一般",
"名詞-固有名詞-地域-国"
]
}
}
]
},
"docs" : [
{
"_index": "index",
"_type": "type",
"_id": "id",
"_source": {
"body" : "10日放送の「中居正広のミになる図書館」(テレビ朝日系)で、SMAPの中居正広が、篠原信一の過去の勘違いを明かす一幕があった。"
}
}
]
}
結果はこちら。
{
"docs": [
{
"doc": {
"_index": "index",
"_id": "id",
"_type": "type",
"_source": {
"noun_field": [
"10日",
"中居正広のミになる図書館",
"テレビ朝日",
"SMAP",
"中居正広",
"篠原信一"
],
"body": "10日放送の「中居正広のミになる図書館」(テレビ朝日系)で、SMAPの中居正広が、篠原信一の過去の勘違いを明かす一幕があった。"
},
"_ingest": {
"timestamp": "2016-07-22T06:18:49.007+0000"
}
}
}
]
}
noun_field
に固有名詞の単語が抜き出せているのがわかるかと思います。
Alexのテンプレートで困った点
テンプレートは便利だったのですが、processor_type
に_
を使用したタイプ名を指定すると次のような問題(?)が発生しました。
- クラス名が
Kuromoji_part_of_speech_extractProcessor
となってしまう
深刻な問題ではないのですが、JavaだとCamel Caseが普通なのでちょっと気になって。 ということで、プルリク作って出してみました。まだ取り込まれてないかな。
取り込み前に使いたい方は以下のコマンドを実行してください。
processor_class_name
という項目が増えています。
デフォルトだとprocessor_type
の_
の部分を取り除きつつCamel Caseにしたものが入ります。
cookiecutter gh:johtani/cookiecutter-elasticsearch-ingest-processor
まとめ
ということで、とりあえず作ってみましたというものになります。 特徴的な単語(固有名詞だけ)を抜き出して、別のフィールドにできるので、タグみたいなものをこれを使って前処理で作れるようになるかなぁと。
参考ブログ(元ネタ?)
インスパイア元となったブログです。
comments powered by Disqus
See Also by Hugo
- elasticsearch-inquisitorプラグインの紹介
- プロキシ環境でのpluginコマンドの実行
- いつも入れているElasticsearchのプラグイン
- 機械学習による検索ランキング改善ガイドを読みました
- Querqyの調査(その2:アーキテクチャ)