ebsco-eds 0.0.1.pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,375 @@
1
+ require 'ebsco/eds/record'
2
+ require 'yaml'
3
+
4
+ module EBSCO
5
+
6
+ module EDS
7
+
8
+ # Search Results
9
+ class Results
10
+
11
+ # Raw results as Hash
12
+ attr_reader :results
13
+ # Array of EBSCO::EDS::Record results
14
+ attr_reader :records
15
+ # Array of EBSCO::EDS::Record Research Starters
16
+ attr_reader :research_starters
17
+ # Array of EBSCO::EDS::Record Exact Publication Matches
18
+ attr_reader :publication_match
19
+
20
+ # Lookup table of databases that have their labels suppressed in the response.
21
+ DBS = YAML::load_file(File.join(__dir__, 'settings.yml'))['databases']
22
+
23
+ # Creates search results from the \EDS API search response. It includes information about the results and a list
24
+ # of Record items.
25
+ def initialize(search_results)
26
+
27
+ @results = search_results
28
+
29
+ # convert all results to a list of records
30
+ @records = []
31
+ if stat_total_hits > 0
32
+ @results['SearchResult']['Data']['Records'].each { |record| @records.push(EBSCO::EDS::Record.new(record)) }
33
+ end
34
+
35
+ # create a special list of research starter records
36
+ @research_starters = []
37
+ _related_records = @results.fetch('SearchResult',{}).fetch('RelatedContent',{}).fetch('RelatedRecords',{})
38
+ if _related_records.count > 0
39
+ _related_records.each do |related_item|
40
+ if related_item['Type'] == 'rs'
41
+ rs_entries = related_item.fetch('Records',{})
42
+ if rs_entries.count > 0
43
+ rs_entries.each do |rs_record|
44
+ @research_starters.push(EBSCO::EDS::Record.new(rs_record))
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ # create a special list of exact match publications
52
+ @publication_match = []
53
+ _related_publications = @results.fetch('SearchResult',{}).fetch('RelatedContent',{}).fetch('RelatedPublications',{})
54
+ if _related_publications.count > 0
55
+ _related_publications.each do |related_item|
56
+ if related_item['Type'] == 'emp'
57
+ _publication_matches = related_item.fetch('PublicationRecords',{})
58
+ if _publication_matches.count > 0
59
+ _publication_matches.each do |publication_record|
60
+ @publication_match.push(EBSCO::EDS::Record.new(publication_record))
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ end
68
+
69
+ # returns solr search response format
70
+ def to_solr
71
+
72
+ solr_start = (page_number-1) * results_per_page
73
+ hl_hash = {}
74
+ solr_docs = []
75
+
76
+ if stat_total_hits > 0
77
+ @records.each { |record|
78
+
79
+ # todo: add solr hl.tag.pre and hl.tag.post to retrieval criteria
80
+ if retrieval_criteria.fetch('Highlight',{}) == 'y'
81
+ hl_title = record.title.gsub('&lt;highlight&gt;', '<em>').gsub('&lt;/highlight&gt;', '</em>')
82
+ hl_hash.update({ record.database_id + '-' + record.accession_number => { 'title_display' => [hl_title]} })
83
+ #hl_hash.merge title_hl
84
+ end
85
+
86
+ solr_docs.push(record.to_hash)
87
+ }
88
+ end
89
+
90
+ # solr response
91
+ {
92
+ 'responseHeader' => {
93
+ 'status' => 0,
94
+ 'QTime' => stat_total_time,
95
+ 'params' => {
96
+ 'q' => search_terms.join(' '),
97
+ 'wt' => 'ruby',
98
+ 'start' => solr_start,
99
+ 'rows' => results_per_page,
100
+ 'facet' => true,
101
+ 'f.subject_topic_facet.facet.limit' => 21,
102
+ 'f.language_facet.facet.limit' => 11,
103
+
104
+ }
105
+ },
106
+ 'response' => {
107
+ 'numFound' => stat_total_hits.to_i,
108
+ 'start' => solr_start,
109
+ 'docs' => solr_docs
110
+ },
111
+ 'highlighting' => hl_hash,
112
+ 'facet_counts' =>
113
+ {
114
+ 'facet_fields' => {
115
+ 'format' => solr_facets('SourceType'),
116
+ 'language_facet' => solr_facets('Language'),
117
+ 'subject_topic_facet' => solr_facets('SubjectEDS')
118
+ }
119
+ }
120
+ }
121
+
122
+ end
123
+
124
+ # Total number of results found.
125
+ def stat_total_hits
126
+ _hits = @results.fetch('SearchResult',{}).fetch('Statistics',{}).fetch('TotalHits',{})
127
+ _hits == {} ? 0 : _hits
128
+ end
129
+
130
+ # Time it took to complete the search in milliseconds.
131
+ def stat_total_time
132
+ @results['SearchResult']['Statistics']['TotalSearchTime']
133
+ end
134
+
135
+ # Search criteria used in the search
136
+ # Returns a hash.
137
+ # ==== Example
138
+ # {
139
+ # "Queries"=>[{"BooleanOperator"=>"AND", "Term"=>"earthquakes"}],
140
+ # "SearchMode"=>"all",
141
+ # "IncludeFacets"=>"y",
142
+ # "Expanders"=>["fulltext", "thesaurus", "relatedsubjects"],
143
+ # "Sort"=>"relevance",
144
+ # "RelatedContent"=>["rs"],
145
+ # "AutoSuggest"=>"n"
146
+ # }
147
+ def search_criteria
148
+ @results['SearchRequest']['SearchCriteria']
149
+ end
150
+
151
+ # Search criteria actions applied.
152
+ # Returns a hash.
153
+ # ==== Example
154
+ # {
155
+ # "QueriesWithAction"=>[{"Query"=>{"BooleanOperator"=>"AND", "Term"=>"earthquakes"}, "RemoveAction"=>"removequery(1)"}],
156
+ # "ExpandersWithAction"=>[{"Id"=>"fulltext", "RemoveAction"=>"removeexpander(fulltext)"}]
157
+ # }
158
+ def search_criteria_with_actions
159
+ @results['SearchRequest']['SearchCriteriaWithActions']
160
+ end
161
+
162
+ # Retrieval criteria that was applied to the search. Returns a hash.
163
+ # ==== Example
164
+ # {"View"=>"brief", "ResultsPerPage"=>20, "PageNumber"=>1, "Highlight"=>"y"}
165
+ def retrieval_criteria
166
+ @results['SearchRequest']['RetrievalCriteria']
167
+ end
168
+
169
+ # Queries used to produce the results. Returns an array of query hashes.
170
+ # ==== Example
171
+ # [{"BooleanOperator"=>"AND", "Term"=>"volcano"}]
172
+ def search_queries
173
+ @results['SearchRequest']['SearchCriteria']['Queries']
174
+ end
175
+
176
+ # Current page number for the results. Returns an integer.
177
+ def page_number
178
+ @results['SearchRequest']['RetrievalCriteria']['PageNumber'] || 1
179
+ end
180
+
181
+ # Results per page. Returns an integer.
182
+ def results_per_page
183
+ @results['SearchRequest']['RetrievalCriteria']['ResultsPerPage'] || 20
184
+ end
185
+
186
+ # List of facets applied to the search.
187
+ # ==== Example
188
+ # [{
189
+ # "FacetValue"=>{"Id"=>"SubjectGeographic", "Value"=>"massachusetts"},
190
+ # "RemoveAction"=>"removefacetfiltervalue(1,SubjectGeographic:massachusetts)"
191
+ # }]
192
+ def applied_facets
193
+ af = []
194
+ applied_facets_section = @results['SearchRequest'].fetch('SearchCriteriaWithActions',{}).fetch('FacetFiltersWithAction',{})
195
+ applied_facets_section.each do |applied_facets|
196
+ applied_facets.fetch('FacetValuesWithAction',{}).each do |applied_facet|
197
+ af.push(applied_facet)
198
+ end
199
+ end
200
+ af
201
+ end
202
+
203
+ # List of limiters applied to the search.
204
+ # ==== Example
205
+ # [{
206
+ # "Id"=>"LA99",
207
+ # "LimiterValuesWithAction"=>[{"Value"=>"French", "RemoveAction"=>"removelimitervalue(LA99:French)"}],
208
+ # "RemoveAction"=>"removelimiter(LA99)"
209
+ # }]
210
+ def applied_limiters
211
+ af = []
212
+ applied_limters_section = @results['SearchRequest'].fetch('SearchCriteriaWithActions',{}).fetch('LimitersWithAction',{})
213
+ applied_limters_section.each do |applied_limter|
214
+ af.push(applied_limter)
215
+ end
216
+ af
217
+ end
218
+
219
+ # Expanders applied to the search.
220
+ # ==== Example
221
+ # [
222
+ # {"Id"=>"fulltext", "RemoveAction"=>"removeexpander(fulltext)"},
223
+ # {"Id"=>"thesaurus", "RemoveAction"=>"removeexpander(thesaurus)"},
224
+ # {"Id"=>"relatedsubjects", "RemoveAction"=>"removeexpander(relatedsubjects)"}
225
+ # ]
226
+ def applied_expanders
227
+ af = []
228
+ applied_expanders_section = @results['SearchRequest'].fetch('SearchCriteriaWithActions',{}).fetch('ExpandersWithAction',{})
229
+ applied_expanders_section.each do |applied_explander|
230
+ af.push(applied_explander)
231
+ end
232
+ af
233
+ end
234
+
235
+ # Publications search was limited to.
236
+ # ==== Example
237
+ # [
238
+ # ["Id", "eric"],
239
+ # ["RemoveAction", "removepublication(eric)"]
240
+ # ]
241
+ def applied_publications
242
+ retval = []
243
+ applied_publications_section = @results['SearchRequest'].fetch('SearchCriteriaWithActions',{}).fetch('PublicationWithAction',{})
244
+ applied_publications_section.each do |item|
245
+ retval.push(item)
246
+ end
247
+ retval
248
+ end
249
+
250
+ # Provides a list of databases searched and the number of hits found in each one.
251
+ # ==== Example
252
+ # [
253
+ # {:id=>"nlebk", :hits=>0, :label=>"eBook Collection (EBSCOhost)"},
254
+ # {:id=>"e000xna", :hits=>30833, :label=>"eBook Academic Collection (EBSCOhost)"},
255
+ # {:id=>"edsart", :hits=>8246, :label=>"ARTstor Digital Library"},
256
+ # {:id=>"e700xna", :hits=>6701, :label=>"eBook Public Library Collection (EBSCOhost)"},
257
+ # {:id=>"cat02060a", :hits=>3464, :label=>"EDS Demo Catalog – US - U of Georgia"},
258
+ # {:id=>"ers", :hits=>1329, :label=>"Research Starters"},
259
+ # {:id=>"asn", :hits=>136406, :label=>"Academic Search Ultimate"}
260
+ # ]
261
+ def database_stats
262
+ databases = []
263
+ databases_facet = @results['SearchResult']['Statistics']['Databases']
264
+ databases_facet.each do |database|
265
+ if DBS.key?(database['Id'].upcase)
266
+ db_label = DBS[database['Id'].upcase];
267
+ else
268
+ db_label = database['Label']
269
+ end
270
+ databases.push({id: database['Id'], hits: database['Hits'], label: db_label})
271
+ end
272
+ databases
273
+ end
274
+
275
+ # Provides a list of facets for the search results.
276
+ # ==== Example
277
+ # [
278
+ # {
279
+ # :id=>"SourceType",
280
+ # :label=>"Source Type",
281
+ # :values=>[
282
+ # {
283
+ # :value=>"Academic Journals",
284
+ # :hitcount=>147,
285
+ # :action=>"addfacetfilter(SourceType:Academic Journals)"
286
+ # },
287
+ # {
288
+ # :value=>"News",
289
+ # :hitcount=>111,
290
+ # :action=>"addfacetfilter(SourceType:News)"
291
+ # },
292
+ #
293
+ # ...
294
+ #
295
+ # }
296
+ # ]
297
+ def facets (facet_provided_id = 'all')
298
+ facets_hash = []
299
+ available_facets = @results.fetch('SearchResult',{}).fetch('AvailableFacets',{})
300
+ available_facets.each do |available_facet|
301
+ if available_facet['Id'] == facet_provided_id || facet_provided_id == 'all'
302
+ facet_label = available_facet['Label']
303
+ facet_id = available_facet['Id']
304
+ facet_values = []
305
+ available_facet['AvailableFacetValues'].each do |available_facet_value|
306
+ facet_value = available_facet_value['Value']
307
+ facet_count = available_facet_value['Count']
308
+ facet_action = available_facet_value['AddAction']
309
+ facet_values.push({value: facet_value, hitcount: facet_count, action: facet_action})
310
+ end
311
+ facets_hash.push(id: facet_id, label: facet_label, values: facet_values)
312
+ end
313
+ end
314
+ facets_hash
315
+ end
316
+
317
+ def solr_facets (facet_provided_id = 'all')
318
+ facet_values = []
319
+ available_facets = @results.fetch('SearchResult',{}).fetch('AvailableFacets',{})
320
+ available_facets.each do |available_facet|
321
+ if available_facet['Id'] == facet_provided_id || facet_provided_id == 'all'
322
+ available_facet['AvailableFacetValues'].each do |available_facet_value|
323
+ facet_value = available_facet_value['Value']
324
+ facet_count = available_facet_value['Count']
325
+ facet_values.push(facet_value, facet_count)
326
+ end
327
+ end
328
+ end
329
+ facet_values
330
+ end
331
+
332
+ # Returns a hash of the date range available for the search.
333
+ # ==== Example
334
+ # {:mindate=>"1501-01", :maxdate=>"2018-04", :minyear=>"1501", :maxyear=>"2018"}
335
+ def date_range
336
+ mindate = @results['SearchResult']['AvailableCriteria']['DateRange']['MinDate']
337
+ maxdate = @results['SearchResult']['AvailableCriteria']['DateRange']['MaxDate']
338
+ minyear = mindate[0..3]
339
+ maxyear = maxdate[0..3]
340
+ {mindate: mindate, maxdate: maxdate, minyear:minyear, maxyear:maxyear}
341
+ end
342
+
343
+ # Provides alternative search terms to correct spelling, etc.
344
+ # ==== Example
345
+ # results = session.simple_search('earthquak')
346
+ # results.did_you_mean
347
+ # => "earthquake"
348
+ def did_you_mean
349
+ dym_suggestions = @results.fetch('SearchResult', {}).fetch('AutoSuggestedTerms',{})
350
+ dym_suggestions.each do |term|
351
+ return term
352
+ end
353
+ nil
354
+ end
355
+
356
+ # Returns a simple list of the search terms used. Boolean operators are not indicated.
357
+ # ==== Example
358
+ # ["earthquakes", "california"]
359
+ def search_terms
360
+ terms = []
361
+ queries = @results.fetch('SearchRequest',{}).fetch('SearchCriteriaWithActions',{}).fetch('QueriesWithAction',{})
362
+ unless queries.nil?
363
+ queries.each do |query|
364
+ query['Query']['Term'].split.each do |word|
365
+ terms.push(word)
366
+ end
367
+ end
368
+ end
369
+ terms
370
+ end
371
+
372
+ end
373
+
374
+ end
375
+ end
@@ -0,0 +1,624 @@
1
+ require 'ebsco/eds/version'
2
+ require 'ebsco/eds/info'
3
+ require 'ebsco/eds/results'
4
+ require 'faraday'
5
+ require 'faraday_middleware'
6
+ require 'logger'
7
+ require 'json'
8
+
9
+ module EBSCO
10
+
11
+ module EDS
12
+
13
+ # Sessions are used to query and retrieve information from the EDS API.
14
+ class Session
15
+
16
+ # Contains search Info available in the session profile. Includes the sort options, search fields, limiters and expanders available to the profile.
17
+ attr_accessor :info
18
+ # Contains the search Options sent as part of each request to the EDS API such as limiters, expanders, search modes, sort order, etc.
19
+ attr_accessor :search_options
20
+ # The authentication token. This is passed along in the x-authenticationToken HTTP header.
21
+ attr_accessor :auth_token # :nodoc:
22
+ # The session token. This is passed along in the x-sessionToken HTTP header.
23
+ attr_accessor :session_token # :nodoc:
24
+
25
+ # Creates a new session.
26
+ #
27
+ # This can be done in one of two ways:
28
+ #
29
+ # === 1. Environment variables
30
+ # * +EDS_AUTH+ - authentication method: 'ip' or 'user'
31
+ # * +EDS_PROFILE+ - profile ID for the EDS API
32
+ # * +EDS_USER+ - user id attached to the profile
33
+ # * +EDS_PASS+ - user password
34
+ # * +EDS_GUEST+ - allow guest access: 'y' or 'n'
35
+ # * +EDS_ORG+ - name of your institution or company
36
+ #
37
+ # ==== Example
38
+ # Once you have environment variables set, simply create a session like this:
39
+ # session = EBSCO::EDS::Session.new
40
+ #
41
+ # === 2. \Options
42
+ # * +:auth+
43
+ # * +:profile+
44
+ # * +:user+
45
+ # * +:pass+
46
+ # * +:guest+
47
+ # * +:org+
48
+ #
49
+ # ==== Example
50
+ # session = EBSCO::EDS::Session.new {
51
+ # :auth => 'user',
52
+ # :profile => 'edsapi',
53
+ # :user => 'joe'
54
+ # :pass => 'secret',
55
+ # :guest => false,
56
+ # :org => 'Acme University'
57
+ # }
58
+ def initialize(options = {})
59
+
60
+ @auth_token = ''
61
+ @session_token = ''
62
+
63
+ if options.has_key? :user
64
+ @user = options[:user]
65
+ elsif ENV.has_key? 'EDS_USER'
66
+ @user = ENV['EDS_USER']
67
+ end
68
+
69
+ if options.has_key? :pass
70
+ @pass = options[:pass]
71
+ elsif ENV.has_key? 'EDS_PASS'
72
+ @pass = ENV['EDS_PASS']
73
+ end
74
+
75
+ if options.has_key? :profile
76
+ @profile = options[:profile]
77
+ elsif ENV.has_key? 'EDS_PROFILE'
78
+ @profile = ENV['EDS_PROFILE']
79
+ end
80
+ raise EBSCO::EDS::InvalidParameter, 'Session must specify a valid api profile.' if blank?(@profile)
81
+
82
+ if options.has_key? :guest
83
+ @guest = options[:guest] ? 'y' : 'n'
84
+ elsif ENV.has_key? 'EDS_GUEST'
85
+ @guest = ENV['EDS_GUEST']
86
+ end
87
+
88
+ if options.has_key? :org
89
+ @org = options[:org]
90
+ elsif ENV.has_key? 'EDS_ORG'
91
+ @org = ENV['EDS_ORG']
92
+ end
93
+
94
+ if options.has_key? :auth
95
+ @auth_type = options[:auth]
96
+ elsif ENV.has_key? 'EDS_AUTH'
97
+ @auth_type = ENV['EDS_AUTH']
98
+ else
99
+ @auth_type = 'user'
100
+ end
101
+
102
+ @max_retries = MAX_ATTEMPTS
103
+ @auth_token = create_auth_token
104
+ @session_token = create_session_token
105
+ @info = EBSCO::EDS::Info.new(do_request(:get, path: INFO_URL))
106
+ @current_page = 0
107
+ @search_options = nil
108
+
109
+ end
110
+
111
+ # :category: Search & Retrieve Methods
112
+ # Performs a search.
113
+ #
114
+ # Returns search Results.
115
+ #
116
+ # ==== \Options
117
+ #
118
+ # * +:query+ - Required. The search terms. Format: {booleanOperator},{fieldCode}:{term}. Example: SU:Hiking
119
+ # * +:mode+ - Search mode to be used. Either: all (default), any, bool, smart
120
+ # * +:results_per_page+ - The number of records retrieved with the search results (between 1-100, default is 20).
121
+ # * +:page+ - Starting page number for the result set returned from a search (if results per page = 10, and page number = 3 , this implies: I am expecting 10 records starting at page 3).
122
+ # * +:sort+ - The sort order for the search results. Either: relevance (default), oldest, newest
123
+ # * +:highlight+ - Specifies whether or not the search term is highlighted using <highlight /> tags. Either true or false.
124
+ # * +:include_facets+ - Specifies whether or not the search term is highlighted using <highlight /> tags. Either true (default) or false.
125
+ # * +:facet_filters+ - Facets to apply to the search. Facets are used to refine previous search results. Format: \{filterID},{facetID}:{value}[,{facetID}:{value}]* Example: 1,SubjectEDS:food,SubjectEDS:fiction
126
+ # * +:view+ - Specifies the amount of data to return with the response. Either 'title': title only; 'brief' (default): Title + Source, Subjects; 'detailed': Brief + full abstract
127
+ # * +:actions+ - Actions to take on the existing query specification. Example: addfacetfilter(SubjectGeographic:massachusetts)
128
+ # * +:limiters+ - Criteria to limit the search results by. Example: LA99:English,French,German
129
+ # * +:expanders+ - Expanders that can be applied to the search. Either: thesaurus, fulltext, relatedsubjects
130
+ # * +:publication_id+ - Publication to search within.
131
+ # * +:related_content+ - Comma separated list of related content types to return with the search results. Either 'rs' (Research Starters) or 'emp' (Exact Publication Match)
132
+ # * +:auto_suggest+ - Specifies whether or not to return search suggestions along with the search results. Either true or false (default).
133
+ #
134
+ # ==== Examples
135
+ #
136
+ # results = session.search({query: 'abraham lincoln', results_per_page: 5, related_content: ['rs','emp']})
137
+ # results = session.search({query: 'volcano', results_per_page: 1, publication_id: 'eric', include_facets: false})
138
+ def search(options = {}, add_actions = false)
139
+
140
+ # create/recreate the search options if nil or not passing actions
141
+ if @search_options.nil? || !add_actions
142
+ @search_options = EBSCO::EDS::Options.new(options, @info)
143
+ end
144
+ #puts JSON.pretty_generate(@search_options)
145
+ _response = do_request(:post, path: SEARCH_URL, payload: @search_options)
146
+ @search_results = EBSCO::EDS::Results.new(_response)
147
+ @current_page = @search_results.page_number
148
+ @search_results
149
+ end
150
+
151
+ # :category: Search & Retrieve Methods
152
+ # Performs a simple search. All other search options assume default values.
153
+ #
154
+ # Returns search Results.
155
+ #
156
+ # ==== Attributes
157
+ #
158
+ # * +query+ - the search query.
159
+ #
160
+ # ==== Examples
161
+ #
162
+ # results = session.simple_search('volcanoes')
163
+ #
164
+ def simple_search(query)
165
+ search({:query => query})
166
+ end
167
+
168
+ # :category: Search & Retrieve Methods
169
+ # Returns a Record based a particular result based on a database ID and accession number.
170
+ #
171
+ # ==== Attributes
172
+ #
173
+ # * +:dbid+ - The database ID (required).
174
+ # * +:an+ - The accession number (required).
175
+ # * +highlight+ - Comma separated list of terms to highlight in the data records (optional).
176
+ # * +ebook+ - Preferred format to return ebook content in. Either ebook-pdf (default) or ebook-pdf.
177
+ #
178
+ # ==== Examples
179
+ # record = session.retrieve({dbid: 'asn', an: '108974507'})
180
+ #
181
+ def retrieve(dbid:, an:, highlight: nil, ebook: 'ebook-pdf')
182
+ payload = {:DbId => dbid, :An => an, :HighlightTerms => highlight, :EbookPreferredFormat => ebook}
183
+ retrieve_response = do_request(:post, path: RETRIEVE_URL, payload: payload)
184
+ EBSCO::EDS::Record.new(retrieve_response)
185
+ end
186
+
187
+ # :category: Search & Retrieve Methods
188
+ # Invalidates the session token. End Session should be called when you know a user has logged out.
189
+ def end
190
+ # todo: catch when there is no valid session?
191
+ do_request(:post, path: END_SESSION_URL, payload: {:SessionToken => @session_token})
192
+ connection.headers['x-sessionToken'] = ''
193
+ @session_token = ''
194
+ end
195
+
196
+ # :category: Search & Retrieve Methods
197
+ # Clear all specified query expressions, facet filters, limiters and expanders, and set the page number back to 1.
198
+ # Returns search Results.
199
+ def clear_search
200
+ add_actions 'ClearSearch()'
201
+ end
202
+
203
+ # :category: Search & Retrieve Methods
204
+ # Clears all queries and facet filters, and set the page number back to 1; limiters and expanders are not modified.
205
+ # Returns search Results.
206
+ def clear_queries
207
+ add_actions 'ClearQueries()'
208
+ end
209
+
210
+ # :category: Search & Retrieve Methods
211
+ # Add a query to the search request. When a query is added, it will be assigned an ordinal, which will be exposed
212
+ # in the search response message. It also removes any specified facet filters and sets the page number to 1.
213
+ # Returns search Results.
214
+ # ==== Examples
215
+ # results = session.add_query('AND,California')
216
+ def add_query(query)
217
+ add_actions "AddQuery(#{query})"
218
+ end
219
+
220
+ # :category: Search & Retrieve Methods
221
+ # Removes query from the currently specified search. It also removes any specified facet filters and sets the
222
+ # page number to 1.
223
+ # Returns search Results.
224
+ # ==== Examples
225
+ # results = session.remove_query(1)
226
+ def remove_query(query_id)
227
+ add_actions "removequery(#{query_id})"
228
+ end
229
+
230
+ # :category: Search & Retrieve Methods
231
+ # Add actions to an existing search session
232
+ # Returns search Results.
233
+ # ==== Examples
234
+ # results = session.add_actions('addfacetfilter(SubjectGeographic:massachusetts)')
235
+ def add_actions(actions)
236
+ # todo: create search options if nil?
237
+ search(@search_options.add_actions(actions, @info), true)
238
+ end
239
+
240
+ # :category: Setter Methods
241
+ # Sets the sort for the search. The available sorts for the specified databases can be obtained from the session’s
242
+ # info attribute. Sets the page number back to 1.
243
+ # Returns search Results.
244
+ # ==== Examples
245
+ # results = session.set_sort('newest')
246
+ def set_sort(val)
247
+ add_actions "SetSort(#{val})"
248
+ end
249
+
250
+ # :category: Setter Methods
251
+ # Sets the search mode. The available search modes are returned from the info method.
252
+ # Returns search Results.
253
+ # ==== Examples
254
+ # results = session.set_search_mode('bool')
255
+ def set_search_mode(mode)
256
+ add_actions "SetSearchMode(#{mode})"
257
+ end
258
+
259
+ # :category: Setter Methods
260
+ # Specifies the view parameter. The view representes the amount of data to return with the search.
261
+ # Returns search Results.
262
+ # ==== Examples
263
+ # results = session.set_view('detailed')
264
+ def set_view(view)
265
+ add_actions "SetView(#{view})"
266
+ end
267
+
268
+ # :category: Setter Methods
269
+ # Sets whether or not to turn highlighting on or off (y|n).
270
+ # Returns search Results.
271
+ # ==== Examples
272
+ # results = session.set_highlight('n')
273
+ def set_highlight(val)
274
+ add_actions "SetHighlight(#{val})"
275
+ end
276
+
277
+ # :category: Setter Methods
278
+ # Sets the page size on the search request.
279
+ # Returns search Results.
280
+ # ==== Examples
281
+ # results = session.results_per_page(50)
282
+ def results_per_page(num)
283
+ add_actions "SetResultsPerPage(#{num})"
284
+ end
285
+
286
+ # :category: Setter Methods
287
+ # A related content type to additionally search for and include with the search results.
288
+ # Returns search Results.
289
+ # ==== Examples
290
+ # results = session.include_related_content('rs')
291
+ def include_related_content(val)
292
+ add_actions "includerelatedcontent(#{val})"
293
+ end
294
+
295
+ # :category: Setter Methods
296
+ # Specify to include facets in the results or not. Either 'y' or 'n'.
297
+ # Returns search Results.
298
+ # ==== Examples
299
+ # results = session.set_include_facets('n')
300
+ def set_include_facets(val)
301
+ add_actions "SetIncludeFacets(#{val})"
302
+ end
303
+
304
+ # --
305
+ # ====================================================================================
306
+ # PAGINATION
307
+ # ====================================================================================
308
+ # ++
309
+
310
+ # :category: Pagination Methods
311
+ # Get the next page of results.
312
+ # Returns search Results.
313
+ def next_page
314
+ page = @current_page + 1
315
+ get_page(page)
316
+ end
317
+
318
+ # :category: Pagination Methods
319
+ # Get the previous page of results.
320
+ # Returns search Results.
321
+ def prev_page
322
+ get_page([1, @current_page - 1].sort.last)
323
+ end
324
+
325
+ # :category: Pagination Methods
326
+ # Get a specified page of results
327
+ # Returns search Results.
328
+ def get_page(page = 1)
329
+ add_actions "GoToPage(#{page})"
330
+ end
331
+
332
+ # :category: Pagination Methods
333
+ # Increments the current results page number by the value specified. If the current page was 5 and the specified value
334
+ # was 2, the page number would be set to 7.
335
+ # Returns search Results.
336
+ def move_page(num)
337
+ add_actions "MovePage(#{num})"
338
+ end
339
+
340
+ # :category: Pagination Methods
341
+ # Get the first page of results.
342
+ # Returns search Results.
343
+ def reset_page
344
+ add_actions 'ResetPaging()'
345
+ end
346
+
347
+ # --
348
+ # ====================================================================================
349
+ # FACETS
350
+ # ====================================================================================
351
+ # ++
352
+
353
+ # :category: Facet Methods
354
+ # Removes all specified facet filters. Sets the page number back to 1.
355
+ # Returns search Results.
356
+ def clear_facets
357
+ add_actions 'ClearFacetFilters()'
358
+ end
359
+
360
+ # :category: Facet Methods
361
+ # Adds a facet filter to the search request. Sets the page number back to 1.
362
+ # Returns search Results.
363
+ # ==== Examples
364
+ # results = session.add_facet('Publisher', 'wiley-blackwell')
365
+ # results = session.add_facet('SubjectEDS', 'water quality')
366
+ #
367
+ def add_facet(facet_id, facet_val)
368
+ add_actions "AddFacetFilter(#{facet_id}:#{facet_val})"
369
+ end
370
+
371
+ # :category: Facet Methods
372
+ # Removes a specified facet filter id. Sets the page number back to 1.
373
+ # Returns search Results.
374
+ # ==== Examples
375
+ # results = session.remove_facet(45)
376
+ def remove_facet(group_id)
377
+ add_actions "RemoveFacetFilter(#{group_id})"
378
+ end
379
+
380
+ # :category: Facet Methods
381
+ # Removes a specific facet filter value from a group. Sets the page number back to 1.
382
+ # Returns search Results.
383
+ # ==== Examples
384
+ # results = session.remove_facet_value(2, 'DE', 'Psychology')
385
+ def remove_facet_value(group_id, facet_id, facet_val)
386
+ add_actions "RemoveFacetFilterValue(#{group_id},#{facet_id}:#{facet_val})"
387
+ end
388
+
389
+ # --
390
+ # ====================================================================================
391
+ # LIMITERS
392
+ # ====================================================================================
393
+ # ++
394
+
395
+ # :category: Limiter Methods
396
+ # Clears all currently specified limiters and sets the page number back to 1.
397
+ # Returns search Results.
398
+ def clear_limiters
399
+ add_actions 'ClearLimiters()'
400
+ end
401
+
402
+ # :category: Limiter Methods
403
+ # Adds a limiter to the currently defined search and sets the page number back to 1.
404
+ # Returns search Results.
405
+ # ==== Examples
406
+ # results = session.add_limiter('FT','y')
407
+ def add_limiter(id, val)
408
+ add_actions "AddLimiter(#{id}:#{val})"
409
+ end
410
+
411
+ # :category: Limiter Methods
412
+ # Removes the specified limiter and sets the page number back to 1.
413
+ # Returns search Results.
414
+ # ==== Examples
415
+ # results = session.remove_limiter('FT')
416
+ def remove_limiter(id)
417
+ add_actions "RemoveLimiter(#{id})"
418
+ end
419
+
420
+ # :category: Limiter Methods
421
+ # Removes a specified limiter value and sets the page number back to 1.
422
+ # Returns search Results.
423
+ # ==== Examples
424
+ # results = session.remove_limiter_value('LA99','French')
425
+ def remove_limiter_value(id, val)
426
+ add_actions "RemoveLimiterValue(#{id}:#{val})"
427
+ end
428
+
429
+ # --
430
+ # ====================================================================================
431
+ # EXPANDERS
432
+ # ====================================================================================
433
+ # ++
434
+
435
+ # :category: Expander Methods
436
+ # Removes all specified expanders and sets the page number back to 1.
437
+ # Returns search Results.
438
+ def clear_expanders
439
+ add_actions 'ClearExpanders()'
440
+ end
441
+
442
+ # :category: Expander Methods
443
+ # Adds expanders and sets the page number back to 1. Multiple expanders should be comma separated.
444
+ # Returns search Results.
445
+ # ==== Examples
446
+ # results = session.add_expander('thesaurus,fulltext')
447
+ def add_expander(val)
448
+ add_actions "AddExpander(#{val})"
449
+ end
450
+
451
+ # :category: Expander Methods
452
+ # Removes a specified expander. Sets the page number to 1.
453
+ # Returns search Results.
454
+ # ==== Examples
455
+ # results = session.remove_expander('fulltext')
456
+ def remove_expander(val)
457
+ add_actions "RemoveExpander(#{val})"
458
+ end
459
+
460
+ # --
461
+ # ====================================================================================
462
+ # PUBLICATION (this is only used for profiles configured for publication searching)
463
+ # ====================================================================================
464
+ # ++
465
+
466
+ # :category: Publication Methods
467
+ # Specifies a publication to search within. Sets the pages number back to 1.
468
+ # Returns search Results.
469
+ # ==== Examples
470
+ # results = session.add_publication('eric')
471
+ def add_publication(pub_id)
472
+ add_actions "AddPublication(#{pub_id})"
473
+ end
474
+
475
+ # :category: Publication Methods
476
+ # Removes a publication from the search. Sets the page number back to 1.
477
+ # Returns search Results.
478
+ def remove_publication(pub_id)
479
+ add_actions "RemovePublication(#{pub_id})"
480
+ end
481
+
482
+ # :category: Publication Methods
483
+ # Removes all publications from the search. Sets the page number back to 1.
484
+ # Returns search Results.
485
+ def remove_all_publications
486
+ add_actions 'RemovePublication()'
487
+ end
488
+
489
+ # --
490
+ # ====================================================================================
491
+ # INTERNAL METHODS
492
+ # ====================================================================================
493
+ # ++
494
+
495
+ def do_request(method, path:, payload: nil, attempt: 0) # :nodoc:
496
+
497
+ if attempt > MAX_ATTEMPTS
498
+ raise EBSCO::EDS::ApiError, 'EBSCO API error: Multiple attempts to perform request failed.'
499
+ end
500
+
501
+ begin
502
+ resp = connection.send(method) do |req|
503
+ case method
504
+ when :get
505
+ req.url path
506
+ when :post
507
+ req.url path
508
+ unless payload.nil?
509
+ req.body = JSON.generate(payload)
510
+ end
511
+ else
512
+ raise EBSCO::EDS::ApiError, "EBSCO API error: Method #{method} not supported for endpoint #{path}"
513
+ end
514
+ end
515
+ resp.body
516
+ rescue Exception => e
517
+ if e.respond_to? 'fault'
518
+ error_code = e.fault[:error_body]['ErrorNumber'] || e.fault[:error_body]['ErrorCode']
519
+ if not error_code.nil?
520
+ case error_code
521
+ # session token missing
522
+ when '108', '109'
523
+ @session_token = create_session_token
524
+ do_request(method, path: path, payload: payload, attempt: attempt+1)
525
+ # auth token invalid
526
+ when '104', '107'
527
+ @auth_token = nil
528
+ @auth_token = create_auth_token
529
+ do_request(method, path: path, payload: payload, attempt: attempt+1)
530
+ else
531
+ raise e
532
+ end
533
+ end
534
+ else
535
+ raise e
536
+ end
537
+ end
538
+ end
539
+
540
+ # --
541
+ # attempts to query profile capabilities
542
+ # dummy search just to get the list of available databases
543
+ # ++
544
+ def get_available_databases # :nodoc:
545
+ search({query: 'supercalifragilisticexpialidocious-supercalifragilisticexpialidocious',
546
+ results_per_page: 1,
547
+ mode: 'all',
548
+ include_facets: false}).database_stats
549
+ end
550
+
551
+ # :category: Profile Settings Methods
552
+ # Get a list of all available database IDs.
553
+ # Returns Array of IDs.
554
+ def get_available_database_ids
555
+ get_available_databases.map{|item| item[:id]}
556
+ end
557
+
558
+ # :category: Profile Settings Methods
559
+ # Determine if a database ID is available in the profile.
560
+ # Returns Boolean.
561
+ def dbid_in_profile(dbid)
562
+ get_available_database_ids.include? dbid
563
+ end
564
+
565
+ # :category: Profile Settings Methods
566
+ # Determine if publication matching is available in the profile.
567
+ # Returns Boolean.
568
+ def publication_match_in_profile
569
+ @info.available_related_content_types.include? 'emp'
570
+ end
571
+
572
+ # :category: Profile Settings Methods
573
+ # Determine if research starters are available in the profile.
574
+ # Returns Boolean.
575
+ def research_starters_match_in_profile
576
+ @info.available_related_content_types.include? 'rs'
577
+ end
578
+
579
+ private
580
+
581
+ def connection
582
+ Faraday.new(url: EDS_API_BASE) do |faraday|
583
+ faraday.headers['Content-Type'] = 'application/json;charset=UTF-8'
584
+ faraday.headers['Accept'] = 'application/json'
585
+ faraday.headers['x-sessionToken'] = @session_token ? @session_token : ''
586
+ faraday.headers['x-authenticationToken'] = @auth_token ? @auth_token : ''
587
+ faraday.headers['User-Agent'] = USER_AGENT
588
+ faraday.request :url_encoded
589
+ faraday.use FaradayMiddleware::RaiseHttpException
590
+ faraday.response :json, :content_type => /\bjson$/
591
+ #faraday.response :logger, Logger.new(LOG)
592
+ faraday.adapter Faraday.default_adapter
593
+ end
594
+ end
595
+
596
+ def create_auth_token
597
+ if blank?(@auth_token)
598
+ # ip auth
599
+ if (blank?(@user) && blank?(@pass)) || @auth_type.casecmp('ip') == 0
600
+ _response = do_request(:post, path: IP_AUTH_URL)
601
+ @auth_token = _response['AuthToken']
602
+ # user auth
603
+ else
604
+ _response = do_request(:post, path: UID_AUTH_URL, payload: {:UserId => @user, :Password => @pass})
605
+ @auth_token = _response['AuthToken']
606
+ end
607
+ end
608
+ @auth_token
609
+ end
610
+
611
+ def create_session_token
612
+ _response = do_request(:post, path: CREATE_SESSION_URL, payload: {:Profile => @profile, :Guest => @guest})
613
+ @session_token = _response['SessionToken']
614
+ end
615
+
616
+ # helper methods
617
+ def blank?(var)
618
+ var.nil? || var.respond_to?(:length) && var.length == 0
619
+ end
620
+
621
+ end
622
+
623
+ end
624
+ end