will_paginate + acts_as_searchableで全文検索+ページネーション
Rails2.0からページネーションがプラグインベースになりました。普通のページネーションであれば、
@memos = @user.memos.paginate(:page => params[:page], :per_page => 10,
rder=>”memos.id DESC”)
だけで使えるようになって便利ですが、任意の情報に対してページネーションする方法がいまいち分かっていませんでした。そして、acts_as_searchableを使って、HyperEstraierで全文検索を行う際に、ページネーションをどのように行えば良いのか調べつつ分かったところを書いてみたいと思います。
実際にページネーション処理を行っているのは、
vendor/plugins/will_paginate/lib/will_paginate/finder.rb
になります。ここの
def paginate_by_sql(sql, options)
WillPaginate::Collection.create(*wp_parse_options!(options)) do |pager|
query = sanitize_sql(sql)
options.update
ffset => pager.offset, :limit => pager.per_page
original_query = query.dup
add_limit! query, options
# perfom the find
pager.replace find_by_sql(query)
unless pager.total_entries
count_query = original_query.sub /bORDERs+BYs+[w`,s]+$/mi, ''
count_query = "SELECT COUNT(*) FROM (#{count_query}) AS count_table"
# perform the count query
pager.total_entries = count_by_sql(count_query)
end
end
end
が参考になります。
pager.replaceに、リストを入れて、pager.total_entriesに行数を入れればOKのようです。この処理をまねて、
app/controllers/application.rb
に次のように定義します。
module WillPaginate
module Finder
module ClassMethods
def paginate_by_fulltext_search(query, options)
WillPaginate::Collection.create(*wp_parse_options!(options)) do |pager|
pager.replace fulltext_search(query, options)
count_options = Hash.new
count_options[:count] = true
count_options[:attributes] = options[:attributes] if options[:attributes]
pager.total_entries = fulltext_search(query, count_options)
end
end
end
end
end
です。そして、呼び出し側では次のようにします。
@memos = Memo.paginate_by_fulltext_search(params[:q], :attributes => “user_id NUMEQ %d” % @user.id, :limit => perpage,
ffset => offset, :page => params[:page], :per_page => perpage)
ここで、:limitはacts_as_searchable用、:per_pageはwill_paginate用になります。:pageや:offsetは共通で利用できるようです。:attributesはあれば設定します。これさえ設定しておけば、ビュー側は
<%= will_paginate @memos, :prev_label=> _(’« Prev’), :next_label=> _(’Next »’) %>
が利用できて便利です。ちなみにpaginate_by_sqlではoptionsがアップデートされるので、デフォルトの30件になってしまうようですね。これはなぜなんだろう…。