blacklight 5.7.2 → 5.8.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 (85) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -3
  3. data/Gemfile +1 -4
  4. data/VERSION +1 -1
  5. data/app/assets/stylesheets/blacklight/_catalog.css.scss +38 -1
  6. data/app/assets/stylesheets/blacklight/_facets.css.scss +10 -0
  7. data/app/assets/stylesheets/blacklight/_layout.css.scss +6 -0
  8. data/app/controllers/bookmarks_controller.rb +3 -163
  9. data/app/helpers/blacklight/blacklight_helper_behavior.rb +18 -186
  10. data/app/helpers/blacklight/catalog_helper_behavior.rb +36 -2
  11. data/app/helpers/blacklight/component_helper_behavior.rb +77 -0
  12. data/app/helpers/blacklight/configuration_helper_behavior.rb +30 -21
  13. data/app/helpers/blacklight/render_partials_helper.rb +185 -0
  14. data/app/helpers/blacklight/url_helper_behavior.rb +24 -3
  15. data/app/helpers/component_helper.rb +3 -0
  16. data/app/views/_user_util_links.html.erb +2 -15
  17. data/app/views/blacklight/nav/_bookmark.html.erb +4 -0
  18. data/app/views/blacklight/nav/_saved_searches.html.erb +1 -0
  19. data/app/views/blacklight/nav/_search_history.html.erb +1 -0
  20. data/app/views/bookmarks/_tools.html.erb +6 -9
  21. data/app/views/bookmarks/index.html.erb +1 -1
  22. data/app/views/catalog/_bookmark_control.html.erb +8 -8
  23. data/app/views/catalog/_constraints_element.html.erb +1 -1
  24. data/app/views/catalog/_document_action.html.erb +4 -0
  25. data/app/views/catalog/_email_form.html.erb +1 -1
  26. data/app/views/catalog/_facet_limit.html.erb +1 -1
  27. data/app/views/catalog/_index_header_default.html.erb +5 -6
  28. data/app/views/catalog/_per_page_widget.html.erb +3 -1
  29. data/app/views/catalog/_results_pagination.html.erb +1 -1
  30. data/app/views/catalog/_search_form.html.erb +5 -8
  31. data/app/views/catalog/_show_more_like_this.html.erb +2 -2
  32. data/app/views/catalog/_show_tools.html.erb +5 -34
  33. data/app/views/catalog/_sms_form.html.erb +1 -1
  34. data/app/views/catalog/_sort_and_per_page.html.erb +2 -6
  35. data/app/views/catalog/_sort_widget.html.erb +1 -1
  36. data/app/views/catalog/_zero_results.html.erb +2 -2
  37. data/app/views/catalog/citation.js.erb +1 -1
  38. data/app/views/catalog/email_sent.html.erb +2 -9
  39. data/app/views/catalog/email_success.html.erb +9 -0
  40. data/app/views/catalog/sms_sent.html.erb +2 -9
  41. data/app/views/catalog/sms_success.html.erb +9 -0
  42. data/app/views/kaminari/blacklight/_gap.html.erb +1 -1
  43. data/app/views/kaminari/blacklight/_page.html.erb +5 -1
  44. data/app/views/shared/_header_navbar.html.erb +1 -1
  45. data/config/locales/blacklight.en.yml +1 -1
  46. data/config/locales/blacklight.es.yml +1 -1
  47. data/config/locales/blacklight.fr.yml +1 -1
  48. data/config/locales/blacklight.pt-BR.yml +1 -1
  49. data/lib/blacklight.rb +3 -0
  50. data/lib/blacklight/base.rb +0 -1
  51. data/lib/blacklight/bookmarks.rb +135 -0
  52. data/lib/blacklight/catalog.rb +58 -77
  53. data/lib/blacklight/catalog/component_configuration.rb +99 -0
  54. data/lib/blacklight/configuration.rb +82 -4
  55. data/lib/blacklight/configuration/tool_config.rb +4 -0
  56. data/lib/blacklight/controller.rb +5 -1
  57. data/lib/blacklight/document_presenter.rb +17 -8
  58. data/lib/blacklight/request_builders.rb +136 -4
  59. data/lib/blacklight/routes.rb +5 -0
  60. data/lib/blacklight/solr_helper.rb +90 -208
  61. data/lib/blacklight/solr_repository.rb +69 -0
  62. data/lib/blacklight/token_based_user.rb +58 -0
  63. data/lib/blacklight/utils.rb +13 -1
  64. data/lib/generators/blacklight/install_generator.rb +6 -7
  65. data/spec/controllers/alternate_controller_spec.rb +19 -0
  66. data/spec/controllers/catalog_controller_spec.rb +89 -4
  67. data/spec/features/alternate_controller_spec.rb +0 -1
  68. data/spec/features/bookmarks_spec.rb +31 -6
  69. data/spec/features/search_results_spec.rb +11 -0
  70. data/spec/features/search_spec.rb +5 -0
  71. data/spec/helpers/blacklight_helper_spec.rb +49 -8
  72. data/spec/helpers/catalog_helper_spec.rb +56 -8
  73. data/spec/helpers/configuration_helper_spec.rb +5 -5
  74. data/spec/helpers/url_helper_spec.rb +15 -8
  75. data/spec/lib/blacklight/catalog/component_configuration_spec.rb +29 -0
  76. data/spec/lib/blacklight/configuration_spec.rb +15 -0
  77. data/spec/lib/blacklight/solr_helper_spec.rb +44 -104
  78. data/spec/lib/blacklight/solr_repository_spec.rb +113 -0
  79. data/spec/lib/utils_spec.rb +27 -0
  80. data/spec/views/_user_util_links.html.erb_spec.rb +6 -3
  81. data/spec/views/catalog/_show_sidebar.erb_spec.rb +8 -2
  82. data/spec/views/catalog/_show_tools.html.erb_spec.rb +82 -0
  83. data/spec/views/catalog/_sort_and_per_page.html.erb_spec.rb +15 -1
  84. data/tasks/blacklight.rake +25 -1
  85. metadata +24 -2
@@ -5,6 +5,7 @@ module Blacklight
5
5
  class Configuration < OpenStructWithHashAccess
6
6
 
7
7
  require 'blacklight/configuration/view_config'
8
+ require 'blacklight/configuration/tool_config'
8
9
  # XXX this isn't very pretty, but it works.
9
10
  require 'blacklight/configuration/fields'
10
11
  require 'blacklight/configuration/solr_field'
@@ -31,6 +32,7 @@ module Blacklight
31
32
  :default_solr_params => {},
32
33
  # the model to load solr response documents into; set below in #initialize_default_values
33
34
  :solr_document_model => nil,
35
+ :solr_response_model => nil,
34
36
  # The solr rqeuest handler to use when requesting only a single document
35
37
  :document_solr_request_handler => 'document',
36
38
  # THe path to send single document requests to solr
@@ -56,6 +58,8 @@ module Blacklight
56
58
  :display_type_field => 'format',
57
59
  # partials to render for each document(see #render_document_partials)
58
60
  :partials => [:index_header, :thumbnail, :index],
61
+ :document_actions => NestedOpenStructWithHashAccess.new(ToolConfig),
62
+ :collection_actions => NestedOpenStructWithHashAccess.new(ToolConfig),
59
63
  # what field, if any, to use to render grouped results
60
64
  :group => false,
61
65
  # additional response formats for search results
@@ -69,8 +73,10 @@ module Blacklight
69
73
  # current controller.
70
74
  route: nil,
71
75
  # partials to render for each document(see #render_document_partials)
72
- partials: [:show_header, :show]
76
+ partials: [:show_header, :show],
77
+ document_actions: NestedOpenStructWithHashAccess.new(ToolConfig)
73
78
  ),
79
+ :navbar => OpenStructWithHashAccess.new(partials: { }),
74
80
  # Configurations for specific types of index views
75
81
  :view => NestedOpenStructWithHashAccess.new(ViewConfig, 'list'),
76
82
  # Maxiumum number of spelling suggestions to offer
@@ -137,6 +143,10 @@ module Blacklight
137
143
  super || SolrDocument
138
144
  end
139
145
 
146
+ def solr_response_model
147
+ super || Blacklight::SolrResponse
148
+ end
149
+
140
150
  ##
141
151
  # DSL helper
142
152
  def configure
@@ -226,10 +236,16 @@ module Blacklight
226
236
  # Provide a 'deep copy' of Blacklight::Configuration that can be modifyed without affecting
227
237
  # the original Blacklight::Configuration instance.
228
238
  #
229
- def deep_copy
230
- Marshal.load(Marshal.dump(self))
239
+ # The Rails 3.x version only copies hashes, and ignores arrays and similar structures
240
+ if ::Rails.version < "4.0"
241
+ def deep_copy
242
+ Marshal.load(Marshal.dump(self))
243
+ end
244
+ alias_method :inheritable_copy, :deep_copy
245
+ else
246
+ alias_method :deep_copy, :deep_dup
247
+ alias_method :inheritable_copy, :deep_dup
231
248
  end
232
- alias_method :inheritable_copy, :deep_copy
233
249
 
234
250
  ##
235
251
  # Get a view configuration for the given view type
@@ -241,5 +257,67 @@ module Blacklight
241
257
  self.index.merge view.fetch(view_type, {})
242
258
  end
243
259
  end
260
+
261
+ ##
262
+ # Add a partial to the tools when rendering a document.
263
+ # @param partial [String] the name of the document partial
264
+ # @param opts [Hash]
265
+ # @option opts [Symbol,Proc] :if render this action if the method identified by the symbol or the proc evaluates to true.
266
+ # The proc will receive the action configuration and the document or documents for the action.
267
+ # @option opts [Symbol,Proc] :unless render this action unless the method identified by the symbol or the proc evaluates to true
268
+ # The proc will receive the action configuration and the document or documents for the action.
269
+ def add_show_tools_partial(name, opts = {})
270
+ opts[:partial] ||= 'document_action'
271
+ add_action(show.document_actions, name, opts)
272
+ end
273
+
274
+ ##
275
+ # Add a tool for the search result list itself
276
+ # @param partial [String] the name of the document partial
277
+ # @param opts [Hash]
278
+ # @option opts [Symbol,Proc] :if render this action if the method identified by the symbol or the proc evaluates to true.
279
+ # The proc will receive the action configuration and the document or documents for the action.
280
+ # @option opts [Symbol,Proc] :unless render this action unless the method identified by the symbol or the proc evaluates to true
281
+ # The proc will receive the action configuration and the document or documents for the action.
282
+ def add_results_collection_tool(name, opts = {})
283
+ add_action(index.collection_actions, name, opts)
284
+ end
285
+
286
+ ##
287
+ # Add a partial to the tools for each document in the search results.
288
+ # @param partial [String] the name of the document partial
289
+ # @param opts [Hash]
290
+ # @option opts [Symbol,Proc] :if render this action if the method identified by the symbol or the proc evaluates to true.
291
+ # The proc will receive the action configuration and the document or documents for the action.
292
+ # @option opts [Symbol,Proc] :unless render this action unless the method identified by the symbol or the proc evaluates to true
293
+ # The proc will receive the action configuration and the document or documents for the action.
294
+ def add_results_document_tool(name, opts = {})
295
+ add_action(index.document_actions, name, opts)
296
+ end
297
+
298
+ ##
299
+ # Add a partial to the header navbar
300
+ # @param partial [String] the name of the document partial
301
+ # @param opts [Hash]
302
+ # @option opts [Symbol,Proc] :if render this action if the method identified by the symbol or the proc evaluates to true.
303
+ # The proc will receive the action configuration and the document or documents for the action.
304
+ # @option opts [Symbol,Proc] :unless render this action unless the method identified by the symbol or the proc evaluates to true
305
+ # The proc will receive the action configuration and the document or documents for the action.
306
+ def add_nav_action name, opts = {}
307
+ add_action(navbar.partials, name, opts)
308
+ end
309
+
310
+ private
311
+
312
+ def add_action config_hash, name, opts
313
+ config = Blacklight::Configuration::ToolConfig.new opts
314
+ config.name = name
315
+
316
+ if block_given?
317
+ yield config
318
+ end
319
+
320
+ config_hash[name] = config
321
+ end
244
322
  end
245
323
  end
@@ -0,0 +1,4 @@
1
+ module Blacklight
2
+ class Configuration::ToolConfig < OpenStructWithHashAccess
3
+ end
4
+ end
@@ -23,7 +23,7 @@ module Blacklight::Controller
23
23
  # extra head content
24
24
  helper_method :has_user_authentication_provider?
25
25
  helper_method :blacklight_config
26
- helper_method :search_action_url, :search_action_path
26
+ helper_method :search_action_url, :search_action_path, :search_facet_url
27
27
 
28
28
 
29
29
  # This callback runs when a user first logs in
@@ -59,6 +59,10 @@ module Blacklight::Controller
59
59
  search_action_url *args
60
60
  end
61
61
 
62
+ def search_facet_url options = {}
63
+ url_for params.merge(action: "facet").merge(options).except(:page)
64
+ end
65
+
62
66
  # Returns a list of Searches from the ids in the user's history.
63
67
  def searches_from_history
64
68
  session[:history].blank? ? Search.none : Search.where(:id => session[:history]).order("updated_at desc")
@@ -2,6 +2,7 @@ module Blacklight
2
2
  class DocumentPresenter
3
3
  include ActionView::Helpers::OutputSafetyHelper
4
4
  include ActionView::Helpers::TagHelper
5
+ extend Deprecation
5
6
 
6
7
  # @param [SolrDocument] document
7
8
  # @param [ActionController::Base] controller scope for linking and generating urls
@@ -55,17 +56,25 @@ module Blacklight
55
56
  ##
56
57
  # Render the document index heading
57
58
  #
58
- # @param [Hash] opts
59
+ # @param [Hash] opts (Deprecated)
59
60
  # @option opts [Symbol] :label Render the given field from the document
60
61
  # @option opts [Proc] :label Evaluate the given proc
61
62
  # @option opts [String] :label Render the given string
62
- def render_document_index_label opts = {}
63
- label = nil
64
- label ||= @document.get(opts[:label], :sep => nil) if opts[:label].instance_of? Symbol
65
- label ||= opts[:label].call(@document, opts) if opts[:label].instance_of? Proc
66
- label ||= opts[:label] if opts[:label].is_a? String
67
- label ||= @document.id
68
- render_field_value label
63
+ # @param [Symbol, Proc, String] field Render the given field or evaluate the proc or render the given string
64
+ def render_document_index_label field, opts ={}
65
+ if field.kind_of? Hash
66
+ Deprecation.warn DocumentPresenter, "Calling render_document_index_label with a hash is deprecated"
67
+ field = field[:label]
68
+ end
69
+ label = case field
70
+ when Symbol
71
+ @document.get(field, :sep => nil)
72
+ when Proc
73
+ field.call(@document, opts)
74
+ when String
75
+ field
76
+ end
77
+ render_field_value label || @document.id
69
78
  end
70
79
 
71
80
  ##
@@ -6,6 +6,8 @@ module Blacklight
6
6
  #
7
7
  module RequestBuilders
8
8
  extend ActiveSupport::Concern
9
+ extend Deprecation
10
+ self.deprecation_horizon = 'blacklight 6.0'
9
11
 
10
12
  included do
11
13
  # We want to install a class-level place to keep
@@ -21,7 +23,11 @@ module Blacklight
21
23
  # CatalogController.include ModuleDefiningNewMethod
22
24
  # CatalogController.solr_search_params_logic += [:new_method]
23
25
  # CatalogController.solr_search_params_logic.delete(:we_dont_want)
24
- self.solr_search_params_logic = [:default_solr_parameters , :add_query_to_solr, :add_facet_fq_to_solr, :add_facetting_to_solr, :add_solr_fields_to_query, :add_paging_to_solr, :add_sorting_to_solr, :add_group_config_to_solr ]
26
+ self.solr_search_params_logic = [:default_solr_parameters, :add_query_to_solr, :add_facet_fq_to_solr, :add_facetting_to_solr, :add_solr_fields_to_query, :add_paging_to_solr, :add_sorting_to_solr, :add_group_config_to_solr ]
27
+
28
+ if self.respond_to?(:helper_method)
29
+ helper_method(:facet_limit_for)
30
+ end
25
31
  end
26
32
 
27
33
  # @returns a params hash for searching solr.
@@ -47,6 +53,86 @@ module Blacklight
47
53
  end
48
54
  end
49
55
 
56
+ ##
57
+ # Retrieve the results for a list of document ids
58
+ def solr_document_ids_params(ids = [])
59
+ solr_documents_by_field_values_params blacklight_config.solr_document_model.unique_key, ids
60
+ end
61
+
62
+ ##
63
+ # Retrieve the results for a list of document ids
64
+ # @deprecated
65
+ def solr_documents_by_field_values_params(field, values)
66
+ q = if Array(values).empty?
67
+ "{!lucene}NOT *:*"
68
+ else
69
+ "{!lucene}#{field}:(#{ Array(values).map { |x| solr_param_quote(x) }.join(" OR ")})"
70
+ end
71
+
72
+ { q: q, spellcheck: 'false', fl: "*" }
73
+ end
74
+
75
+ ##
76
+ # Retrieve a facet's paginated values.
77
+ def solr_facet_params(facet_field, user_params=params || {}, extra_controller_params={})
78
+ input = user_params.deep_merge(extra_controller_params)
79
+ facet_config = blacklight_config.facet_fields[facet_field]
80
+
81
+ solr_params = {}
82
+
83
+ # Now override with our specific things for fetching facet values
84
+ solr_params[:"facet.field"] = with_ex_local_param((facet_config.ex if facet_config.respond_to?(:ex)), facet_field)
85
+
86
+ limit = if respond_to?(:facet_list_limit)
87
+ facet_list_limit.to_s.to_i
88
+ elsif solr_params["facet.limit"]
89
+ solr_params["facet.limit"].to_i
90
+ else
91
+ 20
92
+ end
93
+
94
+ # Need to set as f.facet_field.facet.* to make sure we
95
+ # override any field-specific default in the solr request handler.
96
+ solr_params[:"f.#{facet_field}.facet.limit"] = limit + 1
97
+ solr_params[:"f.#{facet_field}.facet.offset"] = ( input.fetch(Blacklight::Solr::FacetPaginator.request_keys[:page] , 1).to_i - 1 ) * ( limit )
98
+ solr_params[:"f.#{facet_field}.facet.sort"] = input[ Blacklight::Solr::FacetPaginator.request_keys[:sort] ] if input[ Blacklight::Solr::FacetPaginator.request_keys[:sort] ]
99
+ solr_params[:rows] = 0
100
+
101
+ solr_params
102
+ end
103
+
104
+ ##
105
+ # Opensearch autocomplete parameters for plucking a field's value from the results
106
+ def solr_opensearch_params(field=nil)
107
+ if field.nil?
108
+ Deprecation.warn(Blacklight::RequestBuilders, "Calling Blacklight::RequestBuilders#solr_opensearch_params without a field name is deprecated and will be required in Blacklight 6.0.")
109
+ end
110
+
111
+ solr_params = {}
112
+ solr_params[:rows] ||= 10
113
+ solr_params[:fl] = field || blacklight_config.view_config('opensearch').title_field
114
+ solr_params
115
+ end
116
+
117
+ ##
118
+ # Pagination parameters for selecting the previous and next documents
119
+ # out of a result set.
120
+ def previous_and_next_document_params(index, window = 1)
121
+ solr_params = {}
122
+
123
+ if index > 0
124
+ solr_params[:start] = index - window # get one before
125
+ solr_params[:rows] = 2*window + 1 # and one after
126
+ else
127
+ solr_params[:start] = 0 # there is no previous doc
128
+ solr_params[:rows] = 2*window # but there should be one after
129
+ end
130
+
131
+ solr_params[:fl] = '*'
132
+ solr_params[:facet] = false
133
+ solr_params
134
+ end
135
+
50
136
  ####
51
137
  # Start with general defaults from BL config. Need to use custom
52
138
  # merge to dup values, to avoid later mutating the original by mistake.
@@ -55,7 +141,7 @@ module Blacklight
55
141
  solr_parameters[key] = value.dup rescue value
56
142
  end
57
143
  end
58
-
144
+
59
145
  ##
60
146
  # Take the user-entered query, and put it in the solr params,
61
147
  # including config's "search field" params for current search field.
@@ -71,7 +157,7 @@ module Blacklight
71
157
  # rspec'd.
72
158
  solr_parameters[:qt] = user_parameters[:qt] if user_parameters[:qt]
73
159
 
74
- search_field_def = search_field_def_for_key(user_parameters[:search_field])
160
+ search_field_def = blacklight_config.search_fields[user_parameters[:search_field]]
75
161
  if (search_field_def)
76
162
  solr_parameters[:qt] = search_field_def.qt
77
163
  solr_parameters.merge!( search_field_def.solr_parameters) if search_field_def.solr_parameters
@@ -253,8 +339,55 @@ module Blacklight
253
339
  end
254
340
  end
255
341
 
342
+
343
+ DEFAULT_FACET_LIMIT = 10
344
+
345
+ # Look up facet limit for given facet_field. Will look at config, and
346
+ # if config is 'true' will look up from Solr @response if available. If
347
+ # no limit is avaialble, returns nil. Used from #solr_search_params
348
+ # to supply f.fieldname.facet.limit values in solr request (no @response
349
+ # available), and used in display (with @response available) to create
350
+ # a facet paginator with the right limit.
351
+ def facet_limit_for(facet_field)
352
+ facet = blacklight_config.facet_fields[facet_field]
353
+ return if facet.blank?
354
+
355
+ if facet.limit and @response and @response.facet_by_field_name(facet_field)
356
+ limit = @response.facet_by_field_name(facet_field).limit
357
+
358
+ if limit.nil? # we didn't get or a set a limit, so infer one.
359
+ facet.limit if facet.limit != true
360
+ elsif limit == -1 # limit -1 is solr-speak for unlimited
361
+ nil
362
+ else
363
+ limit.to_i - 1 # we added 1 to find out if we needed to paginate
364
+ end
365
+ elsif facet.limit
366
+ facet.limit == true ? DEFAULT_FACET_LIMIT : facet.limit
367
+ end
368
+ end
369
+
370
+ ##
371
+ # A helper method used for generating solr LocalParams, put quotes
372
+ # around the term unless it's a bare-word. Escape internal quotes
373
+ # if needed.
374
+ def solr_param_quote(val, options = {})
375
+ options[:quote] ||= '"'
376
+ unless val =~ /^[a-zA-Z0-9$_\-\^]+$/
377
+ val = options[:quote] +
378
+ # Yes, we need crazy escaping here, to deal with regexp esc too!
379
+ val.gsub("'", "\\\\\'").gsub('"', "\\\\\"") +
380
+ options[:quote]
381
+ end
382
+ return val
383
+ end
384
+
256
385
  private
257
386
 
387
+ def should_add_to_solr field_name, field
388
+ field.include_in_request || (field.include_in_request.nil? && blacklight_config.add_field_configuration_to_solr_request)
389
+ end
390
+
258
391
  ##
259
392
  # Convert a facet/value pair into a solr fq parameter
260
393
  def facet_value_to_fq_string(facet_field, value)
@@ -284,7 +417,6 @@ module Blacklight
284
417
  "{!raw f=#{facet_field}#{(" " + local_params.join(" ")) unless local_params.empty?}}#{value}"
285
418
  end
286
419
 
287
-
288
420
  end
289
421
  end
290
422
  end
@@ -61,6 +61,11 @@ module Blacklight
61
61
  def bookmarks(_)
62
62
  add_routes do |options|
63
63
  delete "bookmarks/clear", :to => "bookmarks#clear", :as => "clear_bookmarks"
64
+ get "bookmarks/email", :as => "email_bookmarks"
65
+ post "bookmarks/email"
66
+ get "bookmarks/sms", :as => "sms_bookmarks"
67
+ post "bookmarks/sms"
68
+ get "bookmarks/citation", :as => "citation_bookmarks"
64
69
  resources :bookmarks
65
70
  end
66
71
  end
@@ -47,65 +47,30 @@
47
47
  module Blacklight::SolrHelper
48
48
  extend ActiveSupport::Concern
49
49
  extend Deprecation
50
- include Blacklight::SearchFields
51
- include Blacklight::Facet
52
- include ActiveSupport::Benchmarkable
50
+ self.deprecation_horizon = 'blacklight 6.0'
53
51
 
54
- included do
55
- if self.respond_to?(:helper_method)
56
- helper_method(:facet_limit_for)
57
- end
58
-
59
- include Blacklight::RequestBuilders
60
-
61
- end
52
+ include Blacklight::RequestBuilders
62
53
 
63
54
  ##
64
55
  # Execute a solr query
65
- # @see [RSolr::Client#send_and_receive]
66
- # @overload find(solr_path, params)
67
- # Execute a solr query at the given path with the parameters
68
- # @param [String] solr path (defaults to blacklight_config.solr_path)
69
- # @param [Hash] parameters for RSolr::Client#send_and_receive
70
- # @overload find(params)
71
- # @param [Hash] parameters for RSolr::Client#send_and_receive
56
+ # @see [Blacklight::SolrRepository#send_and_receive]
72
57
  # @return [Blacklight::SolrResponse] the solr response object
73
- def find(*args)
74
- # In later versions of Rails, the #benchmark method can do timing
75
- # better for us.
76
- benchmark("Solr fetch", level: :debug) do
77
- solr_params = args.extract_options!
78
- path = args.first || blacklight_config.solr_path
79
- solr_params[:qt] ||= blacklight_config.qt
80
- # delete these parameters, otherwise rsolr will pass them through.
81
- key = blacklight_config.http_method == :post ? :data : :params
82
- res = blacklight_solr.send_and_receive(path, {key=>solr_params.to_hash, method:blacklight_config.http_method})
83
-
84
- solr_response = Blacklight::SolrResponse.new(res, solr_params, solr_document_model: blacklight_config.solr_document_model)
85
-
86
- Rails.logger.debug("Solr query: #{solr_params.inspect}")
87
- Rails.logger.debug("Solr response: #{solr_response.inspect}") if defined?(::BLACKLIGHT_VERBOSE_LOGGING) and ::BLACKLIGHT_VERBOSE_LOGGING
88
- solr_response
89
- end
90
- rescue Errno::ECONNREFUSED => e
91
- raise Blacklight::Exceptions::ECONNREFUSED.new("Unable to connect to Solr instance using #{blacklight_solr.inspect}")
58
+ def find *args
59
+ solr_params = args.extract_options!
60
+ path = args.first || blacklight_config.solr_path
61
+
62
+ solr_params[:qt] ||= blacklight_config.qt
63
+
64
+ solr_repository.send_and_receive path, solr_params
92
65
  end
93
-
94
-
95
- # A helper method used for generating solr LocalParams, put quotes
96
- # around the term unless it's a bare-word. Escape internal quotes
97
- # if needed.
98
- def solr_param_quote(val, options = {})
99
- options[:quote] ||= '"'
100
- unless val =~ /^[a-zA-Z0-9$_\-\^]+$/
101
- val = options[:quote] +
102
- # Yes, we need crazy escaping here, to deal with regexp esc too!
103
- val.gsub("'", "\\\\\'").gsub('"', "\\\\\"") +
104
- options[:quote]
105
- end
106
- return val
66
+ deprecation_deprecate :find
67
+
68
+ # returns a params hash for finding a single solr document (CatalogController #show action)
69
+ def solr_doc_params(id=nil)
70
+ default_solr_doc_params(id)
107
71
  end
108
-
72
+ deprecation_deprecate :solr_doc_params
73
+
109
74
  # a solr query method
110
75
  # given a user query, return a solr response containing both result docs and facets
111
76
  # - mixes in the Blacklight::Solr::SpellingSuggestions module
@@ -132,111 +97,66 @@ module Blacklight::SolrHelper
132
97
  def query_solr(user_params = params || {}, extra_controller_params = {})
133
98
  solr_params = self.solr_search_params(user_params).merge(extra_controller_params)
134
99
 
135
- find(solr_params)
100
+ solr_repository.search(solr_params)
136
101
  end
137
-
138
- # returns a params hash for finding a single solr document (CatalogController #show action)
139
- # If the id arg is nil, then the value is fetched from params[:id]
140
- # This method is primary called by the get_solr_response_for_doc_id method.
141
- def solr_doc_params(id=nil)
142
- id ||= params[:id]
143
102
 
144
- # add our document id to the document_unique_id_param query parameter
145
- p = blacklight_config.default_document_solr_params.merge({
146
- # this assumes the request handler will map the unique id param
147
- # to the unique key field using either solr local params, the
148
- # real-time get handler, etc.
149
- blacklight_config.document_unique_id_param => id
150
- })
151
-
152
- p[:qt] ||= blacklight_config.document_solr_request_handler
153
-
154
- p
155
- end
156
-
157
103
  # a solr query method
158
104
  # retrieve a solr document, given the doc id
159
105
  # @return [Blacklight::SolrResponse, Blacklight::SolrDocument] the solr response object and the first document
160
106
  def get_solr_response_for_doc_id(id=nil, extra_controller_params={})
161
- solr_params = solr_doc_params(id).merge(extra_controller_params)
162
- solr_response = find(blacklight_config.document_solr_path, solr_params)
163
- raise Blacklight::Exceptions::InvalidSolrID.new if solr_response.documents.empty?
107
+ if id.nil?
108
+ Deprecation.warn Blacklight::SolrHelper, "Calling #get_solr_response_for_doc_id without an explicit id argument is deprecated"
109
+ id ||= params[:id]
110
+ end
111
+
112
+ old_solr_doc_params = Deprecation.silence(Blacklight::SolrHelper) do
113
+ solr_doc_params(id)
114
+ end
115
+
116
+ if default_solr_doc_params(id) != old_solr_doc_params
117
+ Deprecation.warn Blacklight::SolrHelper, "The #solr_doc_params method is deprecated. Instead, you should provide a custom SolrRepository implementation for the additional behavior you're offering"
118
+ extra_controller_params = extra_controller_params.merge(old_solr_doc_params)
119
+ end
120
+
121
+ solr_response = solr_repository.find id, extra_controller_params
164
122
  [solr_response, solr_response.documents.first]
165
123
  end
166
124
 
167
- def get_solr_response_for_document_ids(ids=[], extra_solr_params = {})
168
- get_solr_response_for_field_values(blacklight_config.solr_document_model.unique_key, ids, extra_solr_params)
169
- end
170
-
171
- # given a field name and array of values, get the matching SOLR documents
172
- # @return [Blacklight::SolrResponse, Array<Blacklight::SolrDocument>] the solr response object and a list of solr documents
173
- def get_solr_response_for_field_values(field, values, extra_solr_params = {})
174
- q = if Array(values).empty?
175
- "NOT *:*"
125
+ ##
126
+ # Retrieve a set of documents by id
127
+ # @overload get_solr_response_for_document_ids(ids, extra_controller_params)
128
+ # @overload get_solr_response_for_document_ids(ids, user_params, extra_controller_params)
129
+ def get_solr_response_for_document_ids(ids=[], *args)
130
+ # user_params = params || {}, extra_controller_params = {}
131
+ if args.length == 1
132
+ user_params = params
133
+ extra_controller_params = args.first || {}
176
134
  else
177
- "#{field}:(#{ Array(values).map { |x| solr_param_quote(x)}.join(" OR ")})"
135
+ user_params, extra_controller_params = args
136
+ user_params ||= params
137
+ extra_controller_params ||= {}
178
138
  end
179
139
 
180
- solr_params = {
181
- :defType => "lucene", # need boolean for OR
182
- :q => q,
183
- # not sure why fl * is neccesary, why isn't default solr_search_params
184
- # sufficient, like it is for any other search results solr request?
185
- # But tests fail without this. I think because some functionality requires
186
- # this to actually get solr_doc_params, not solr_search_params. Confused
187
- # semantics again.
188
- :fl => "*",
189
- :facet => 'false',
190
- :spellcheck => 'false'
191
- }.merge(extra_solr_params)
192
-
193
- solr_response = find(self.solr_search_params().merge(solr_params) )
140
+ solr_response = query_solr(user_params, extra_controller_params.merge(solr_document_ids_params(ids)))
141
+
194
142
  [solr_response, solr_response.documents]
195
143
  end
196
-
197
- # returns a params hash for a single facet field solr query.
198
- # used primary by the get_facet_pagination method.
199
- # Looks up Facet Paginator request params from current request
200
- # params to figure out sort and offset.
201
- # Default limit for facet list can be specified by defining a controller
202
- # method facet_list_limit, otherwise 20.
203
- def solr_facet_params(facet_field, user_params=params || {}, extra_controller_params={})
204
- input = user_params.deep_merge(extra_controller_params)
205
- facet_config = blacklight_config.facet_fields[facet_field]
206
-
207
- # First start with a standard solr search params calculations,
208
- # for any search context in our request params.
209
- solr_params = solr_search_params(user_params).merge(extra_controller_params)
210
-
211
- # Now override with our specific things for fetching facet values
212
- solr_params[:"facet.field"] = with_ex_local_param((facet_config.ex if facet_config.respond_to?(:ex)), facet_field)
213
-
214
- limit =
215
- if respond_to?(:facet_list_limit)
216
- facet_list_limit.to_s.to_i
217
- elsif solr_params["facet.limit"]
218
- solr_params["facet.limit"].to_i
219
- else
220
- 20
221
- end
222
-
223
- # Need to set as f.facet_field.facet.* to make sure we
224
- # override any field-specific default in the solr request handler.
225
- solr_params[:"f.#{facet_field}.facet.limit"] = limit + 1
226
- solr_params[:"f.#{facet_field}.facet.offset"] = ( input.fetch(Blacklight::Solr::FacetPaginator.request_keys[:page] , 1).to_i - 1 ) * ( limit )
227
- solr_params[:"f.#{facet_field}.facet.sort"] = input[ Blacklight::Solr::FacetPaginator.request_keys[:sort] ] if input[ Blacklight::Solr::FacetPaginator.request_keys[:sort] ]
228
- solr_params[:rows] = 0
229
-
230
- return solr_params
144
+
145
+ # given a field name and array of values, get the matching SOLR documents
146
+ # @return [Blacklight::SolrResponse, Array<Blacklight::SolrDocument>] the solr response object and a list of solr documents
147
+ def get_solr_response_for_field_values(field, values, extra_controller_params = {})
148
+ solr_response = query_solr(params, extra_controller_params.merge(solr_documents_by_field_values_params(field, values)))
149
+
150
+ [solr_response, solr_response.documents]
231
151
  end
232
-
152
+ deprecation_deprecate :get_solr_response_for_field_values
153
+
233
154
  ##
234
155
  # Get the solr response when retrieving only a single facet field
235
156
  # @return [Blacklight::SolrResponse] the solr response
236
157
  def get_facet_field_response(facet_field, user_params = params || {}, extra_controller_params = {})
237
158
  solr_params = solr_facet_params(facet_field, user_params, extra_controller_params)
238
- # Make the solr call
239
- find(solr_params)
159
+ query_solr(user_params, extra_controller_params.merge(solr_facet_params(facet_field, user_params, extra_controller_params)))
240
160
  end
241
161
 
242
162
  # a solr query method
@@ -251,28 +171,28 @@ module Blacklight::SolrHelper
251
171
  # Actually create the paginator!
252
172
  # NOTE: The sniffing of the proper sort from the solr response is not
253
173
  # currently tested for, tricky to figure out how to test, since the
254
- # default setup we test against doesn't use this feature.
255
- return Blacklight::Solr::FacetPaginator.new(response.facets.first.items,
256
- :offset => response.params[:"f.#{facet_field}.facet.offset"],
174
+ # default setup we test against doesn't use this feature.
175
+ return Blacklight::Solr::FacetPaginator.new(response.facets.first.items,
176
+ :offset => response.params[:"f.#{facet_field}.facet.offset"],
257
177
  :limit => limit,
258
178
  :sort => response.params[:"f.#{facet_field}.facet.sort"] || response.params["facet.sort"]
259
179
  )
260
180
  end
261
181
  deprecation_deprecate :get_facet_pagination
262
-
182
+
263
183
  # a solr query method
264
- # this is used when selecting a search result: we have a query and a
184
+ # this is used when selecting a search result: we have a query and a
265
185
  # position in the search results and possibly some facets
266
186
  # Pass in an index where 1 is the first document in the list, and
267
- # the Blacklight app-level request params that define the search.
187
+ # the Blacklight app-level request params that define the search.
268
188
  # @return [Blacklight::SolrDocument, nil] the found document or nil if not found
269
189
  def get_single_doc_via_search(index, request_params)
270
190
  solr_params = solr_search_params(request_params)
271
191
 
272
- solr_params[:start] = (index - 1) # start at 0 to get 1st doc, 1 to get 2nd.
192
+ solr_params[:start] = (index - 1) # start at 0 to get 1st doc, 1 to get 2nd.
273
193
  solr_params[:rows] = 1
274
194
  solr_params[:fl] = '*'
275
- solr_response = find(solr_params)
195
+ solr_response = solr_repository.search(solr_params)
276
196
  solr_response.documents.first
277
197
  end
278
198
  deprecation_deprecate :get_single_doc_via_search
@@ -281,19 +201,7 @@ module Blacklight::SolrHelper
281
201
  # @return [Blacklight::SolrResponse, Array<Blacklight::SolrDocument>] the solr response and a list of the first and last document
282
202
  def get_previous_and_next_documents_for_search(index, request_params, extra_controller_params={})
283
203
 
284
- solr_params = solr_search_params(request_params).merge(extra_controller_params)
285
-
286
- if index > 0
287
- solr_params[:start] = index - 1 # get one before
288
- solr_params[:rows] = 3 # and one after
289
- else
290
- solr_params[:start] = 0 # there is no previous doc
291
- solr_params[:rows] = 2 # but there should be one after
292
- end
293
-
294
- solr_params[:fl] = '*'
295
- solr_params[:facet] = false
296
- solr_response = find(solr_params)
204
+ solr_response = query_solr(request_params, extra_controller_params.merge(previous_and_next_document_params(index)))
297
205
 
298
206
  document_list = solr_response.documents
299
207
 
@@ -303,16 +211,6 @@ module Blacklight::SolrHelper
303
211
 
304
212
  [solr_response, [prev_doc, next_doc]]
305
213
  end
306
-
307
- # returns a solr params hash
308
- # the :fl (solr param) is set to the "field" value.
309
- # per_page is set to 10
310
- def solr_opensearch_params(field=nil)
311
- solr_params = solr_search_params
312
- solr_params[:per_page] = 10
313
- solr_params[:fl] = field || blacklight_config.view_config('opensearch').title_field
314
- solr_params
315
- end
316
214
 
317
215
  # a solr query method
318
216
  # does a standard search but returns a simplified object.
@@ -320,38 +218,12 @@ module Blacklight::SolrHelper
320
218
  # the second item is an other array. This second array contains
321
219
  # all of the field values for each of the documents...
322
220
  # where the field is the "field" argument passed in.
323
- def get_opensearch_response(field=nil, extra_controller_params={})
324
- solr_params = solr_opensearch_params().merge(extra_controller_params)
325
- response = find(solr_params)
326
- a = [solr_params[:q]]
327
- a << response.documents.map {|doc| doc[solr_params[:fl]].to_s }
328
- end
329
-
330
- DEFAULT_FACET_LIMIT = 10
331
-
332
- # Look up facet limit for given facet_field. Will look at config, and
333
- # if config is 'true' will look up from Solr @response if available. If
334
- # no limit is avaialble, returns nil. Used from #solr_search_params
335
- # to supply f.fieldname.facet.limit values in solr request (no @response
336
- # available), and used in display (with @response available) to create
337
- # a facet paginator with the right limit.
338
- def facet_limit_for(facet_field)
339
- facet = blacklight_config.facet_fields[facet_field]
340
- return if facet.blank?
341
-
342
- if facet.limit and @response and @response.facet_by_field_name(facet_field)
343
- limit = @response.facet_by_field_name(facet_field).limit
344
-
345
- if limit.nil? # we didn't get or a set a limit, so infer one.
346
- facet.limit if facet.limit != true
347
- elsif limit == -1 # limit -1 is solr-speak for unlimited
348
- nil
349
- else
350
- limit.to_i - 1 # we added 1 to find out if we needed to paginate
351
- end
352
- elsif facet.limit
353
- facet.limit == true ? DEFAULT_FACET_LIMIT : facet.limit
354
- end
221
+ def get_opensearch_response(field=nil, request_params = params || {}, extra_controller_params={})
222
+ field ||= blacklight_config.view_config('opensearch').title_field
223
+
224
+ response = query_solr(request_params, solr_opensearch_params(field).merge(extra_controller_params))
225
+
226
+ [response.params[:q], response.documents.flat_map {|doc| doc[field] }.uniq]
355
227
  end
356
228
 
357
229
  ##
@@ -360,18 +232,28 @@ module Blacklight::SolrHelper
360
232
  blacklight_config.index.group
361
233
  end
362
234
 
363
- def blacklight_solr
364
- @solr ||= RSolr.connect(blacklight_solr_config)
365
- end
366
-
367
- def blacklight_solr_config
368
- Blacklight.solr_config
235
+ def solr_repository
236
+ @solr_repository ||= Blacklight::SolrRepository.new(blacklight_config)
369
237
  end
370
238
 
371
239
  private
372
240
 
373
- def should_add_to_solr field_name, field
374
- field.include_in_request || (field.include_in_request.nil? && blacklight_config.add_field_configuration_to_solr_request)
241
+ ##
242
+ # @deprecated
243
+ def default_solr_doc_params(id=nil)
244
+ id ||= params[:id]
245
+
246
+ # add our document id to the document_unique_id_param query parameter
247
+ p = blacklight_config.default_document_solr_params.merge({
248
+ # this assumes the request handler will map the unique id param
249
+ # to the unique key field using either solr local params, the
250
+ # real-time get handler, etc.
251
+ blacklight_config.document_unique_id_param => id
252
+ })
253
+
254
+ p[:qt] ||= blacklight_config.document_solr_request_handler
255
+
256
+ p
375
257
  end
376
258
 
377
259
  end