lib/rsolr/pagination.rb
module RSolr::Pagination # Calculates the "start" and "rows" Solr params # by inspecting the :per_page and :page params. def self.calculate_start_and_rows page, per_page per_page ||= 10 page = page.to_s.to_i-1 page = page < 1 ? 0 : page start = page * per_page [start, per_page] end # A mixin module for RSolr::Client # -- note, this must mixed-in via # "extend" on a RSolr::Client instance. module Client # A paginated request method. def paginate page, per_page, path, opts = {} request_context = build_paginated_request page, per_page, path, opts = {} puts request_context.inspect execute request_context end # Just like RSolr::Client #build_request # but converts the page and per_page # arguments into :rows and :start. def build_paginated_request page, per_page, path, opts = {} opts[:page] = page opts[:per_page] = per_page opts[:params] ||= {} values = RSolr::Pagination.calculate_start_and_rows(page, per_page) opts[:params][:start] = values[0] opts[:params][:rows] = values[1] build_request path, opts end protected # Checks if the called method starts # with "paginate_*" and # converts the * to the solr # request path. It then calls paginate # with the appropriate arguments. # If the called method doesn't # start with "paginate_", # the original/super # RSolr::Client #method_missing # method is called. def method_missing name, *args if name.to_s =~ /^paginate_(.+)$/ paginate args[0], args[1], $1, *args[2..-1] else super name, *args end end # Overrides the RSolr::Client #evaluate_ruby_response method. # Calls the original/super # RSolr::Client #evaluate_ruby_response method. # Mixes in the PaginatedResponse if # the request[:page] and request[:per_page] # opts are set. def evaluate_ruby_response request, response result = super request, response result.extend(PaginatedResponse) if request[:page] && request[:per_page] result end end module PaginatedResponse # TODO: self["responseHeader"]["params"]["rows"] # will not be available if omitHeader is false... # so, a simple "extend" probably isn't going to cut it. def self.extended base return unless base["response"] && base["response"]["docs"] d = base['response']['docs'] d.extend PaginatedDocSet d.per_page = self["responseHeader"]["params"]["rows"].to_s.to_i rescue 10 d.start = base["response"]["start"].to_s.to_i d.total = base["response"]["numFound"].to_s.to_i end end # A response module which gets mixed into the solr ["response"]["docs"] array. module PaginatedDocSet attr_accessor :start, :per_page, :total # Returns the current page calculated from 'rows' and 'start' # WillPaginate hook def current_page return 1 if start < 1 per_page_normalized = per_page < 1 ? 1 : per_page @current_page ||= (start / per_page_normalized).ceil + 1 end # Calcuates the total pages from 'numFound' and 'rows' # WillPaginate hook def total_pages @total_pages ||= per_page > 0 ? (total / per_page.to_f).ceil : 1 end # returns the previous page number or 1 # WillPaginate hook def previous_page @previous_page ||= (current_page > 1) ? current_page - 1 : 1 end # returns the next page number or the last # WillPaginate hook def next_page @next_page ||= (current_page == total_pages) ? total_pages : current_page+1 end def has_next? current_page < total_pages end def has_previous? current_page > 1 end end end