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



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

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.



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

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|
    filter_query_builder_class_or_proc = filter.config.filter_query_builder || DefaultFilterQueryBuilder
    if filter_query_builder_class_or_proc.is_a?(Class)
      filter_query_builder = filter_query_builder_class_or_proc.new(blacklight_config: blacklight_config)
      filter_query, subqueries = filter_query_builder.call(filter, solr_parameters)
    else
      # TODO: Maybe deprecate proc?
      filter_query, subqueries = filter_query_builder_class_or_proc.call(self, filter, solr_parameters)
    end

    Array(filter_query).each do |fq|
      solr_parameters.append_filter_query(fq)
    end
    solr_parameters.merge!(subqueries) if subqueries
  end
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.



165
166
167
168
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
# File 'lib/blacklight/solr/search_builder_behavior.rb', line 165

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

    facet.solr_params&.each do |k, v|
      solr_parameters[:"f.#{facet.field}.#{k}"] = v
    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)



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

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.



217
218
219
220
221
222
223
# File 'lib/blacklight/solr/search_builder_behavior.rb', line 217

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
91
92
# 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["spellcheck.q"] ||= 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



146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/blacklight/solr/search_builder_behavior.rb', line 146

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.compact_blank
end

#add_solr_fields_to_query(solr_parameters) ⇒ Object



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/blacklight/solr/search_builder_behavior.rb', line 195

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
  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
  end
end

#add_sorting_to_solr(solr_parameters) ⇒ Object

copy sorting params from BL app over to solr



227
228
229
# File 'lib/blacklight/solr/search_builder_behavior.rb', line 227

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



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

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.



250
251
252
253
254
255
256
257
# File 'lib/blacklight/solr/search_builder_behavior.rb', line 250

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.



262
263
264
265
266
267
268
269
270
271
272
# File 'lib/blacklight/solr/search_builder_behavior.rb', line 262

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.



278
279
280
281
282
283
284
285
286
287
288
# File 'lib/blacklight/solr/search_builder_behavior.rb', line 278

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



236
237
238
239
240
241
242
# File 'lib/blacklight/solr/search_builder_behavior.rb', line 236

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