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

Pod local relatedness facets #109

Open
wants to merge 25 commits into
base: pod-local
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
aec4201
add stock add_facet_paging_to_solr, pre-override
magibney Oct 16, 2020
37cda82
integrate json facets more closely with add_facet_field, legacy facets
magibney Oct 20, 2020
b5904fb
move some config inline in request object; delegate (to support use o…
magibney Oct 21, 2020
baf427a
pay attention to limit and offset
magibney Oct 21, 2020
c126b28
delegation allows us to use standard partials now
magibney Oct 21, 2020
461214b
illustrate straightforward reuse of this component (for authors, in t…
magibney Oct 21, 2020
5c9e482
Merge branch 'closer_integration_of_json_facets' into pod-local
magibney Oct 22, 2020
cf83eba
presentation_domain should be same as combo, but should include clust…
magibney Oct 22, 2020
a87b0c9
for pod-local/test, temporarily hardcode ENABLE_SUBJECT_CORRELATION=true
magibney Oct 23, 2020
71c2dd3
processEmpty, and some overreq/refine adjustments
magibney Oct 24, 2020
a7e70ab
disable refinement caching; fine-tune overrequest/overrefine
magibney Oct 27, 2020
863b597
show facet sort options including correlation in facet modal
mksndz Oct 27, 2020
4002bd8
overrequest, overrefine, processEmpty
magibney Oct 28, 2020
40eab63
Merge branch 'closer_integration_of_json_facets' into pod-local-relat…
magibney Oct 28, 2020
fce13a4
cleanup from 37cda821; solr_params were getting dropped in facet pagi…
magibney Oct 28, 2020
2533498
Merge branch 'closer_integration_of_json_facets' into pod-local-relat…
magibney Oct 28, 2020
88b4e74
add cluster to correlation ignorelist
magibney Oct 28, 2020
340739e
move sort options to config; make conditional
magibney Oct 28, 2020
e236cbc
Merge branch 'closer_integration_of_json_facets' into pod-local-relat…
magibney Oct 28, 2020
5165c52
add stock `should_collapse_facet?` and _facet_limit.html.erb, pre-ove…
magibney Oct 28, 2020
bc42b13
ensure accessibility of count/index sort when no relatedness results
magibney Oct 28, 2020
8f4e6e6
Merge branch 'closer_integration_of_json_facets' into pod-local-relat…
magibney Oct 28, 2020
da09a0d
temporarily exclude Stanford from correlation domain
magibney Oct 28, 2020
781c2ba
properly respect ENABLE_SUBJECT_CORRELATION everywhere
magibney Oct 29, 2020
6293f09
Merge branch 'closer_integration_of_json_facets' into pod-local-relat…
magibney Oct 29, 2020
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
121 changes: 97 additions & 24 deletions app/controllers/catalog_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class CatalogController < ApplicationController
include AssociateExpandedDocs
include HandleEmptyEmail

ENABLE_SUBJECT_CORRELATION = ENV['ENABLE_SUBJECT_CORRELATION']&.downcase == 'true'
ENABLE_SUBJECT_CORRELATION = true #ENV['ENABLE_SUBJECT_CORRELATION']&.downcase == 'true'

# expire session if needed
before_action :expire_session
Expand Down Expand Up @@ -55,7 +55,7 @@ class CatalogController < ApplicationController
#cache: 'false',
defType: 'perEndPosition_dense_shingle_graphSpans',
combo: '{!filters param=$q param=$fq excludeTags=cluster,no_correlation}', # NOTE: $correlation_domain is applied within facets
presentation_domain: '{!query v=$combo}', # default, overridden for first-class subject_correlation search_field
presentation_domain: '{!filters param=$q param=$fq}', # default, overridden for first-class subject_correlation search_field
post_1928: 'content_max_dtsort:[1929-01-01T00:00:00Z TO *]',
culture_filter: "{!bool should='{!terms f=subject_search v=literature,customs,religion,ethics,society,social,culture,cultural}' should='{!prefix f=subject_search v=art}'}",
#combo: '{!bool must=$q filter=\'{!filters param=$fq v=*:*}\'}',
Expand Down Expand Up @@ -99,7 +99,7 @@ class CatalogController < ApplicationController
# relatedness over a subset of that domain. This is not always necessary -- e.g., in the case where counts are not
# needed, such as for "expert help", which can use correlation_domain directly
correlation_domain: '{!bool filter=$cluster filter=$correlation_domain_refine}',
correlation_domain_refine: '{!bool filter=elvl_rank_isort:0}',
correlation_domain_refine: '{!bool filter=elvl_rank_isort:0 must_not=\'{!term f=record_source_f v=Stanford}\'}',
expand: 'true',
'expand.field': 'cluster_id',
'expand.q': '*:*',
Expand Down Expand Up @@ -176,11 +176,11 @@ class CatalogController < ApplicationController
CORRELATION_IGNORELIST = {
:access_f => nil,
:record_source_f => nil,
:cluster => nil,
: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 @@ -195,6 +195,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 @@ -282,23 +297,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 = 24 # 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 ex=cluster 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'},
mincount: 2, # guarantee fgSize >= 2
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 @@ -341,17 +386,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 @@ -410,11 +487,7 @@ class CatalogController < ApplicationController
'3D object' => { :label => '3D object', :fq => "{!term f=format_f v='3D object'}"},
'Projected graphic' => { :label => 'Projected graphic', :fq => "{!term f=format_f v='Projected graphic'}"},
}
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', :if => local_only, solr_params: MINCOUNT
config.add_facet_field 'specific_location_f', label: 'Specific location', limit: 5, :ex => 'orig_q', :if => local_only, 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