lib/blacklight/solr/search_builder_behavior.rb in blacklight-7.14.1 vs lib/blacklight/solr/search_builder_behavior.rb in blacklight-7.15.0

- old
+ new

@@ -6,11 +6,12 @@ included do self.default_processor_chain = [ :default_solr_parameters, :add_query_to_solr, :add_facet_fq_to_solr, :add_facetting_to_solr, :add_solr_fields_to_query, :add_paging_to_solr, :add_sorting_to_solr, :add_group_config_to_solr, - :add_facet_paging_to_solr + :add_facet_paging_to_solr, :add_adv_search_clauses, + :add_additional_filters ] end #### # Start with general defaults from BL config. Need to use custom @@ -59,16 +60,66 @@ if search_field&.query_builder.present? add_search_field_query_builder_params(solr_parameters) elsif search_field&.solr_local_parameters.present? add_search_field_with_local_parameters(solr_parameters) elsif search_state.query_param.is_a? Hash - add_multifield_search_query(solr_parameters) - elsif blacklight_params[:q] + add_additional_filters(solr_parameters, search_state.query_param) + elsif search_state.query_param solr_parameters[:q] = search_state.query_param end end + def add_additional_filters(solr_parameters, additional_filters = nil) + q = additional_filters || @additional_filters + + return if q.blank? + + solr_parameters[:q] = if q.values.any?(&:blank?) + # if any field parameters are empty, exclude _all_ results + "{!lucene}NOT *:*" + else + "{!lucene}" + q.map do |field, values| + "#{field}:(#{Array(values).map { |x| solr_param_quote(x) }.join(' OR ')})" + end.join(" AND ") + end + + solr_parameters[:defType] = 'lucene' + solr_parameters[:spellcheck] = 'false' + end + + # Transform "clause" parameters into the Solr JSON Query DSL + def add_adv_search_clauses(solr_parameters) + return if search_state.clause_params.blank? + + defaults = { must: [], must_not: [], should: [] } + bool_query = (solr_parameters.dig(:json, :query, :bool) || {}).reverse_merge(defaults) + + default_op = blacklight_params[:op]&.to_sym || :must + + search_state.clause_params.each_value do |clause| + op, query = adv_search_clause(clause, default_op) + bool_query[op] << query if defaults.key?(op) && query + end + + return if bool_query.values.all?(&:blank?) + + solr_parameters[:mm] = 1 if default_op == :should + solr_parameters[:json] ||= { query: { bool: {} } } + solr_parameters[:json][:query] ||= { bool: {} } + solr_parameters[:json][:query][:bool] = bool_query.reject { |_k, v| v.blank? } + end + + # @return [Array] the first element is the query operator and the second is the value to add + def adv_search_clause(clause, default_op) + op = clause[:op]&.to_sym || default_op + field = (blacklight_config.search_fields || {})[clause[:field]] if clause[:field] + + return unless field&.clause_params && clause[:query].present? + + [op, field.clause_params.transform_values { |v| v.merge(query: clause[:query]) }] + end + ## # Add any existing facet limits, stored in app-level HTTP query # as :f, to solr as appropriate :fq query. def add_facet_fq_to_solr(solr_parameters) # convert a String value into an Array @@ -82,12 +133,17 @@ solr_parameters.append_filter_query(filter_query) solr_parameters.merge!(subqueries) if subqueries else filter.values.reject(&:blank?).each do |value| - filter_query, subqueries = facet_value_to_fq_string(filter.config.key, value) - solr_parameters.append_filter_query(filter_query) + filter_query, subqueries = if value.is_a?(Array) + facet_inclusive_value_to_fq_string(filter.key, value.reject(&:blank?)) + else + facet_value_to_fq_string(filter.config.key, value) + end + + solr_parameters.append_filter_query filter_query solr_parameters.merge!(subqueries) if subqueries end end end end @@ -248,35 +304,58 @@ private ## # Convert a facet/value pair into a solr fq parameter - def facet_value_to_fq_string(facet_field, value) + def facet_value_to_fq_string(facet_field, value, use_local_params: true) facet_config = blacklight_config.facet_fields[facet_field] solr_field = facet_config.field if facet_config && !facet_config.query solr_field ||= facet_field local_params = [] - local_params << "tag=#{facet_config.tag}" if facet_config && facet_config.tag - prefix = "{!#{local_params.join(' ')}}" unless local_params.empty? + if use_local_params + local_params << "tag=#{facet_config.tag}" if facet_config && facet_config.tag + end if facet_config && facet_config.query if facet_config.query[value] facet_config.query[value][:fq] else # exclude all documents if the custom facet key specified was not found '-*:*' end elsif value.is_a?(Range) + prefix = "{!#{local_params.join(' ')}}" unless local_params.empty? "#{prefix}#{solr_field}:[#{value.first} TO #{value.last}]" else "{!term f=#{solr_field}#{(' ' + local_params.join(' ')) unless local_params.empty?}}#{convert_to_term_value(value)}" end end + def facet_inclusive_value_to_fq_string(facet_field, values) + return if values.blank? + + return facet_value_to_fq_string(facet_field, values.first) if values.length == 1 + + facet_config = blacklight_config.facet_fields[facet_field] + + local_params = [] + local_params << "tag=#{facet_config.tag}" if facet_config && facet_config.tag + + solr_filters = values.each_with_object({}).with_index do |(v, h), index| + h["f_inclusive.#{facet_field}.#{index}"] = facet_value_to_fq_string(facet_field, v, use_local_params: false) + end + + filter_query = solr_filters.keys.map do |k| + "{!query v=$#{k}}" + end.join(' OR ') + + ["{!lucene#{(' ' + local_params.join(' ')) unless local_params.empty?}}#{filter_query}", solr_filters] + end + def convert_to_term_value(value) if value.is_a?(DateTime) || value.is_a?(Time) value.utc.strftime("%Y-%m-%dT%H:%M:%SZ") elsif value.is_a?(Date) value.to_time(:local).strftime("%Y-%m-%dT%H:%M:%SZ") @@ -319,23 +398,8 @@ ## # Set Solr spellcheck.q to be original user-entered query, without # our local params, otherwise it'll try and spellcheck the local # params! solr_parameters["spellcheck.q"] ||= search_state.query_param - end - - def add_multifield_search_query(solr_parameters) - q = search_state.query_param - solr_parameters[:q] = if q.values.any?(&:blank?) - # if any field parameters are empty, exclude _all_ results - "{!lucene}NOT *:*" - else - "{!lucene}" + q.map do |field, values| - "#{field}:(#{Array(values).map { |x| solr_param_quote(x) }.join(' OR ')})" - end.join(" AND ") - end - - solr_parameters[:defType] = 'lucene' - solr_parameters[:spellcheck] = 'false' end end end