Skip to content

Commit

Permalink
Merge pull request #1226 from rwd/abstract_facet_paginator
Browse files Browse the repository at this point in the history
Create a generic FacetPaginator class
  • Loading branch information
cbeer committed Jul 2, 2015
2 parents 52cb15e + 40ae458 commit dd3dff9
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 113 deletions.
1 change: 1 addition & 0 deletions lib/blacklight.rb
Expand Up @@ -34,6 +34,7 @@ module Blacklight

autoload :SolrResponse, 'blacklight/solr_response'
autoload :Facet, 'blacklight/facet'
autoload :FacetPaginator, 'blacklight/facet_paginator'

extend SearchFields
extend Deprecation
Expand Down
121 changes: 121 additions & 0 deletions lib/blacklight/facet_paginator.rb
@@ -0,0 +1,121 @@
module Blacklight
# Pagination for facet values -- works by setting the limit to max
# displayable. You have to ask Solr for limit+1, to get enough
# results to see if 'more' are available'. That is, the all_facet_values
# arg in constructor should be the result of asking solr for limit+1
# values.
# This is a workaround for the fact that Solr itself can't compute
# the total values for a given facet field,
# so we cannot know how many "pages" there are.
#
class FacetPaginator
extend Deprecation

self.deprecation_horizon = 'blacklight version 6.0.0'

# What request keys will we use for the parameters need. Need to
# make sure they do NOT conflict with catalog/index request params,
# and need to make them accessible in a list so we can easily
# strip em out before redirecting to catalog/index.
mattr_accessor :request_keys do
{ sort: :'facet.sort', page: :'facet.page' }
end

if Rails.version < "4.1"
self.request_keys = { sort: :'facet.sort', page: :'facet.page' }
end

attr_reader :offset, :limit, :sort

# all_facet_values is a list of facet value objects returned by solr,
# asking solr for n+1 facet values.
# options:
# :limit => number to display per page, or (default) nil. Nil means
# display all with no previous or next.
# :offset => current item offset, default 0
# :sort => 'count' or 'index', solr tokens for facet value sorting, default 'count'.
def initialize(all_facet_values, arguments)
# to_s.to_i will conveniently default to 0 if nil
@offset = arguments[:offset].to_s.to_i
@limit = arguments[:limit]
@sort = arguments[:sort]

@all = all_facet_values
end

# The number of records solr gave us when we asked for limit + 1 records at the current offset
def total_count
@all.size
end

def items
items_for_limit(@all)
end

def prev_page
current_page - 1 unless first_page?
end

def current_page
if limit == 0 #check for divide by zero
1
else
@offset / limit + 1
end
end

def next_page
current_page + 1 unless last_page?
end

#@deprecated
def has_next?
!last_page?
end
deprecation_deprecate :has_next? => "use #!last_page?"

#@deprecated
def has_previous?
!first_page?
end
deprecation_deprecate :has_next? => "use #!first_page?"

def last_page?
limit.nil? || total_count <= limit
end

def first_page?
current_page == 1
end

# We're implementing total_pages so that this matches the API from kaminari, even though we can't
# know the total number of pages.
# https://github.com/amatsuda/kaminari/blob/v0.16.1/lib/kaminari/models/page_scope_methods.rb#L21
def total_pages
-1
end

# Pass in a desired solr facet solr key ('count' or 'index', see
# http://wiki.apache.org/solr/SimpleFacetParameters#facet.limit
# under facet.sort ), and your current request params.
# Get back params suitable to passing to an ActionHelper method for
# creating a url, to resort by that method.
def params_for_resort_url(sort_method, params)
# When resorting, we've got to reset the offset to start at beginning,
# no way to make it make sense otherwise.
params.merge(request_keys[:sort] => sort_method, request_keys[:page] => nil)
end

def as_json(_ = nil)
{ 'items' => items.as_json, 'limit' => limit, 'offset' => offset, 'sort' => sort }
end

private

# setting limit to nil implies no limit
# @return an array of facets on the page
def items_for_limit(values)
limit.nil? ? values : values.take(limit)
end
end
end
105 changes: 4 additions & 101 deletions lib/blacklight/solr/facet_paginator.rb
@@ -1,7 +1,5 @@
# -*- encoding : utf-8 -*-
module Blacklight::Solr


# Pagination for facet values -- works by setting the limit to max
# displayable. You have to ask Solr for limit+1, to get enough
# results to see if 'more' are available'. That is, the all_facet_values
Expand All @@ -11,25 +9,7 @@ module Blacklight::Solr
# the total values for a given facet field,
# so we cannot know how many "pages" there are.
#
class FacetPaginator
extend Deprecation

self.deprecation_horizon = 'blacklight version 6.0.0'

# What request keys will we use for the parameters need. Need to
# make sure they do NOT conflict with catalog/index request params,
# and need to make them accessible in a list so we can easily
# strip em out before redirecting to catalog/index.
mattr_accessor :request_keys do
{ sort: :'facet.sort', page: :'facet.page' }
end

if Rails.version < "4.1"
self.request_keys = { sort: :'facet.sort', page: :'facet.page' }
end

attr_reader :offset, :limit, :sort

class FacetPaginator < Blacklight::FacetPaginator
# all_facet_values is a list of facet value objects returned by solr,
# asking solr for n+1 facet values.
# options:
Expand All @@ -38,87 +18,10 @@ class FacetPaginator
# :offset => current item offset, default 0
# :sort => 'count' or 'index', solr tokens for facet value sorting, default 'count'.
def initialize(all_facet_values, arguments)
# to_s.to_i will conveniently default to 0 if nil
@offset = arguments[:offset].to_s.to_i
@limit = arguments[:limit]
# count is solr's default
@sort = arguments[:sort] || "count"

@all = all_facet_values
end

# The number of records solr gave us when we asked for limit + 1 records at the current offset
def total_count
@all.size
end

def items
items_for_limit(@all)
end

def prev_page
current_page - 1 unless first_page?
end

def current_page
if limit == 0 #check for divide by zero
1
else
@offset / limit + 1
end
end

def next_page
current_page + 1 unless last_page?
end

#@deprecated
def has_next?
!last_page?
end
deprecation_deprecate :has_next? => "use #!last_page?"

#@deprecated
def has_previous?
!first_page?
end
deprecation_deprecate :has_next? => "use #!first_page?"

def last_page?
limit.nil? || total_count <= limit
end
super

def first_page?
current_page == 1
end

# We're implementing total_pages so that this matches the API from kaminari, even though we can't
# know the total number of pages.
# https://github.com/amatsuda/kaminari/blob/v0.16.1/lib/kaminari/models/page_scope_methods.rb#L21
def total_pages
-1
end

# Pass in a desired solr facet solr key ('count' or 'index', see
# http://wiki.apache.org/solr/SimpleFacetParameters#facet.limit
# under facet.sort ), and your current request params.
# Get back params suitable to passing to an ActionHelper method for
# creating a url, to resort by that method.
def params_for_resort_url(sort_method, params)
# When resorting, we've got to reset the offset to start at beginning,
# no way to make it make sense otherwise.
params.merge(request_keys[:sort] => sort_method, request_keys[:page] => nil)
end

def as_json(_ = nil)
{ 'items' => items.as_json, 'limit' => limit, 'offset' => offset, 'sort' => sort }
# count is solr's default
@sort ||= 'count'
end

private
# setting limit to nil implies no limit
# @return an array of facets on the page
def items_for_limit(values)
limit.nil? ? values : values.take(limit)
end
end
end
24 changes: 12 additions & 12 deletions spec/lib/blacklight/facet_paginator_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'

describe 'Blacklight::Solr::FacetPaginator' do
describe Blacklight::FacetPaginator do

let(:f1) { Blacklight::SolrResponse::Facets::FacetItem.new(hits: '792', value: 'Book') }
let(:f2) { Blacklight::SolrResponse::Facets::FacetItem.new(hits: '65', value: 'Musical Score') }
Expand All @@ -14,7 +14,7 @@
let(:limit) { 6 }

context 'on the first page of two pages' do
subject { Blacklight::Solr::FacetPaginator.new(seven_facet_values, limit: limit) }
subject { described_class.new(seven_facet_values, limit: limit) }
it { should be_first_page }
it { should_not be_last_page }
its(:current_page) { should eq 1 }
Expand All @@ -26,7 +26,7 @@
end

context 'on the last page of two pages' do
subject { Blacklight::Solr::FacetPaginator.new([f7], offset: 6, limit: limit) }
subject { described_class.new([f7], offset: 6, limit: limit) }
it { should_not be_first_page }
it { should be_last_page }
its(:current_page) { should eq 2 }
Expand All @@ -38,7 +38,7 @@
end

context 'on the second page of three pages' do
subject { Blacklight::Solr::FacetPaginator.new(seven_facet_values, offset: 6, limit: limit) }
subject { described_class.new(seven_facet_values, offset: 6, limit: limit) }
it { should_not be_first_page }
it { should_not be_last_page }
its(:current_page) { should eq 2 }
Expand All @@ -50,15 +50,15 @@
end

context 'on the first page of one page' do
subject { Blacklight::Solr::FacetPaginator.new(six_facet_values, offset: 0, limit: limit) }
subject { described_class.new(six_facet_values, offset: 0, limit: limit) }
it { should be_first_page }
it { should be_last_page }
end

describe "params_for_resort_url" do
let(:sort_key) { Blacklight::Solr::FacetPaginator.request_keys[:sort] }
let(:page_key) { Blacklight::Solr::FacetPaginator.request_keys[:page] }
subject { Blacklight::Solr::FacetPaginator.new([], offset: 100, limit: limit, sort: 'index') }
let(:sort_key) { described_class.request_keys[:sort] }
let(:page_key) { described_class.request_keys[:page] }
subject { described_class.new([], offset: 100, limit: limit, sort: 'index') }

it 'should know a manually set sort, and produce proper sort url' do
expect(subject.sort).to eq 'index'
Expand All @@ -71,24 +71,24 @@
end

context "for a nil :limit" do
subject { Blacklight::Solr::FacetPaginator.new(seven_facet_values, offset: 0, limit: nil) }
subject { described_class.new(seven_facet_values, offset: 0, limit: nil) }
it "should return all the items" do
expect(subject.items).to eq seven_facet_values
end
it { should be_last_page }
end

describe "#as_json" do
subject { Blacklight::Solr::FacetPaginator.new([f1], offset: 0, limit: nil).as_json }
subject { described_class.new([f1], offset: 0, limit: nil).as_json }
it "should be well structured" do
expect(subject).to eq("items" => [{"hits"=>"792", "value"=>"Book"}], "limit" => nil,
"offset" => 0, "sort" => "count")
"offset" => 0, "sort" => nil)
end
end

describe "#total_pages" do
# this method is just for API compatability with kaminari 0.16.1
subject { Blacklight::Solr::FacetPaginator.new([f1], offset: 0, limit: nil).total_pages }
subject { described_class.new([f1], offset: 0, limit: nil).total_pages }
it { should eq -1 }
end
end
12 changes: 12 additions & 0 deletions spec/lib/blacklight/solr/facet_paginator_spec.rb
@@ -0,0 +1,12 @@
require 'spec_helper'

describe Blacklight::Solr::FacetPaginator do
let(:f1) { Blacklight::SolrResponse::Facets::FacetItem.new(hits: '792', value: 'Book') }
describe "#as_json" do
subject { described_class.new([f1], offset: 0, limit: nil).as_json }
it "should be well structured" do
expect(subject).to eq("items" => [{"hits"=>"792", "value"=>"Book"}], "limit" => nil,
"offset" => 0, "sort" => "count")
end
end
end

0 comments on commit dd3dff9

Please sign in to comment.