Posted at

SolrでDataImportHandlerのTransformerをJavaScriptで書く

More than 1 year has passed since last update.

自部署で運営しているサービスのバックエンドにSolrを使用しており

DBからDataImportHandler(DIH)を介してドキュメントを登録しています。

登録の一部で、取得したデータをJSON文字列にして組み込みたい箇所があり

本記事ではそれを実現するために使った機能についてまとめます。

Solrのバージョンは5.5.3ですが

かなり古いバージョンからある機能を使用するため、特に重要ではありません。


背景

DIHでのインポート時にデータを加工するためには

データ取得からインデックス作成までの間に処理を挟む必要があります。

この処理のためには様々なTransformerが提供されています。


Transformers manipulate the fields in a document returned by an entity. A transformer can create new fields or modify existing ones.


ドキュメント内のフィールドを加工したり、新しいものを作ることができます。


問題点

既存のTransformerの中に、都合よく「JSON文字列を作る」ものはありません。


You can also write your own custom transformers, as described in the Solr Wiki (see http://wiki.apache.org/solr/DIHCustomTransformer).


そして、ないならカスタムで作りましょうと記載されています。

ただし私は大学の授業でしかJavaを書いたことがなく

少なくともすぐには真っ当なコードを書ける自信がありません。

そこで見つけたのがScriptTransformerでした。


ScriptTransformerとは


The script transformer allows arbitrary transformer functions to be written in any scripting language supported by Java, such as Javascript, JRuby, Jython, Groovy, or BeanShell. Javascript is integrated into Java 8; you'll need to integrate other languages yourself.


(雑な和訳)

Javaでの動作がサポートされている言語で書けるTransformer。

色んな言語で使える。JavascriptだけはJava 8の環境ならデフォルトで動く。

Javaよりは書いた経験もあるので、今回はJavascriptで書くことにしました。


やったこと


ScriptTransformerの定義

data-config.xml(DIHの設定が書かれたファイル)内で

<script>エレメントに処理を記載します。

ここに記載した処理はインポートの開始時に一度実行されます。


data-config.xml

<script><![CDATA[                                                

var mapper = new com.fasterxml.jackson.databind.ObjectMapper();

function toJsonString(row) {
row.put('json_string', mapper.writeValueAsString(row));

return row;
}
]]></script>



  • functionはJava Map<String,Object>row)を1つ受け取る


  • rowにはget,put,removeの処理が可能


  • Java Map<String,Object>のかたちで値を返す必要がある

Javaのライブラリを呼び出すこともできるため、今回はJacksonを使用し

row全体をJSON文字列化したものをjson_stringカラムでrowに追加しました。

なお、Jacksonは別途Solrに組み込んでおく必要があります。

JavaScriptらしくJSON.stringify()を先に試しましたが

この方法ではrowをパースする際にエラーが出てしまいました。


ScriptTransformerの呼出

通常のTransformerと同様に、entityエレメントに記載します。


data-config.xml

<entity name="articles"

query="{{ 1レコードを取得するクエリ }}"
transformer="script:toJsonString">
<field column="json_string" name="articles" />
</entity>

前述のtoJsonString()が返すjson_stringカラムの値を

articlesという名称のフィールドに追加して完了です。

ちなみに、デバッグは面倒ですがコチラを参考にログを吐かせると

Solrのコンソールログ内に情報が出力されます。


まとめ

結局JavaとJavaScriptのシンタックスが入り混じっていましたが

比較的簡単にTransformerを作成することができました。

具体的に計測はしていませんが、純粋なJava以外の処理が動くため

多少のオーバーヘッドは存在するのではないかと思います。

インポート時の速度が強く求められる場合は、必ず事前にご検証ください。