blacklight 7.14.1 → 7.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/app/components/blacklight/advanced_search_form_component.html.erb +46 -0
  4. data/app/components/blacklight/advanced_search_form_component.rb +75 -0
  5. data/app/components/blacklight/constraint_component.html.erb +1 -1
  6. data/app/components/blacklight/constraints_component.rb +36 -17
  7. data/app/components/blacklight/document/thumbnail_component.html.erb +1 -1
  8. data/app/components/blacklight/document/thumbnail_component.rb +4 -1
  9. data/app/components/blacklight/document_component.rb +7 -2
  10. data/app/components/blacklight/facet_field_checkboxes_component.html.erb +23 -0
  11. data/app/components/blacklight/facet_field_checkboxes_component.rb +24 -0
  12. data/app/components/blacklight/facet_field_inclusive_constraint_component.html.erb +6 -0
  13. data/app/components/blacklight/facet_field_inclusive_constraint_component.rb +29 -0
  14. data/app/components/blacklight/facet_field_list_component.html.erb +1 -0
  15. data/app/components/blacklight/facet_item_component.rb +2 -0
  16. data/app/components/blacklight/search_bar_component.html.erb +4 -0
  17. data/app/components/blacklight/search_bar_component.rb +4 -2
  18. data/app/controllers/concerns/blacklight/catalog.rb +6 -0
  19. data/app/helpers/blacklight/render_constraints_helper_behavior.rb +2 -2
  20. data/app/presenters/blacklight/clause_presenter.rb +37 -0
  21. data/app/presenters/blacklight/document_presenter.rb +5 -1
  22. data/app/presenters/blacklight/facet_field_presenter.rb +4 -0
  23. data/app/presenters/blacklight/facet_grouped_item_presenter.rb +45 -0
  24. data/app/presenters/blacklight/facet_item_presenter.rb +32 -20
  25. data/app/presenters/blacklight/inclusive_facet_item_presenter.rb +16 -0
  26. data/app/presenters/blacklight/search_bar_presenter.rb +4 -0
  27. data/app/views/catalog/_advanced_search_form.html.erb +7 -0
  28. data/app/views/catalog/_advanced_search_help.html.erb +24 -0
  29. data/app/views/catalog/_search_form.html.erb +1 -0
  30. data/app/views/catalog/advanced_search.html.erb +17 -0
  31. data/blacklight.gemspec +1 -1
  32. data/config/i18n-tasks.yml +1 -0
  33. data/config/locales/blacklight.en.yml +17 -0
  34. data/lib/blacklight/configuration.rb +2 -1
  35. data/lib/blacklight/routes/searchable.rb +1 -0
  36. data/lib/blacklight/search_builder.rb +2 -0
  37. data/lib/blacklight/search_state.rb +5 -1
  38. data/lib/blacklight/search_state/filter_field.rb +17 -7
  39. data/lib/blacklight/solr/repository.rb +11 -2
  40. data/lib/blacklight/solr/search_builder_behavior.rb +87 -23
  41. data/spec/components/blacklight/advanced_search_form_component_spec.rb +51 -0
  42. data/spec/components/blacklight/document_component_spec.rb +15 -0
  43. data/spec/components/blacklight/facet_field_checkboxes_component_spec.rb +55 -0
  44. data/spec/components/blacklight/facet_field_list_component_spec.rb +39 -4
  45. data/spec/controllers/catalog_controller_spec.rb +9 -0
  46. data/spec/features/advanced_search_spec.rb +67 -0
  47. data/spec/lib/blacklight/search_state/filter_field_spec.rb +65 -0
  48. data/spec/models/blacklight/solr/repository_spec.rb +12 -0
  49. data/spec/models/blacklight/solr/search_builder_spec.rb +28 -0
  50. data/spec/presenters/blacklight/clause_presenter_spec.rb +34 -0
  51. data/spec/presenters/blacklight/document_presenter_spec.rb +13 -0
  52. data/spec/presenters/blacklight/facet_grouped_item_presenter_spec.rb +41 -0
  53. metadata +29 -7
@@ -97,8 +97,12 @@ module Blacklight
97
97
  field_presenter(field_config, options).render
98
98
  end
99
99
 
100
+ def thumbnail_presenter_class
101
+ view_config.thumbnail_presenter || thumbnail_presenter
102
+ end
103
+
100
104
  def thumbnail
101
- @thumbnail ||= thumbnail_presenter.new(document, view_context, view_config)
105
+ @thumbnail ||= thumbnail_presenter_class.new(document, view_context, view_config)
102
106
  end
103
107
 
104
108
  ##
@@ -40,6 +40,10 @@ module Blacklight
40
40
  view_context.facet_field_label(key)
41
41
  end
42
42
 
43
+ def values
44
+ search_state&.filter(facet_field)&.values || []
45
+ end
46
+
43
47
  # @private
44
48
  # @deprecated
45
49
  def html_id
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Blacklight
4
+ class FacetGroupedItemPresenter < Blacklight::FacetItemPresenter
5
+ attr_reader :group
6
+
7
+ delegate :key, to: :facet_config
8
+
9
+ def initialize(group, facet_item, facet_config, view_context, facet_field, search_state = view_context.search_state)
10
+ @group = group
11
+ @facet_item = facet_item
12
+ @facet_config = facet_config
13
+ @view_context = view_context
14
+ @facet_field = facet_field
15
+ @search_state = search_state
16
+ end
17
+
18
+ ##
19
+ # Check if the query parameters have the given facet field with the
20
+ # given value.
21
+ def selected?
22
+ group.include?(facet_item)
23
+ end
24
+
25
+ # @private
26
+ def remove_href(path = search_state)
27
+ new_state = path.filter(facet_config).remove(group)
28
+ new_state = new_state.filter(facet_config).add(group - [facet_item])
29
+
30
+ view_context.search_action_path(new_state)
31
+ end
32
+
33
+ # @private
34
+ def add_href(_path_options = {})
35
+ if facet_config.url_method
36
+ return view_context.public_send(facet_config.url_method, facet_config.key, facet_item)
37
+ end
38
+
39
+ new_state = search_state.filter(facet_config).remove(@group)
40
+ new_state = new_state.filter(facet_config).add(@group + [facet_item])
41
+
42
+ view_context.search_action_path(new_state)
43
+ end
44
+ end
45
+ end
@@ -4,7 +4,7 @@ module Blacklight
4
4
  class FacetItemPresenter
5
5
  attr_reader :facet_item, :facet_config, :view_context, :search_state, :facet_field
6
6
 
7
- delegate :hits, :items, to: :facet_item
7
+ delegate :key, to: :facet_config
8
8
 
9
9
  def initialize(facet_item, facet_config, view_context, facet_field, search_state = view_context.search_state)
10
10
  @facet_item = facet_item
@@ -14,12 +14,24 @@ module Blacklight
14
14
  @search_state = search_state
15
15
  end
16
16
 
17
+ def hits
18
+ return unless @facet_item.respond_to? :hits
19
+
20
+ @facet_item.hits
21
+ end
22
+
23
+ def items
24
+ return unless @facet_item.respond_to? :items
25
+
26
+ @facet_item.items
27
+ end
28
+
17
29
  ##
18
30
  # Check if the query parameters have the given facet field with the
19
31
  # given value.
20
32
  def selected?
21
33
  Deprecation.silence(Blacklight::SearchState) do
22
- search_state.has_facet? facet_config, value: facet_value
34
+ search_state.has_facet? facet_config, value: value
23
35
  end
24
36
  end
25
37
 
@@ -34,21 +46,29 @@ module Blacklight
34
46
  def label
35
47
  return @view_context.facet_display_value(@facet_field, @facet_item) unless @view_context.method(:facet_display_value).owner == Blacklight::FacetsHelperBehavior
36
48
 
37
- value = if facet_item.respond_to? :label
38
- facet_item.label
39
- else
40
- facet_value
41
- end
49
+ label_value = if facet_item.respond_to? :label
50
+ facet_item.label
51
+ else
52
+ value
53
+ end
42
54
 
43
55
  if facet_config.helper_method
44
- view_context.public_send(facet_config.helper_method, value)
45
- elsif facet_config.query && facet_config.query[value]
46
- facet_config.query[value][:label]
56
+ view_context.public_send(facet_config.helper_method, label_value)
57
+ elsif facet_config.query && facet_config.query[label_value]
58
+ facet_config.query[label_value][:label]
47
59
  elsif facet_config.date
48
60
  localization_options = facet_config.date == true ? {} : facet_config.date
49
- I18n.l(Time.zone.parse(value), **localization_options)
61
+ I18n.l(Time.zone.parse(label_value), **localization_options)
62
+ else
63
+ label_value
64
+ end
65
+ end
66
+
67
+ def value
68
+ if facet_item.respond_to? :value
69
+ facet_item.value
50
70
  else
51
- value
71
+ facet_item
52
72
  end
53
73
  end
54
74
 
@@ -78,14 +98,6 @@ module Blacklight
78
98
 
79
99
  private
80
100
 
81
- def facet_value
82
- if facet_item.respond_to? :value
83
- facet_item.value
84
- else
85
- facet_item
86
- end
87
- end
88
-
89
101
  def facet_field_presenter
90
102
  @facet_field_presenter ||= view_context.facet_field_presenter(facet_config, {})
91
103
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Blacklight
4
+ class InclusiveFacetItemPresenter < Blacklight::FacetItemPresenter
5
+ ##
6
+ # Get the displayable version of a facet's value
7
+ #
8
+ # @return [String]
9
+ def label
10
+ view_context.safe_join(
11
+ Array(facet_item).map { |value| Blacklight::FacetGroupedItemPresenter.new(facet_item, value, facet_config, view_context, facet_field, search_state).label },
12
+ view_context.t('blacklight.advanced_search.or_html')
13
+ )
14
+ end
15
+ end
16
+ end
@@ -37,5 +37,9 @@ module Blacklight
37
37
  controller.action_name == "index" &&
38
38
  !controller.has_search_parameters?
39
39
  end
40
+
41
+ def advanced_search_enabled?
42
+ configuration.advanced_search.enabled
43
+ end
40
44
  end
41
45
  end
@@ -0,0 +1,7 @@
1
+ <%= render(Blacklight::AdvancedSearchFormComponent.new(
2
+ url: search_action_url,
3
+ classes: ['advanced', 'form-horizontal'],
4
+ params: search_state.params_for_search.except(:qt),
5
+ search_fields: Deprecation.silence(Blacklight::ConfigurationHelperBehavior) { search_fields },
6
+ response: @response
7
+ )) %>
@@ -0,0 +1,24 @@
1
+ <div class='card card-default'>
2
+ <div class="card-body">
3
+ <h4 class="card-title">Search tips</h4>
4
+ <ul class="advanced-help">
5
+ <li>Select "match all" to require all fields.
6
+ </li>
7
+
8
+ <li>Select "match any" to find at least one field.
9
+ </li>
10
+
11
+ <li>Combine keywords and attributes to find specific items.
12
+ </li>
13
+
14
+ <li>Use quotation marks to search as a phrase.
15
+
16
+ <li>Use "+" before a term to make it required. (Otherwise results matching only some of your terms may be included).</li>
17
+
18
+ <li>Use "-" before a word or phrase to exclude.
19
+
20
+ <li>Use "OR", "AND", and "NOT" to create complex boolean logic. You can use parentheses in your complex expressions. </li>
21
+ <li>Truncation and wildcards are not supported - word-stemming is done automatically.</li>
22
+ </ul>
23
+ </div>
24
+ </div>
@@ -1,5 +1,6 @@
1
1
  <%= render(Blacklight::SearchBarComponent.new(
2
2
  url: search_action_url,
3
+ advanced_search_url: search_action_url(action: 'advanced_search'),
3
4
  params: search_state.params_for_search.except(:qt),
4
5
  search_fields: Deprecation.silence(Blacklight::ConfigurationHelperBehavior) { search_fields },
5
6
  presenter: presenter,
@@ -0,0 +1,17 @@
1
+ <% @page_title = t('blacklight.advanced_search.page_title', application_name: application_name) %>
2
+
3
+ <div class="advanced-search-form col-sm-12">
4
+ <h1 class="advanced page-header">
5
+ <%= t('blacklight.advanced_search.form.title') %>
6
+ </h1>
7
+
8
+ <div class="row">
9
+ <div class="col-md-8">
10
+ <%= render 'advanced_search_form' %>
11
+ </div>
12
+
13
+ <div class="col-md-4">
14
+ <%= render "advanced_search_help" %>
15
+ </div>
16
+ </div>
17
+ </div>
@@ -23,7 +23,7 @@ Gem::Specification.new do |s|
23
23
  s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
24
  s.require_paths = ["lib"]
25
25
 
26
- s.required_ruby_version = '>= 2.3'
26
+ s.required_ruby_version = '>= 2.5'
27
27
 
28
28
  s.add_dependency "rails", '>= 5.1', '< 7'
29
29
  s.add_dependency "globalid"
@@ -18,3 +18,4 @@ search:
18
18
 
19
19
  ignore_missing:
20
20
  - 'button_label_html'
21
+ - 'blacklight.advanced_search.*'
@@ -239,3 +239,20 @@ en:
239
239
  main:
240
240
  aria:
241
241
  main_container: 'Main content'
242
+
243
+ advanced_search:
244
+ or_html: ' OR '
245
+ more_options: More options
246
+ any_of: 'Any of:'
247
+ op:
248
+ must: all
249
+ should: any
250
+ page_title: Advanced search - %{application_name}
251
+ form:
252
+ title: Advanced search
253
+ search_context: Within search
254
+ limit_criteria_heading_html: "<strong>AND</strong> have these attributes"
255
+ query_criteria_heading_html: "Match %{select_menu} of the fields below"
256
+ sort_label: "Sort results by"
257
+ start_over_html: "Start over"
258
+ search_btn_html: 'Search'
@@ -132,7 +132,8 @@ module Blacklight
132
132
  crawler_detector: nil,
133
133
  autocomplete_suggester: 'mySuggester',
134
134
  raw_endpoint: OpenStructWithHashAccess.new(enabled: false),
135
- track_search_session: true
135
+ track_search_session: true,
136
+ advanced_search: OpenStruct.new(enabled: false)
136
137
  }
137
138
  end
138
139
  # rubocop:enable Metrics/MethodLength
@@ -8,6 +8,7 @@ module Blacklight
8
8
 
9
9
  def call(mapper, _options = {})
10
10
  mapper.match '/', action: 'index', as: 'search', via: [:get, :post]
11
+ mapper.get '/advanced', action: 'advanced_search', as: 'advanced_search'
11
12
 
12
13
  mapper.post ":id/track", action: 'track', as: 'track'
13
14
  mapper.get ":id/raw", action: 'raw', as: 'raw', defaults: { format: 'json' }
@@ -28,6 +28,7 @@ module Blacklight
28
28
 
29
29
  @blacklight_params = {}
30
30
  @search_state = Blacklight::SearchState.new(@blacklight_params, @scope&.blacklight_config, @scope)
31
+ @additional_filters = {}
31
32
  @merged_params = {}
32
33
  @reverse_merged_params = {}
33
34
  end
@@ -47,6 +48,7 @@ module Blacklight
47
48
  params_will_change!
48
49
  @search_state = @search_state.reset(@search_state.params.merge(q: conditions))
49
50
  @blacklight_params = @search_state.params.dup
51
+ @additional_filters = conditions
50
52
  self
51
53
  end
52
54
 
@@ -82,7 +82,7 @@ module Blacklight
82
82
 
83
83
  def has_constraints?
84
84
  Deprecation.silence(Blacklight::SearchState) do
85
- !(query_param.blank? && filter_params.blank? && filters.blank?)
85
+ !(query_param.blank? && filter_params.blank? && filters.blank? && clause_params.blank?)
86
86
  end
87
87
  end
88
88
 
@@ -90,6 +90,10 @@ module Blacklight
90
90
  params[:q]
91
91
  end
92
92
 
93
+ def clause_params
94
+ params[:clause] || {}
95
+ end
96
+
93
97
  def filter_params
94
98
  params[:f] || {}
95
99
  end
@@ -36,12 +36,16 @@ module Blacklight
36
36
  end
37
37
 
38
38
  params = new_state.params
39
+ param = :f
39
40
  value = as_url_parameter(item)
41
+ param = :f_inclusive if value.is_a?(Array)
40
42
 
41
43
  # value could be a string
42
44
  params[param] = (params[param] || {}).dup
43
45
 
44
- if config.single
46
+ if value.is_a? Array
47
+ params[param][key] = value
48
+ elsif config.single
45
49
  params[param][key] = [value]
46
50
  else
47
51
  params[param][key] = Array(params[param][key] || []).dup
@@ -60,7 +64,10 @@ module Blacklight
60
64
  end
61
65
 
62
66
  params = new_state.params
67
+
68
+ param = :f
63
69
  value = as_url_parameter(item)
70
+ param = :f_inclusive if value.is_a?(Array)
64
71
 
65
72
  # need to dup the facet values too,
66
73
  # if the values aren't dup'd, then the values
@@ -86,7 +93,10 @@ module Blacklight
86
93
  # @return [Array] an array of applied filters
87
94
  def values
88
95
  params = search_state.params
89
- Array(params.dig(param, key)) || []
96
+ f = Array(params.dig(:f, key))
97
+ f_inclusive = [params.dig(:f_inclusive, key)] if params.dig(:f_inclusive, key).present?
98
+
99
+ f + (f_inclusive || [])
90
100
  end
91
101
  delegate :any?, to: :values
92
102
 
@@ -100,15 +110,15 @@ module Blacklight
100
110
  value = as_url_parameter(item)
101
111
  params = search_state.params
102
112
 
103
- (params.dig(param, key) || []).include?(value)
113
+ if value.is_a?(Array)
114
+ (params.dig(:f_inclusive, key) || []).to_set == value.to_set
115
+ else
116
+ (params.dig(:f, key) || []).include?(value)
117
+ end
104
118
  end
105
119
 
106
120
  private
107
121
 
108
- def param
109
- :f
110
- end
111
-
112
122
  # TODO: this code is duplicated in Blacklight::FacetsHelperBehavior
113
123
  def as_url_parameter(item)
114
124
  if item.respond_to? :value
@@ -58,8 +58,17 @@ module Blacklight::Solr
58
58
  # @return [Blacklight::Solr::Response] the solr response object
59
59
  def send_and_receive(path, solr_params = {})
60
60
  benchmark("Solr fetch", level: :debug) do
61
- key = blacklight_config.http_method == :post ? :data : :params
62
- res = connection.send_and_receive(path, { key => solr_params.to_hash, method: blacklight_config.http_method })
61
+ res = if solr_params[:json].present?
62
+ connection.send_and_receive(
63
+ path,
64
+ data: { params: solr_params.to_hash.except(:json) }.merge(solr_params[:json]).to_json,
65
+ method: :post,
66
+ headers: { 'Content-Type' => 'application/json' }
67
+ )
68
+ else
69
+ key = blacklight_config.http_method == :post ? :data : :params
70
+ connection.send_and_receive(path, { key => solr_params.to_hash, method: blacklight_config.http_method })
71
+ end
63
72
 
64
73
  solr_response = blacklight_config.response_model.new(res, solr_params, document_model: blacklight_config.document_model, blacklight_config: blacklight_config)
65
74
 
@@ -8,7 +8,8 @@ module Blacklight::Solr
8
8
  :default_solr_parameters, :add_query_to_solr, :add_facet_fq_to_solr,
9
9
  :add_facetting_to_solr, :add_solr_fields_to_query, :add_paging_to_solr,
10
10
  :add_sorting_to_solr, :add_group_config_to_solr,
11
- :add_facet_paging_to_solr
11
+ :add_facet_paging_to_solr, :add_adv_search_clauses,
12
+ :add_additional_filters
12
13
  ]
13
14
  end
14
15
 
@@ -61,12 +62,62 @@ module Blacklight::Solr
61
62
  elsif search_field&.solr_local_parameters.present?
62
63
  add_search_field_with_local_parameters(solr_parameters)
63
64
  elsif search_state.query_param.is_a? Hash
64
- add_multifield_search_query(solr_parameters)
65
- elsif blacklight_params[:q]
65
+ add_additional_filters(solr_parameters, search_state.query_param)
66
+ elsif search_state.query_param
66
67
  solr_parameters[:q] = search_state.query_param
67
68
  end
68
69
  end
69
70
 
71
+ def add_additional_filters(solr_parameters, additional_filters = nil)
72
+ q = additional_filters || @additional_filters
73
+
74
+ return if q.blank?
75
+
76
+ solr_parameters[:q] = if q.values.any?(&:blank?)
77
+ # if any field parameters are empty, exclude _all_ results
78
+ "{!lucene}NOT *:*"
79
+ else
80
+ "{!lucene}" + q.map do |field, values|
81
+ "#{field}:(#{Array(values).map { |x| solr_param_quote(x) }.join(' OR ')})"
82
+ end.join(" AND ")
83
+ end
84
+
85
+ solr_parameters[:defType] = 'lucene'
86
+ solr_parameters[:spellcheck] = 'false'
87
+ end
88
+
89
+ # Transform "clause" parameters into the Solr JSON Query DSL
90
+ def add_adv_search_clauses(solr_parameters)
91
+ return if search_state.clause_params.blank?
92
+
93
+ defaults = { must: [], must_not: [], should: [] }
94
+ bool_query = (solr_parameters.dig(:json, :query, :bool) || {}).reverse_merge(defaults)
95
+
96
+ default_op = blacklight_params[:op]&.to_sym || :must
97
+
98
+ search_state.clause_params.each_value do |clause|
99
+ op, query = adv_search_clause(clause, default_op)
100
+ bool_query[op] << query if defaults.key?(op) && query
101
+ end
102
+
103
+ return if bool_query.values.all?(&:blank?)
104
+
105
+ solr_parameters[:mm] = 1 if default_op == :should
106
+ solr_parameters[:json] ||= { query: { bool: {} } }
107
+ solr_parameters[:json][:query] ||= { bool: {} }
108
+ solr_parameters[:json][:query][:bool] = bool_query.reject { |_k, v| v.blank? }
109
+ end
110
+
111
+ # @return [Array] the first element is the query operator and the second is the value to add
112
+ def adv_search_clause(clause, default_op)
113
+ op = clause[:op]&.to_sym || default_op
114
+ field = (blacklight_config.search_fields || {})[clause[:field]] if clause[:field]
115
+
116
+ return unless field&.clause_params && clause[:query].present?
117
+
118
+ [op, field.clause_params.transform_values { |v| v.merge(query: clause[:query]) }]
119
+ end
120
+
70
121
  ##
71
122
  # Add any existing facet limits, stored in app-level HTTP query
72
123
  # as :f, to solr as appropriate :fq query.
@@ -84,8 +135,13 @@ module Blacklight::Solr
84
135
  solr_parameters.merge!(subqueries) if subqueries
85
136
  else
86
137
  filter.values.reject(&:blank?).each do |value|
87
- filter_query, subqueries = facet_value_to_fq_string(filter.config.key, value)
88
- solr_parameters.append_filter_query(filter_query)
138
+ filter_query, subqueries = if value.is_a?(Array)
139
+ facet_inclusive_value_to_fq_string(filter.key, value.reject(&:blank?))
140
+ else
141
+ facet_value_to_fq_string(filter.config.key, value)
142
+ end
143
+
144
+ solr_parameters.append_filter_query filter_query
89
145
  solr_parameters.merge!(subqueries) if subqueries
90
146
  end
91
147
  end
@@ -250,16 +306,17 @@ module Blacklight::Solr
250
306
 
251
307
  ##
252
308
  # Convert a facet/value pair into a solr fq parameter
253
- def facet_value_to_fq_string(facet_field, value)
309
+ def facet_value_to_fq_string(facet_field, value, use_local_params: true)
254
310
  facet_config = blacklight_config.facet_fields[facet_field]
255
311
 
256
312
  solr_field = facet_config.field if facet_config && !facet_config.query
257
313
  solr_field ||= facet_field
258
314
 
259
315
  local_params = []
260
- local_params << "tag=#{facet_config.tag}" if facet_config && facet_config.tag
261
316
 
262
- prefix = "{!#{local_params.join(' ')}}" unless local_params.empty?
317
+ if use_local_params
318
+ local_params << "tag=#{facet_config.tag}" if facet_config && facet_config.tag
319
+ end
263
320
 
264
321
  if facet_config && facet_config.query
265
322
  if facet_config.query[value]
@@ -269,12 +326,34 @@ module Blacklight::Solr
269
326
  '-*:*'
270
327
  end
271
328
  elsif value.is_a?(Range)
329
+ prefix = "{!#{local_params.join(' ')}}" unless local_params.empty?
272
330
  "#{prefix}#{solr_field}:[#{value.first} TO #{value.last}]"
273
331
  else
274
332
  "{!term f=#{solr_field}#{(' ' + local_params.join(' ')) unless local_params.empty?}}#{convert_to_term_value(value)}"
275
333
  end
276
334
  end
277
335
 
336
+ def facet_inclusive_value_to_fq_string(facet_field, values)
337
+ return if values.blank?
338
+
339
+ return facet_value_to_fq_string(facet_field, values.first) if values.length == 1
340
+
341
+ facet_config = blacklight_config.facet_fields[facet_field]
342
+
343
+ local_params = []
344
+ local_params << "tag=#{facet_config.tag}" if facet_config && facet_config.tag
345
+
346
+ solr_filters = values.each_with_object({}).with_index do |(v, h), index|
347
+ h["f_inclusive.#{facet_field}.#{index}"] = facet_value_to_fq_string(facet_field, v, use_local_params: false)
348
+ end
349
+
350
+ filter_query = solr_filters.keys.map do |k|
351
+ "{!query v=$#{k}}"
352
+ end.join(' OR ')
353
+
354
+ ["{!lucene#{(' ' + local_params.join(' ')) unless local_params.empty?}}#{filter_query}", solr_filters]
355
+ end
356
+
278
357
  def convert_to_term_value(value)
279
358
  if value.is_a?(DateTime) || value.is_a?(Time)
280
359
  value.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
@@ -322,20 +401,5 @@ module Blacklight::Solr
322
401
  # params!
323
402
  solr_parameters["spellcheck.q"] ||= search_state.query_param
324
403
  end
325
-
326
- def add_multifield_search_query(solr_parameters)
327
- q = search_state.query_param
328
- solr_parameters[:q] = if q.values.any?(&:blank?)
329
- # if any field parameters are empty, exclude _all_ results
330
- "{!lucene}NOT *:*"
331
- else
332
- "{!lucene}" + q.map do |field, values|
333
- "#{field}:(#{Array(values).map { |x| solr_param_quote(x) }.join(' OR ')})"
334
- end.join(" AND ")
335
- end
336
-
337
- solr_parameters[:defType] = 'lucene'
338
- solr_parameters[:spellcheck] = 'false'
339
- end
340
404
  end
341
405
  end