at posts/single.html

Rails に(再)挑戦 6日目 - ページの検索

いよいよ RandomNote の特徴である検索に取り掛かる。 本家 RandomNote では、データを管理しているテキストを順に開いて、正規表現で検索している。 さて、どうしようか。

  • Page.find(:all) して正規表現 → メモリが持たない
  • 全文検索エンジン (Hyper Estraier) を使う → セットアップが大変
  • データベースの全文検索機能を使う → データベースに依存する & SQLiteじゃ無理?

悩ましい…ので今は深く考えずに、データベースに対する like 検索(!)で済ますことにする。 AND検索はできないし、インデックスを張れないから処理は重いし、欠点ばかりだけどね。 これも後で考えよう。 まずは Rails での実現方法を覚えるのが先だ。

ビュー

どこから手を付けようか迷ったけど、まずは分かりやすいビューから。 検索フォームは全てのページについているから、レイアウト (app/views/layouts/note.rhtml) に手を加える。

<%= start_form_tag :action => 'search' %>
  <%= text_field 'search', 'word' %>
  <%= submit_tag 'Search' %>
<%= end_form_tag %>

検索フィールド (text_field) には2つのパラメータ ('search', 'word') を渡している。 こうすると、実際に出力される HTML は以下のようになる。

<form action="/note/search" method="post">
  <input id="search_word" name="search[word]" size="30" type="text" />
  <input name="commit" type="submit" value="Search" />
</form>

Rails で特長的なのは name="search[word]" かな。 データをハッシュとして扱うのがポイント。

このあたりの処理は scaffold で生成されたコードを読むと参考になる。 ページを生成するときの処理 (NoteController#create) は、

@page = Page.new(params[:page])

としているし、ページを更新するときの処理 (NoteController#update) は

@page.update_attributes(params[:page])

となっている。 モデルに対してハッシュをそのまま突っ込めるようにしているのね。 豪快だ…。

さて、少し脱線したよ。 とにかく、検索フィールドに入力したデータは params[:search][:word] で取得できるみたい。 検索フィールドに前回の検索語句を表示するようにしておこう。

<% word = params[:search][:word] if params[:search] %>
<%= start_form_tag :action => 'search' %>
  <%= text_field 'search', 'word', :value => word %>
  <%= submit_tag 'Search' %>
<%= end_form_tag %>

コントローラに検索機能を追加

ビューを作ったので、次はコントローラ。 NoteController に search メソッドを追加する。

def search
  @pages = Page.fulltext_search(params[:search][:word],
                                :order => 'updated_on DESC')
end

ほとんどモデルに丸投げという…。 まぁ、一覧表示 (list) と同じノリだね。

モデルに検索メソッドを追加

ってことで、ようやくメインの処理。 app/models/page.rb を修正し、モデル (Page) にクラスメソッド (Page.fulltext_search) を追加する。 といっても、 find(:all) の検索条件に body like '%検索語句%' を指定するだけ。

def Page.fulltext_search(word, options = {})
  options[:conditions] = ["body like ?", "%#{word}%"]
  find(:all, options)
end

さらにビュー

忘れてた。 NoteControll#search の後に呼ばれるビュー (search.rhtml) が必要。 といっても、基本的には一覧表示 (list.rhtml) と同じものが使える。 Pagenate の使い方が分からないので、とりあえずは検索結果を全て表示するか。

<%= render(:partial => 'page', :collection => @pages) %>

検索してみる

ちゃんと動いているみたい。まだ AND 検索はできないけど…。

screenshot (Ruby on Rails) - (8)

今日もリポジトリに登録

関連する日記