ホーム DoRuby RailsでOracle~ Oracle Textで日本語検索
RailsでOracle~ Oracle Textで日本語検索
 

RailsでOracle~ Oracle Textで日本語検索

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

Oracleに標準で備わっている日本語検索機能をRailsから使ってみましょう

データベース内のテキストから特定の語句が含まれるレコードを検索する必要がある場合、通常は弊社の提供する AdvantageSearch というサービスをお薦めしています。

しかし、OracleにはOracle Textというテキスト検索のための仕組みが標準で備わっているので、今回はそちらを使って検索機能を実装してみます。
https://blogs.oracle.com/oracle4engineer/oracle-text-v5

Oracle Textとは

Oracle Database本体に標準でバンドルされている、テキスト検索機能です。

欧文に対応したBasic Lexserのほかに、JAPANESE_VGRAM_LEXERと呼ばれる日本語に特化したトークン処理のエンジンを持っており、分かち書きのない日本語テキストに対して高速な部分一致検索を行うことができます。

V-GRAMとは

2文字単位で文字を区切っていくBIGRAM(2-GRAM)を、日本語の特性に合わせて拡張したアルゴリズムです。文頭に置いてはいけない禁則文字がトークンの先頭にこないように、トークンの長さを基本の2文字から伸ばしていくことから 可変gram という意味でV-GRAMと名付けられたようです。

詳しくはこちらが参考になります。
http://otndnld.oracle.co.jp/products/oracle8i/intermedia/htdocs/imt.htm

Railsからの利用

下準備

下準備として、DBユーザーに権限を付与したり、JAPANESE_VGRAM_LEXERのプレファレンスを作成したりする必要があります。こちらを参考に各自の環境に合わせて作業して下さい。
https://blogs.oracle.com/oracle4engineer/oracle-text-v5

マイグレーション

マイグレーションファイルを作成して、検索を提供したい VARCHAR2型, CLOB型のカラムにOracle Text用のインデックス(Context Index)を付与します。

add_index :users, :address, options: "INDEXTYPE IS ctxsys.context PARAMETERS ('lexer jp_lexer sync (on commit)')"

ここでは、前述の下準備で作成したプレファレンスに jp_lexer という名前を使用しています。

また、sync (on commit) というオプションによって、データベースにコミットした時点でインデックスの更新を行うように指定します。通常のインデックスとくらべて更新コストが重いため、文字量が多いカラムの場合は別のタイミングを選択する必要があるかも知れません。

今回使用しているOracle enhanced adapterには、もともとContext Indexを作成する add_context_index というメソッドが用意されているのですが、デフォルト以外のレクサーを指定することができないため、 add_index にオプションを渡すという方法をとっています。

ActiveRecordからの検索

Oracle TextLは独自の CONTAINS() という関数を使って検索するため、Oracle enhanced adapterには専用のメソッドが用意されてます。

class User
  has_context_index
end

この has_context_index というクラスマクロを記述することで contains というクラスメソッドがミックスインされ、

User.contains(:address, '大井町').order(:created_at).limit(10)

といった検索ができるようになります。

ただしこのメソッドには、別のテーブルと joins して検索したときにカラム名が重複していると正常に検索できないというバグがあるため、例えば

Corporation.joins(:customers).merge(Customer.contains("customers.address", '大井町'))

のように、contains メソッドにテーブル名も含めた名前を文字列で渡してあげる必要があります。

CONTAINS関数の注意点

CONTAINS() の検索式の中で使える独自の文法や予約語があるため、一般ユーザーの入力をそのまま渡すとSQLエラーになる場合があります。
意図して複雑な検索式を使用しない場合は、下記のドキュメントに記載されている予約語を { と } でエスケープするような処理が必要です。
http://otndnld.oracle.co.jp/document/products/oracle10g/102/doc_cd/text.102/B19214-01/cqspcl.htm

日本語以外の検索について

JAPANESE_VGRAM_LEXER を使ったインデックスでは、いわゆる英数文字で書かれたテキストは、空白区切りでトークンとして分解されるため、「目的の単語が含まれるレコードを検索する」という動作になります。

hogehoge fugafuga という文字列が格納されたレコードにヒットする検索語は hogehoge と fugafuga だけで、 hoge や fuga ではヒットしないことに注意して下さい。

まとめ

RailsからOracle Textを使ったテキスト検索機能を利用する方法について紹介しました。
AdvantageSearch にはかないませんが、なかなか便利な検索エンジンですのでみなさんも利用してみて下さい。

記事を共有

最近人気な記事