diff --git a/lib/xapian_db/resultset.rb b/lib/xapian_db/resultset.rb index 9ba216a..e65ef49 100644 --- a/lib/xapian_db/resultset.rb +++ b/lib/xapian_db/resultset.rb @@ -45,7 +45,9 @@ class Resultset < Array # Pass nil to get an empty result set. # @param [Hash] options # @option options [Integer] :db_size The current size (nr of docs) of the database - # @option options [Integer] :limit The maximum number of documents to retrieve + # @option options [Integer] :limit The maximum number of documents to retrieve (in total, not per request) + # @option options [Integer] :offset The index of the first result to retrieve + # @option options [Integer] :count The maximum number of documents to retrieve per request (analogous to "limit" in SQL) # @option options [Integer] :page (1) The page number to retrieve # @option options [Integer] :per_page (10) How many docs per page? Ignored if a limit option is given # @option options [String] :spelling_suggestion (nil) The spelling corrected query (if a language is configured) @@ -56,25 +58,33 @@ def initialize(enquiry, options={}) db_size = params.delete :db_size @spelling_suggestion = params.delete :spelling_suggestion limit = params.delete :limit + offset = params.delete :offset + count = params.delete :count page = params.delete :page per_page = params.delete :per_page raise ArgumentError.new "unsupported options for resultset: #{params}" if params.size > 0 raise ArgumentError.new "db_size option is required" unless db_size + raise ArgumentError.new "impossible combination of parameters" unless (page.nil? && per_page.nil?) || (offset.nil? && count.nil?) + + calculated_page = offset.nil? || count.nil? ? nil : (offset.to_f / count.to_f) + 1 limit = limit.nil? ? db_size : limit.to_i - per_page = per_page.nil? ? limit : per_page.to_i - page = page.nil? ? 1 : page.to_i - offset = (page - 1) * per_page - count = offset + per_page < limit ? per_page : limit - offset + per_page = per_page.nil? ? (count.nil? ? limit.to_i : count.to_i) : per_page.to_i + page = page.nil? ? (calculated_page.nil? ? 1 : calculated_page) : page.to_i + offset = offset.nil? ? (page - 1) * per_page : offset.to_i + count = count.nil? ? (offset + per_page < limit ? per_page : limit - offset) : count.to_i + + raise ArgumentError.new "page #{page} does not exist" if (page - 1) * per_page > db_size result_window = enquiry.mset(offset, count) @hits = result_window.matches_estimated return build_empty_resultset if @hits == 0 - raise ArgumentError.new "page #{@page} does not exist" if @hits > 0 && offset >= limit + + raise ArgumentError.new "page #{page} does not exist within given limit" if @hits > 0 && offset >= limit self.replace result_window.matches.map{|match| decorate(match).document} @total_pages = (@hits / per_page.to_f).ceil - @current_page = page + @current_page = (page == page.to_i) ? page.to_i : page @limit_value = per_page end diff --git a/spec/xapian_db/resultset_spec.rb b/spec/xapian_db/resultset_spec.rb index cf3e5ec..b5ea1e6 100644 --- a/spec/xapian_db/resultset_spec.rb +++ b/spec/xapian_db/resultset_spec.rb @@ -75,6 +75,29 @@ expect(resultset.size).to eq(2) expect(resultset.current_page).to eq(1) expect(resultset.total_pages).to eq(2) + expect(resultset.limit_value).to eq(2) + expect(resultset.total_count).to eq(@matches.size) + expect(resultset.total_entries).to eq(@matches.size) + end + + it "accepts a count option (as a string or an integer)" do + allow(@mset).to receive(:matches).and_return(@matches[0..1]) + resultset = XapianDb::Resultset.new(@enquiry, :db_size => @matches.size, :count => "2") + expect(resultset.hits).to eq(3) + expect(resultset.size).to eq(2) + expect(resultset.current_page).to eq(1) + expect(resultset.total_pages).to eq(2) + expect(resultset.total_count).to eq(@matches.size) + expect(resultset.total_entries).to eq(@matches.size) + end + + it "accepts an offset option (as a string or an integer)" do + allow(@mset).to receive(:matches).and_return(@matches[1..2]) + resultset = XapianDb::Resultset.new(@enquiry, :db_size => @matches.size, :count => "2", offset: "1") + expect(resultset.hits).to eq(3) + expect(resultset.size).to eq(2) + expect(resultset.current_page).to eq(1.5) + expect(resultset.total_pages).to eq(2) expect(resultset.total_count).to eq(@matches.size) expect(resultset.total_entries).to eq(@matches.size) end @@ -110,7 +133,16 @@ end it "raises an exception if page is requested that does not exist" do - expect{XapianDb::Resultset.new(@enquiry, :db_size => @matches.size, :per_page => 2, :page => 3)}.to raise_error "page does not exist" + expect{XapianDb::Resultset.new(@enquiry, :db_size => @matches.size, :per_page => 2, :page => 3)}.to raise_error ArgumentError, "page 3 does not exist" + end + + it "raises an exception if page is requested that does not exist within given limit" do + expect{XapianDb::Resultset.new(@enquiry, :db_size => @matches.size, :per_page => 2, :page => 2, limit: 2)}.to raise_error ArgumentError, "page 2 does not exist within given limit" + end + + it "raises an exception if elements of both page / per_page and offset / count are given" do + expect{XapianDb::Resultset.new(@enquiry, :db_size => @matches.size, :per_page => 2, :page => 2, offset: 1, count: 3)}.to raise_error ArgumentError, "impossible combination of parameters" + expect{XapianDb::Resultset.new(@enquiry, :db_size => @matches.size, :per_page => 2, offset: 1)}.to raise_error ArgumentError, "impossible combination of parameters" end it "should populate itself with found xapian documents" do