|
| 1 | +# frozen_string_literal: true |
| 2 | + |
| 3 | +module Blacklight |
| 4 | + ## |
| 5 | + # Blacklight's SearchBuilder converts blacklight request parameters into |
| 6 | + # query parameters appropriate for search index. It does so by evaluating a |
| 7 | + # chain of processing methods to populate a result hash (see {#to_hash}). |
| 8 | + class AbstractSearchBuilder |
| 9 | + class_attribute :default_processor_chain |
| 10 | + self.default_processor_chain = [] |
| 11 | + |
| 12 | + attr_reader :processor_chain, :search_state, :blacklight_params |
| 13 | + |
| 14 | + # @overload initialize(scope) |
| 15 | + # @param [Object] scope scope the scope where the filter methods reside in. |
| 16 | + # @overload initialize(processor_chain, scope) |
| 17 | + # @param [List<Symbol>,TrueClass] processor_chain options a list of filter methods to run or true, to use the default methods |
| 18 | + # @param [Object] scope the scope where the filter methods reside in. |
| 19 | + def initialize(*options) |
| 20 | + case options.size |
| 21 | + when 1 |
| 22 | + @processor_chain = default_processor_chain.dup |
| 23 | + @scope = options.first |
| 24 | + when 2 |
| 25 | + @processor_chain, @scope = options |
| 26 | + else |
| 27 | + raise ArgumentError, "wrong number of arguments. (#{options.size} for 1..2)" |
| 28 | + end |
| 29 | + |
| 30 | + @blacklight_params = {} |
| 31 | + search_state_class = @scope.try(:search_state_class) || Blacklight::SearchState |
| 32 | + @search_state = search_state_class.new(@blacklight_params, @scope&.blacklight_config, @scope) |
| 33 | + @additional_filters = {} |
| 34 | + @merged_params = {} |
| 35 | + @reverse_merged_params = {} |
| 36 | + end |
| 37 | + |
| 38 | + delegate :blacklight_config, to: :scope |
| 39 | + |
| 40 | + ## |
| 41 | + # Set the parameters to pass through the processor chain |
| 42 | + def with(blacklight_params_or_search_state = {}) |
| 43 | + params_will_change! |
| 44 | + @search_state = blacklight_params_or_search_state.is_a?(Blacklight::SearchState) ? blacklight_params_or_search_state : @search_state.reset(blacklight_params_or_search_state) |
| 45 | + @blacklight_params = @search_state.params.dup |
| 46 | + self |
| 47 | + end |
| 48 | + |
| 49 | + # sets the facet that this query pertains to, for the purpose of facet pagination |
| 50 | + def facet=(value) |
| 51 | + params_will_change! |
| 52 | + @facet = value |
| 53 | + end |
| 54 | + |
| 55 | + # @param [Object] value |
| 56 | + def facet(value = nil) |
| 57 | + if value |
| 58 | + self.facet = value |
| 59 | + return self |
| 60 | + end |
| 61 | + @facet |
| 62 | + end |
| 63 | + |
| 64 | + ## |
| 65 | + # Merge additional, repository-specific parameters |
| 66 | + def merge(extra_params, &) |
| 67 | + if extra_params |
| 68 | + params_will_change! |
| 69 | + @merged_params.merge!(extra_params.to_hash, &) |
| 70 | + end |
| 71 | + self |
| 72 | + end |
| 73 | + |
| 74 | + ## |
| 75 | + # "Reverse merge" additional, repository-specific parameters |
| 76 | + def reverse_merge(extra_params, &) |
| 77 | + if extra_params |
| 78 | + params_will_change! |
| 79 | + @reverse_merged_params.reverse_merge!(extra_params.to_hash, &) |
| 80 | + end |
| 81 | + self |
| 82 | + end |
| 83 | + |
| 84 | + delegate :[], :key?, to: :to_hash |
| 85 | + |
| 86 | + # a solr query method |
| 87 | + # @return [Blacklight::Solr::Response] the solr response object |
| 88 | + def to_hash |
| 89 | + return @params unless params_need_update? |
| 90 | + |
| 91 | + @params = processed_parameters |
| 92 | + .reverse_merge(@reverse_merged_params) |
| 93 | + .merge(@merged_params) |
| 94 | + .tap { clear_changes } |
| 95 | + end |
| 96 | + |
| 97 | + alias query to_hash |
| 98 | + alias to_h to_hash |
| 99 | + |
| 100 | + delegate :search_field, to: :search_state |
| 101 | + |
| 102 | + private |
| 103 | + |
| 104 | + attr_reader :scope |
| 105 | + |
| 106 | + def should_add_field_to_request? _field_name, field |
| 107 | + field.include_in_request || (field.include_in_request.nil? && blacklight_config.add_field_configuration_to_solr_request) |
| 108 | + end |
| 109 | + |
| 110 | + def request |
| 111 | + Blacklight::Solr::Request.new |
| 112 | + end |
| 113 | + |
| 114 | + # The CatalogController #index and #facet actions use this. |
| 115 | + # Solr parameters can come from a number of places. From lowest |
| 116 | + # precedence to highest: |
| 117 | + # 1. General defaults in blacklight config (are trumped by) |
| 118 | + # 2. defaults for the particular search field identified by params[:search_field] (are trumped by) |
| 119 | + # 3. certain parameters directly on input HTTP query params |
| 120 | + # * not just any parameter is grabbed willy nilly, only certain ones are allowed by HTTP input) |
| 121 | + # * for legacy reasons, qt in http query does not over-ride qt in search field definition default. |
| 122 | + # 4. extra parameters passed in as argument. |
| 123 | + # |
| 124 | + # spellcheck.q will be supplied with the [:q] value unless specifically |
| 125 | + # specified otherwise. |
| 126 | + # |
| 127 | + # Incoming parameter :f is mapped to :fq solr parameter. |
| 128 | + # |
| 129 | + # @return a params hash for searching solr. |
| 130 | + def processed_parameters |
| 131 | + request.tap do |request_parameters| |
| 132 | + processor_chain.each do |method_name| |
| 133 | + send(method_name, request_parameters) |
| 134 | + end |
| 135 | + end |
| 136 | + end |
| 137 | + |
| 138 | + def params_will_change! |
| 139 | + @dirty = true |
| 140 | + end |
| 141 | + |
| 142 | + def params_changed? |
| 143 | + !!@dirty |
| 144 | + end |
| 145 | + |
| 146 | + def clear_changes |
| 147 | + @dirty = false |
| 148 | + end |
| 149 | + |
| 150 | + def params_need_update? |
| 151 | + params_changed? || @params.nil? |
| 152 | + end |
| 153 | + end |
| 154 | +end |
0 commit comments