Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closer integration of json facets #108

Draft
wants to merge 13 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 92 additions & 20 deletions app/controllers/catalog_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,7 @@ class CatalogController < ApplicationController
:format_f => ['Database & Article Index']
}.freeze

actionable_filters = lambda { |a, b, c|
params = a.params
params_actionable_filters = lambda { |params|
return true if params[:q].present?
f = params[:f]
return false if f.nil?
Expand All @@ -194,6 +193,21 @@ class CatalogController < ApplicationController
return false
}

actionable_filters = lambda { |a, b, c|
params_actionable_filters.call(a.params)
}

conditional_sort_options = lambda { |params|
return nil unless ENABLE_SUBJECT_CORRELATION # fallback to legacy/default behavior
if params_actionable_filters.call(params)
['index', 'count', 'r1 desc']
else
['index', 'count']
end
}

relatedness_sort_applied = lambda { |display_facet| display_facet.sort == 'r1 desc' }

search_field_accept = lambda { |field_names, other_conditions = []|
return lambda { |a, b, c|
return false unless field_names.include?(a.params[:search_field])
Expand Down Expand Up @@ -281,23 +295,53 @@ class CatalogController < ApplicationController

MINCOUNT = { 'facet.mincount' => 1 }.freeze

SUBJECT_CORRELATION = {
subject_correlation: {
CORRELATION_JSON_FACET = lambda { |field, limit, offset, sort, prefix|
return nil unless ENABLE_SUBJECT_CORRELATION && sort == 'r1 desc'
if field == 'subject_f'
overRequestRatio = 0.7 # default is 0.1
overRefineRatio = 6 # often based on number of shards
overrequest = (limit * overRequestRatio).to_i + 4 # based on the default forumula
## overrefine = ((offset + limit) * 0.1).to_i + 4 # the default
# essentially, we want to refine *everything* that comes back from the
# initial requests, because we're dealing with high-cardinality and
# correspondingly low-frequency/unevently distributed fields/values.
overrefine = ((offset + limit + overrequest) * overRefineRatio).to_i + 4
else
overrequest = -1
overrefine = -1
end
{
type: 'query',
domain: {
query: '{!query v=$cluster}'
},
q: '{!query tag=REFINE v=$correlation_domain}',
blacklight_options: {
parse: {
delegate: lambda { |subs| subs[:correlation] }
}
},
facet: {
correlation:{
type: 'terms',
field: 'subject_f',
field: field,
# NOTE: mincount pre-filters vals that could not possibly match min_pop
mincount: 3, # guarantee fgSize >= 3
limit: 25,
refine: true,
sort: {r1: 'desc'},
limit: limit,
offset: offset,
overrequest: overrequest,
overrefine: overrefine,
refine: 'simple',
cacheDf: -1, # disable caching; only affects refinement. esp. important b/c of extent of overrefining
sort: sort,
blacklight_options: {
parse: {
filter: lambda { |bucket| bucket[:r1][:relatedness] > 0 },
get_hits: lambda { |bucket| bucket[:fg_filtered_count][:count] }
}
},
facet: {
processEmpty: true,
r1: {
type: 'func',
min_popularity: 0.0000002, # lower bound n/bgSize to guarantee fgCount>=n (here, n==2)
Expand Down Expand Up @@ -336,17 +380,49 @@ class CatalogController < ApplicationController
:facet_type => :database, solr_params: MINCOUNT
# NOTE: set facet_type=nil below, to bypass normal facet display
config.add_facet_field 'subject_specialists', label: 'Subject Area Correlation', collapse: true, :facet_type => nil,
:json_facet => PennLib::SubjectSpecialists::QUERIES, :if => actionable_filters
:json_facet => PennLib::JsonFacet::Config.new(PennLib::SubjectSpecialists::QUERIES), :if => actionable_filters

config.add_facet_field 'subject_correlation',
label: 'Subject Correlation',
config.add_facet_field 'subject_f',
label: 'Subject',
collapse: false,
partial: 'blacklight/hierarchy/facet_relatedness',
json_facet: SUBJECT_CORRELATION,
:facet_type => lambda { |params|
limit: 5,
index_range: 'A'..'Z',
solr_params: MINCOUNT,
sort_options: conditional_sort_options,
render_empty?: relatedness_sort_applied,
always_show_more_link?: relatedness_sort_applied,
sort: 'r1 desc', # 'r1 desc', 'count', or 'index'
#partial: 'catalog/json_facet_limit',
json_facet: PennLib::JsonFacet::Config.new(CORRELATION_JSON_FACET, {
if: actionable_filters,
fallback: {
map_sort: lambda { |sort, blacklight_params| sort == 'r1 desc' ? 'count' : sort }
}
}),
facet_type: lambda { |params|
params[:search_field] == 'subject_correlation' ? :first_class : :default
},
:if => actionable_filters if ENABLE_SUBJECT_CORRELATION
}

config.add_facet_field 'author_creator_f',
label: 'Author/Creator',
collapse: false,
limit: 5,
index_range: 'A'..'Z',
solr_params: MINCOUNT,
sort_options: conditional_sort_options,
render_empty?: relatedness_sort_applied,
always_show_more_link?: relatedness_sort_applied,
sort: 'r1 desc', # 'r1 desc', 'count', or 'index'
#partial: 'catalog/json_facet_limit',
json_facet: PennLib::JsonFacet::Config.new(CORRELATION_JSON_FACET, {
if: actionable_filters,
fallback: {
map_sort: lambda { |sort, blacklight_params| sort == 'r1 desc' ? 'count' : sort }
}
}),
facet_type: lambda { |params|
params[:search_field] == 'author_creator_correlation' ? :first_class : :default
}

config.add_facet_field 'azlist', label: 'A-Z List', collapse: false, single: :manual, :facet_type => :header,
options: {:layout => 'horizontal_facet_list'}, solr_params: { 'facet.mincount' => 0 },
Expand Down Expand Up @@ -388,11 +464,7 @@ class CatalogController < ApplicationController
'Penn' => { :label => 'Penn', :fq => "{!join ex=orig_q from=cluster_id to=cluster_id v='{!term f=record_source_f v=\\'Penn\\'}'}"},
}
config.add_facet_field 'format_f', label: 'Format', limit: 5, collapse: false, :ex => 'orig_q', solr_params: MINCOUNT
config.add_facet_field 'author_creator_f', label: 'Author/Creator', limit: 5, index_range: 'A'..'Z', collapse: false,
:ex => 'orig_q', solr_params: MINCOUNT
#config.add_facet_field 'subject_taxonomy', label: 'Subject Taxonomy', collapse: false, :partial => 'blacklight/hierarchy/facet_hierarchy', :json_facet => SUBJECT_TAXONOM, :helper_method => :render_subcategories
config.add_facet_field 'subject_f', label: 'Subject', limit: 5, index_range: 'A'..'Z', collapse: false,
:ex => 'orig_q', solr_params: MINCOUNT
config.add_facet_field 'language_f', label: 'Language', limit: 5, collapse: false, :ex => 'orig_q', solr_params: MINCOUNT
config.add_facet_field 'library_f', label: 'Library', limit: 5, collapse: false, :ex => 'orig_q', solr_params: MINCOUNT
config.add_facet_field 'specific_location_f', label: 'Specific location', limit: 5, :ex => 'orig_q', solr_params: MINCOUNT
Expand Down
8 changes: 4 additions & 4 deletions app/helpers/blacklight_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ def render_search_bar
def render_expert_help(specialists_facet_field)
return render partial: 'catalog/ask' unless specialists_facet_field
specialists = extract_and_sort_by_relatedness(specialists_facet_field)
if specialists.blank? || specialists.first.subs[:r1][:relatedness] < EXPERT_HELP_CORRELATION_THRESHOLD
if specialists.blank? || specialists.first.items[0].subs[:r1][:relatedness] < EXPERT_HELP_CORRELATION_THRESHOLD
return render partial: 'catalog/ask'
end
subject = specialists.first.value
subject = specialists.first.items[0].value
specialist_data = PennLib::SubjectSpecialists.data
relevant_specialists = specialist_data[subject]
if relevant_specialists.present?
Expand All @@ -36,10 +36,10 @@ def render_expert_help(specialists_facet_field)
# items, sorted by relatedness
def extract_and_sort_by_relatedness(specialists_facet_field)
specialists_facet_field.items[0].subs.each_with_object([]) do |(k, v), arr|
if k != :count && v.subs[:r1]
if k != :count && v.items[0].subs[:r1]
arr << v
end
end.sort! { |a,b| b.subs[:r1][:relatedness] <=> a.subs[:r1][:relatedness] }
end.sort! { |a,b| b.items[0].subs[:r1][:relatedness] <=> a.items[0].subs[:r1][:relatedness] }
end

# override so that we can insert separators
Expand Down
48 changes: 45 additions & 3 deletions app/helpers/facets_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ def get_display_facet_types
hash = {}
blacklight_config.facet_fields.values.each_with_object(hash) do |facet_config, hash|
display_facet = agg[facet_config.field]
next if display_facet.nil? || display_facet.items.empty? || !should_render_field?(facet_config, display_facet)
next if display_facet.nil? || !should_render_facet?(display_facet)
facet_type = facet_config[:facet_type] || :default
facet_type = facet_type.call(params) if facet_type.respond_to?(:lambda?)
if fields_for_facet_type = hash[facet_type]
Expand All @@ -33,7 +33,7 @@ def get_display_facet_types
# @param [Hash] options
# @return [Boolean]
def has_facet_values? fields = facet_field_names, options = {}
facets_from_request(fields).any? { |display_facet| !display_facet.items.empty? && should_render_facet?(display_facet) }
facets_from_request(fields).any? { |display_facet| should_render_facet?(display_facet) }
end

##
Expand Down Expand Up @@ -79,6 +79,7 @@ def render_facet_item(facet_field, item, options={})
render_facet_value(facet_field, item, options)
end
end

##
# Standard display of a facet value in a list. Used in both _facets sidebar
# partial and catalog/facet expanded list. Will output facet value name as
Expand Down Expand Up @@ -125,11 +126,52 @@ def should_render_facet? display_facet
# display when show is nil or true
facet_config = facet_configuration_for_field(display_facet.name)
display = should_render_field?(facet_config, display_facet)
display && display_facet.items.present?
display && (facet_config.render_empty?&.call(display_facet) || display_facet.items.present?)
end

##
# Determine whether a facet should be rendered as collapsed or not.
# - if the facet is 'active', don't collapse
# - if the facet is configured to collapse (the default), collapse
# - if the facet is configured not to collapse, don't collapse
#
# @param [Blacklight::Configuration::FacetField] facet_field
# @param [Blacklight::Solr::Response::Facets::FacetField] display_facet
# @return [Boolean]
def should_collapse_facet? facet_field, display_facet
!facet_field_in_params?(facet_field.key) && (facet_field.collapse || display_facet.items.empty?)
end

def render_subcategories(v)
idx = v.index('--')
idx.nil? ? v : v.slice((idx + 2)..-1)
end

# Display facet sort options for modal with active sort selected
# @param [Blacklight::Solr::FacetPaginator] pagination
# @return [ActiveSupport::SafeBuffer] links html
def modal_sort_options(pagination, facet_config)
sort_options = facet_config.sort_options&.call(params) || ['index', 'count']
links = sort_options.map do |possible_sort|
active = pagination.sort == possible_sort
modal_sort_link possible_sort, active
end
links.join.html_safe
end

# Generate a link for a facet sort option
# @param [String] type
# @param [TrueClass, FalseClass] active
# @return [ActiveSupport::SafeBuffer] link html
def modal_sort_link(type, active)
label = t "blacklight.search.facets.sort.#{type}"
if active
content_tag :span, label, class: 'active numeric btn btn-default'
else
link_to label,
@pagination.params_for_resort_url(type, search_state.to_h),
class: 'sort_change numeric btn btn-default',
data: { ajax_modal: 'preserve' }
end
end
end
Loading