• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pulibrary / orangelight / 62bad3f1-d46d-40af-822c-403d653da2a8

17 Jun 2025 05:30PM UTC coverage: 0.447% (-94.9%) from 95.337%
62bad3f1-d46d-40af-822c-403d653da2a8

push

circleci

maxkadel
Install chrome & chromedriver for smoke specs

43 of 9610 relevant lines covered (0.45%)

0.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/app/models/search_builder.rb
1
# frozen_string_literal: true
2

3
class SearchBuilder < Blacklight::SearchBuilder
×
4
  include Blacklight::Solr::SearchBuilderBehavior
×
5
  include BlacklightRangeLimit::RangeLimitBuilder
×
6
  include BlacklightHelper
×
7

8
  default_processor_chain.unshift(:conditionally_configure_json_query_dsl)
×
9

10
  self.default_processor_chain += %i[parslet_trick cleanup_boolean_operators
×
11
                                     cjk_mm wildcard_char_strip
×
12
                                     only_home_facets prepare_left_anchor_search
×
13
                                     series_title_results pul_holdings html_facets
×
14
                                     numismatics_facets numismatics_advanced
×
15
                                     adjust_mm remove_unneeded_facets]
×
16

17
  # mutate the solr_parameters to remove words that are
18
  # boolean operators, but not intended as such.
19
  def cleanup_boolean_operators(solr_parameters)
×
20
    transform_queries!(solr_parameters) { |query| cleaned_query(query) }
×
21
  end
×
22

23
  # Blacklight uses Parslet https://rubygems.org/gems/parslet/versions/2.0.0 to parse the user query
24
  # and unfortunately Parslet gets confused when the user's query ends with "()". Here we tweak the
25
  # query to prevent the error and let the query to be parsed as if the ending "()" was not present.
26
  # Notice that we must update the value in `blacklight_params[:q]`
27
  def parslet_trick(_solr_parameters)
×
28
    return unless blacklight_params[:q].is_a?(String)
×
29
    return unless blacklight_params[:q].strip.end_with?("()")
×
30
    blacklight_params[:q] = blacklight_params[:q].strip.gsub("()", "")
×
31
  end
×
32

33
  # Only search for coin records when querying with the numismatics advanced search
34
  def numismatics_advanced(solr_parameters)
×
35
    return unless blacklight_params[:advanced_type] == 'numismatics'
×
36
    solr_parameters[:fq] ||= []
×
37
    solr_parameters[:fq] << "format:Coin"
×
38
  end
×
39

40
  def numismatics_facets(solr_parameters)
×
41
    return unless blacklight_params[:action] == 'numismatics'
×
42
    blacklight_config.advanced_search[:form_solr_parameters]['facet.field'] = blacklight_config.numismatics_search['facet_fields']
×
43
    solr_parameters['facet.field'] = blacklight_config.numismatics_search['facet_fields']
×
44
  end
×
45

46
  def facets_for_advanced_search_form(solr_p)
×
47
    # Reject any facets that are meant to display on the advanced
48
    # search form, so that the form displays accurate counts for
49
    # them in its dropdowns
50
    advanced_search_facets = blacklight_config.advanced_search.form_solr_parameters['facet.field']
×
51
    solr_p[:fq]&.compact!
×
52
    solr_p[:fq]&.reject! do |facet_from_query|
×
53
      advanced_search_facets.any? { |facet_to_exclude| facet_from_query.include? facet_to_exclude }
×
54
    end
×
55
  end
×
56

57
  def only_home_facets(solr_parameters)
×
58
    return if search_parameters? || advanced_search?
×
59
    solr_parameters['facet.field'] = blacklight_config.facet_fields.select { |_, v| v[:home] }.keys
×
60
    solr_parameters['facet.pivot'] = []
×
61
  end
×
62

63
  ##
64
  # Check if we are on an advanced search page
65
  # @return [Boolean]
66
  def advanced_search?
×
67
    blacklight_params[:advanced_type] == 'advanced' ||
×
68
      search_state.controller.try(:params).try(:[], :action) == 'advanced_search' ||
×
69
      blacklight_params[:advanced_type] == 'numismatics' ||
×
70
      # The next two are required for the advanced search gem
71
      blacklight_params[:search_field] == 'advanced' ||
×
72
      blacklight_params[:action] == 'numismatics'
×
73
  end
×
74

75
  ##
76
  # Check if any search parameters have been set
77
  # @return [Boolean]
78
  def search_parameters?
×
79
    search_query_present? || facet_query_present?
×
80
  end
×
81

82
  def conditionally_configure_json_query_dsl(_solr_parameters)
×
83
    advanced_fields = %w[all_fields title author subject left_anchor publisher in_series notes series_title isbn issn]
×
84
    add_edismax(advanced_fields:)
×
85
  end
×
86

87
  def adjust_mm(solr_parameters)
×
88
    # If the user is attempting a boolean OR query,
89
    # for example: activism OR "social justice"
90
    # don't want to cancel out the boolean OR with
91
    # an mm configuration that requires all the clauses
92
    # to be in the document
93
    return unless includes_written_boolean?
×
94
    solr_parameters['mm'] = 0
×
95
  end
×
96

97
  def includes_written_boolean?
×
98
    if advanced_search? && search_query_present?
×
99
      json_query_dsl_clauses&.any? { |clause| clause&.dig('query')&.include?('OR') }
×
100
    else
×
101
      blacklight_params[:q].to_s.split.include? 'OR'
×
102
    end
×
103
  end
×
104

105
  # When the user is viewing the values of a specific facet
106
  # by clicking the "more" link in a facet, solr doesn't
107
  # need to perform expensive calculations related to other
108
  # facets that the user is not displaying
109
  # :reek:FeatureEnvy
110
  def remove_unneeded_facets(solr_parameters)
×
111
    return unless facet
×
112
    remove_unneeded_stats(solr_parameters)
×
113
    solr_parameters.delete('facet.pivot') unless solr_parameters['facet.pivot']&.split(',')&.include? facet
×
114
    solr_parameters.delete('facet.query') unless solr_parameters['facet.query']&.any? { |query| query.partition(':').first == facet }
×
115
  end
×
116

117
  def wildcard_char_strip(solr_parameters)
×
118
    transform_queries!(solr_parameters) { |query| query.delete('?') }
×
119
  end
×
120

121
  private
×
122

123
    def search_query_present?
×
124
      !blacklight_params[:q].nil? || json_query_dsl_clauses&.any? { |clause| clause.dig('query')&.present? }
×
125
    end
×
126

127
    def facet_query_present?
×
128
      blacklight_params[:f].present? || blacklight_params[:action] == 'facet'
×
129
    end
×
130

131
    def json_query_dsl_clauses
×
132
      blacklight_params.dig('clause')&.values
×
133
    end
×
134

135
    def q_param_needs_boolean_cleanup(solr_parameters)
×
136
      solr_parameters[:q].present? &&
×
137
        cleaned_query(solr_parameters[:q]) == solr_parameters[:q]
×
138
    end
×
139

140
    def using_json_query_dsl(solr_parameters)
×
141
      solr_parameters.dig('json', 'query', 'bool', 'must')&.present?
×
142
    end
×
143

144
    def add_edismax(advanced_fields:)
×
145
      advanced_fields.each do |field|
×
146
        solr_params = blacklight_config.search_fields[field]['solr_parameters']
×
147
        edismax = solr_params.present? ? solr_params.dup : {}
×
148
        blacklight_config.search_fields[field]['clause_params'] = { edismax: }
×
149
      end
×
150
    end
×
151

152
    # :reek:FeatureEnvy
153
    def remove_unneeded_stats(solr_parameters)
×
154
      return if solr_parameters['stats.field'].to_a.include? facet
×
155
      solr_parameters.delete('stats')
×
156
      solr_parameters.delete('stats.field')
×
157
    end
×
158

159
    # :reek:DuplicateMethodCall
160
    # :reek:MissingSafeMethod
161
    # :reek:UtilityFunction
162
    def transform_queries!(solr_parameters)
×
163
      solr_parameters[:q] = yield solr_parameters[:q] if solr_parameters[:q]
×
164
      solr_parameters.dig('json', 'query', 'bool', 'must')&.map! do |search_element|
×
165
        search_element[:edismax][:query] = yield search_element[:edismax][:query]
×
166
        search_element
×
167
      end
×
168
    end
×
169

170
    def cleaned_query(query)
×
171
      return query if query.nil?
×
172
      query.gsub(/([A-Z]) (NOT|OR|AND) ([A-Z])/) do
×
173
        "#{Regexp.last_match(1)} #{Regexp.last_match(2).downcase} #{Regexp.last_match(3)}"
×
174
      end
×
175
    end
×
176
end
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc