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

pulibrary / orangelight / 5c5fd8cc-c42a-44cb-88c5-ff68a1eb5f3a

16 Jul 2025 10:42PM UTC coverage: 95.366% (-0.001%) from 95.367%
5c5fd8cc-c42a-44cb-88c5-ff68a1eb5f3a

Pull #4962

circleci

web-flow
Merge pull request #4992 from pulibrary/library_display-search-results

Display only the library name in search results
Pull Request #4962: Orangelight pos workcycle 07-07-2025

88 of 92 new or added lines in 15 files covered. (95.65%)

1 existing line in 1 file now uncovered.

6051 of 6345 relevant lines covered (95.37%)

1513.59 hits per line

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

99.2
/app/helpers/blacklight_helper.rb
1
# frozen_string_literal: false
2

3
require 'library_stdnums'
3✔
4

5
module BlacklightHelper
3✔
6
  include Blacklight::BlacklightHelperBehavior
3✔
7
  require './lib/orangelight/string_functions'
3✔
8

9
  def json_field?(field)
3✔
10
    field[:hash]
879✔
11
  end
12

13
  def linked_record_field?(field)
3✔
14
    field[:link_field]
812✔
15
  end
16

17
  # Escape all whitespace characters within Solr queries specifying left anchor query facets
18
  # Ends all left-anchor searches with wildcards for matches that begin with search string
19
  # @param solr_parameters [Blacklight::Solr::Request] the parameters for the Solr query
20
  def prepare_left_anchor_search(solr_parameters)
3✔
21
    return unless left_anchor_search?(solr_parameters)
272✔
22
    solr_parameters.dig('json', 'query', 'bool').each_value do |value|
107✔
23
      value.select { |boolean_query| boolean_query_searches_left_anchored_field?(boolean_query) }.map! do |clause|
235✔
24
        query = escape_left_anchor_query(clause.dig(:edismax, :query).dup)
41✔
25
        query = add_wildcard(query)
41✔
26
        clause.dig(:edismax)[:query] = query
41✔
27
      end
28
    end
29
  end
30

31
  def left_anchor_search?(solr_parameters)
3✔
32
    return false unless solr_parameters.dig('json', 'query', 'bool')
273✔
33
    has_left_anchor = solr_parameters.dig('json', 'query', 'bool')
108✔
34
                                     .values
35
                                     .any? { |value| value.select { |clause| boolean_query_searches_left_anchored_field?(clause) } }
231✔
36
    return false unless has_left_anchor
108✔
37

38
    true
108✔
39
  end
40

41
  # Escape all whitespace characters within Solr queries specifying left anchor query facets
42
  def escape_left_anchor_query(query)
3✔
43
    query.gsub!(/(\s)/, '\\\\\1')
41✔
44
    query.gsub!(/(["\{\}\[\]\^\~])/, '\\\\\1')
41✔
45
    query.gsub!(/[\(\)]/, '')
41✔
46
    query
41✔
47
  end
48

49
  # Ends all left-anchor searches with wildcards for matches that begin with search string
50
  def add_wildcard(query)
3✔
51
    query.end_with?('*') ? query : query + '*'
41✔
52
  end
53

54
  def pul_holdings(solr_parameters)
3✔
55
    return unless blacklight_params[:f_inclusive] && blacklight_params[:f_inclusive][:advanced_location_s]&.include?('pul')
268✔
56
    solr_parameters[:fq].map! { |fq| fq.gsub '"pul"', '*' }
2✔
57
                        .reject! { |fq| fq == '{!term f=advanced_location_s}pul' }
1✔
58
    solr_parameters[:fq] << '-id:SCSB*'
1✔
59
  end
60

61
  def series_title_results(solr_parameters)
3✔
62
    return unless includes_series_search?
268✔
63
    solr_parameters[:fl] = 'id,score,author_display,marc_relator_display,format,pub_created_display,'\
5✔
64
                           'title_display,title_vern_display,isbn_s,oclc_s,lccn_s,holdings_1display,'\
65
                           'electronic_access_1display,electronic_portfolio_s,cataloged_tdt,series_display'
66
  end
67

68
  def includes_series_search?
3✔
69
    blacklight_params['clause'].map { |clause| clause[1]["field"] }.include?('series_title' || 'in_series') if blacklight_params['clause'].present?
415✔
70
  end
71

72
  # only fetch facets when an html page is requested
73
  def html_facets(solr_parameters)
3✔
74
    return if blacklight_params[:format].nil? || blacklight_params[:format] == 'html' ||
270✔
75
              blacklight_params[:format] == 'json'
76
    solr_parameters[:facet] = false
2✔
77
  end
78

79
  # Adapted from http://discovery-grindstone.blogspot.com/2014/01/cjk-with-solr-for-libraries-part-12.html
80
  def cjk_mm(solr_parameters)
3✔
81
    if blacklight_params && blacklight_params[:q].present?
271✔
82
      q_str = blacklight_params[:q]
66✔
83
      number_of_unigrams = cjk_unigrams_size(q_str)
66✔
84
      if number_of_unigrams > 2
66✔
85
        num_non_cjk_tokens = q_str.scan(/[[:alnum]]+/).size
3✔
86
        if num_non_cjk_tokens.positive?
3✔
87
          lower_limit = cjk_mm_val[0].to_i
1✔
88
          mm = (lower_limit + num_non_cjk_tokens).to_s + cjk_mm_val[1, cjk_mm_val.size]
1✔
89
          solr_parameters['mm'] = mm
1✔
90
        else
91
          solr_parameters['mm'] = cjk_mm_val
2✔
92
        end
93
      end
94
    end
95
  end
96

97
  def cjk_unigrams_size(str)
3✔
98
    if str&.is_a?(String)
66✔
99
      str.scan(/\p{Han}|\p{Katakana}|\p{Hiragana}|\p{Hangul}/).size
56✔
100
    else
101
      0
10✔
102
    end
103
  end
104

105
  def cjk_mm_val
3✔
106
    '3<86%'
6✔
107
  end
108

109
  def browse_related_name_hash(name)
3✔
110
    link_to(name, "/?f[author_s][]=#{CGI.escape name}", class: 'search-related-name', 'data-original-title' => "Search: #{name}") + '  ' +
270✔
111
      link_to('[Browse]', "/browse/names?q=#{CGI.escape name}", class: 'browse-related-name', 'data-original-title' => "Search: #{name}")
112
  end
113

114
  # render_document_heading from Blacklight v7.23.0.1
115
  # https://github.com/projectblacklight/blacklight/blob/242880eacb1c73a2a6a3d7cdf4e24cec151179f8/app/helpers/blacklight/blacklight_helper_behavior.rb#L245
116
  def render_document_heading(*args)
3✔
117
    options = args.extract_options!
109✔
118
    document = args.first
109✔
119
    tag = options.fetch(:tag, :h4)
109✔
120
    document ||= @document
109✔
121
    content_tag(tag, document_presenter(document).heading, itemprop: "name", lang: language_iana)
109✔
122
  end
123

124
  ##
125
  # Render the heading partial for a document
126
  #
127
  # @param [SolrDocument]
128
  # @return [String]
129
  def render_document_heading_partial(_document = @document)
3✔
130
    render partial: 'show_header_default'
109✔
131
  end
132

133
  # Generates markup for a <span> elements containing icons given a string value
134
  # @param value [String] value used for the CSS class
135
  # @return [String] markup for the <span> element
136
  def render_icon(var)
3✔
UNCOV
137
    "<span class='icon icon-#{var.parameterize}' aria-hidden='true'></span>".html_safe
×
138
  end
139

140
  # Generate the link to "start over" searches
141
  # @param path [String] the URL path for the link
142
  # @return [String] the markup for the link
143
  def render_start_over_link(path)
3✔
144
    child = "<span class=\"icon-refresh\" aria-hidden=\"true\"></span> <span>#{t('blacklight.search.start_over')}</span>"
401✔
145
    link_to(child.html_safe, path, class: 'catalog_startOverLink btn btn-primary', id: 'startOverLink')
401✔
146
  end
147

148
  # Generate the link to citations for Documents
149
  # @param path [String] the URL path for the link
150
  # @return [String] the markup for the link
151
  def render_cite_link(path)
3✔
152
    child = "<span class=\"icon-cite\" aria-hidden=\"true\"></span> #{t('blacklight.search.cite')}"
93✔
153
    link_to(child.html_safe, path, id: 'citeLink', data: { blacklight_modal: 'trigger' }, class: 'btn btn-default')
93✔
154
  end
155

156
  # Retrieve an instance of the FacetedQueryService
157
  # @return [FacetedQueryService] an instance of the service object
158
  def faceted_query_service
3✔
159
    @faceted_query_service ||= FacetedQueryService.new(Blacklight)
5✔
160
  end
161

162
  def oclc_resolve(oclc)
3✔
163
    oclc_norm = StringFunctions.oclc_normalize(oclc)
2✔
164
    unless oclc_norm.nil?
2✔
165
      fq = "oclc_s:#{oclc_norm}"
2✔
166
      resp = faceted_query_service.get_fq_solr_response(fq)
2✔
167
      req = JSON.parse(resp.body)
2✔
168
    end
169
    if oclc_norm.to_i.zero? || req['response']['docs'].empty?
2✔
170
      "/catalog?q=#{oclc}"
1✔
171
    else
172
      "/catalog/#{req['response']['docs'].first['id']}"
1✔
173
    end
174
  end
175

176
  def isbn_resolve(isbn)
3✔
177
    isbn_norm = StdNum::ISBN.normalize(isbn)
2✔
178
    unless isbn_norm.nil?
2✔
179
      fq = "isbn_s:#{isbn_norm}"
1✔
180
      resp = faceted_query_service.get_fq_solr_response(fq)
1✔
181
      req = JSON.parse(resp.body)
1✔
182
    end
183
    if isbn_norm.nil? || req['response']['docs'].empty?
2✔
184
      "/catalog?q=#{isbn}"
1✔
185
    else
186
      "/catalog/#{req['response']['docs'].first['id']}"
1✔
187
    end
188
  end
189

190
  def issn_resolve(issn)
3✔
191
    issn_norm = StdNum::ISSN.normalize(issn)
2✔
192
    unless issn_norm.nil?
2✔
193
      fq = "issn_s:#{issn_norm}"
1✔
194
      resp = faceted_query_service.get_fq_solr_response(fq)
1✔
195
      req = JSON.parse(resp.body)
1✔
196
    end
197
    if issn_norm.nil? || req['response']['docs'].empty?
2✔
198
      "/catalog?q=#{issn}"
1✔
199
    else
200
      "/catalog/#{req['response']['docs'].first['id']}"
1✔
201
    end
202
  end
203

204
  def lccn_resolve(lccn)
3✔
205
    lccn_norm = StdNum::LCCN.normalize(lccn)
2✔
206
    unless lccn_norm.nil?
2✔
207
      fq = "lccn_s:#{lccn_norm}"
1✔
208
      resp = faceted_query_service.get_fq_solr_response(fq)
1✔
209
      req = JSON.parse(resp.body)
1✔
210
    end
211
    if lccn_norm.nil? || req['response']['docs'].empty?
2✔
212
      "/catalog?q=#{lccn}"
1✔
213
    else
214
      "/catalog/#{req['response']['docs'].first['id']}"
1✔
215
    end
216
  end
217

218
  # Links to correct advanced search page based on advanced_type parameter value
219
  def edit_search_link
3✔
220
    url = advanced_path(params.permit!.except(:controller, :action).to_h)
197✔
221
    if params[:advanced_type] == 'numismatics'
197✔
222
      url.gsub('/advanced', '/numismatics')
10✔
223
    else
224
      url
187✔
225
    end
226
  end
227

228
  def link_back_to_catalog_safe(opts = { label: nil })
3✔
229
    link_back_to_catalog(opts)
2✔
230
  rescue ActionController::UrlGenerationError
231
    # This exception is triggered if the user's session has information that results in an
232
    # invalid back to catalog link. In that case, rather than blowing up on the user, we
233
    # render a valid link. This link does not preserve the user's previous setings and that is
234
    # OK because very likely their session is corrupted.
235
    link_to "Back to search", root_url
1✔
236
  end
237

238
    private
3✔
239

240
      def boolean_query_searches_left_anchored_field?(boolean_query)
3✔
241
        ["${left_anchor_qf}", "${in_series_qf}"].include? boolean_query.dig(:edismax, :qf)
248✔
242
      end
243
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