Building Ferret queries in models and ferret pagination
August 28th, 2007
With the help of acts_as_ferret plugin, it is very easy to add ferret search functionalities to active records. However, it doesn’t come with pagination out of the box. Following the good practise of fat models and skinny controllers, I’ll show you how I structure my search methods with ferret pagination.
First – get the pagination find plugin. Ilya released a very useful script to combine with paginationfind, however it is not using exactly the same syntax as paginationfind and there’s a tiny issue with the results count when active record :conditions are included.
I’ve created my own patched version here, it accepts the same :page => { :size => 10, :current => 1 } hash.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
def paginating_ferret_search(query,options = {}, find_options = {}) page_options = find_options.delete(:page) || options.delete(:page) || {} current = page_options[:current] && page_options[:current].to_i > 0 ? page_options[:current] : 1 first = page_options[:first] || 1 auto = page_options[:auto] || false # Total size is either AR find result count or Search result total_hits or from limit param, whichever is less limit = options.delete(:limit) total_hits = if find_options[:group] || find_options[:conditions] || find_options[:select] ids = [] raw_hits = find_id_by_contents(query, {:limit => :all}) {|type, id, score, data_hash| ids << id } conditions_array = combine_conditions([ "#{self.table_name}.#{self.primary_key} in (?)", ids ], find_options[:conditions]) count :all, find_options.update({:conditions => conditions_array}) #find_by_contents(query, {:limit => :all},find_options).total_hits # <- bad way of doing it else find_by_contents(query, {:lazy => true}).total_hits end total_size = limit ? [limit, total_hits].min : total_hits # If :size isn't specified, then use the lesser of the total_size # and the default page size page_size = page_options[:size] || [total_size, DEFAULT_PAGE_SIZE].min PagingEnumerator.new(page_size, total_size, auto, current, first) do |page| # Set appropriate :offset and :limit options for this page offset = {:offset => (page - 1) * page_size } limit = {:limit => (page_size) < total_size ? page_size : total_size} # set paging settings on AR results if AR find option exists. # otherwise set paging settings on Ferret results. if find_options[:group] || find_options[:conditions] || find_options[:select] find_options.merge!(offset).merge!(limit) options[:limit] = :all else options.merge!(offset).merge!(limit) end find_by_contents(query, options, find_options) end end |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
class Product < ActiveRecord::Base def self.search(raw_query, *args) options = args.extract_options! brand_query = options.delete(:brand) query = Ferret::Search::BooleanQuery.new() query.add_query Ferret::Search::FuzzyQuery.new("*", raw_query) query.add_query Ferret::Search::TermQuery.new(:brand_name, brand_query), :must unless brand_query.blank? query.add_query Ferret::Search::TermQuery.new(:hidden, "N"), :must if options.delete(:public) == true puts query.to_s self.search_by_ferret_query(query.to_s, options) end def self.search_by_ferret_query(query,options) if options[:page] self.paginating_ferret_search(query, options) else self.find_by_contents(query, options) end end end |

Leave a Reply