dpla-sparql-client 1.1.3.2.pre.dpla.1
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 +7 -0
- data/AUTHORS +3 -0
- data/CREDITS +12 -0
- data/README +166 -0
- data/UNLICENSE +24 -0
- data/VERSION +1 -0
- data/lib/sparql/client.rb +730 -0
- data/lib/sparql/client/query.rb +412 -0
- data/lib/sparql/client/repository.rb +302 -0
- data/lib/sparql/client/update.rb +293 -0
- data/lib/sparql/client/version.rb +19 -0
- metadata +172 -0
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
module SPARQL; class Client
|
|
2
|
+
##
|
|
3
|
+
# A SPARQL query builder.
|
|
4
|
+
#
|
|
5
|
+
# @example Iterating over all found solutions
|
|
6
|
+
# query.each_solution { |solution| puts solution.inspect }
|
|
7
|
+
#
|
|
8
|
+
class Query < RDF::Query
|
|
9
|
+
##
|
|
10
|
+
# @return [Symbol]
|
|
11
|
+
# @see http://www.w3.org/TR/sparql11-query/#QueryForms
|
|
12
|
+
attr_reader :form
|
|
13
|
+
|
|
14
|
+
##
|
|
15
|
+
# @return [Hash{Symbol => Object}]
|
|
16
|
+
attr_reader :options
|
|
17
|
+
|
|
18
|
+
##
|
|
19
|
+
# @return [Array<[key, RDF::Value]>]
|
|
20
|
+
attr_reader :values
|
|
21
|
+
|
|
22
|
+
##
|
|
23
|
+
# Creates a boolean `ASK` query.
|
|
24
|
+
#
|
|
25
|
+
# @param [Hash{Symbol => Object}] options
|
|
26
|
+
# @return [Query]
|
|
27
|
+
# @see http://www.w3.org/TR/sparql11-query/#ask
|
|
28
|
+
def self.ask(options = {})
|
|
29
|
+
self.new(:ask, options)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
##
|
|
33
|
+
# Creates a tuple `SELECT` query.
|
|
34
|
+
#
|
|
35
|
+
# @param [Array<Symbol>] variables
|
|
36
|
+
# @return [Query]
|
|
37
|
+
#
|
|
38
|
+
# @overload self.select(*variables, options)
|
|
39
|
+
# @param [Array<Symbol>] variables
|
|
40
|
+
# @return [Query]
|
|
41
|
+
# @see http://www.w3.org/TR/sparql11-query/#select
|
|
42
|
+
def self.select(*variables)
|
|
43
|
+
options = variables.last.is_a?(Hash) ? variables.pop : {}
|
|
44
|
+
self.new(:select, options).select(*variables)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
##
|
|
48
|
+
# Creates a `DESCRIBE` query.
|
|
49
|
+
#
|
|
50
|
+
# @param [Array<Symbol, RDF::URI>] variables
|
|
51
|
+
# @return [Query]
|
|
52
|
+
#
|
|
53
|
+
# @overload self.describe(*variables, options)
|
|
54
|
+
# @param [Array<Symbol, RDF::URI>] variables
|
|
55
|
+
# @return [Query]
|
|
56
|
+
# @see http://www.w3.org/TR/sparql11-query/#describe
|
|
57
|
+
def self.describe(*variables)
|
|
58
|
+
options = variables.last.is_a?(Hash) ? variables.pop : {}
|
|
59
|
+
self.new(:describe, options).describe(*variables)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
##
|
|
63
|
+
# Creates a graph `CONSTRUCT` query.
|
|
64
|
+
#
|
|
65
|
+
# @param [Array<RDF::Query::Pattern, Array>] patterns
|
|
66
|
+
# @return [Query]
|
|
67
|
+
#
|
|
68
|
+
# @overload self.construct(*variables, options)
|
|
69
|
+
# @param [Array<RDF::Query::Pattern, Array>] patterns
|
|
70
|
+
# @param [Hash{Symbol => Object}] options
|
|
71
|
+
# @return [Query]
|
|
72
|
+
# @see http://www.w3.org/TR/sparql11-query/#construct
|
|
73
|
+
def self.construct(*patterns)
|
|
74
|
+
options = patterns.last.is_a?(Hash) ? patterns.pop : {}
|
|
75
|
+
self.new(:construct, options).construct(*patterns) # FIXME
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
##
|
|
79
|
+
# @param [Symbol, #to_s] form
|
|
80
|
+
# @overload self.construct(*variables, options)
|
|
81
|
+
# @param [Symbol, #to_s] form
|
|
82
|
+
# @param [Hash{Symbol => Object}] options
|
|
83
|
+
# @yield [query]
|
|
84
|
+
# @yieldparam [Query]
|
|
85
|
+
def initialize(form = :ask, options = {}, &block)
|
|
86
|
+
@subqueries = []
|
|
87
|
+
@form = form.respond_to?(:to_sym) ? form.to_sym : form.to_s.to_sym
|
|
88
|
+
super([], options, &block)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
##
|
|
92
|
+
# @return [Query]
|
|
93
|
+
# @see http://www.w3.org/TR/sparql11-query/#ask
|
|
94
|
+
def ask
|
|
95
|
+
@form = :ask
|
|
96
|
+
self
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
##
|
|
100
|
+
# @param [Array<Symbol>] variables
|
|
101
|
+
# @return [Query]
|
|
102
|
+
# @see http://www.w3.org/TR/sparql11-query/#select
|
|
103
|
+
def select(*variables)
|
|
104
|
+
@values = variables.map { |var| [var, RDF::Query::Variable.new(var)] }
|
|
105
|
+
self
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
##
|
|
109
|
+
# @param [Array<Symbol>] variables
|
|
110
|
+
# @return [Query]
|
|
111
|
+
# @see http://www.w3.org/TR/sparql11-query/#describe
|
|
112
|
+
def describe(*variables)
|
|
113
|
+
@values = variables.map { |var|
|
|
114
|
+
[var, var.is_a?(RDF::URI) ? var : RDF::Query::Variable.new(var)]
|
|
115
|
+
}
|
|
116
|
+
self
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
##
|
|
120
|
+
# @param [Array<RDF::Query::Pattern, Array>] patterns
|
|
121
|
+
# @return [Query]
|
|
122
|
+
# @see http://www.w3.org/TR/sparql11-query/#construct
|
|
123
|
+
def construct(*patterns)
|
|
124
|
+
options[:template] = build_patterns(patterns)
|
|
125
|
+
self
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# @param [RDF::URI] uri
|
|
129
|
+
# @return [Query]
|
|
130
|
+
# @see http://www.w3.org/TR/sparql11-query/#specifyingDataset
|
|
131
|
+
def from(uri)
|
|
132
|
+
options[:from] = uri
|
|
133
|
+
self
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
##
|
|
137
|
+
# @param [Array<RDF::Query::Pattern, Array>] patterns_queries
|
|
138
|
+
# splat of zero or more patterns followed by zero or more queries.
|
|
139
|
+
# @return [Query]
|
|
140
|
+
# @see http://www.w3.org/TR/sparql11-query/#GraphPattern
|
|
141
|
+
def where(*patterns_queries)
|
|
142
|
+
subqueries, patterns = patterns_queries.partition {|pq| pq.is_a? SPARQL::Client::Query}
|
|
143
|
+
@patterns += build_patterns(patterns)
|
|
144
|
+
@subqueries += subqueries
|
|
145
|
+
self
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
alias_method :whether, :where
|
|
149
|
+
|
|
150
|
+
##
|
|
151
|
+
# @param [Array<Symbol, String>] variables
|
|
152
|
+
# @return [Query]
|
|
153
|
+
# @see http://www.w3.org/TR/sparql11-query/#modOrderBy
|
|
154
|
+
def order(*variables)
|
|
155
|
+
options[:order_by] = variables
|
|
156
|
+
self
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
alias_method :order_by, :order
|
|
160
|
+
|
|
161
|
+
##
|
|
162
|
+
# @param [Array<Symbol, String>] variables
|
|
163
|
+
# @return [Query]
|
|
164
|
+
# @see http://www.w3.org/TR/sparql11-query/#groupby
|
|
165
|
+
def group(*variables)
|
|
166
|
+
options[:group_by] = variables
|
|
167
|
+
self
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
alias_method :group_by, :group
|
|
171
|
+
|
|
172
|
+
##
|
|
173
|
+
# @return [Query]
|
|
174
|
+
# @see http://www.w3.org/TR/sparql11-query/#modDuplicates
|
|
175
|
+
def distinct(state = true)
|
|
176
|
+
options[:distinct] = state
|
|
177
|
+
self
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
##
|
|
181
|
+
# @return [Query]
|
|
182
|
+
# @see http://www.w3.org/TR/sparql11-query/#modDuplicates
|
|
183
|
+
def reduced(state = true)
|
|
184
|
+
options[:reduced] = state
|
|
185
|
+
self
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
##
|
|
189
|
+
# @param [RDF::Value] graph_uri_or_var
|
|
190
|
+
# @return [Query]
|
|
191
|
+
# @see http://www.w3.org/TR/sparql11-query/#queryDataset
|
|
192
|
+
def graph(graph_uri_or_var)
|
|
193
|
+
options[:graph] = case graph_uri_or_var
|
|
194
|
+
when Symbol then RDF::Query::Variable.new(graph_uri_or_var)
|
|
195
|
+
when String then RDF::URI(graph_uri_or_var)
|
|
196
|
+
when RDF::Value then graph_uri_or_var
|
|
197
|
+
else raise ArgumentError
|
|
198
|
+
end
|
|
199
|
+
self
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
##
|
|
203
|
+
# @param [Integer, #to_i] start
|
|
204
|
+
# @return [Query]
|
|
205
|
+
# @see http://www.w3.org/TR/sparql11-query/#modOffset
|
|
206
|
+
def offset(start)
|
|
207
|
+
slice(start, nil)
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
##
|
|
211
|
+
# @param [Integer, #to_i] length
|
|
212
|
+
# @return [Query]
|
|
213
|
+
# @see http://www.w3.org/TR/sparql11-query/#modResultLimit
|
|
214
|
+
def limit(length)
|
|
215
|
+
slice(nil, length)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
##
|
|
219
|
+
# @param [Integer, #to_i] start
|
|
220
|
+
# @param [Integer, #to_i] length
|
|
221
|
+
# @return [Query]
|
|
222
|
+
def slice(start, length)
|
|
223
|
+
options[:offset] = start.to_i if start
|
|
224
|
+
options[:limit] = length.to_i if length
|
|
225
|
+
self
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
##
|
|
229
|
+
# @return [Query]
|
|
230
|
+
# @see http://www.w3.org/TR/sparql11-query/#prefNames
|
|
231
|
+
def prefix(string)
|
|
232
|
+
(options[:prefixes] ||= []) << string
|
|
233
|
+
self
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
##
|
|
237
|
+
# @return [Query]
|
|
238
|
+
# @see http://www.w3.org/TR/sparql11-query/#optionals
|
|
239
|
+
def optional(*patterns)
|
|
240
|
+
(options[:optionals] ||= []) << build_patterns(patterns)
|
|
241
|
+
self
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
##
|
|
245
|
+
# @return expects_statements?
|
|
246
|
+
def expects_statements?
|
|
247
|
+
[:construct, :describe].include?(form)
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
##
|
|
251
|
+
# @private
|
|
252
|
+
def build_patterns(patterns)
|
|
253
|
+
patterns.map do |pattern|
|
|
254
|
+
case pattern
|
|
255
|
+
when RDF::Query::Pattern then pattern
|
|
256
|
+
else RDF::Query::Pattern.new(*pattern.to_a)
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
##
|
|
262
|
+
# @private
|
|
263
|
+
def filter(string)
|
|
264
|
+
((options[:filters] ||= []) << string) if string and not string.empty?
|
|
265
|
+
self
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
##
|
|
269
|
+
# @return [Boolean]
|
|
270
|
+
def true?
|
|
271
|
+
case result
|
|
272
|
+
when TrueClass, FalseClass then result
|
|
273
|
+
when RDF::Literal::Boolean then result.true?
|
|
274
|
+
when Enumerable then !result.empty?
|
|
275
|
+
else false
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
##
|
|
280
|
+
# @return [Boolean]
|
|
281
|
+
def false?
|
|
282
|
+
!true?
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
##
|
|
286
|
+
# @return [Enumerable<RDF::Query::Solution>]
|
|
287
|
+
def solutions
|
|
288
|
+
result
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
##
|
|
292
|
+
# @yield [statement]
|
|
293
|
+
# @yieldparam [RDF::Statement]
|
|
294
|
+
# @return [Enumerator]
|
|
295
|
+
def each_statement(&block)
|
|
296
|
+
result.each_statement(&block)
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# Enumerates over each matching query solution.
|
|
300
|
+
#
|
|
301
|
+
# @yield [solution]
|
|
302
|
+
# @yieldparam [RDF::Query::Solution] solution
|
|
303
|
+
# @return [Enumerator]
|
|
304
|
+
def each_solution(&block)
|
|
305
|
+
@solutions = result
|
|
306
|
+
super
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
##
|
|
310
|
+
# @return [Object]
|
|
311
|
+
def result
|
|
312
|
+
@result ||= execute
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
##
|
|
316
|
+
# @return [Object]
|
|
317
|
+
def execute
|
|
318
|
+
raise NotImplementedError
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
##
|
|
322
|
+
# Returns the string representation of this query.
|
|
323
|
+
#
|
|
324
|
+
# @return [String]
|
|
325
|
+
def to_s
|
|
326
|
+
buffer = [form.to_s.upcase]
|
|
327
|
+
|
|
328
|
+
case form
|
|
329
|
+
when :select, :describe
|
|
330
|
+
only_count = values.empty? && options[:count]
|
|
331
|
+
buffer << 'DISTINCT' if options[:distinct] and not only_count
|
|
332
|
+
buffer << 'REDUCED' if options[:reduced]
|
|
333
|
+
buffer << ((values.empty? and not options[:count]) ? '*' : values.map { |v| SPARQL::Client.serialize_value(v[1]) }.join(' '))
|
|
334
|
+
if options[:count]
|
|
335
|
+
options[:count].each do |var, count|
|
|
336
|
+
buffer << '( COUNT(' + (options[:distinct] ? 'DISTINCT ' : '') +
|
|
337
|
+
(var.is_a?(String) ? var : "?#{var}") + ') AS ' + (count.is_a?(String) ? count : "?#{count}") + ' )'
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
when :construct
|
|
341
|
+
buffer << '{'
|
|
342
|
+
buffer += SPARQL::Client.serialize_patterns(options[:template])
|
|
343
|
+
buffer << '}'
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
buffer << "FROM #{SPARQL::Client.serialize_value(options[:from])}" if options[:from]
|
|
347
|
+
|
|
348
|
+
unless patterns.empty? && form == :describe
|
|
349
|
+
buffer << 'WHERE {'
|
|
350
|
+
|
|
351
|
+
if options[:graph]
|
|
352
|
+
buffer << 'GRAPH ' + SPARQL::Client.serialize_value(options[:graph])
|
|
353
|
+
buffer << '{'
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
@subqueries.each do |sq|
|
|
357
|
+
buffer << "{ #{sq.to_s} } ."
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
buffer += SPARQL::Client.serialize_patterns(patterns)
|
|
361
|
+
if options[:optionals]
|
|
362
|
+
options[:optionals].each do |patterns|
|
|
363
|
+
buffer << 'OPTIONAL {'
|
|
364
|
+
buffer += SPARQL::Client.serialize_patterns(patterns)
|
|
365
|
+
buffer << '}'
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
if options[:filters]
|
|
369
|
+
buffer += options[:filters].map { |filter| "FILTER(#{filter})" }
|
|
370
|
+
end
|
|
371
|
+
if options[:graph]
|
|
372
|
+
buffer << '}' # GRAPH
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
buffer << '}' # WHERE
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
if options[:group_by]
|
|
379
|
+
buffer << 'GROUP BY'
|
|
380
|
+
buffer += options[:group_by].map { |var| var.is_a?(String) ? var : "?#{var}" }
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
if options[:order_by]
|
|
384
|
+
buffer << 'ORDER BY'
|
|
385
|
+
buffer += options[:order_by].map { |var| var.is_a?(String) ? var : "?#{var}" }
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
buffer << "OFFSET #{options[:offset]}" if options[:offset]
|
|
389
|
+
buffer << "LIMIT #{options[:limit]}" if options[:limit]
|
|
390
|
+
options[:prefixes].reverse.each { |e| buffer.unshift("PREFIX #{e}") } if options[:prefixes]
|
|
391
|
+
|
|
392
|
+
buffer.join(' ')
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
##
|
|
396
|
+
# Outputs a developer-friendly representation of this query to `stderr`.
|
|
397
|
+
#
|
|
398
|
+
# @return [void]
|
|
399
|
+
def inspect!
|
|
400
|
+
warn(inspect)
|
|
401
|
+
self
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
##
|
|
405
|
+
# Returns a developer-friendly representation of this query.
|
|
406
|
+
#
|
|
407
|
+
# @return [String]
|
|
408
|
+
def inspect
|
|
409
|
+
sprintf("#<%s:%#0x(%s)>", self.class.name, __id__, to_s)
|
|
410
|
+
end
|
|
411
|
+
end
|
|
412
|
+
end; end
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
module SPARQL; class Client
|
|
2
|
+
##
|
|
3
|
+
# A read-only repository view of a SPARQL endpoint.
|
|
4
|
+
#
|
|
5
|
+
# @see RDF::Repository
|
|
6
|
+
class Repository < RDF::Repository
|
|
7
|
+
# @return [SPARQL::Client]
|
|
8
|
+
attr_reader :client
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
# @param [String, #to_s] endpoint
|
|
12
|
+
# @param [Hash{Symbol => Object}] options
|
|
13
|
+
def initialize(endpoint, options = {})
|
|
14
|
+
@options = options.dup
|
|
15
|
+
@update_client = SPARQL::Client.new(options.delete(:update_endpoint), options) if options[:update_endpoint]
|
|
16
|
+
@client = SPARQL::Client.new(endpoint, options)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
##
|
|
20
|
+
# Returns the client for the update_endpoint if specified, otherwise the
|
|
21
|
+
# {client}.
|
|
22
|
+
#
|
|
23
|
+
# @return [SPARQL::Client]
|
|
24
|
+
def update_client
|
|
25
|
+
@update_client || @client
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
##
|
|
29
|
+
# Queries `self` using the given basic graph pattern (BGP) query,
|
|
30
|
+
# yielding each matched solution to the given block.
|
|
31
|
+
#
|
|
32
|
+
# Overrides Queryable::query_execute to use SPARQL::Client::query
|
|
33
|
+
#
|
|
34
|
+
# @param [RDF::Query] query
|
|
35
|
+
# the query to execute
|
|
36
|
+
# @param [Hash{Symbol => Object}] options ({})
|
|
37
|
+
# Any other options passed to `query.execute`
|
|
38
|
+
# @yield [solution]
|
|
39
|
+
# @yieldparam [RDF::Query::Solution] solution
|
|
40
|
+
# @yieldreturn [void] ignored
|
|
41
|
+
# @return [void] ignored
|
|
42
|
+
# @see RDF::Queryable#query
|
|
43
|
+
# @see RDF::Query#execute
|
|
44
|
+
def query_execute(query, options = {}, &block)
|
|
45
|
+
return nil unless block_given?
|
|
46
|
+
q = SPARQL::Client::Query.select(query.variables).where(*query.patterns)
|
|
47
|
+
client.query(q, options).each do |solution|
|
|
48
|
+
yield solution
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
##
|
|
53
|
+
# Enumerates each RDF statement in this repository.
|
|
54
|
+
#
|
|
55
|
+
# @yield [statement]
|
|
56
|
+
# @yieldparam [RDF::Statement] statement
|
|
57
|
+
# @see RDF::Repository#each
|
|
58
|
+
def each(&block)
|
|
59
|
+
client.construct([:s, :p, :o]).where([:s, :p, :o]).each_statement(&block)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
##
|
|
63
|
+
# @private
|
|
64
|
+
# @see RDF::Enumerable#supports?
|
|
65
|
+
def supports?(feature)
|
|
66
|
+
case feature.to_sym
|
|
67
|
+
# statement contexts / named graphs
|
|
68
|
+
when :context then false
|
|
69
|
+
when :inference then false # forward-chaining inference
|
|
70
|
+
when :validity then false
|
|
71
|
+
else false
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
##
|
|
76
|
+
# Returns `true` if this repository contains the given subject.
|
|
77
|
+
#
|
|
78
|
+
# @param [RDF::Resource] subject
|
|
79
|
+
# @return [Boolean]
|
|
80
|
+
# @see RDF::Repository#has_subject?
|
|
81
|
+
def has_subject?(subject)
|
|
82
|
+
client.ask.whether([subject, :p, :o]).true?
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
##
|
|
86
|
+
# Returns `true` if this repository contains the given predicate.
|
|
87
|
+
#
|
|
88
|
+
# @param [RDF::URI] predicate
|
|
89
|
+
# @return [Boolean]
|
|
90
|
+
# @see RDF::Repository#has_predicate?
|
|
91
|
+
def has_predicate?(predicate)
|
|
92
|
+
client.ask.whether([:s, predicate, :o]).true?
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
##
|
|
96
|
+
# Returns `true` if this repository contains the given object.
|
|
97
|
+
#
|
|
98
|
+
# @param [RDF::Value] object
|
|
99
|
+
# @return [Boolean]
|
|
100
|
+
# @see RDF::Repository#has_object?
|
|
101
|
+
def has_object?(object)
|
|
102
|
+
client.ask.whether([:s, :p, object]).true?
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
##
|
|
106
|
+
# Iterates over each subject in this repository.
|
|
107
|
+
#
|
|
108
|
+
# @yield [subject]
|
|
109
|
+
# @yieldparam [RDF::Resource] subject
|
|
110
|
+
# @return [Enumerator]
|
|
111
|
+
# @see RDF::Repository#each_subject?
|
|
112
|
+
def each_subject(&block)
|
|
113
|
+
if block_given?
|
|
114
|
+
client.select(:s, :distinct => true).where([:s, :p, :o]).each_solution { |solution| block.call(solution[:s]) }
|
|
115
|
+
end
|
|
116
|
+
enum_subject
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
##
|
|
120
|
+
# Iterates over each predicate in this repository.
|
|
121
|
+
#
|
|
122
|
+
# @yield [predicate]
|
|
123
|
+
# @yieldparam [RDF::URI] predicate
|
|
124
|
+
# @return [Enumerator]
|
|
125
|
+
# @see RDF::Repository#each_predicate?
|
|
126
|
+
def each_predicate(&block)
|
|
127
|
+
if block_given?
|
|
128
|
+
client.select(:p, :distinct => true).where([:s, :p, :o]).each_solution { |solution| block.call(solution[:p]) }
|
|
129
|
+
end
|
|
130
|
+
enum_predicate
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
##
|
|
134
|
+
# Iterates over each object in this repository.
|
|
135
|
+
#
|
|
136
|
+
# @yield [object]
|
|
137
|
+
# @yieldparam [RDF::Value] object
|
|
138
|
+
# @return [Enumerator]
|
|
139
|
+
# @see RDF::Repository#each_object?
|
|
140
|
+
def each_object(&block)
|
|
141
|
+
if block_given?
|
|
142
|
+
client.select(:o, :distinct => true).where([:s, :p, :o]).each_solution { |solution| block.call(solution[:o]) }
|
|
143
|
+
end
|
|
144
|
+
enum_object
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
##
|
|
148
|
+
# Returns `true` if this repository contains the given `triple`.
|
|
149
|
+
#
|
|
150
|
+
# @param [Array<RDF::Resource, RDF::URI, RDF::Value>] triple
|
|
151
|
+
# @return [Boolean]
|
|
152
|
+
# @see RDF::Repository#has_triple?
|
|
153
|
+
def has_triple?(triple)
|
|
154
|
+
client.ask.whether(triple.to_a[0...3]).true?
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
##
|
|
158
|
+
# Returns `true` if this repository contains the given `statement`.
|
|
159
|
+
#
|
|
160
|
+
# @param [RDF::Statement] statement
|
|
161
|
+
# @return [Boolean]
|
|
162
|
+
# @see RDF::Repository#has_statement?
|
|
163
|
+
def has_statement?(statement)
|
|
164
|
+
has_triple?(statement.to_triple)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
##
|
|
168
|
+
# Returns the number of statements in this repository.
|
|
169
|
+
#
|
|
170
|
+
# @return [Integer]
|
|
171
|
+
# @see RDF::Repository#count?
|
|
172
|
+
def count
|
|
173
|
+
begin
|
|
174
|
+
binding = client.query("SELECT COUNT(*) WHERE { ?s ?p ?o }").first.to_hash
|
|
175
|
+
binding[binding.keys.first].value.to_i
|
|
176
|
+
rescue SPARQL::Client::MalformedQuery => e
|
|
177
|
+
# SPARQL 1.0 does not include support for aggregate functions:
|
|
178
|
+
count = 0
|
|
179
|
+
each_statement { count += 1 } # TODO: optimize this
|
|
180
|
+
count
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
alias_method :size, :count
|
|
185
|
+
alias_method :length, :count
|
|
186
|
+
|
|
187
|
+
##
|
|
188
|
+
# Returns `true` if this repository contains no statements.
|
|
189
|
+
#
|
|
190
|
+
# @return [Boolean]
|
|
191
|
+
# @see RDF::Repository#empty?
|
|
192
|
+
def empty?
|
|
193
|
+
client.ask.whether([:s, :p, :o]).false?
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
##
|
|
197
|
+
# Queries `self` for RDF statements matching the given `pattern`.
|
|
198
|
+
#
|
|
199
|
+
# @example
|
|
200
|
+
# repository.query([nil, RDF::DOAP.developer, nil])
|
|
201
|
+
# repository.query(:predicate => RDF::DOAP.developer)
|
|
202
|
+
#
|
|
203
|
+
# @fixme This should use basic SPARQL query mechanism.
|
|
204
|
+
#
|
|
205
|
+
# @param [Pattern] pattern
|
|
206
|
+
# @see RDF::Queryable#query_pattern
|
|
207
|
+
# @yield [statement]
|
|
208
|
+
# @yieldparam [Statement]
|
|
209
|
+
# @return [Enumerable<Statement>]
|
|
210
|
+
def query_pattern(pattern, &block)
|
|
211
|
+
pattern = pattern.dup
|
|
212
|
+
pattern.subject ||= RDF::Query::Variable.new
|
|
213
|
+
pattern.predicate ||= RDF::Query::Variable.new
|
|
214
|
+
pattern.object ||= RDF::Query::Variable.new
|
|
215
|
+
pattern.initialize!
|
|
216
|
+
query = client.construct(pattern).where(pattern)
|
|
217
|
+
|
|
218
|
+
if block_given?
|
|
219
|
+
query.each_statement(&block)
|
|
220
|
+
else
|
|
221
|
+
query.solutions.to_a.extend(RDF::Enumerable, RDF::Queryable)
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
##
|
|
226
|
+
# Returns `false` to indicate that this is a read-only repository.
|
|
227
|
+
#
|
|
228
|
+
# @return [Boolean]
|
|
229
|
+
# @see RDF::Mutable#mutable?
|
|
230
|
+
def writable?
|
|
231
|
+
true
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
##
|
|
235
|
+
# @private
|
|
236
|
+
# @see RDF::Mutable#clear
|
|
237
|
+
def clear_statements
|
|
238
|
+
update_client.clear(:all)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
##
|
|
242
|
+
# Deletes RDF statements from `self`.
|
|
243
|
+
# If any statement contains a {Query::Variable}, it is
|
|
244
|
+
# considered to be a pattern, and used to query
|
|
245
|
+
# self to find matching statements to delete.
|
|
246
|
+
#
|
|
247
|
+
# @param [Enumerable<RDF::Statement>] statements
|
|
248
|
+
# @raise [TypeError] if `self` is immutable
|
|
249
|
+
# @return [Mutable]
|
|
250
|
+
def delete(*statements)
|
|
251
|
+
delete_statements(statements) unless statements.empty?
|
|
252
|
+
return self
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
protected
|
|
256
|
+
|
|
257
|
+
##
|
|
258
|
+
# Deletes the given RDF statements from the underlying storage.
|
|
259
|
+
#
|
|
260
|
+
# Overridden here to use SPARQL/UPDATE
|
|
261
|
+
#
|
|
262
|
+
# @param [RDF::Enumerable] statements
|
|
263
|
+
# @return [void]
|
|
264
|
+
def delete_statements(statements)
|
|
265
|
+
|
|
266
|
+
constant = statements.all? do |value|
|
|
267
|
+
# needs to be flattened... urgh
|
|
268
|
+
!value.respond_to?(:each_statement) && begin
|
|
269
|
+
statement = RDF::Statement.from(value)
|
|
270
|
+
statement.constant? && !statement.has_blank_nodes?
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
if constant
|
|
275
|
+
update_client.delete_data(statements)
|
|
276
|
+
else
|
|
277
|
+
update_client.delete_insert(statements)
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
##
|
|
282
|
+
# Inserts the given RDF statements into the underlying storage or output
|
|
283
|
+
# stream.
|
|
284
|
+
#
|
|
285
|
+
# Overridden here to use SPARQL/UPDATE
|
|
286
|
+
#
|
|
287
|
+
# @param [RDF::Enumerable] statements
|
|
288
|
+
# @return [void]
|
|
289
|
+
# @since 0.1.6
|
|
290
|
+
def insert_statements(statements)
|
|
291
|
+
update_client.insert_data(statements)
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
##
|
|
295
|
+
# @private
|
|
296
|
+
# @see RDF::Mutable#insert
|
|
297
|
+
def insert_statement(statement)
|
|
298
|
+
update_client.insert_data([statement])
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
end
|
|
302
|
+
end; end
|