Mapping CharFilterFactoryの使い方

Solrには文字列解析の為の仕組みがたくさん用意されています。またそれらは柔軟に組み合わせやパラメータによるカスタマイズが可能で、色んな用途に対応できるようになっています。それらは個々のカスタマイズ可能なコンポーネントとして、下記のURLで説明されています。

http://wiki.apache.org/solr/AnalyzersTokenizersTokenFilters

コンポーネントによっては、上記ページの他にjavadocを見てもあまり詳しく説明がなかったり、パラメータにも何を設定できるのかまでは細かく書かれていません。そこで勉強がてら、これらのコンポーネントの使い方を一つずつ紹介していきます。また、使い方だけではなく、ソースコードから内部の詳細についても一緒に見ていこうと思っています。
(全体的な仕組みの詳細はSolr本を参照してください。機会があれば書いてみるつもりです。)

先程のページを上から順番にこのシリーズで取り上げていきます。まずは一番目のMappingCharFilterFactoryからです。

MappingCharFilterFactoryは、Tokenizerが処理を行う前段階で、文字に対する処理を行うためのコンポーネントのひとつで、文字(もしくは文字列)を別の文字(もしくは文字列)に、変換を行う事ができます。主な利用目的としては解析対象の文章内の文字列の、半角と全角の表記の揺れを吸収するために使います。変換の設定パターンとしては以下のようなものが考えられます。

#半角→全角
ア=>ア
A=>A
#全角→半角
イ=>イ
U=>U
#半角カナ二文字→全角カナ一文字
バ=>バ
#全角カナ一文字→半角カナ一二文字
ビ=>ビ
#記号→全角カナ
㍗=>ワット
㌔=>キロ

上記の変換ルールをファイルに定義して利用します。

利用シーンとしてよくあるのがカナの正規化です。下記の例のように、保存する文字列と検索文字列の両方で半角を全角に変換して、全角半角のどちらで書かれていてもhitするようにします。
例:バービーボーイズ→バービーボーイズ
また用途は限定的でしょうけど、こんな事もできます。
例:ビールの品揃えがオ㍗ル。それでもイ㌔→ビールの品揃えがオワットル。それでもイキロ
googleだと㍗で検索はできません

それでは設定方法について見ていきましょう。

MappingCharFilterFactoryの設定方法

設定できる項目は、文字変換ルールを記述したファイル(以降マッピングファイルと表記)の指定のみです。

scheme.xml設定例

<fieldType name="・・・(略)・・・
  <analyzer>
    <charFilter class="solr.MappingCharFilterFactory" mapping="mapping.txt"/>
    <tokenizer class="・・・(略)・・・
  </analyzer>
</fieldType>
属性 説明 備考
mapping 文字変換ルールを記述したファイルを指定 カンマ区切りで複数指定可

注意点

  1. 文字コードUTF-8で(BOMがあっても大丈夫)
  2. カンマが含まれるファイル名を指定する場合は、\でエスケープする
  3. 変換前の文字列が重複しているとエラーになるので注意(複数ファイルに分割している場合も同様)

マッピングファイルの書き方

  • 変換前の文字列と変換後の文字列をそれぞれダブルクォートでくくり、間に=>を記述します。ダブルクォートと=>の間はスペースが入っても構いませんが、一行で記述して下さい。各行は次のパターンにマッチしてればOKです。"(.*)"\s*=>\s*"(.*)"\s*$
  • #から始まる行はコメント行です。
  • ファイルの文字コードUTF-8固定で、変更できません。BOMはあってもなくても大丈夫です。

# 記述例1
"エ" => "エ"
"糸冬"=>"終"

また、エスケープ文字を使っていくつかの特殊文字が使えます。

# エスケープ文字入り記述例
"タブ"=>"\t"
"\r\n"=>"\n"
"バックスペース"=>"\b"
"円"=>"\\"
"ユニコードあ"=>"\u3042"

MappingCharFilterFactoryの使い方は以上です。

MappingCharFilterFactoryの仕組み

MappingCharFilterFactoryの主要なクラスの関係は以下のようになっています。
(パッケージは分かりやすくするためパッケージ名を正確には記述していません)

実際の文字列のreadは、LuceneのMappingCharFilterが行い、変換ルールはNormalizeCharMapが保持しています。これらをFactoryの役割としてインスタンスの生成を行うのが、MappingCharFilterFactoryです。先述のマッピングファイルはNormalizeCharMapとして構築され、文字列解析の度にMappingCharFilterのインスタンスを生成します。

NormalizeCharMapは変換前の文字列を一文字ずつ階層化した構造でキーとして変換後の文字列を紐付けています。文章だけだと分かりにくいと思うので、イメージは下記の図を見て下さい。

※決して分かりやすくなかったことを心から反省・・・。

上記の図のようにNormalizeCharMapを用いて、一文字ずつ対象があるかどうかを確認しながら変換処理を行ないます。

最後にまたMappingCharFilterFactoryですが、このクラスはResourceLoaderAwareというinterfaceを実装していて、solrからResourceLoaderというマッピングファイルを読み込む為のインスタンスを受け取ります。この処理の中でNormalizeCharMapの構築を行っています。あとは先述の通り、FactoryとしてMappingCharFilterのインスタンスを生成する役目を持っています。

MappingCharFilterFactoryについてはざっとこんな感じです。

以下、読んでみての感想です。

  • もっと簡単かなと思ってたけど、文字列処理は効率的にやろうとすると大変(コードがすぐに大きくなる)
  • MappingCharFilterは、Readerを継承したおかげで使いやすいけど実装はやっぱり大変
  • MappingCharFilterはとても汎用的でLuceneのコードにほぼ依存していないので、Luceneのパッケージ内ではなく、もっと汎用的なプロジェクトにそのまま持っていけるかも。(commons-langとか)

はsolr.PatternReplaceCharFilterFactoryです。