blacklight 7.14.1 → 7.15.0

Sign up to get free protection for your applications and to get access to all the features.
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