独自CharFilter作ってみた

今回はCharFilterの総集編(?)ということで、「昭和フィルター」という独自フィルターを題材に、独自CharFilterの作り方と使い方を紹介します。「独自」とは言っても、MappingCharFilterPatternReplaceCharFilterでほとんどのCharFilterでやるべきことは出来るはずで、なかなか独自のものを必要とする機会は少ないと思います。(こういうのが、というのがあれば是非教えて下さい)この記事を書くにあたり考えてみたのですが、私には実用的なものは思いつきませんでした。(今回の例はほぼネタです。実際には使わないで下さい)さて、まずは昭和フィルターの紹介からです。

昭和フィルターとは

昔のレトロな表記でよくある「ろどいび」や「こばた」など“右から左“に書いてある文字を、Tokenizerが解析できるように逆順(「びいどろ」や「たばこ」)にしてあげるCharFilterです。あくまで検索に使うINDEXが逆順になるだけで、内容は右から左のまま変わりません。動作イメージは以下のスクリーンショットの通りです。

利用シーン

コンテンツ例 マッチするクエリー文字列
社会式株CBA
ABC株式会社
華中しや冷
たしまめじは
冷やし中華
送運ーらーそ
運送
(“ーそ“がハイライトされます・・・)

制限事項

  • Luceneの制限だと思うのですが、文字の順番が入れ替わる位置調整は試したところうまく動作しませんでした。それが原因で、上記の例にもある通り、行全体とマッチするもの以外は適切にハイライトしません(※)
  • サロゲートペアには対応していません(面倒だったので・・)
  • 1行が長すぎる文章の場合、メモリを大量に消費します・・

※そもそも、今回の方法は逆順で登録するのではなく、通常通りLTRで登録し、スタイルシートでRTLを指定すればいいだけの事です。このフィルターを使わなくても実現できますし、そうした方が正しくハイライトできます。あくまで独自CharFilterのサンプルという事で。

コード

1ファイルにするために、CharFilterFactoryのstaticなメンバークラスとしてCharFilterを定義しています。CharFilterFactoryはorg.apache.solr.analysis.CharFilterFactoryを実装し、CharFilterはorg.apache.lucene.analysis.CharStreamを継承したクラスを作成します。それぞれに共通の処理を定義した、BaseCharFilterFactoryBaseCharFilterという抽象クラスが用意されているので、今回のサンプルと同じように、通常はそれらを継承して作成するといいでしょう。では実際にコードを見ていきましょう。

このコードのポイントは42〜58行目(行数が出てませんが・・・)のreadLineメソッドです。ここでは必要に応じてReaderから1行読み込み、逆順にしつつ、文字ごとのオフセットの調整(addOffCorrectMapメソッドの呼び出し)を行っています。オフセットの調整は、本当なら逆順にしなければいけませんが、先の制限事項でも書いた通り、ちゃんと指定してしまうと正しく動作しませんので、今回の実装では固定で0を設定しています。CharFilterの実装では、このOffsetの調整が面倒ですが、とても重要です。正しく行わないと、ハイライトが正しく動かなくなってしまいますので。
また、今回のサンプルでは行いませんでしたが、外部ファイルを利用する場合は、org.apache.solr.util.plugin.ResourceLoaderAwareを実装する事で、informメソッドでリソースの読込を行うことが出来ます。この際、パラメータのResourceLoaderによって、coreのconfからの相対パス、カレントディレクトリからの相対パス絶対パス、もしくはClassLoaderが自動的に選択される、とても便利な読込手段が提供されていますので、独自に作りこまずにResourceLoaderを利用するようにしましょう。

Solrへの導入方法

以下の三つを行うだけです。

  1. jarを作る
  2. solrconfig.xmlで指定するか、sharedLibに入れる
  3. schema.xmlのcharFilter要素のclassに今回作った"analysis.ShowaFilterFactory"を設定(Tokenizerはお好きなものを)

※パッケージ名はorg.apache.solr.analysisに属している場合、solr.クラス名と省略して記述できます。

なかなか独自のCharFilterが必要となる事はなさそうですが、実装もそれほど難しくなく、また、容易に組み込めるようになっていますので、機会があればどんどん作ってみましょう。

次回はTokenizerFactoryを紹介していく予定です。