uri_service 0.1.1 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3e2701d3c51e490b0ed59b0d69c0b4f4d0606fab
4
- data.tar.gz: 345a4844c537e35bbcffe25a87627b24e6dc870e
3
+ metadata.gz: ea6881c7a5b2ef53d0c1ac66821ee4ea30820f46
4
+ data.tar.gz: 56cc8b75b8756a3084a018e5d1cbe26b2fe8632e
5
5
  SHA512:
6
- metadata.gz: d349b930a9f9b30148b58c56430bcb4095ae1f410b3cc9491b614037ffdeedd9b8f39acb901e3178660d03ea11ab2f48e7ed4163932ae46645661dbbdd919f85
7
- data.tar.gz: fb7f23d7561b83092053599962b70ebea5fbf431318d7966e910123f1c9c4c135ef190b7b7764ae0e1f3964fe8a8ed12eeefa5f9d58dc345ad3b3f110e71dbaa
6
+ metadata.gz: 8ba7fd8de87e27a5d32f1c7ac4afebb58e05b0226adc7ddbcc9b1cc1f7783e835673c4c062cfecd554f51d7b7ae7a750d4f13740cece73f5d8c9bef62b8199cb
7
+ data.tar.gz: c3e4f1f75f2083fbd9050eaaf7367c59a6afb36f1f1a9b01714d15710bd558d2d86a5e4108d99deea3db5b1da7fbe1eb09315d8f83d152381d3d5d8ed027b13d
data/lib/uri_service.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'active_support/inflector'
1
+ require 'active_support/all'
2
2
  require 'connection_pool'
3
3
  require 'rsolr'
4
4
  require 'sequel'
@@ -4,6 +4,7 @@ class UriService::Client
4
4
 
5
5
  ALPHANUMERIC_UNDERSCORE_KEY_REGEX = /\A[a-z]+[a-z0-9_]*\z/
6
6
  VALID_URI_REGEX = /\A#{URI::regexp(['http', 'https'])}\z/
7
+ CORE_FIELD_NAMES = ['uri', 'vocabulary_string_key', 'value', 'is_local']
7
8
 
8
9
  def initialize(opts)
9
10
  raise UriService::InvalidOptsError, "Must supply opts['local_uri_base'] to initialize method." if opts['local_uri_base'].nil?
@@ -92,6 +93,7 @@ class UriService::Client
92
93
 
93
94
  def create_vocabulary(string_key, display_label)
94
95
  if string_key.to_s == 'all'
96
+ # Note: There isn't currently a use case for searching across 'all' vocabularies, but I'm leaving this restriction as a placeholder in case that changes.
95
97
  raise UriService::InvalidVocabularyStringKeyError, 'The value "all" is a reserved word and cannot be used as the string_key value for a vocabulary.'
96
98
  end
97
99
  unless string_key =~ ALPHANUMERIC_UNDERSCORE_KEY_REGEX
@@ -110,7 +112,7 @@ class UriService::Client
110
112
 
111
113
  # Creates a new term.
112
114
  def create_term(vocabulary_string_key, value, term_uri, additional_fields={})
113
- self.create_term_impl(vocabulary_string_key, value, term_uri, additional_fields, false)
115
+ return self.create_term_impl(vocabulary_string_key, value, term_uri, additional_fields, false)
114
116
  end
115
117
 
116
118
  # Creates a new local term, auto-generating a URI
@@ -124,18 +126,19 @@ class UriService::Client
124
126
  # Getting a duplicate UUID from SecureRandom.uuid is EXTREMELY unlikely, but we'll account for it just in case (by making a few more attempts).
125
127
  5.times {
126
128
  begin
127
- self.create_term_impl(vocabulary_string_key, value, term_uri, additional_fields, true)
128
- break
129
+ return self.create_term_impl(vocabulary_string_key, value, term_uri, additional_fields, true)
129
130
  rescue UriService::ExistingUriError
130
- if defined?(Rails)
131
- Rails.logger.error "UriService generated a duplicate random UUID (via SecureRandom.uuid) and will now attempt to create another. This type of problem is EXTREMELY rare."
132
- end
131
+ raise UriService::ExistingUriError, "UriService generated a duplicate random UUID (via SecureRandom.uuid) and will now attempt to create another. This type of problem is EXTREMELY rare."
133
132
  end
134
133
  }
134
+
135
+ return nil
135
136
  end
136
137
 
137
138
  def create_term_impl(vocabulary_string_key, value, term_uri, additional_fields, is_local)
138
139
 
140
+ additional_fields.stringify_keys!
141
+
139
142
  #Ensure that vocabulary with vocabulary_string_key exists
140
143
  if self.find_vocabulary(vocabulary_string_key).nil?
141
144
  raise UriService::NonExistentVocabularyError, "There is no vocabulary with string key: " + vocabulary_string_key
@@ -155,46 +158,83 @@ class UriService::Client
155
158
  vocabulary_string_key: vocabulary_string_key,
156
159
  additional_fields: JSON.generate(additional_fields)
157
160
  )
158
-
159
- self.send_term_to_solr(@db[UriService::TERMS].where(uri: term_uri).first)
160
-
161
+ send_term_to_solr(vocabulary_string_key, value, term_uri, additional_fields, is_local)
161
162
  rescue Sequel::UniqueConstraintViolation
162
163
  raise UriService::ExistingUriError, "A term already exists with uri: " + term_uri + " (conflict found via uri_hash check)"
163
164
  end
164
165
  end
165
166
 
167
+ return generate_frozen_term_hash(vocabulary_string_key, value, term_uri, additional_fields, is_local)
166
168
  end
167
169
 
168
- def term_db_row_to_solr_doc(term_row_data_from_db)
170
+ def generate_frozen_term_hash(vocabulary_string_key, value, uri, additional_fields, is_local)
171
+ hash_to_return = {}
172
+ hash_to_return['uri'] = uri
173
+ hash_to_return['value'] = value
174
+ hash_to_return['is_local'] = is_local
175
+ hash_to_return['vocabulary_string_key'] = vocabulary_string_key
176
+
177
+ additional_fields.each do |key, val|
178
+ hash_to_return[key] = val
179
+ end
180
+
181
+ hash_to_return.freeze # To make this a read-only hash
182
+
183
+ return hash_to_return
184
+ end
185
+
186
+ def create_term_solr_doc(vocabulary_string_key, value, uri, additional_fields, is_local)
169
187
  doc = {}
170
- doc['uri'] = term_row_data_from_db[:uri]
171
- doc['value'] = term_row_data_from_db[:value]
172
- doc['is_local_bsi'] = term_row_data_from_db[:is_local]
173
- doc['vocabulary_string_key_ssi'] = term_row_data_from_db[:vocabulary_string_key]
174
- JSON.parse(term_row_data_from_db[:additional_fields]).each do |key, val|
175
- doc[key + '_ssi'] = val
188
+ doc['uri'] = uri
189
+ doc['value'] = value
190
+ doc['is_local'] = is_local
191
+ doc['vocabulary_string_key'] = vocabulary_string_key
192
+
193
+ additional_fields.each do |key, val|
194
+ doc[key + self.class.get_solr_suffix_for_object(val)] = val
176
195
  end
177
196
 
178
197
  return doc
179
198
  end
180
199
 
200
+ def self.get_solr_suffix_for_object(obj)
201
+ if obj.is_a?(Array)
202
+ # Note boolean arrays aren't supported because they don't seem useful in this context
203
+ if obj[0].is_a?(Fixnum)
204
+ return '_isim'
205
+ else
206
+ # Treat like a string array
207
+ return '_ssim'
208
+ end
209
+ else
210
+ if obj.is_a?(String)
211
+ return '_ssi'
212
+ elsif obj.is_a?(TrueClass) || obj.is_a?(FalseClass)
213
+ return '_bsi'
214
+ elsif obj.is_a?(Fixnum)
215
+ return '_isi'
216
+ else
217
+ raise UriService::UnsupportedObjectTypeError, "Unable to determine solr suffix for unsupported object type."
218
+ end
219
+ end
220
+ end
221
+
181
222
  # Index the DB row term data into solr
182
- def send_term_to_solr(term_row_data_from_db, commit=true)
183
-
223
+ def send_term_to_solr(vocabulary_string_key, value, term_uri, additional_fields, is_local, commit=true)
224
+ doc = create_term_solr_doc(vocabulary_string_key, value, term_uri, additional_fields, is_local)
184
225
  @rsolr_pool.with do |rsolr|
185
- rsolr.add(term_db_row_to_solr_doc(term_row_data_from_db))
226
+ rsolr.add(doc)
186
227
  rsolr.commit if commit
187
228
  end
188
229
  end
189
230
 
190
231
  # Validates additional_fields and verifies that no reserved words are supplied
191
232
  def validate_additional_fields(additional_fields)
192
- reserved_keys = ['is_local', 'uri', 'value', 'vocabulary_string_key']
193
233
  additional_fields.each do |key, value|
194
- if reserved_keys.include?(key.to_s)
234
+ if CORE_FIELD_NAMES.include?(key.to_s)
195
235
  raise UriService::InvalidAdditionalFieldKeyError, "Cannot supply the key \"#{key.to_s}\" as an additional field because it is a reserved key."
196
236
  end
197
- unless key =~ ALPHANUMERIC_UNDERSCORE_KEY_REGEX
237
+ unless key.to_s =~ ALPHANUMERIC_UNDERSCORE_KEY_REGEX
198
238
  raise UriService::InvalidAdditionalFieldKeyError, "Invalid key (can only include lower case letters, numbers or underscores, but cannot start with an underscore): " + key
199
239
  end
200
240
  end
@@ -212,37 +252,47 @@ class UriService::Client
212
252
  UriService.client.rsolr_pool.with do |rsolr|
213
253
  response = rsolr.get('select', params: { :q => '*:*', :fq => 'uri:' + UriService.solr_escape(uri) })
214
254
  if response['response']['numFound'] == 1
215
- return term_solr_doc_to_term_hash(response['response']['docs'].first)
255
+ return term_solr_doc_to_frozen_term_hash(response['response']['docs'].first)
216
256
  end
217
257
  end
218
258
  return nil
219
259
  end
220
260
 
221
- def term_solr_doc_to_term_hash(term_solr_doc)
222
- term_hash = {}
223
- term_solr_doc.each do |key, value|
224
- next if ['_version_', 'timestamp', 'score'].include?(key) # Skip certain automatically added fields that we don't care about
225
- term_hash[key.gsub(/_[^_]+$/, '')] = value # Remove trailing '_si', '_bi', etc. if present
261
+ def term_solr_doc_to_frozen_term_hash(term_solr_doc)
262
+
263
+ uri = term_solr_doc.delete('uri')
264
+ vocabulary_string_key = term_solr_doc.delete('vocabulary_string_key')
265
+ value = term_solr_doc.delete('value')
266
+ is_local = term_solr_doc.delete('is_local')
267
+ additional_fields = {}
268
+
269
+ # Iterate through remaining keys and put them in additional_fields after removing suffixes
270
+ term_solr_doc.each do |key, val|
271
+ # Skip certain automatically added fields that aren't part of the term_hash
272
+ next if ['_version_', 'timestamp', 'score'].include?(key)
273
+
274
+ # Remove trailing '_si', '_bi', etc. if present for solr-suffixed fields
275
+ additional_fields[key.gsub(/_[^_]+$/, '')] = val
226
276
  end
227
- return term_hash
277
+
278
+ return generate_frozen_term_hash(vocabulary_string_key, value, uri, additional_fields, is_local)
228
279
  end
229
280
 
230
- def find_terms_by_query(vocabulary_string_key, value_query)
231
-
232
- return [] if value_query.empty?
233
-
281
+ def find_terms_by_query(vocabulary_string_key, value_query, limit=10, start=0)
234
282
  terms_to_return = []
235
283
  UriService.client.rsolr_pool.with do |rsolr|
236
284
 
237
285
  solr_params = {
238
- :q => UriService.solr_escape(value_query),
239
- :fq => 'vocabulary_string_key_ssi:' + UriService.solr_escape(vocabulary_string_key)
286
+ :q => value_query == '' ? '*' : UriService.solr_escape(value_query),
287
+ :fq => 'vocabulary_string_key:' + UriService.solr_escape(vocabulary_string_key),
288
+ :rows => limit,
289
+ :start => start
240
290
  }
241
291
 
242
292
  response = rsolr.get('suggest', params: solr_params)
243
293
  if response['response']['numFound'] > 0
244
294
  response['response']['docs'].each do |doc|
245
- terms_to_return << term_solr_doc_to_term_hash(doc)
295
+ terms_to_return << term_solr_doc_to_frozen_term_hash(doc)
246
296
  end
247
297
  end
248
298
  end
@@ -280,39 +330,32 @@ class UriService::Client
280
330
  end
281
331
  end
282
332
 
283
- def update_term_value(term_uri, value)
284
- self.update_term_impl(term_uri, value, {}, true)
285
- end
286
-
287
- def update_term_additional_fields(term_uri, additional_fields, merge=false)
288
- self.update_term_impl(term_uri, nil, additional_fields, merge)
289
- end
290
-
291
- def update_term_impl(term_uri, value, additional_fields, merge_additional_fields)
292
-
333
+ # opts format: {:value => 'new value', :additional_fields => {'key' => 'value'}}
334
+ def update_term(term_uri, opts, merge_additional_fields=true)
293
335
  dataset = @db[UriService::TERMS].where(uri: term_uri)
294
336
  raise UriService::NonExistentUriError, "No term found with uri: " + term_uri if dataset.count == 0
295
- validate_additional_fields(additional_fields)
296
337
 
297
- term = dataset.first
298
- term_additional_fields = term['additional_fields'].nil? ? {} : JSON.parse(term['additional_fields'])
338
+ term_db_row = dataset.first
299
339
 
300
- if merge_additional_fields
301
- term_additional_fields.merge!(additional_fields)
302
- term_additional_fields.delete_if { |k, v| v.nil? } # Delete nil values. This is a way to clear data in additional_fields.
303
- else
304
- term_additional_fields = additional_fields
305
- end
340
+ new_value = opts[:value] || term_db_row[:value]
341
+ new_additional_fields = term_db_row[:additional_fields].nil? ? {} : JSON.parse(term_db_row[:additional_fields])
306
342
 
307
- new_data = {}
308
- new_data[:value] = value unless value.nil?
309
- new_data[:additional_fields] = JSON.generate(term_additional_fields)
343
+ unless opts[:additional_fields].nil?
344
+ if merge_additional_fields
345
+ new_additional_fields.merge!(opts[:additional_fields])
346
+ new_additional_fields.delete_if { |k, v| v.nil? } # Delete nil values. This is a way to clear data in additional_fields.
347
+ else
348
+ new_additional_fields = opts[:additional_fields]
349
+ end
350
+ end
351
+ validate_additional_fields(new_additional_fields)
310
352
 
311
353
  @db.transaction do
312
- dataset.update(new_data)
313
- self.send_term_to_solr(@db[UriService::TERMS].where(uri: term_uri).first)
354
+ dataset.update(value: new_value, additional_fields: JSON.generate(new_additional_fields))
355
+ self.send_term_to_solr(term_db_row[:vocabulary_string_key], new_value, term_uri, new_additional_fields, term_db_row[:is_local])
314
356
  end
315
357
 
358
+ return generate_frozen_term_hash(term_db_row[:vocabulary_string_key], new_value, term_uri, new_additional_fields, term_db_row[:is_local])
316
359
  end
317
360
 
318
361
  end
@@ -325,3 +368,4 @@ class UriService::ExistingUriError < StandardError;end
325
368
  class UriService::ExistingVocabularyStringKeyError < StandardError;end
326
369
  class UriService::NonExistentUriError < StandardError;end
327
370
  class UriService::NonExistentVocabularyError < StandardError;end
371
+ class UriService::UnsupportedObjectTypeError < StandardError;end
@@ -1,6 +1,6 @@
1
1
  module UriService
2
2
 
3
- VERSION = '0.1.1'
3
+ VERSION = '0.2.0'
4
4
 
5
5
  def self.version
6
6
  VERSION
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uri_service
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric O'Hanlon
@@ -202,4 +202,3 @@ specification_version: 4
202
202
  summary: A service for registering local URIs and performing both local and remote
203
203
  URI lookups.
204
204
  test_files: []
205
- has_rdoc: