triannon 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +21 -16
- data/Rakefile +15 -18
- data/app/controllers/concerns/rdf_response_formats.rb +16 -14
- data/app/controllers/triannon/annotations_controller.rb +6 -6
- data/app/models/triannon/annotation.rb +5 -5
- data/app/models/triannon/annotation_ldp.rb +17 -16
- data/app/services/triannon/ldp_loader.rb +14 -4
- data/app/services/triannon/ldp_to_oa_mapper.rb +68 -59
- data/app/services/triannon/ldp_writer.rb +180 -59
- data/app/services/triannon/solr_searcher.rb +8 -8
- data/app/services/triannon/solr_writer.rb +12 -12
- data/config/jetty/etc/fedora-override-web.xml +67 -0
- data/config/routes.rb +2 -2
- data/config/triannon.yml +29 -6
- data/lib/generators/triannon/install_generator.rb +23 -3
- data/lib/rdf/triannon_vocab.rb +2 -2
- data/lib/tasks/triannon_tasks.rake +19 -5
- data/lib/triannon.rb +13 -20
- data/lib/triannon/iiif_anno_list.rb +4 -4
- data/lib/triannon/oa_graph_helper.rb +51 -0
- data/lib/triannon/version.rb +1 -1
- metadata +35 -39
- data/app/services/triannon/root_annotation_creator.rb +0 -36
- data/lib/triannon/graph.rb +0 -173
- data/lib/triannon/jsonld_context.rb +0 -13
@@ -4,7 +4,8 @@ module Triannon
|
|
4
4
|
class LdpWriter
|
5
5
|
|
6
6
|
# use LDP protocol to create the OpenAnnotation.Annotation in an RDF store
|
7
|
-
# @param [Triannon::Annotation] anno a Triannon::Annotation object, from
|
7
|
+
# @param [Triannon::Annotation] anno a Triannon::Annotation object, from
|
8
|
+
# which we use the graph
|
8
9
|
def self.create_anno(anno)
|
9
10
|
if anno && anno.graph
|
10
11
|
# TODO: special case if the Annotation object already has an id --
|
@@ -12,13 +13,13 @@ module Triannon
|
|
12
13
|
ldp_writer = Triannon::LdpWriter.new anno
|
13
14
|
id = ldp_writer.create_base
|
14
15
|
|
15
|
-
bodies_solns = anno.graph.query([nil, RDF::
|
16
|
+
bodies_solns = anno.graph.query([nil, RDF::Vocab::OA.hasBody, nil])
|
16
17
|
if bodies_solns.size > 0
|
17
18
|
ldp_writer.create_body_container
|
18
19
|
ldp_writer.create_body_resources
|
19
20
|
end
|
20
21
|
|
21
|
-
targets_solns = anno.graph.query([nil, RDF::
|
22
|
+
targets_solns = anno.graph.query([nil, RDF::Vocab::OA.hasTarget, nil])
|
22
23
|
# NOTE: Annotation is invalid if there are no target statements
|
23
24
|
if targets_solns.size > 0
|
24
25
|
ldp_writer.create_target_container
|
@@ -29,39 +30,130 @@ module Triannon
|
|
29
30
|
end
|
30
31
|
end
|
31
32
|
|
32
|
-
# deletes the indicated container and all its child containers from the LDP
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
33
|
+
# deletes the indicated container and all its child containers from the LDP
|
34
|
+
# store
|
35
|
+
# @param [String] id the unique id for the LDP container for an annotation.
|
36
|
+
# May be a compound id, such as uuid1/t/uuid2, in which case the LDP
|
37
|
+
# container object uuid2 and its children are deleted from the LDP
|
38
|
+
# store, but LDP containers uuid1/t and uuid1 are not deleted from
|
39
|
+
# the LDP store.
|
36
40
|
def self.delete_container id
|
37
41
|
if id && id.size > 0
|
38
42
|
ldpw = Triannon::LdpWriter.new nil
|
39
43
|
ldpw.delete_containers id
|
40
44
|
end
|
41
45
|
end
|
42
|
-
|
43
46
|
class << self
|
44
47
|
alias_method :delete_anno, :delete_container
|
45
48
|
end
|
46
49
|
|
50
|
+
# @param [String] path the path part of the container url, after the ldp base url
|
51
|
+
# @return [Boolean] true if container already exists; false otherwise
|
52
|
+
def self.container_exist? path
|
53
|
+
base_url = Triannon.config[:ldp]['url'].strip
|
54
|
+
path.strip!
|
55
|
+
separator = (base_url.end_with?('/') || path.start_with?('/')) ? "" : '/'
|
56
|
+
conn = Faraday.new url: base_url + separator + path
|
57
|
+
resp = conn.head
|
58
|
+
if resp.status.between?(400, 600)
|
59
|
+
false
|
60
|
+
else
|
61
|
+
true
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Creates an empty LDP BasicContainer in LDP Storage
|
66
|
+
# @param [String] parent_path the path part, after the ldp base url -- in
|
67
|
+
# essence, the LDP BasicContainer that will be the parent of the
|
68
|
+
# to-be-created BasicContainer.
|
69
|
+
# @param [String] slug the value to send in Http Header 'Slug' -- this is
|
70
|
+
# appended to the parent container's path to become the id of the newly
|
71
|
+
# created BasicContainer
|
72
|
+
# @return [Boolean] true if the container was created; false otherwise
|
73
|
+
def self.create_basic_container parent_path, slug
|
74
|
+
if slug.blank?
|
75
|
+
puts "create_basic_container called with nil or empty slug, parent_path '#{parent_path}'"
|
76
|
+
return false
|
77
|
+
end
|
78
|
+
base_url = Triannon.config[:ldp]['url'].strip
|
79
|
+
base_url.chop! if base_url.end_with?('/')
|
80
|
+
slug.strip!
|
81
|
+
slug = slug[1..-1] if slug.start_with?('/')
|
82
|
+
if parent_path
|
83
|
+
parent_path.strip!
|
84
|
+
parent_path = parent_path[1..-1] if parent_path.start_with?('/')
|
85
|
+
parent_path.chop! if parent_path.end_with?('/')
|
86
|
+
end
|
87
|
+
|
88
|
+
full_path = (parent_path ? parent_path + '/' : "") + slug
|
89
|
+
full_url = base_url + '/' + full_path
|
90
|
+
|
91
|
+
if container_exist? full_path
|
92
|
+
puts "Container #{full_url} already exists."
|
93
|
+
false
|
94
|
+
else
|
95
|
+
g = RDF::Graph.new
|
96
|
+
null_rel_uri = RDF::URI.new
|
97
|
+
g << [null_rel_uri, RDF.type, RDF::Vocab::LDP.BasicContainer]
|
98
|
+
conn = Faraday.new url: base_url + (parent_path ? '/' + parent_path : "")
|
99
|
+
resp = conn.post do |req|
|
100
|
+
# Note from Fcrepo docs:
|
101
|
+
# https://wiki.duraspace.org/display/FEDORA41/RESTful+HTTP+API+-+Containers#RESTfulHTTPAPI-Containers-BluePOSTCreatenewresourceswithinaLDPcontainer
|
102
|
+
# "If the MIME type corresponds to a supported RDF format or SPARQL-Update, the uploaded content will be
|
103
|
+
# parsed as RDF and used to populate the child node properties. RDF will be interpreted using the current
|
104
|
+
# resource as the base URI (e.g. <> will be expanded to the current URI)."
|
105
|
+
# Thus, the next line is needed even if the body of this POST is empty
|
106
|
+
req.headers['Content-Type'] = 'text/turtle'
|
107
|
+
req.headers['Slug'] = slug
|
108
|
+
req.body = g.to_ttl
|
109
|
+
end
|
110
|
+
|
111
|
+
if resp.status == 201
|
112
|
+
new_url = resp.headers['Location'] ? resp.headers['Location'] : resp.headers['location']
|
113
|
+
if new_url == full_url
|
114
|
+
puts "Created Basic Container #{new_url}"
|
115
|
+
true
|
116
|
+
else
|
117
|
+
puts "Created Basic Container #{new_url} instead of #{full_url}"
|
118
|
+
false
|
119
|
+
end
|
120
|
+
else
|
121
|
+
puts "Unable to create Basic Container #{full_url}: LDP Storage response status #{resp.status}; body #{resp.body}"
|
122
|
+
false
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
|
47
128
|
# @param [Triannon::Annotation] anno a Triannon::Annotation object
|
48
|
-
# @param [String] id the unique id for the LDP container for the passed
|
49
|
-
|
129
|
+
# @param [String] id the unique id for the LDP container for the passed
|
130
|
+
# annotation; defaults to nil
|
131
|
+
def initialize(anno, id = nil)
|
50
132
|
@anno = anno
|
51
133
|
@id = id
|
52
|
-
|
134
|
+
base_url = Triannon.config[:ldp]['url'].strip
|
135
|
+
base_url.chop! if base_url.end_with?('/')
|
136
|
+
container_path = Triannon.config[:ldp]['uber_container']
|
137
|
+
if container_path
|
138
|
+
container_path.strip!
|
139
|
+
container_path = container_path[1..-1] if container_path.start_with?('/')
|
140
|
+
container_path.chop! if container_path.end_with?('/')
|
141
|
+
end
|
142
|
+
@base_uri = "#{base_url}/#{container_path}"
|
53
143
|
end
|
54
144
|
|
55
|
-
# creates a stored LDP container for this object's Annotation, without its
|
145
|
+
# creates a stored LDP container for this object's Annotation, without its
|
146
|
+
# targets or bodies (as those are put in descendant containers)
|
56
147
|
# SIDE EFFECT: assigns the uuid of the container created to @id
|
57
|
-
# @return [String] the unique id for the LDP container created for this
|
148
|
+
# @return [String] the unique id for the LDP container created for this
|
149
|
+
# annotation
|
58
150
|
def create_base
|
59
151
|
if @anno.graph.query([nil, RDF::Triannon.externalReference, nil]).count > 0
|
60
|
-
|
152
|
+
fail Triannon::ExternalReferenceError, "Incoming annotations may not have http://triannon.stanford.edu/ns/externalReference as a predicate."
|
61
153
|
end
|
62
154
|
|
63
155
|
if @anno.graph.id_as_url && @anno.graph.id_as_url.size > 0
|
64
|
-
|
156
|
+
fail Triannon::ExternalReferenceError, "Incoming new annotations may not have an existing id (yet)."
|
65
157
|
end
|
66
158
|
|
67
159
|
# TODO: special case if the Annotation object already has an id --
|
@@ -72,7 +164,7 @@ module Triannon
|
|
72
164
|
@anno.graph.each { |s|
|
73
165
|
g << s
|
74
166
|
}
|
75
|
-
g =
|
167
|
+
g = OA::Graph.new(g)
|
76
168
|
g.remove_non_base_statements
|
77
169
|
g.make_null_relative_uri_out_of_blank_node
|
78
170
|
|
@@ -81,26 +173,27 @@ module Triannon
|
|
81
173
|
|
82
174
|
# creates the LDP container for any and all bodies for this annotation
|
83
175
|
def create_body_container
|
84
|
-
create_direct_container RDF::
|
176
|
+
create_direct_container RDF::Vocab::OA.hasBody
|
85
177
|
end
|
86
178
|
|
87
179
|
# creates the LDP container for any and all targets for this annotation
|
88
180
|
def create_target_container
|
89
|
-
create_direct_container RDF::
|
181
|
+
create_direct_container RDF::Vocab::OA.hasTarget
|
90
182
|
end
|
91
183
|
|
92
184
|
# create the body resources inside the (already created) body container
|
93
185
|
def create_body_resources
|
94
|
-
create_resources_in_container RDF::
|
186
|
+
create_resources_in_container RDF::Vocab::OA.hasBody
|
95
187
|
end
|
96
188
|
|
97
189
|
# create the target resources inside the (already created) target container
|
98
190
|
def create_target_resources
|
99
|
-
create_resources_in_container RDF::
|
191
|
+
create_resources_in_container RDF::Vocab::OA.hasTarget
|
100
192
|
end
|
101
193
|
|
102
|
-
# @param [Array<String>] ldp_container_uris an Array of ids for LDP
|
103
|
-
# e.g. [@base_uri/(uuid1)/t/(uuid2),
|
194
|
+
# @param [Array<String>] ldp_container_uris an Array of ids for LDP
|
195
|
+
# containers. (can also be a String) e.g. [@base_uri/(uuid1)/t/(uuid2),
|
196
|
+
# @base_uri/(uuid1)/t/(uuid3)] or [@base_uri/(uuid)] or (uuid)
|
104
197
|
# @return true if a resource was deleted; false otherwise
|
105
198
|
def delete_containers ldp_container_uris
|
106
199
|
return false if !ldp_container_uris || ldp_container_uris.empty?
|
@@ -112,7 +205,7 @@ module Triannon
|
|
112
205
|
ldp_id = uri.to_s.split(@base_uri + '/').last
|
113
206
|
resp = conn.delete { |req| req.url ldp_id }
|
114
207
|
if resp.status != 204
|
115
|
-
|
208
|
+
fail Triannon::LDPStorageError.new("Unable to delete LDP container #{ldp_id}", resp.status, resp.body)
|
116
209
|
end
|
117
210
|
something_deleted = true
|
118
211
|
}
|
@@ -121,36 +214,57 @@ module Triannon
|
|
121
214
|
|
122
215
|
protected
|
123
216
|
|
124
|
-
# POSTS a ttl representation of a graph to an existing LDP container in the
|
125
|
-
#
|
126
|
-
# @param [String]
|
127
|
-
#
|
128
|
-
#
|
129
|
-
#
|
130
|
-
#
|
217
|
+
# POSTS a ttl representation of a graph to an existing LDP container in the
|
218
|
+
# LDP store
|
219
|
+
# @param [String] ttl a turtle representation of RDF data to be put in the
|
220
|
+
# LDP container
|
221
|
+
# @param [String] parent_path the path portion of the url for the LDP parent
|
222
|
+
# container for this resource if no path is supplied, then the resource
|
223
|
+
# will be created as a child of the root annotation; expected paths would
|
224
|
+
# also be (anno_id)/t for a target resource (inside the target container
|
225
|
+
# of anno_id) or (anno_id)/b for a body resource (inside the body
|
226
|
+
# container of anno_id)
|
227
|
+
# @return [String] path_id representing the unique path of the newly created LDP
|
228
|
+
# container
|
131
229
|
def create_resource ttl, parent_path = nil
|
132
230
|
return if !ttl || ttl.empty?
|
231
|
+
|
232
|
+
base_url = @base_uri.strip
|
233
|
+
base_url.chop! if base_url.end_with?('/')
|
234
|
+
if parent_path
|
235
|
+
parent_path.strip!
|
236
|
+
parent_path = parent_path[1..-1] if parent_path.start_with?('/')
|
237
|
+
parent_path.chop! if parent_path.end_with?('/')
|
238
|
+
end
|
239
|
+
|
133
240
|
resp = conn.post do |req|
|
134
241
|
req.url parent_path if parent_path
|
135
242
|
req.headers['Content-Type'] = 'application/x-turtle'
|
136
243
|
req.body = ttl
|
137
244
|
end
|
138
245
|
if resp.status != 200 && resp.status != 201
|
139
|
-
|
246
|
+
fail Triannon::LDPStorageError.new("Unable to create LDP resource in container #{parent_path}; RDF sent: #{ttl}", resp.status, resp.body)
|
140
247
|
end
|
141
248
|
new_url = resp.headers['Location'] ? resp.headers['Location'] : resp.headers['location']
|
142
|
-
|
249
|
+
if new_url
|
250
|
+
new_url = new_url.split(base_url + '/' + parent_path.to_s).last
|
251
|
+
new_url = new_url[1..-1] if new_url.start_with?('/')
|
252
|
+
end
|
253
|
+
new_url
|
143
254
|
end
|
144
255
|
|
145
|
-
# Creates an empty LDP DirectContainer in LDP Storage that is a member of
|
146
|
-
#
|
147
|
-
#
|
256
|
+
# Creates an empty LDP DirectContainer in LDP Storage that is a member of
|
257
|
+
# the base container at @id and has the memberRelation per the
|
258
|
+
# oa_vocab_term. The id of the created container will be (base container
|
259
|
+
# id)/b if hasBody or (base container id)/t if hasTarget
|
260
|
+
# @param [RDF::Vocabulary::Term] oa_vocab_term RDF::Vocab::OA.hasTarget or
|
261
|
+
# RDF::Vocab::OA.hasBody
|
148
262
|
def create_direct_container oa_vocab_term
|
149
263
|
null_rel_uri = RDF::URI.new
|
150
264
|
g = RDF::Graph.new
|
151
|
-
g << [null_rel_uri, RDF.type, RDF::LDP.DirectContainer]
|
152
|
-
g << [null_rel_uri, RDF::LDP.hasMemberRelation, oa_vocab_term]
|
153
|
-
g << [null_rel_uri, RDF::LDP.membershipResource, RDF::URI.new("#{@base_uri}/#{@id}")]
|
265
|
+
g << [null_rel_uri, RDF.type, RDF::Vocab::LDP.DirectContainer]
|
266
|
+
g << [null_rel_uri, RDF::Vocab::LDP.hasMemberRelation, oa_vocab_term]
|
267
|
+
g << [null_rel_uri, RDF::Vocab::LDP.membershipResource, RDF::URI.new("#{@base_uri}/#{@id}")]
|
154
268
|
|
155
269
|
resp = conn.post do |req|
|
156
270
|
req.url "#{@id}"
|
@@ -160,13 +274,15 @@ module Triannon
|
|
160
274
|
req.body = g.to_ttl
|
161
275
|
end
|
162
276
|
if resp.status != 201
|
163
|
-
|
277
|
+
fail Triannon::LDPStorageError.new("Unable to create #{oa_vocab_term.fragment.sub('has', '')} LDP container for anno; RDF sent: #{g.to_ttl}", resp.status, resp.body)
|
164
278
|
end
|
165
279
|
resp
|
166
280
|
end
|
167
281
|
|
168
|
-
# create the target/body resources inside the (already created) target/body
|
169
|
-
#
|
282
|
+
# create the target/body resources inside the (already created) target/body
|
283
|
+
# container
|
284
|
+
# @param [RDF::URI] predicate either RDF::Vocab::OA.hasTarget or
|
285
|
+
# RDF::Vocab::OA.hasBody
|
170
286
|
def create_resources_in_container(predicate)
|
171
287
|
predicate_solns = @anno.graph.query([nil, predicate, nil])
|
172
288
|
resource_ids = []
|
@@ -174,11 +290,13 @@ module Triannon
|
|
174
290
|
graph_for_resource = RDF::Graph.new
|
175
291
|
predicate_obj = predicate_stmt.object
|
176
292
|
if predicate_obj.is_a?(RDF::Node)
|
177
|
-
# we need to use the null relative URI representation of blank nodes
|
293
|
+
# we need to use the null relative URI representation of blank nodes
|
294
|
+
# to write to LDP
|
178
295
|
predicate_subject = RDF::URI.new
|
179
296
|
else
|
180
|
-
# it's already a URI, but we need to use the null relative URI
|
181
|
-
# write out as a Triannon:externalRef
|
297
|
+
# it's already a URI, but we need to use the null relative URI
|
298
|
+
# representation so we can write out as a Triannon:externalRef
|
299
|
+
# property with the URL, and any addl props too.
|
182
300
|
if predicate_obj.to_str
|
183
301
|
predicate_subject = RDF::URI.new
|
184
302
|
graph_for_resource << RDF::Statement({:subject => predicate_subject,
|
@@ -198,19 +316,21 @@ module Triannon
|
|
198
316
|
# add statements with predicate_obj as the subject
|
199
317
|
orig_hash_uri_objs = [] # the orig URI objects from [targetObject, OA.hasSource/.default/.item, (uri)] statements
|
200
318
|
hash_uri_counter = 1
|
201
|
-
|
319
|
+
OA::Graph.subject_statements(predicate_obj, @anno.graph).each { |s|
|
202
320
|
if s.subject == predicate_obj
|
203
321
|
# deal with any external URI references which may occur in:
|
204
|
-
#
|
322
|
+
# OA.hasSource (from SpecificResource), OA.default or
|
323
|
+
# OA.item (from Choice, Composite, List)
|
205
324
|
if s.object.is_a?(RDF::URI) && s.object.to_s
|
206
|
-
# do we need to represent the URL as an externalReference
|
207
|
-
|
325
|
+
# do we need to represent the URL as an externalReference
|
326
|
+
# with hash URI?
|
327
|
+
if s.predicate == RDF::Vocab::OA.hasSource
|
208
328
|
hash_uri_str = "#source"
|
209
|
-
elsif s.predicate == RDF::
|
329
|
+
elsif s.predicate == RDF::Vocab::OA.default
|
210
330
|
hash_uri_str = "#default"
|
211
|
-
elsif s.predicate == RDF::
|
331
|
+
elsif s.predicate == RDF::Vocab::OA.item
|
212
332
|
hash_uri_str = "#item#{hash_uri_counter}"
|
213
|
-
hash_uri_counter
|
333
|
+
hash_uri_counter += 1
|
214
334
|
else
|
215
335
|
# we don't need to represent the object URI as an external ref
|
216
336
|
hash_uri_str = nil
|
@@ -224,7 +344,7 @@ module Triannon
|
|
224
344
|
new_hash_uri_obj = RDF::URI.new(hash_uri_str)
|
225
345
|
orig_hash_uri_obj = s.object
|
226
346
|
orig_hash_uri_objs << orig_hash_uri_obj
|
227
|
-
# add [targetObj, OA.hasSource/.default/.item, (hash URI)]
|
347
|
+
# add [targetObj, OA.hasSource/.default/.item, (hash URI)]
|
228
348
|
graph_for_resource << RDF::Statement({:subject => predicate_subject,
|
229
349
|
:predicate => s.predicate,
|
230
350
|
:object => new_hash_uri_obj})
|
@@ -234,7 +354,7 @@ module Triannon
|
|
234
354
|
:predicate => RDF::Triannon.externalReference,
|
235
355
|
:object => RDF::URI.new(orig_hash_uri_obj.to_s)})
|
236
356
|
# and all of the orig URL's addl props
|
237
|
-
|
357
|
+
OA::Graph.subject_statements(orig_hash_uri_obj, @anno.graph).each { |ss|
|
238
358
|
if ss.subject == orig_hash_uri_obj
|
239
359
|
graph_for_resource << RDF::Statement({:subject => new_hash_uri_obj,
|
240
360
|
:predicate => ss.predicate,
|
@@ -246,7 +366,8 @@ module Triannon
|
|
246
366
|
end
|
247
367
|
# NOTE: already dealt with case where there is no hash uri above
|
248
368
|
else
|
249
|
-
# s.object is not a URI, and subject = predicate_subject -- it may
|
369
|
+
# s.object is not a URI, and subject = predicate_subject -- it may
|
370
|
+
# be a blank node
|
250
371
|
graph_for_resource << RDF::Statement({:subject => predicate_subject,
|
251
372
|
:predicate => s.predicate,
|
252
373
|
:object => s.object})
|
@@ -256,14 +377,14 @@ module Triannon
|
|
256
377
|
graph_for_resource << s
|
257
378
|
end
|
258
379
|
}
|
259
|
-
# make sure the graph we will write contains no extraneous statements
|
260
|
-
#
|
380
|
+
# make sure the graph we will write contains no extraneous statements
|
381
|
+
# about URIs now represented as hash URIs
|
261
382
|
orig_hash_uri_objs.each { |uri_node|
|
262
|
-
|
383
|
+
OA::Graph.subject_statements(uri_node, graph_for_resource).each { |s|
|
263
384
|
graph_for_resource.delete(s)
|
264
385
|
}
|
265
386
|
}
|
266
|
-
if (predicate == RDF::
|
387
|
+
if (predicate == RDF::Vocab::OA.hasTarget)
|
267
388
|
resource_ids << create_resource(graph_for_resource.to_ttl, "#{@id}/t")
|
268
389
|
else
|
269
390
|
resource_ids << create_resource(graph_for_resource.to_ttl, "#{@id}/b")
|
@@ -279,4 +400,4 @@ module Triannon
|
|
279
400
|
end
|
280
401
|
|
281
402
|
end
|
282
|
-
end
|
403
|
+
end
|
@@ -1,16 +1,16 @@
|
|
1
1
|
module Triannon
|
2
2
|
class SolrSearcher
|
3
3
|
|
4
|
-
# convert RSolr::Response object into an array of
|
4
|
+
# convert RSolr::Response object into an array of OA::Graph objects,
|
5
5
|
# where each graph object contains a single annotation returned in the response docs
|
6
6
|
# @param [Hash] rsolr_response an RSolr response to a query. It's actually an
|
7
7
|
# RSolr::HashWithResponse but let's not quibble
|
8
|
-
# @return [Array<
|
8
|
+
# @return [Array<OA::Graph>]
|
9
9
|
def self.anno_graphs_array(rsolr_response)
|
10
10
|
result = []
|
11
11
|
# TODO: deal with Solr pagination
|
12
12
|
rsolr_response['response']['docs'].each { |solr_doc_hash|
|
13
|
-
result <<
|
13
|
+
result << OA::Graph.new(RDF::Graph.new.from_jsonld(solr_doc_hash['anno_jsonld']))
|
14
14
|
}
|
15
15
|
result
|
16
16
|
end
|
@@ -129,12 +129,12 @@ module Triannon
|
|
129
129
|
# 2. sends request to Solr
|
130
130
|
# 3. converts Solr response object to array of anno graphs
|
131
131
|
# @param [Hash<String => String>] controller_params params from Controller
|
132
|
-
# @return [Array<
|
132
|
+
# @return [Array<OA::Graph>] array of OA::Graph objects,
|
133
133
|
# where each graph object contains a single annotation returned in the response docs
|
134
134
|
def find(controller_params)
|
135
135
|
solr_params = self.class.solr_params(controller_params)
|
136
136
|
solr_response = search(solr_params)
|
137
|
-
|
137
|
+
self.class.anno_graphs_array(solr_response)
|
138
138
|
end
|
139
139
|
|
140
140
|
|
@@ -149,9 +149,9 @@ module Triannon
|
|
149
149
|
@logger.debug "#{exception.inspect} on Solr search attempt #{attempt_cnt} for #{solr_params.inspect}"
|
150
150
|
if exception.kind_of?(RSolr::Error::Http)
|
151
151
|
# Note there are extra shenanigans b/c RSolr hijacks the Solr error to return RSolr Error
|
152
|
-
|
152
|
+
fail Triannon::SearchError.new("error searching Solr with params #{solr_params.inspect}: #{exception.message}", exception.response[:status], exception.response[:body])
|
153
153
|
elsif exception.kind_of?(StandardError)
|
154
|
-
|
154
|
+
fail Triannon::SearchError.new("error searching Solr with params #{solr_params.inspect}: #{exception.message}")
|
155
155
|
end
|
156
156
|
end
|
157
157
|
|
@@ -170,4 +170,4 @@ module Triannon
|
|
170
170
|
end
|
171
171
|
|
172
172
|
end
|
173
|
-
end
|
173
|
+
end
|