Rails で jQuery を使って Elasticsearch 全文検索による検索文字をハイライトさせる

スポンサーリンク

Rails アプリで、Elasticsearch での全文検索による検索テキストをハイライトさせるコードを、jQuery を使って書きました。Elasticsearch 自体にも ハイライト(highlight)の機能があるみたいなのですけど、使い方がよく分からなかったのでとりあえず jQuery で代替。

Rails 側で serialize したデータを Elasticsearch のインデックスに入れたりしている都合もあって、Rails 本体の highlight ヘルパーも使い辛かったりしましたので。

highlight – リファレンス – Railsドキュメント

【お知らせ】 英単語を画像イメージで楽に暗記できる辞書サイトを作りました。英語学習中の方は、ぜひご利用ください!
画像付き英語辞書 Imagict | 英単語をイメージで暗記
【開発記録】
英単語を画像イメージで暗記できる英語辞書サービスを作って公開しました
スポンサーリンク

Rails コントローラーでの検索機能実装

まずは Rails のコントローラー側で検索フォームから検索文字列を取得して、elasticsearch-ruby で検索を実行するロジック。posts_controller.rb の search アクションを例とします。必要な箇所だけ表示するため端折ってます。

app/controllers/posts_controller.rb

Elasticsearch::Client.new#search メソッドは、検索結果をハッシュと配列で返しますので分かりやすいです。sanitize_string_for_elasticsearch メソッドについては以下。

elasticsearch-ruby で外部入力から検索時の json 用文字列のエスケープ処理 | EasyRamble

この search メソッド自体は、前半で Elasticsearch による検索を実行、後半で検索文字列を一旦トークンに分割して、,(コンマ)で join して文字列にしてます。これは入力された検索テキストを、Ruby から JavaScript 側に引数で渡すため。

elasticsearch-ruby でトークナイザーを指定してトークン分割 | EasyRamble

検索文字列は、入力されたキーワードが日本語の場合と英語などその他の場合で処理を分けています。日本語の場合のひらがな一文字のトークン(助詞)は、トークン文字列から除外しています。「てにをは」をハイライトから除外するため。

views/posts/search.html.erb

ビューで JavaScript を呼び出す部分。ビューファイルの先頭に以下を書きました。ここで @searched_words_string を Ruby から JavaScript 側へと渡す。

検索文字列が日本語か英語かで、それぞれ highlight_searched_ja_words(), highlight_searched_en_words() という jQuery メソッドを呼び出す。@es_result[“hits”][“total”] は、検索によりヒットした件数が入ってます。

続いて、ビューファイルで検索結果を出力する部分。

@es_result[“hits”][“hits”] に検索結果自体がハッシュの配列で入っています。これをぐるぐる回すだけ。ハイライトさせるのは、<div id="search-result-body-<%= index %>"></div> で囲まれた部分の文字列。id 指定のために each_with_index で回して index を指定しています。

キーワードをハイライトさせる jQuery 関数

検索文字列が日本語かそれ以外(英語など)で、2つ関数を作成しました。類似箇所が多いので要リファクタリングです。

app/assets/javascripts/posts.js

検索がヒットした件数分 for ループで回して、$(‘#search-result-body-‘ + i) の html 中の、検索キーワードを <span class="highlight"></span> で囲った文字列に置き換えます。英語など日本語以外の場合は、正規表現の \b でスペースなどの単語境界を指定しています。

CSS

あとは、css で highlight のデザインを指定。

app/assets/stylesheets/custom.css.scss

検索文字の色を変えるようにしました。background-color でも何でも都合の良いように。

以上で、複数の単語からなる文章による検索や、スペース繋ぎによる複数単語の検索などの際にも、Elasticsearch の全文検索による検索文字列が良い感じにトークン分割されて、分割された単語が全てハイライトされるようになりました。重複箇所等あるので、もう少しリファクタリングが必要ですけど、とりあえずやりたかったことが実現できたので今日はここまで。

スポンサーリンク
全文検索システムを実装するには、ElasticSearch がおすすめです。
 
スポンサーリンク

Leave Your Message!