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 +4 -4
- data/lib/uri_service.rb +1 -1
- data/lib/uri_service/client.rb +104 -60
- data/lib/uri_service/version.rb +1 -1
- metadata +1 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ea6881c7a5b2ef53d0c1ac66821ee4ea30820f46
|
4
|
+
data.tar.gz: 56cc8b75b8756a3084a018e5d1cbe26b2fe8632e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8ba7fd8de87e27a5d32f1c7ac4afebb58e05b0226adc7ddbcc9b1cc1f7783e835673c4c062cfecd554f51d7b7ae7a750d4f13740cece73f5d8c9bef62b8199cb
|
7
|
+
data.tar.gz: c3e4f1f75f2083fbd9050eaaf7367c59a6afb36f1f1a9b01714d15710bd558d2d86a5e4108d99deea3db5b1da7fbe1eb09315d8f83d152381d3d5d8ed027b13d
|
data/lib/uri_service.rb
CHANGED
data/lib/uri_service/client.rb
CHANGED
@@ -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
|
-
|
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
|
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'] =
|
171
|
-
doc['value'] =
|
172
|
-
doc['
|
173
|
-
doc['
|
174
|
-
|
175
|
-
|
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(
|
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(
|
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
|
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
|
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
|
222
|
-
|
223
|
-
term_solr_doc.
|
224
|
-
|
225
|
-
|
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
|
-
|
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 => '
|
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 <<
|
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
|
-
|
284
|
-
|
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
|
-
|
298
|
-
term_additional_fields = term['additional_fields'].nil? ? {} : JSON.parse(term['additional_fields'])
|
338
|
+
term_db_row = dataset.first
|
299
339
|
|
300
|
-
|
301
|
-
|
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
|
-
|
308
|
-
|
309
|
-
|
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(
|
313
|
-
self.send_term_to_solr(
|
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
|
data/lib/uri_service/version.rb
CHANGED
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.
|
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:
|