uri_service 0.1.1 → 0.2.0

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