europeana-blacklight 0.1.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.
@@ -0,0 +1,41 @@
1
+ require 'kaminari'
2
+
3
+ module Europeana
4
+ module Blacklight
5
+ class Response
6
+ ##
7
+ # Pagination for{Europeana::Blacklight::Response}
8
+ #
9
+ # Based on {Blacklight::SolrResponse::Pagination} v5.10.2
10
+ module Pagination
11
+ include Kaminari::PageScopeMethods
12
+ include Kaminari::ConfigurationMethods::ClassMethods
13
+
14
+ def limit_value
15
+ rows
16
+ end
17
+
18
+ def offset_value
19
+ start
20
+ end
21
+
22
+ def total_count
23
+ total
24
+ end
25
+
26
+ def model_name
27
+ return unless docs.present? && docs.first.respond_to?(:model_name)
28
+ docs.first.model_name
29
+ end
30
+
31
+ def next_page
32
+ current_page + 1 unless last_page?
33
+ end
34
+
35
+ def prev_page
36
+ current_page - 1 unless first_page?
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,195 @@
1
+ module Europeana
2
+ module Blacklight
3
+ ##
4
+ # Core search builder for {Europeana::Blacklight::ApiRepository}
5
+ class SearchBuilder < ::Blacklight::SearchBuilder
6
+ require 'europeana/blacklight/search_builder/channels'
7
+ require 'europeana/blacklight/search_builder/facet_pagination'
8
+ require 'europeana/blacklight/search_builder/more_like_this'
9
+ require 'europeana/blacklight/search_builder/ranges'
10
+
11
+ self.default_processor_chain = [
12
+ :default_api_parameters, :add_profile_to_api,
13
+ :add_wskey_to_api, :add_query_to_api, :add_qf_to_api,
14
+ :add_facet_qf_to_api, :add_reusability_to_api, :add_facetting_to_api,
15
+ :add_paging_to_api, :add_sorting_to_api
16
+ ]
17
+
18
+ include Channels
19
+ include FacetPagination
20
+ include MoreLikeThis
21
+ include Ranges
22
+
23
+ delegate :to_query, to: :to_hash
24
+
25
+ ##
26
+ # Start with general defaults from BL config. Need to use custom
27
+ # merge to dup values, to avoid later mutating the original by mistake.
28
+ #
29
+ # @todo Rename default_solr_params to default_params upstream
30
+ def default_api_parameters(api_parameters)
31
+ blacklight_config.default_solr_params.each do |key, value|
32
+ if value.respond_to?(:deep_dup)
33
+ api_parameters[key] = value.deep_dup
34
+ elsif value.respond_to?(:dup) && value.duplicable?
35
+ api_parameters[key] = value.dup
36
+ else
37
+ api_parameters[key] = value
38
+ end
39
+ end
40
+ end
41
+
42
+ ##
43
+ # Set the profile type
44
+ #
45
+ # @see http://labs.europeana.eu/api/search/#profile-parameter
46
+ def add_profile_to_api(api_parameters)
47
+ if blacklight_config.facet_fields
48
+ api_parameters[:profile] = 'params facets rich'
49
+ else
50
+ api_parameters[:profile] = 'params rich'
51
+ end
52
+ end
53
+
54
+ ##
55
+ # Add the Europeana REST API key
56
+ #
57
+ # @see http://labs.europeana.eu/api/authentication/#basic-authentication
58
+ def add_wskey_to_api(api_parameters)
59
+ api_parameters[:wskey] = blacklight_config.connection_config[:europeana_api_key]
60
+ end
61
+
62
+ ##
63
+ # Take the user-entered query, and put it in the API params,
64
+ # including config's "search field" params for current search field.
65
+ #
66
+ # @see http://labs.europeana.eu/api/query/
67
+ def add_query_to_api(api_parameters)
68
+ if [blacklight_params[:q]].flatten.reject(&:blank?).blank?
69
+ query = '*:*'
70
+ elsif search_field && search_field.field.present?
71
+ query = "#{search_field.field}:#{blacklight_params[:q]}"
72
+ elsif blacklight_params[:q].is_a?(Hash)
73
+ # @todo when would it be a Hash?
74
+ query = nil
75
+ elsif blacklight_params[:q]
76
+ query = blacklight_params[:q]
77
+ end
78
+ append_to_query_param(api_parameters, query)
79
+ end
80
+
81
+ ##
82
+ # Add the user's query filter terms
83
+ def add_qf_to_api(api_parameters)
84
+ return unless blacklight_params[:qf]
85
+ api_parameters[:qf] ||= []
86
+ api_parameters[:qf] = api_parameters[:qf] + blacklight_params[:qf]
87
+ end
88
+
89
+ ##
90
+ # Facet *filtering* of results
91
+ #
92
+ # Maps Blacklight's :f param to API's :qf param.
93
+ #
94
+ # @see http://labs.europeana.eu/api/query/#faceted-search
95
+ # @todo Handle different types of value, like
96
+ # {Blacklight::Solr::SearchBuilder#facet_value_to_fq_string} does
97
+ def add_facet_qf_to_api(api_parameters)
98
+ return unless blacklight_params[:f]
99
+
100
+ salient_facets = blacklight_params[:f].reject do |k, _v|
101
+ k == 'REUSABILITY'
102
+ end
103
+
104
+ salient_facets.each_pair do |facet_field, value_list|
105
+ Array(value_list).reject(&:blank?).each do |value|
106
+ api_parameters[:qf] ||= []
107
+ api_parameters[:qf] << "#{facet_field}:\"#{value}\""
108
+ end
109
+ end
110
+ end
111
+
112
+ # bizarrely, reusability is a distinct API param, even though it
113
+ # is returned with the facets in a search response
114
+ def add_reusability_to_api(api_parameters)
115
+ if blacklight_params[:f] && blacklight_params[:f]['REUSABILITY']
116
+ api_parameters[:reusability] = blacklight_params[:f]['REUSABILITY'].join(',')
117
+ end
118
+ end
119
+
120
+ ##
121
+ # Request facet data in results, respecting configured limits
122
+ #
123
+ # @todo Handle facet settings like query, sort, pivot, etc, like
124
+ # {Blacklight::Solr::SearchBuilder#add_facetting_to_solr} does
125
+ # @see http://labs.europeana.eu/api/search/#individual-facets
126
+ # @see http://labs.europeana.eu/api/search/#offset-and-limit-of-facets
127
+ def add_facetting_to_api(api_parameters)
128
+ api_parameters[:facet] = api_request_facet_fields.keys.join(',')
129
+
130
+ api_request_facet_fields.each do |field_name, facet|
131
+ api_parameters[:"f.#{facet.field}.facet.limit"] = (facet_limit_for(field_name) + 1) if facet_limit_for(field_name)
132
+ end
133
+ end
134
+
135
+ ##
136
+ # copy paging params from BL app over to API, changing
137
+ # app level per_page and page to API rows and start.
138
+ def add_paging_to_api(api_parameters)
139
+ rows(api_parameters[:rows] || 10) if rows.nil?
140
+ api_parameters[:rows] = rows
141
+
142
+ api_parameters[:start] = start unless start == 0
143
+ end
144
+
145
+ ##
146
+ # @todo Implement when the API supports sorting
147
+ def add_sorting_to_api(_api_parameters)
148
+ return if sort.blank?
149
+ Europeana::API.logger.warn('Europeana REST API does not support sorting')
150
+ end
151
+
152
+ ##
153
+ # Europeana API start param counts from 1
154
+ def start(start = nil)
155
+ super_start = super
156
+ super_start == self ? super_start : super_start + 1
157
+ end
158
+
159
+ protected
160
+
161
+ # Look up facet limit for given facet_field. Will look at config, and
162
+ # if config is 'true' will look up from Solr @response if available. If
163
+ # no limit is avaialble, returns nil. Used from #add_facetting_to_solr
164
+ # to supply f.fieldname.facet.limit values in solr request (no @response
165
+ # available), and used in display (with @response available) to create
166
+ # a facet paginator with the right limit.
167
+ def facet_limit_for(facet_field)
168
+ facet = blacklight_config.facet_fields[facet_field]
169
+ return if facet.blank? || !facet.limit
170
+
171
+ if facet.limit == true
172
+ blacklight_config.default_facet_limit
173
+ else
174
+ facet.limit
175
+ end
176
+ end
177
+
178
+ def api_request_facet_fields
179
+ @api_request_facet_fields ||= blacklight_config.facet_fields.select do |field_name, facet|
180
+ !facet.query &&
181
+ (facet.include_in_request || (facet.include_in_request.nil? && blacklight_config.add_facet_fields_to_solr_request)) &&
182
+ (field_name == 'REUSABILITY' || Europeana::API::Search::Fields.include?(field_name))
183
+ end
184
+ end
185
+
186
+ def append_to_query_param(api_parameters, query)
187
+ return if query.blank?
188
+ return if query == '*:*' && api_parameters[:query].present?
189
+ api_parameters[:query] ||= ''
190
+ api_parameters[:query] << ' ' unless api_parameters[:query].blank?
191
+ api_parameters[:query] << query
192
+ end
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,18 @@
1
+ module Europeana
2
+ module Blacklight
3
+ class SearchBuilder
4
+ ##
5
+ # Search builder with content channel qf
6
+ module Channels
7
+ # @todo remove hard-coded controller check
8
+ def add_channel_qf_to_api(api_parameters)
9
+ return unless blacklight_params[:controller] == 'channels' && blacklight_params[:id].present?
10
+ channel_qf = scope.channels_search_query
11
+ return if channel_qf.blank?
12
+ api_parameters[:qf] ||= []
13
+ api_parameters[:qf] << channel_qf
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,38 @@
1
+ module Europeana
2
+ module Blacklight
3
+ class SearchBuilder
4
+ ##
5
+ # Search builder methods for facet pagination
6
+ module FacetPagination
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ default_processor_chain << :add_facet_paging_to_api
11
+ end
12
+
13
+ def add_facet_paging_to_api(api_parameters)
14
+ return unless facet.present?
15
+
16
+ facet_config = blacklight_config.facet_fields[facet]
17
+
18
+ limit = if scope.respond_to?(:facet_list_limit)
19
+ scope.facet_list_limit.to_s.to_i
20
+ elsif api_parameters['facet.limit']
21
+ api_parameters['facet.limit'].to_i
22
+ else
23
+ 20
24
+ end
25
+
26
+ offset = (blacklight_params.fetch(blacklight_config.facet_paginator_class.request_keys[:page], 1).to_i - 1) * (limit)
27
+
28
+ # Need to set as f.facet_field.facet.* to make sure we
29
+ # override any field-specific default in the solr request handler.
30
+ api_parameters[:"f.#{facet}.facet.limit"] = limit + 1
31
+ api_parameters[:"f.#{facet}.facet.offset"] = offset
32
+ api_parameters[:rows] = 0
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+
@@ -0,0 +1,23 @@
1
+ module Europeana
2
+ module Blacklight
3
+ class SearchBuilder
4
+ ##
5
+ # Search builder methods for more like this queries
6
+ module MoreLikeThis
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ default_processor_chain.unshift :add_mlt_to_api
11
+ end
12
+
13
+ def add_mlt_to_api(api_parameters)
14
+ return unless blacklight_params[:mlt]
15
+ repository = blacklight_config.repository_class.new(blacklight_config)
16
+ doc_response = repository.find(blacklight_params[:mlt])
17
+ query = doc_response.documents.first.more_like_this_query(blacklight_params[:mltf])
18
+ append_to_query_param(api_parameters, query)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ module Europeana
2
+ module Blacklight
3
+ class SearchBuilder
4
+ ##
5
+ # Search builder with content channel qf
6
+ module Ranges
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ default_processor_chain << :add_range_qf_to_api
11
+ end
12
+
13
+ def add_range_qf_to_api(api_parameters)
14
+ return unless blacklight_params.key?(:range) && blacklight_params[:range].is_a?(Hash)
15
+ blacklight_params[:range].each_pair do |range_field, range_values|
16
+ api_parameters[:qf] ||= []
17
+ api_parameters[:qf] << "#{range_field}:[#{range_values[:begin]} TO #{range_values[:end]}]"
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,6 @@
1
+ module Europeana
2
+ #:nodoc:
3
+ module Blacklight
4
+ VERSION = '0.1.0'
5
+ end
6
+ end
metadata ADDED
@@ -0,0 +1,164 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: europeana-blacklight
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Richard Doe
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-07-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: blacklight
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 5.12.0
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: 6.0.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 5.12.0
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: 6.0.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: europeana-api
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: 0.3.2
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: 0.3.2
47
+ - !ruby/object:Gem::Dependency
48
+ name: kaminari
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '0.16'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0.16'
61
+ - !ruby/object:Gem::Dependency
62
+ name: bundler
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '1.8'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '1.8'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rake
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '10.0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '10.0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: rspec
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '3.2'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '3.2'
103
+ description:
104
+ email:
105
+ - richard.doe@rwdit.net
106
+ executables: []
107
+ extensions: []
108
+ extra_rdoc_files: []
109
+ files:
110
+ - ".gitignore"
111
+ - ".hound.yml"
112
+ - ".rspec"
113
+ - ".rubocop.yml"
114
+ - ".ruby-style.yml"
115
+ - ".travis.yml"
116
+ - Gemfile
117
+ - LICENSE.md
118
+ - README.md
119
+ - Rakefile
120
+ - bin/console
121
+ - bin/setup
122
+ - europeana-blacklight.gemspec
123
+ - lib/europeana/blacklight.rb
124
+ - lib/europeana/blacklight/api_repository.rb
125
+ - lib/europeana/blacklight/document.rb
126
+ - lib/europeana/blacklight/document/more_like_this.rb
127
+ - lib/europeana/blacklight/document/relations.rb
128
+ - lib/europeana/blacklight/document_presenter.rb
129
+ - lib/europeana/blacklight/facet_paginator.rb
130
+ - lib/europeana/blacklight/response.rb
131
+ - lib/europeana/blacklight/response/facets.rb
132
+ - lib/europeana/blacklight/response/more_like_this.rb
133
+ - lib/europeana/blacklight/response/pagination.rb
134
+ - lib/europeana/blacklight/search_builder.rb
135
+ - lib/europeana/blacklight/search_builder/channels.rb
136
+ - lib/europeana/blacklight/search_builder/facet_pagination.rb
137
+ - lib/europeana/blacklight/search_builder/more_like_this.rb
138
+ - lib/europeana/blacklight/search_builder/ranges.rb
139
+ - lib/europeana/blacklight/version.rb
140
+ homepage: https://github.com/europeana/europeana-blacklight
141
+ licenses:
142
+ - EUPL 1.1
143
+ metadata: {}
144
+ post_install_message:
145
+ rdoc_options: []
146
+ require_paths:
147
+ - lib
148
+ required_ruby_version: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ required_rubygems_version: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ requirements: []
159
+ rubyforge_project:
160
+ rubygems_version: 2.4.8
161
+ signing_key:
162
+ specification_version: 4
163
+ summary: Europeana REST API adapter for Blacklight
164
+ test_files: []