require 'blacklight_advanced_search/parsing_nesting_parser' # This module gets included into CatalogController, or another SolrHelper # includer, to add behavior into solr_search_params_logic. module BlacklightAdvancedSearch::Controller extend ActiveSupport::Concern included do if blacklight_config[:advanced_parse_q] # Only to parse AND/OR in ordinary 'q' solr_search_params_logic << :add_advanced_parse_q_to_solr end # Always, to parse adv search form params. solr_search_params_logic << :add_advanced_search_to_solr helper BlacklightAdvancedSearch::RenderConstraintsOverride helper BlacklightAdvancedSearch::CatalogHelperOverride end # this method should get added into the solr_search_params_logic # list, in a position AFTER normal query handling (:add_query_to_solr), # so it'll overwrite that if and only if it's an advanced search. # adds a 'q' and 'fq's based on advanced search form input. def add_advanced_search_to_solr(solr_parameters, req_params = params) # If we've got the hint that we're doing an 'advanced' search, then # map that to solr #q, over-riding whatever some other logic may have set, yeah. # the hint right now is :search_field request param is set to a magic # key. if (req_params[:search_field] == ::AdvancedController.blacklight_config.advanced_search[:url_key] || req_params[:f_inclusive]) # Set this as a controller instance variable, not sure if some views/helpers depend on it. Better to leave it as a local variable # if not, more investigation later. @advanced_query = BlacklightAdvancedSearch::QueryParser.new(req_params, ::AdvancedController.blacklight_config ) deep_merge!(solr_parameters, @advanced_query.to_solr ) if @advanced_query.keyword_queries.length > 0 # force :qt if set solr_parameters[:qt] = ::AdvancedController.blacklight_config.advanced_search[:qt] solr_parameters[:defType] = "lucene" end end end # This method can be included in solr_search_params_logic to have us # parse an ordinary entered :q for AND/OR/NOT and produce appropriate # Solr query. Note that it is NOT included in solr_search_params_logic # by default when this module is included, because it is optional behavior. # BlacklightAdvancedSearch init code will add it to CatalogController # if it's configured to do so. You can of course add it yourself # manually too. # # Note: For syntactically invalid input, we'll just skip the adv # parse and send it straight to solr same as if advanced_parse_q # were not being used. def add_advanced_parse_q_to_solr(solr_parameters, req_params = params) unless req_params[:q].blank? field_def = search_field_def_for_key( req_params[:search_field]) || default_search_field # If the individual field has advanced_parse_q suppressed, punt return if field_def[:advanced_parse_q] == false solr_direct_params = field_def[:solr_parameters] || {} solr_local_params = field_def[:solr_local_parameters] || {} # See if we can parse it, if we can't, we're going to give up # and just allow basic search, perhaps with a warning. begin adv_search_params = ParsingNesting::Tree.parse(req_params[:q]).to_single_query_params( solr_local_params ) deep_merge!(solr_parameters, solr_direct_params) deep_merge!( solr_parameters, adv_search_params ) rescue Parslet::UnconsumedInput => e # do nothing, don't merge our input in, keep basic search # optional TODO, display error message in flash here, but hard to # display a good one. return end end end protected # Merges new_hash into source_hash, without modifying arguments, but # will merge nested arrays and hashes too. Also will NOT merge nil or blank # from new_hash into old_hash def deep_merge!(source_hash, new_hash) source_hash.merge!(new_hash) do |key, old, new| if new.respond_to?(:blank) && new.blank? old elsif (old.kind_of?(Hash) and new.kind_of?(Hash)) deep_merge!(old, new) elsif (old.kind_of?(Array) and new.kind_of?(Array)) old.concat(new).uniq elsif new.nil? # Allowing nil values to over-write on merge messes things up. # don't set a nil value if you really want to force blank, set # empty string. old else new end end end end