Module: Blacklight::Solr::SearchBuilderBehavior

Extended by:
ActiveSupport::Concern
Included in:
SearchBuilder
Defined in:
lib/blacklight/solr/search_builder_behavior.rb

Instance Method Summary collapse

Instance Method Details

#add_additional_filters(solr_parameters, additional_filters = nil) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/blacklight/solr/search_builder_behavior.rb', line 65

def add_additional_filters(solr_parameters, additional_filters = nil)
  q = additional_filters || @additional_filters

  return if q.blank?

  if q.values.any?(&:blank?)
    # if any field parameters are empty, exclude _all_ results
    solr_parameters.append_query "{!lucene}NOT *:*"
  else
    composed_query = q.map do |field, values|
      "#{field}:(#{Array(values).map { |x| solr_param_quote(x) }.join(' OR ')})"
    end.join(" AND ")

    solr_parameters.append_query "{!lucene}#{composed_query}"
  end

  solr_parameters[:defType] = 'lucene'
  solr_parameters[:spellcheck] = 'false'
end

#add_adv_search_clauses(solr_parameters) ⇒ Object

Transform “clause” parameters into the Solr JSON Query DSL



93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/blacklight/solr/search_builder_behavior.rb', line 93

def add_adv_search_clauses(solr_parameters)
  return if search_state.clause_params.blank?

  defaults = { must: [], must_not: [], should: [] }
  default_op = blacklight_params[:op]&.to_sym || :must
  solr_parameters[:mm] = 1 if default_op == :should && search_state.clause_params.values.any? { |clause| }

  search_state.clause_params.each_value do |clause|
    op, query = adv_search_clause(clause, default_op)
    next unless defaults.key?(op)

    solr_parameters.append_boolean_query(op, query)
  end
end

#add_facet_fq_to_solr(solr_parameters) ⇒ Object

Add any existing facet limits, stored in app-level HTTP query as :f, to solr as appropriate :fq query.



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/blacklight/solr/search_builder_behavior.rb', line 121

def add_facet_fq_to_solr(solr_parameters)
  # convert a String value into an Array
  if solr_parameters[:fq].is_a? String
    solr_parameters[:fq] = [solr_parameters[:fq]]
  end

  search_state.filters.each do |filter|
    if filter.config.filter_query_builder
      filter_query, subqueries = filter.config.filter_query_builder.call(self, filter, solr_parameters)

      Array(filter_query).each do |fq|
        solr_parameters.append_filter_query(fq)
      end
      solr_parameters.merge!(subqueries) if subqueries
    else
      filter.values.compact_blank.each do |value|
        filter_query, subqueries = if value.is_a?(Array)
                                     facet_inclusive_value_to_fq_string(filter.key, value.compact_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

#add_facet_paging_to_solr(solr_params) ⇒ Object



242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/blacklight/solr/search_builder_behavior.rb', line 242

def add_facet_paging_to_solr(solr_params)
  return if facet.blank?

  facet_config = blacklight_config.facet_fields[facet]

  solr_params[:rows] = 0

  limit = if solr_params["facet.limit"]
            solr_params["facet.limit"].to_i
          else
            facet_config.fetch(:more_limit, blacklight_config.default_more_limit)
          end

  page = search_state.facet_page
  sort = search_state.facet_sort
  prefix = search_state.facet_prefix
  offset = (page - 1) * limit

  if facet_config.json
    add_solr_facet_json_params(solr_parameters, facet, facet_config, limit: limit + 1, offset: offset, sort: sort, prefix: prefix)
    return
  end

  # Now override with our specific things for fetching facet values
  facet_ex = facet_config.respond_to?(:ex) ? facet_config.ex : nil
  solr_params[:'facet.field'] = with_ex_local_param(facet_ex, facet_config.field)

  # Need to set as f.facet_field.facet.* to make sure we
  # override any field-specific default in the solr request handler.
  solr_params[:"f.#{facet_config.field}.facet.limit"] = limit + 1
  solr_params[:"f.#{facet_config.field}.facet.offset"] = offset
  solr_params[:"f.#{facet_config.field}.facet.sort"] = sort if sort
  solr_params[:"f.#{facet_config.field}.facet.prefix"] = prefix if prefix
end

#add_facetting_to_solr(solr_parameters) ⇒ Object

Add appropriate Solr facetting directives in, including taking account of our facet paging/‘more’. This is not about solr ‘fq’, this is about solr facet.* params.



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/blacklight/solr/search_builder_behavior.rb', line 169

def add_facetting_to_solr(solr_parameters)
  facet_fields_to_include_in_request.each do |field_name, facet|
    solr_parameters[:facet] ||= true

    if facet.json
      add_solr_facet_json_params(solr_parameters, field_name, facet)
      next
    end

    if facet.pivot
      solr_parameters.append_facet_pivot with_ex_local_param(facet.ex, facet.pivot.join(","))
    elsif facet.query
      solr_parameters.append_facet_query facet.query.values.map { |x| with_ex_local_param(facet.ex, x[:fq]) }
    else
      solr_parameters.append_facet_fields with_ex_local_param(facet.ex, facet.field)
    end

    if facet.sort
      solr_parameters[:"f.#{facet.field}.facet.sort"] = facet.sort
    end

    if facet.solr_params
      facet.solr_params.each do |k, v|
        solr_parameters[:"f.#{facet.field}.#{k}"] = v
      end
    end

    limit = facet_limit_with_pagination(field_name)
    solr_parameters[:"f.#{facet.field}.facet.limit"] = limit if limit
  end
end

#add_group_config_to_solr(solr_parameters) ⇒ Object

Remove the group parameter if we’ve faceted on the group field (e.g. for the full results for a group)



238
239
240
# File 'lib/blacklight/solr/search_builder_behavior.rb', line 238

def add_group_config_to_solr solr_parameters
  solr_parameters[:group] = false if search_state.filter(grouped_key_for_results).any?
end

#add_paging_to_solr(solr_params) ⇒ Object

copy paging params from BL app over to solr, changing app level per_page and page to Solr rows and start.



223
224
225
226
227
228
229
# File 'lib/blacklight/solr/search_builder_behavior.rb', line 223

def add_paging_to_solr(solr_params)
  rows(solr_params[:rows] || 10) if rows.nil?

  solr_params[:rows] = rows

  solr_params[:start] = start if start.nonzero?
end

#add_query_to_solr(solr_parameters) ⇒ Object

Take the user-entered query, and put it in the solr params, including config’s “search field” params for current search field. also include setting spellcheck.q.



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/blacklight/solr/search_builder_behavior.rb', line 48

def add_query_to_solr(solr_parameters)
  ##
  # Create Solr 'q' including the user-entered q, prefixed by any
  # solr LocalParams in config, using solr LocalParams syntax.
  # http://wiki.apache.org/solr/LocalParams
  ##
  if search_field&.query_builder.present?
    add_search_field_query_builder_params(solr_parameters)
  elsif search_field&.clause_params.present?
    add_search_field_with_json_query_parameters(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)
    solr_parameters.append_query search_state.query_param
  end
end

#add_search_field_default_parameters(solr_parameters) ⇒ Object



33
34
35
36
37
38
39
40
41
42
# File 'lib/blacklight/solr/search_builder_behavior.rb', line 33

def add_search_field_default_parameters(solr_parameters)
  ###
  # Merge in search field configured values, if present, over-writing general
  # defaults
  if search_field
    solr_parameters[:qt] = search_field.qt if search_field.qt

    solr_parameters.deep_merge!(search_field.solr_parameters) if search_field.solr_parameters
  end
end

#add_search_field_with_json_query_parameters(solr_parameters) ⇒ Object



85
86
87
88
89
90
# File 'lib/blacklight/solr/search_builder_behavior.rb', line 85

def add_search_field_with_json_query_parameters(solr_parameters)
  return unless search_state.query_param

  bool_query = search_field.clause_params.transform_values { |v| v.merge(query: search_state.query_param) }
  solr_parameters.append_boolean_query(:must, bool_query)
end

#add_solr_facet_json_params(solr_parameters, field_name, facet, **additional_parameters) ⇒ Object



150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/blacklight/solr/search_builder_behavior.rb', line 150

def add_solr_facet_json_params(solr_parameters, field_name, facet, **additional_parameters)
  solr_parameters[:json] ||= { facet: {} }
  solr_parameters[:json][:facet] ||= {}

  field_config = facet.json.respond_to?(:reverse_merge) ? facet.json : {}

  field_config = field_config.reverse_merge(
    type: 'terms',
    field: facet.field,
    limit: facet_limit_with_pagination(field_name)
  ).merge(additional_parameters)

  solr_parameters[:json][:facet][field_name] = field_config.select { |_k, v| v.present? }
end

#add_solr_fields_to_query(solr_parameters) ⇒ Object



201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/blacklight/solr/search_builder_behavior.rb', line 201

def add_solr_fields_to_query solr_parameters
  blacklight_config.show_fields.select(&method(:should_add_field_to_request?)).each_value do |field|
    field.solr_params.each do |k, v|
      solr_parameters[:"f.#{field.field}.#{k}"] = v
    end if field.solr_params
  end

  blacklight_config.index_fields.select(&method(:should_add_field_to_request?)).each_value do |field|
    if field.highlight
      solr_parameters[:hl] = true
      solr_parameters.append_highlight_field field.field
    end

    field.solr_params.each do |k, v|
      solr_parameters[:"f.#{field.field}.#{k}"] = v
    end if field.solr_params
  end
end

#add_sorting_to_solr(solr_parameters) ⇒ Object

copy sorting params from BL app over to solr



233
234
235
# File 'lib/blacklight/solr/search_builder_behavior.rb', line 233

def add_sorting_to_solr(solr_parameters)
  solr_parameters[:sort] = sort if sort.present?
end

#adv_search_clause(clause, default_op) ⇒ Array

Returns the first element is the query operator and the second is the value to add.

Returns:

  • (Array)

    the first element is the query operator and the second is the value to add



109
110
111
112
113
114
115
116
# File 'lib/blacklight/solr/search_builder_behavior.rb', line 109

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

#default_solr_parameters(solr_parameters) ⇒ Object

Start with general defaults from BL config. Need to use custom merge to dup values, to avoid later mutating the original by mistake.



21
22
23
24
25
26
27
28
29
30
31
# File 'lib/blacklight/solr/search_builder_behavior.rb', line 21

def default_solr_parameters(solr_parameters)
  blacklight_config.default_solr_params.each do |key, value|
    solr_parameters[key] ||= if value.respond_to? :deep_dup
                               value.deep_dup
                             elsif value.respond_to?(:dup) && value.duplicable?
                               value.dup
                             else
                               value
                             end
  end
end

#facet_limit_for(facet_field) ⇒ Object

Look up facet limit for given facet_field. Will look at config, and if config is ‘true’ will look up from Solr @response if available. If no limit is avaialble, returns nil. Used from #add_facetting_to_solr to supply f.fieldname.facet.limit values in solr request (no @response available), and used in display (with @response available) to create a facet paginator with the right limit.



291
292
293
294
295
296
297
298
# File 'lib/blacklight/solr/search_builder_behavior.rb', line 291

def facet_limit_for(facet_field)
  facet = blacklight_config.facet_fields[facet_field]
  return if facet.blank?

  if facet.limit
    facet.limit == true ? blacklight_config.default_facet_limit : facet.limit
  end
end

#facet_limit_with_pagination(field_name) ⇒ Object

Support facet paging and ‘more’ links, by sending a facet.limit one more than what we want to page at, according to configured facet limits.



303
304
305
306
307
308
309
310
311
312
313
# File 'lib/blacklight/solr/search_builder_behavior.rb', line 303

def facet_limit_with_pagination(field_name)
  limit = facet_limit_for(field_name)

  return if limit.nil?

  if limit > 0
    limit + 1
  else
    limit
  end
end

#solr_param_quote(val, options = {}) ⇒ Object

A helper method used for generating solr LocalParams, put quotes around the term unless it’s a bare-word. Escape internal quotes if needed.



319
320
321
322
323
324
325
326
327
328
329
# File 'lib/blacklight/solr/search_builder_behavior.rb', line 319

def solr_param_quote(val, options = {})
  val = val.to_s
  options[:quote] ||= '"'
  unless val =~ /^[a-zA-Z0-9$_\-\^]+$/
    val = options[:quote] +
          # Yes, we need crazy escaping here, to deal with regexp esc too!
          val.gsub("'", "\\\\\'").gsub('"', "\\\\\"") +
          options[:quote]
  end
  val
end

#with_ex_local_param(ex, value) ⇒ Object



277
278
279
280
281
282
283
# File 'lib/blacklight/solr/search_builder_behavior.rb', line 277

def with_ex_local_param(ex, value)
  if ex
    "{!ex=#{ex}}#{value}"
  else
    value
  end
end