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

Try harder to map response parameters provided using the JSON Request API #2435

Merged
merged 3 commits into from
Apr 1, 2021
Merged
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
18 changes: 2 additions & 16 deletions lib/blacklight/solr/response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ class Blacklight::Solr::Response < ActiveSupport::HashWithIndifferentAccess
autoload :MoreLikeThis
autoload :GroupResponse
autoload :Group
autoload :Params
end

include PaginationMethods
include Spelling
include Facets
include Response
include MoreLikeThis
include Params

attr_reader :request_params
attr_accessor :blacklight_config, :options
Expand All @@ -33,22 +35,6 @@ def header
self['responseHeader'] || {}
end

def params
header['params'] || request_params
end

def start
params[:start].to_i
end

def rows
params[:rows].to_i
end

def sort
params[:sort]
end

def documents
@documents ||= (response['docs'] || []).collect { |doc| document_factory.build(doc, self, options) }
end
Expand Down
17 changes: 0 additions & 17 deletions lib/blacklight/solr/response/facets.rb
Original file line number Diff line number Diff line change
Expand Up @@ -180,23 +180,6 @@ def facet_field_aggregations
end
end

def facet_field_aggregation_options(facet_field_name)
options = {}
options[:sort] = (params[:"f.#{facet_field_name}.facet.sort"] || params[:'facet.sort'])
if params[:"f.#{facet_field_name}.facet.limit"] || params[:"facet.limit"]
options[:limit] = (params[:"f.#{facet_field_name}.facet.limit"] || params[:"facet.limit"]).to_i
end

if params[:"f.#{facet_field_name}.facet.offset"] || params[:'facet.offset']
options[:offset] = (params[:"f.#{facet_field_name}.facet.offset"] || params[:'facet.offset']).to_i
end

if params[:"f.#{facet_field_name}.facet.prefix"] || params[:'facet.prefix']
options[:prefix] = (params[:"f.#{facet_field_name}.facet.prefix"] || params[:'facet.prefix'])
end
options
end

##
# Aggregate Solr's facet_query response into the virtual facet fields defined
# in the blacklight configuration
Expand Down
96 changes: 96 additions & 0 deletions lib/blacklight/solr/response/params.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# frozen_string_literal: true
module Blacklight::Solr::Response::Params
# From https://solr.apache.org/guide/8_8/json-request-api.html#supported-properties-and-syntax
QUERY_PARAMETER_TO_JSON_PARAMETER_MAPPING = {
q: :query,
fq: :filter,
start: :offset,
rows: :limit,
fl: :fields,
sort: :sort
}.freeze

def params
header['params'] || request_params
end

def start
search_builder&.start || single_valued_param(:start).to_i
end

def rows
search_builder&.rows || single_valued_param(:rows).to_i
end

def sort
search_builder&.sort || single_valued_param(:sort)
end

def facet_field_aggregation_options(facet_field_name)
sort = single_valued_param(:"f.#{facet_field_name}.facet.sort") || single_valued_param(:'facet.sort')
limit_param = single_valued_param(:"f.#{facet_field_name}.facet.limit") || single_valued_param(:"facet.limit")
limit = (limit_param.to_i if limit_param.present?) || 100
offset = single_valued_param(:"f.#{facet_field_name}.facet.offset") || single_valued_param(:"facet.offset")
prefix = single_valued_param(:"f.#{facet_field_name}.facet.prefix") || single_valued_param(:"facet.prefix")

{
sort: sort || (limit.positive? ? 'count' : 'index'),
limit: limit,
offset: (offset.to_i if offset.present?) || 0,
prefix: prefix
}
end

private

def search_builder
request_params if request_params.is_a?(Blacklight::SearchBuilder)
end

# Extract JSON Request API parameters from the response header or the request itself
def json_params
encoded_json_params = header&.dig('params', 'json')

return request_params['json'] || {} if encoded_json_params.blank?

@json_params ||= JSON.parse(encoded_json_params).with_indifferent_access
end

# Handle merging solr parameters from the myriad of ways they may be expressed by applying the single-value
# precedence logic:
#
# From https://solr.apache.org/guide/8_8/json-request-api.html#json-parameter-merging :
# When multiple parameter values conflict with one another a single value is chosen based on the following precedence rules:
# - Traditional query parameters (q, rows, etc.) take first precedence and are used over any other specified values.
# - json-prefixed query parameters are considered next.
# - Values specified in the JSON request body have the lowest precedence and are only used if specified nowhere else.
#
# @param [String] key the solr parameter to use
def single_valued_param(key)
json_key = QUERY_PARAMETER_TO_JSON_PARAMETER_MAPPING[key]

params[key] ||
params["json.#{key}"] ||
json_params[json_key || key] ||
json_params.dig(:params, key) ||
json_params.dig(:params, "json.#{key}")
end

# Merge together multi-valued solr parameters from the myriad of ways they may be expressed.
# Unlike single-valued parameters, this merges all the values across the params.
#
# @param [String] key the solr parameter to use
def multivalued_param(key)
json_key = QUERY_PARAMETER_TO_JSON_PARAMETER_MAPPING[key]

[
params[key],
params["json.#{key}"],
json_params[json_key || key],
json_params.dig(:params, key),
json_params.dig(:params, "json.#{key}")
].select(&:present?).inject([]) do |memo, arr|
memo.concat(Array.wrap(arr))
end
end
end
10 changes: 10 additions & 0 deletions spec/models/blacklight/solr/response_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,16 @@
expect(r.params['test']).to eq :test
end

it 'extracts json params' do
raw_response = eval(mock_query_response)
raw_response['responseHeader']['params']['test'] = 'from query'
raw_response['responseHeader']['params'].delete('rows')
raw_response['responseHeader']['params']['json'] = { limit: 5, params: { test: 'from json params' } }.to_json
r = described_class.new(raw_response, raw_response['params'])
expect(r.params['test']).to eq 'from query'
expect(r.rows).to eq 5
end

it 'provides the solr-returned params and "rows" should be 11' do
raw_response = eval(mock_query_response)
r = described_class.new(raw_response, {})
Expand Down