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 +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:
|