sparql-client 2.1.0 → 3.1.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 +5 -5
- data/README.md +128 -96
- data/UNLICENSE +1 -1
- data/VERSION +1 -1
- data/lib/sparql/client.rb +157 -77
- data/lib/sparql/client/query.rb +399 -92
- data/lib/sparql/client/repository.rb +42 -18
- data/lib/sparql/client/update.rb +38 -39
- data/lib/sparql/client/version.rb +2 -2
- metadata +26 -27
data/lib/sparql/client/query.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
class SPARQL::Client
|
2
4
|
##
|
3
5
|
# A SPARQL query builder.
|
4
6
|
#
|
@@ -10,19 +12,13 @@ module SPARQL; class Client
|
|
10
12
|
# The form of the query.
|
11
13
|
#
|
12
14
|
# @return [:select, :ask, :construct, :describe]
|
13
|
-
# @see
|
15
|
+
# @see https://www.w3.org/TR/sparql11-query/#QueryForms
|
14
16
|
attr_reader :form
|
15
17
|
|
16
18
|
##
|
17
19
|
# @return [Hash{Symbol => Object}]
|
18
20
|
attr_reader :options
|
19
21
|
|
20
|
-
##
|
21
|
-
# Values returned from previous query.
|
22
|
-
#
|
23
|
-
# @return [Array<[key, RDF::Value]>]
|
24
|
-
attr_reader :values
|
25
|
-
|
26
22
|
##
|
27
23
|
# Creates a boolean `ASK` query.
|
28
24
|
#
|
@@ -31,28 +27,33 @@ module SPARQL; class Client
|
|
31
27
|
#
|
32
28
|
# @param [Hash{Symbol => Object}] options (see {#initialize})
|
33
29
|
# @return [Query]
|
34
|
-
# @see
|
35
|
-
def self.ask(options
|
36
|
-
self.new(:ask, options)
|
30
|
+
# @see https://www.w3.org/TR/sparql11-query/#ask
|
31
|
+
def self.ask(**options)
|
32
|
+
self.new(:ask, **options)
|
37
33
|
end
|
38
34
|
|
39
35
|
##
|
40
36
|
# Creates a tuple `SELECT` query.
|
41
37
|
#
|
42
|
-
# @example SELECT * WHERE { ?s ?p ?o . }
|
38
|
+
# @example `SELECT * WHERE { ?s ?p ?o . }`
|
43
39
|
# Query.select.where([:s, :p, :o])
|
44
40
|
#
|
41
|
+
# @example `SELECT ?s WHERE {?s ?p ?o .}`
|
42
|
+
# Query.select(:s).where([:s, :p, :o])
|
43
|
+
#
|
44
|
+
# @example `SELECT COUNT(?uri as ?c) WHERE {?uri a owl:Class}`
|
45
|
+
# Query.select(count: {uri: :c}).where([:uri, RDF.type, RDF::OWL.Class])
|
46
|
+
#
|
45
47
|
# @param [Array<Symbol>] variables
|
46
48
|
# @return [Query]
|
47
49
|
#
|
48
|
-
# @overload self.select(*variables, options)
|
50
|
+
# @overload self.select(*variables, **options)
|
49
51
|
# @param [Array<Symbol>] variables
|
50
52
|
# @param [Hash{Symbol => Object}] options (see {#initialize})
|
51
53
|
# @return [Query]
|
52
|
-
# @see
|
53
|
-
def self.select(*variables)
|
54
|
-
options
|
55
|
-
self.new(:select, options).select(*variables)
|
54
|
+
# @see https://www.w3.org/TR/sparql11-query/#select
|
55
|
+
def self.select(*variables, **options)
|
56
|
+
self.new(:select, **options).select(*variables)
|
56
57
|
end
|
57
58
|
|
58
59
|
##
|
@@ -64,14 +65,13 @@ module SPARQL; class Client
|
|
64
65
|
# @param [Array<Symbol, RDF::URI>] variables
|
65
66
|
# @return [Query]
|
66
67
|
#
|
67
|
-
# @overload self.describe(*variables, options)
|
68
|
+
# @overload self.describe(*variables, **options)
|
68
69
|
# @param [Array<Symbol, RDF::URI>] variables
|
69
70
|
# @param [Hash{Symbol => Object}] options (see {#initialize})
|
70
71
|
# @return [Query]
|
71
|
-
# @see
|
72
|
-
def self.describe(*variables)
|
73
|
-
options
|
74
|
-
self.new(:describe, options).describe(*variables)
|
72
|
+
# @see https://www.w3.org/TR/sparql11-query/#describe
|
73
|
+
def self.describe(*variables, **options)
|
74
|
+
self.new(:describe, **options).describe(*variables)
|
75
75
|
end
|
76
76
|
|
77
77
|
##
|
@@ -83,27 +83,30 @@ module SPARQL; class Client
|
|
83
83
|
# @param [Array<RDF::Query::Pattern, Array>] patterns
|
84
84
|
# @return [Query]
|
85
85
|
#
|
86
|
-
# @overload self.construct(*variables, options)
|
86
|
+
# @overload self.construct(*variables, **options)
|
87
87
|
# @param [Array<RDF::Query::Pattern, Array>] patterns
|
88
88
|
# @param [Hash{Symbol => Object}] options (see {#initialize})
|
89
89
|
# @return [Query]
|
90
|
-
# @see
|
91
|
-
def self.construct(*patterns)
|
92
|
-
options
|
93
|
-
self.new(:construct, options).construct(*patterns) # FIXME
|
90
|
+
# @see https://www.w3.org/TR/sparql11-query/#construct
|
91
|
+
def self.construct(*patterns, **options)
|
92
|
+
self.new(:construct, **options).construct(*patterns) # FIXME
|
94
93
|
end
|
95
94
|
|
96
95
|
##
|
97
96
|
# @param [Symbol, #to_s] form
|
98
|
-
# @overload self.construct(*variables, options)
|
97
|
+
# @overload self.construct(*variables, **options)
|
99
98
|
# @param [Symbol, #to_s] form
|
100
99
|
# @param [Hash{Symbol => Object}] options (see {Client#initialize})
|
100
|
+
# @option options [Hash{Symbol => Symbol}] :count
|
101
|
+
# Contents are symbols relating a variable described within the query,
|
102
|
+
# to the projected variable.
|
103
|
+
#
|
101
104
|
# @yield [query]
|
102
105
|
# @yieldparam [Query]
|
103
|
-
def initialize(form = :ask, options
|
106
|
+
def initialize(form = :ask, **options, &block)
|
104
107
|
@subqueries = []
|
105
108
|
@form = form.respond_to?(:to_sym) ? form.to_sym : form.to_s.to_sym
|
106
|
-
super([], options, &block)
|
109
|
+
super([], **options, &block)
|
107
110
|
end
|
108
111
|
|
109
112
|
##
|
@@ -111,21 +114,31 @@ module SPARQL; class Client
|
|
111
114
|
# query.ask.where([:s, :p, :o])
|
112
115
|
#
|
113
116
|
# @return [Query]
|
114
|
-
# @see
|
117
|
+
# @see https://www.w3.org/TR/sparql11-query/#ask
|
115
118
|
def ask
|
116
119
|
@form = :ask
|
117
120
|
self
|
118
121
|
end
|
119
122
|
|
120
123
|
##
|
121
|
-
# @example SELECT * WHERE { ?s ?p ?o . }
|
124
|
+
# @example `SELECT * WHERE { ?s ?p ?o . }`
|
122
125
|
# query.select.where([:s, :p, :o])
|
123
126
|
#
|
124
|
-
# @
|
127
|
+
# @example `SELECT ?s WHERE {?s ?p ?o .}`
|
128
|
+
# query.select(:s).where([:s, :p, :o])
|
129
|
+
#
|
130
|
+
# @example `SELECT COUNT(?uri as ?c) WHERE {?uri a owl:Class}`
|
131
|
+
# query.select(count: {uri: :c}).where([:uri, RDF.type, RDF::OWL.Class])
|
132
|
+
#
|
133
|
+
# @param [Array<Symbol>, Hash{Symbol => RDF::Query::Variable}] variables
|
125
134
|
# @return [Query]
|
126
|
-
# @see
|
135
|
+
# @see https://www.w3.org/TR/sparql11-query/#select
|
127
136
|
def select(*variables)
|
128
|
-
@values = variables.
|
137
|
+
@values = if variables.length == 1 && variables.first.is_a?(Hash)
|
138
|
+
variables.to_a
|
139
|
+
else
|
140
|
+
variables.map { |var| [var, RDF::Query::Variable.new(var)] }
|
141
|
+
end
|
129
142
|
self
|
130
143
|
end
|
131
144
|
|
@@ -135,7 +148,7 @@ module SPARQL; class Client
|
|
135
148
|
#
|
136
149
|
# @param [Array<Symbol>] variables
|
137
150
|
# @return [Query]
|
138
|
-
# @see
|
151
|
+
# @see https://www.w3.org/TR/sparql11-query/#describe
|
139
152
|
def describe(*variables)
|
140
153
|
@values = variables.map { |var|
|
141
154
|
[var, var.is_a?(RDF::URI) ? var : RDF::Query::Variable.new(var)]
|
@@ -149,7 +162,7 @@ module SPARQL; class Client
|
|
149
162
|
#
|
150
163
|
# @param [Array<RDF::Query::Pattern, Array>] patterns
|
151
164
|
# @return [Query]
|
152
|
-
# @see
|
165
|
+
# @see https://www.w3.org/TR/sparql11-query/#construct
|
153
166
|
def construct(*patterns)
|
154
167
|
options[:template] = build_patterns(patterns)
|
155
168
|
self
|
@@ -161,7 +174,7 @@ module SPARQL; class Client
|
|
161
174
|
#
|
162
175
|
# @param [RDF::URI] uri
|
163
176
|
# @return [Query]
|
164
|
-
# @see
|
177
|
+
# @see https://www.w3.org/TR/sparql11-query/#specifyingDataset
|
165
178
|
def from(uri)
|
166
179
|
options[:from] = uri
|
167
180
|
self
|
@@ -172,19 +185,55 @@ module SPARQL; class Client
|
|
172
185
|
# query.select.where([:s, :p, :o])
|
173
186
|
# query.select.whether([:s, :p, :o])
|
174
187
|
#
|
188
|
+
# @example SELECT * WHERE { { SELECT * WHERE { ?s ?p ?o . } } . ?s ?p ?o . }
|
189
|
+
# subquery = query.select.where([:s, :p, :o])
|
190
|
+
# query.select.where([:s, :p, :o], subquery)
|
191
|
+
#
|
192
|
+
# @example SELECT * WHERE { { SELECT * WHERE { ?s ?p ?o . } } . ?s ?p ?o . }
|
193
|
+
# query.select.where([:s, :p, :o]) do |q|
|
194
|
+
# q.select.where([:s, :p, :o])
|
195
|
+
# end
|
196
|
+
#
|
197
|
+
# Block form can be used for chaining calls in addition to creating sub-select queries.
|
198
|
+
#
|
199
|
+
# @example SELECT * WHERE { ?s ?p ?o . } ORDER BY ?o
|
200
|
+
# query.select.where([:s, :p, :o]) do
|
201
|
+
# order(:o)
|
202
|
+
# end
|
203
|
+
#
|
175
204
|
# @param [Array<RDF::Query::Pattern, Array>] patterns_queries
|
176
205
|
# splat of zero or more patterns followed by zero or more queries.
|
206
|
+
# @yield [query]
|
207
|
+
# Yield form with or without argument; without an argument, evaluates within the query.
|
208
|
+
# @yieldparam [SPARQL::Client::Query] query Actually a delegator to query. Methods other than `#select` are evaluated against `self`. For `#select`, a new Query is created, and the result added as a subquery.
|
177
209
|
# @return [Query]
|
178
|
-
# @see
|
179
|
-
def where(*patterns_queries)
|
210
|
+
# @see https://www.w3.org/TR/sparql11-query/#GraphPattern
|
211
|
+
def where(*patterns_queries, &block)
|
180
212
|
subqueries, patterns = patterns_queries.partition {|pq| pq.is_a? SPARQL::Client::Query}
|
181
213
|
@patterns += build_patterns(patterns)
|
182
214
|
@subqueries += subqueries
|
215
|
+
|
216
|
+
if block_given?
|
217
|
+
decorated_query = WhereDecorator.new(self)
|
218
|
+
case block.arity
|
219
|
+
when 1 then block.call(decorated_query)
|
220
|
+
else decorated_query.instance_eval(&block)
|
221
|
+
end
|
222
|
+
end
|
183
223
|
self
|
184
224
|
end
|
185
225
|
|
186
226
|
alias_method :whether, :where
|
187
227
|
|
228
|
+
# @private
|
229
|
+
class WhereDecorator < SimpleDelegator
|
230
|
+
def select(*variables)
|
231
|
+
query = SPARQL::Client::Query.select(*variables)
|
232
|
+
__getobj__.instance_variable_get(:@subqueries) << query
|
233
|
+
query
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
188
237
|
##
|
189
238
|
# @example SELECT * WHERE { ?s ?p ?o . } ORDER BY ?o
|
190
239
|
# query.select.where([:s, :p, :o]).order(:o)
|
@@ -194,11 +243,11 @@ module SPARQL; class Client
|
|
194
243
|
# query.select.where([:s, :p, :o]).order_by(:o, :p)
|
195
244
|
#
|
196
245
|
# @example SELECT * WHERE { ?s ?p ?o . } ORDER BY ASC(?o) DESC(?p)
|
197
|
-
# query.select.where([:s, :p, :o]).order_by(:
|
246
|
+
# query.select.where([:s, :p, :o]).order_by(o: :asc, p: :desc)
|
198
247
|
#
|
199
248
|
# @param [Array<Symbol, String>] variables
|
200
249
|
# @return [Query]
|
201
|
-
# @see
|
250
|
+
# @see https://www.w3.org/TR/sparql11-query/#modOrderBy
|
202
251
|
def order(*variables)
|
203
252
|
options[:order_by] = variables
|
204
253
|
self
|
@@ -213,7 +262,7 @@ module SPARQL; class Client
|
|
213
262
|
#
|
214
263
|
# @param [Array<Symbol, String>] var
|
215
264
|
# @return [Query]
|
216
|
-
# @see
|
265
|
+
# @see https://www.w3.org/TR/sparql11-query/#modOrderBy
|
217
266
|
def asc(var)
|
218
267
|
(options[:order_by] ||= []) << {var => :asc}
|
219
268
|
self
|
@@ -226,7 +275,7 @@ module SPARQL; class Client
|
|
226
275
|
#
|
227
276
|
# @param [Array<Symbol, String>] var
|
228
277
|
# @return [Query]
|
229
|
-
# @see
|
278
|
+
# @see https://www.w3.org/TR/sparql11-query/#modOrderBy
|
230
279
|
def desc(var)
|
231
280
|
(options[:order_by] ||= []) << {var => :desc}
|
232
281
|
self
|
@@ -238,7 +287,7 @@ module SPARQL; class Client
|
|
238
287
|
#
|
239
288
|
# @param [Array<Symbol, String>] variables
|
240
289
|
# @return [Query]
|
241
|
-
# @see
|
290
|
+
# @see https://www.w3.org/TR/sparql11-query/#groupby
|
242
291
|
def group(*variables)
|
243
292
|
options[:group_by] = variables
|
244
293
|
self
|
@@ -251,7 +300,7 @@ module SPARQL; class Client
|
|
251
300
|
# query.select(:s).distinct.where([:s, :p, :o])
|
252
301
|
#
|
253
302
|
# @return [Query]
|
254
|
-
# @see
|
303
|
+
# @see https://www.w3.org/TR/sparql11-query/#modDuplicates
|
255
304
|
def distinct(state = true)
|
256
305
|
options[:distinct] = state
|
257
306
|
self
|
@@ -262,7 +311,7 @@ module SPARQL; class Client
|
|
262
311
|
# query.select(:s).reduced.where([:s, :p, :o])
|
263
312
|
#
|
264
313
|
# @return [Query]
|
265
|
-
# @see
|
314
|
+
# @see https://www.w3.org/TR/sparql11-query/#modDuplicates
|
266
315
|
def reduced(state = true)
|
267
316
|
options[:reduced] = state
|
268
317
|
self
|
@@ -273,7 +322,7 @@ module SPARQL; class Client
|
|
273
322
|
# query.select.graph(:g).where([:s, :p, :o])
|
274
323
|
# @param [RDF::Value] graph_uri_or_var
|
275
324
|
# @return [Query]
|
276
|
-
# @see
|
325
|
+
# @see https://www.w3.org/TR/sparql11-query/#queryDataset
|
277
326
|
def graph(graph_uri_or_var)
|
278
327
|
options[:graph] = case graph_uri_or_var
|
279
328
|
when Symbol then RDF::Query::Variable.new(graph_uri_or_var)
|
@@ -290,7 +339,7 @@ module SPARQL; class Client
|
|
290
339
|
#
|
291
340
|
# @param [Integer, #to_i] start
|
292
341
|
# @return [Query]
|
293
|
-
# @see
|
342
|
+
# @see https://www.w3.org/TR/sparql11-query/#modOffset
|
294
343
|
def offset(start)
|
295
344
|
slice(start, nil)
|
296
345
|
end
|
@@ -301,7 +350,7 @@ module SPARQL; class Client
|
|
301
350
|
#
|
302
351
|
# @param [Integer, #to_i] length
|
303
352
|
# @return [Query]
|
304
|
-
# @see
|
353
|
+
# @see https://www.w3.org/TR/sparql11-query/#modResultLimit
|
305
354
|
def limit(length)
|
306
355
|
slice(nil, length)
|
307
356
|
end
|
@@ -320,16 +369,38 @@ module SPARQL; class Client
|
|
320
369
|
end
|
321
370
|
|
322
371
|
##
|
323
|
-
# @
|
324
|
-
#
|
325
|
-
#
|
326
|
-
#
|
327
|
-
#
|
372
|
+
# @overload prefix(prefix: uri)
|
373
|
+
# @example PREFIX dc: <http://purl.org/dc/elements/1.1/> PREFIX foaf: <http://xmlns.com/foaf/0.1/> SELECT * WHERE \{ ?s ?p ?o . \}
|
374
|
+
# query.select.
|
375
|
+
# prefix(dc: RDF::URI("http://purl.org/dc/elements/1.1/")).
|
376
|
+
# prefix(foaf: RDF::URI("http://xmlns.com/foaf/0.1/")).
|
377
|
+
# where([:s, :p, :o])
|
328
378
|
#
|
329
|
-
#
|
330
|
-
#
|
331
|
-
|
332
|
-
|
379
|
+
# @param [RDF::URI] uri
|
380
|
+
# @param [Symbol, String] prefix
|
381
|
+
# @return [Query]
|
382
|
+
#
|
383
|
+
# @overload prefix(string)
|
384
|
+
# @example PREFIX dc: <http://purl.org/dc/elements/1.1/> PREFIX foaf: <http://xmlns.com/foaf/0.1/> SELECT * WHERE \{ ?s ?p ?o . \}
|
385
|
+
# query.select.
|
386
|
+
# prefix("dc: <http://purl.org/dc/elements/1.1/>").
|
387
|
+
# prefix("foaf: <http://xmlns.com/foaf/0.1/>").
|
388
|
+
# where([:s, :p, :o])
|
389
|
+
#
|
390
|
+
# @param [string] string
|
391
|
+
# @return [Query]
|
392
|
+
# @see https://www.w3.org/TR/sparql11-query/#prefNames
|
393
|
+
def prefix(val)
|
394
|
+
options[:prefixes] ||= []
|
395
|
+
if val.kind_of? String
|
396
|
+
options[:prefixes] << val
|
397
|
+
elsif val.kind_of? Hash
|
398
|
+
val.each do |k, v|
|
399
|
+
options[:prefixes] << "#{k}: <#{v}>"
|
400
|
+
end
|
401
|
+
else
|
402
|
+
raise ArgumentError, "prefix must be a kind of String or a Hash"
|
403
|
+
end
|
333
404
|
self
|
334
405
|
end
|
335
406
|
|
@@ -338,10 +409,206 @@ module SPARQL; class Client
|
|
338
409
|
# query.select.where([:s, :p, :o]).
|
339
410
|
# optional([:s, RDF.type, :o], [:s, RDF::Vocab::DC.abstract, :o])
|
340
411
|
#
|
412
|
+
# The block form can be used for adding filters:
|
413
|
+
#
|
414
|
+
# @example ASK WHERE { ?s ?p ?o . OPTIONAL { ?s ?p ?o . FILTER(regex(?s, 'Abiline, Texas'))} }
|
415
|
+
# query.ask.where([:s, :p, :o]).optional([:s, :p, :o]) do
|
416
|
+
# filter("regex(?s, 'Abiline, Texas')")
|
417
|
+
# end
|
418
|
+
#
|
419
|
+
# @param [Array<RDF::Query::Pattern, Array>] patterns
|
420
|
+
# splat of zero or more patterns followed by zero or more queries.
|
421
|
+
# @yield [query]
|
422
|
+
# Yield form with or without argument; without an argument, evaluates within the query.
|
423
|
+
# @yieldparam [SPARQL::Client::Query] query used for creating filters on the optional patterns.
|
341
424
|
# @return [Query]
|
342
|
-
# @see
|
343
|
-
def optional(*patterns)
|
425
|
+
# @see https://www.w3.org/TR/sparql11-query/#optionals
|
426
|
+
def optional(*patterns, &block)
|
344
427
|
(options[:optionals] ||= []) << build_patterns(patterns)
|
428
|
+
|
429
|
+
if block_given?
|
430
|
+
# Steal options[:filters]
|
431
|
+
query_filters = options[:filters]
|
432
|
+
options[:filters] = []
|
433
|
+
case block.arity
|
434
|
+
when 1 then block.call(self)
|
435
|
+
else instance_eval(&block)
|
436
|
+
end
|
437
|
+
options[:optionals].last.concat(options[:filters])
|
438
|
+
options[:filters] = query_filters
|
439
|
+
end
|
440
|
+
|
441
|
+
self
|
442
|
+
end
|
443
|
+
|
444
|
+
##
|
445
|
+
# @example SELECT * WHERE \{ ?book dc:title ?title \} UNION \{ ?book dc11:title ?title \}
|
446
|
+
# query.select.where([:book, RDF::Vocab::DC.title, :title]).
|
447
|
+
# union([:book, RDF::Vocab::DC11.title, :title])
|
448
|
+
#
|
449
|
+
# @example SELECT * WHERE \{ ?book dc:title ?title \} UNION \{ ?book dc11:title ?title . FILTER(langmatches(lang(?title), 'EN'))\}
|
450
|
+
# query1 = SPARQL::Client::Query.select.
|
451
|
+
# where([:book, RDF::Vocab::DC11.title, :title]).
|
452
|
+
# filter("langmatches(?title, 'en')")
|
453
|
+
# query.select.where([:book, RDF::Vocab::DC.title, :title]).union(query1)
|
454
|
+
#
|
455
|
+
# The block form can be used for more complicated queries, using the `select` form (note, use either block or argument forms, not both):
|
456
|
+
#
|
457
|
+
# @example SELECT * WHERE \{ ?book dc:title ?title \} UNION \{ ?book dc11:title ?title . FILTER(langmatches(lang(?title), 'EN'))\}
|
458
|
+
# query1 = SPARQL::Client::Query.select.where([:book, RDF::Vocab::DC11.title, :title]).filter("langmatches(?title, 'en')")
|
459
|
+
# query.select.where([:book, RDF::Vocab::DC.title, :title]).union do |q|
|
460
|
+
# q.select.
|
461
|
+
# where([:book, RDF::Vocab::DC11.title, :title]).
|
462
|
+
# filter("langmatches(?title, 'en')")
|
463
|
+
# end
|
464
|
+
#
|
465
|
+
# @param [Array<RDF::Query::Pattern, Array>] patterns
|
466
|
+
# splat of zero or more patterns followed by zero or more queries.
|
467
|
+
# @yield [query]
|
468
|
+
# Yield form with or without argument; without an argument, evaluates within the query.
|
469
|
+
# @yieldparam [SPARQL::Client::Query] query used for adding select clauses.
|
470
|
+
# @return [Query]
|
471
|
+
# @see https://www.w3.org/TR/sparql11-query/#optionals
|
472
|
+
def union(*patterns, &block)
|
473
|
+
options[:unions] ||= []
|
474
|
+
|
475
|
+
if block_given?
|
476
|
+
raise ArgumentError, "#union requires either arguments or a block, not both." unless patterns.empty?
|
477
|
+
# Evaluate calls in a new query instance
|
478
|
+
query = self.class.select
|
479
|
+
case block.arity
|
480
|
+
when 1 then block.call(query)
|
481
|
+
else query.instance_eval(&block)
|
482
|
+
end
|
483
|
+
options[:unions] << query
|
484
|
+
elsif patterns.all? {|p| p.is_a?(SPARQL::Client::Query)}
|
485
|
+
# With argument form, all must be patterns or queries
|
486
|
+
options[:unions] += patterns
|
487
|
+
elsif patterns.all? {|p| p.is_a?(Array)}
|
488
|
+
# With argument form, all must be patterns, or queries
|
489
|
+
options[:unions] << self.class.select.where(*patterns)
|
490
|
+
else
|
491
|
+
raise ArgumentError, "#union arguments are triple patters or queries, not both."
|
492
|
+
end
|
493
|
+
|
494
|
+
self
|
495
|
+
end
|
496
|
+
|
497
|
+
##
|
498
|
+
# @example SELECT * WHERE \{ ?book dc:title ?title . MINUS \{ ?book dc11:title ?title \} \}
|
499
|
+
# query.select.where([:book, RDF::Vocab::DC.title, :title]).
|
500
|
+
# minus([:book, RDF::Vocab::DC11.title, :title])
|
501
|
+
#
|
502
|
+
# @example SELECT * WHERE \{ ?book dc:title ?title MINUS \{ ?book dc11:title ?title . FILTER(langmatches(lang(?title), 'EN')) \} \}
|
503
|
+
# query1 = SPARQL::Client::Query.select.
|
504
|
+
# where([:book, RDF::Vocab::DC11.title, :title]).
|
505
|
+
# filter("langmatches(?title, 'en')")
|
506
|
+
# query.select.where([:book, RDF::Vocab::DC.title, :title]).minus(query1)
|
507
|
+
#
|
508
|
+
# The block form can be used for more complicated queries, using the `select` form (note, use either block or argument forms, not both):
|
509
|
+
#
|
510
|
+
# @example SELECT * WHERE \{ ?book dc:title ?title MINUS \{ ?book dc11:title ?title . FILTER(langmatches(lang(?title), 'EN'))\} \}
|
511
|
+
# query1 = SPARQL::Client::Query.select.where([:book, RDF::Vocab::DC11.title, :title]).filter("langmatches(?title, 'en')")
|
512
|
+
# query.select.where([:book, RDF::Vocab::DC.title, :title]).minus do |q|
|
513
|
+
# q.select.
|
514
|
+
# where([:book, RDF::Vocab::DC11.title, :title]).
|
515
|
+
# filter("langmatches(?title, 'en')")
|
516
|
+
# end
|
517
|
+
#
|
518
|
+
# @param [Array<RDF::Query::Pattern, Array>] patterns
|
519
|
+
# splat of zero or more patterns followed by zero or more queries.
|
520
|
+
# @yield [query]
|
521
|
+
# Yield form with or without argument; without an argument, evaluates within the query.
|
522
|
+
# @yieldparam [SPARQL::Client::Query] query used for adding select clauses.
|
523
|
+
# @return [Query]
|
524
|
+
# @see https://www.w3.org/TR/sparql11-query/#optionals
|
525
|
+
def minus(*patterns, &block)
|
526
|
+
options[:minuses] ||= []
|
527
|
+
|
528
|
+
if block_given?
|
529
|
+
raise ArgumentError, "#minus requires either arguments or a block, not both." unless patterns.empty?
|
530
|
+
# Evaluate calls in a new query instance
|
531
|
+
query = self.class.select
|
532
|
+
case block.arity
|
533
|
+
when 1 then block.call(query)
|
534
|
+
else query.instance_eval(&block)
|
535
|
+
end
|
536
|
+
options[:minuses] << query
|
537
|
+
elsif patterns.all? {|p| p.is_a?(SPARQL::Client::Query)}
|
538
|
+
# With argument form, all must be patterns or queries
|
539
|
+
options[:minuses] += patterns
|
540
|
+
elsif patterns.all? {|p| p.is_a?(Array)}
|
541
|
+
# With argument form, all must be patterns, or queries
|
542
|
+
options[:minuses] << self.class.select.where(*patterns)
|
543
|
+
else
|
544
|
+
raise ArgumentError, "#minus arguments are triple patters or queries, not both."
|
545
|
+
end
|
546
|
+
|
547
|
+
self
|
548
|
+
end
|
549
|
+
|
550
|
+
##
|
551
|
+
# Specify inline data for a query
|
552
|
+
#
|
553
|
+
# @overload values
|
554
|
+
# Values returned from previous query.
|
555
|
+
#
|
556
|
+
# @return [Array<Array(key, RDF::Value)>]
|
557
|
+
#
|
558
|
+
# @overload values(vars, *data)
|
559
|
+
# @example single variable with multiple values
|
560
|
+
# query.select
|
561
|
+
# .where([:s, RDF::URI('http://purl.org/dc/terms/title'), :title])
|
562
|
+
# .values(:title, "This title", "Another title")
|
563
|
+
#
|
564
|
+
# @example multiple variables with multiple values
|
565
|
+
# query.select
|
566
|
+
# .where([:s, RDF::URI('http://purl.org/dc/terms/title'), :title],
|
567
|
+
# [:s, RDF.type, :type])
|
568
|
+
# .values([:type, :title],
|
569
|
+
# [RDF::URI('http://pcdm.org/models#Object'), "This title"],
|
570
|
+
# [RDF::URI('http://pcdm.org/models#Collection', 'Another title'])
|
571
|
+
#
|
572
|
+
# @example multiple variables with UNDEF
|
573
|
+
# query.select
|
574
|
+
# .where([:s, RDF::URI('http://purl.org/dc/terms/title'), :title],
|
575
|
+
# [:s, RDF.type, :type])
|
576
|
+
# .values([:type, :title],
|
577
|
+
# [nil "This title"],
|
578
|
+
# [RDF::URI('http://pcdm.org/models#Collection', nil])
|
579
|
+
#
|
580
|
+
# @param [Symbol, Array<Symbol>] vars
|
581
|
+
# @param [Array<RDF::Term, String, nil>] *data
|
582
|
+
# @return [Query]
|
583
|
+
def values(*args)
|
584
|
+
return @values if args.empty?
|
585
|
+
vars, *data = *args
|
586
|
+
vars = Array(vars).map {|var| RDF::Query::Variable.new(var)}
|
587
|
+
if vars.length == 1
|
588
|
+
# data may be a in array form or simple form
|
589
|
+
if data.any? {|d| d.is_a?(Array)} && !data.all? {|d| d.is_a?(Array)}
|
590
|
+
raise ArgumentError, "values data must all be in array form or all simple"
|
591
|
+
end
|
592
|
+
data = data.map {|d| Array(d)}
|
593
|
+
end
|
594
|
+
|
595
|
+
# Each data value must be an array with the same number of entries as vars
|
596
|
+
unless data.all? {|d| d.is_a?(Array) && d.all? {|dd| dd.is_a?(RDF::Value) || dd.is_a?(String) || dd.nil?}}
|
597
|
+
raise ArgumentError, "values data must each be an array of terms, strings, or nil"
|
598
|
+
end
|
599
|
+
|
600
|
+
# Turn strings into Literals
|
601
|
+
data = data.map do |d|
|
602
|
+
d.map do |nil_literal_or_term|
|
603
|
+
case nil_literal_or_term
|
604
|
+
when nil then nil
|
605
|
+
when String then RDF::Literal(nil_literal_or_term)
|
606
|
+
when RDF::Value then nil_literal_or_term
|
607
|
+
else raise ArgumentError
|
608
|
+
end
|
609
|
+
end
|
610
|
+
end
|
611
|
+
options[:values] = [vars, *data]
|
345
612
|
self
|
346
613
|
end
|
347
614
|
|
@@ -352,15 +619,17 @@ module SPARQL; class Client
|
|
352
619
|
end
|
353
620
|
|
354
621
|
##
|
355
|
-
# @private
|
622
|
+
# @private
|
356
623
|
def build_patterns(patterns)
|
357
624
|
patterns.map {|pattern| RDF::Query::Pattern.from(pattern)}
|
358
625
|
end
|
359
626
|
|
360
627
|
##
|
361
|
-
# @
|
628
|
+
# @example ASK WHERE { ?s ?p ?o . FILTER(regex(?s, 'Abiline, Texas')) }
|
629
|
+
# query.ask.where([:s, :p, :o]).filter("regex(?s, 'Abiline, Texas')")
|
630
|
+
# @return [Query]
|
362
631
|
def filter(string)
|
363
|
-
((options[:filters] ||= []) << string) if string and not string.empty?
|
632
|
+
((options[:filters] ||= []) << Filter.new(string)) if string and not string.empty?
|
364
633
|
self
|
365
634
|
end
|
366
635
|
|
@@ -445,33 +714,11 @@ module SPARQL; class Client
|
|
445
714
|
buffer << "FROM #{SPARQL::Client.serialize_value(options[:from])}" if options[:from]
|
446
715
|
|
447
716
|
unless patterns.empty? && form == :describe
|
448
|
-
buffer
|
449
|
-
|
450
|
-
if options[:graph]
|
451
|
-
buffer << 'GRAPH ' + SPARQL::Client.serialize_value(options[:graph])
|
452
|
-
buffer << '{'
|
453
|
-
end
|
454
|
-
|
455
|
-
@subqueries.each do |sq|
|
456
|
-
buffer << "{ #{sq.to_s} } ."
|
457
|
-
end
|
458
|
-
|
459
|
-
buffer += SPARQL::Client.serialize_patterns(patterns)
|
460
|
-
if options[:optionals]
|
461
|
-
options[:optionals].each do |patterns|
|
462
|
-
buffer << 'OPTIONAL {'
|
463
|
-
buffer += SPARQL::Client.serialize_patterns(patterns)
|
464
|
-
buffer << '}'
|
465
|
-
end
|
466
|
-
end
|
467
|
-
if options[:filters]
|
468
|
-
buffer += options[:filters].map { |filter| "FILTER(#{filter})" }
|
469
|
-
end
|
470
|
-
if options[:graph]
|
471
|
-
buffer << '}' # GRAPH
|
472
|
-
end
|
717
|
+
buffer += self.to_s_ggp.unshift('WHERE')
|
718
|
+
end
|
473
719
|
|
474
|
-
|
720
|
+
options.fetch(:unions, []).each do |query|
|
721
|
+
buffer += query.to_s_ggp.unshift('UNION')
|
475
722
|
end
|
476
723
|
|
477
724
|
if options[:group_by]
|
@@ -483,7 +730,7 @@ module SPARQL; class Client
|
|
483
730
|
buffer << 'ORDER BY'
|
484
731
|
options[:order_by].map { |elem|
|
485
732
|
case elem
|
486
|
-
# .order_by({ :
|
733
|
+
# .order_by({ var1: :asc, var2: :desc})
|
487
734
|
when Hash
|
488
735
|
elem.each { |key, val|
|
489
736
|
# check provided values
|
@@ -524,6 +771,55 @@ module SPARQL; class Client
|
|
524
771
|
buffer.join(' ')
|
525
772
|
end
|
526
773
|
|
774
|
+
# Serialize a Group Graph Pattern
|
775
|
+
# @private
|
776
|
+
def to_s_ggp
|
777
|
+
buffer = ["{"]
|
778
|
+
|
779
|
+
if options[:graph]
|
780
|
+
buffer << 'GRAPH ' + SPARQL::Client.serialize_value(options[:graph])
|
781
|
+
buffer << '{'
|
782
|
+
end
|
783
|
+
|
784
|
+
@subqueries.each do |sq|
|
785
|
+
buffer << "{ #{sq.to_s} } ."
|
786
|
+
end
|
787
|
+
|
788
|
+
buffer += SPARQL::Client.serialize_patterns(patterns)
|
789
|
+
if options[:optionals]
|
790
|
+
options[:optionals].each do |patterns|
|
791
|
+
buffer << 'OPTIONAL {'
|
792
|
+
buffer += SPARQL::Client.serialize_patterns(patterns)
|
793
|
+
buffer << '}'
|
794
|
+
end
|
795
|
+
end
|
796
|
+
if options[:filters]
|
797
|
+
buffer += options[:filters].map(&:to_s)
|
798
|
+
end
|
799
|
+
if options[:values]
|
800
|
+
vars = options[:values].first.map {|var| SPARQL::Client.serialize_value(var)}
|
801
|
+
buffer << "VALUES (#{vars.join(' ')}) {"
|
802
|
+
options[:values][1..-1].each do |data_block_value|
|
803
|
+
buffer << '('
|
804
|
+
buffer << data_block_value.map do |value|
|
805
|
+
value.nil? ? 'UNDEF' : SPARQL::Client.serialize_value(value)
|
806
|
+
end.join(' ')
|
807
|
+
buffer << ')'
|
808
|
+
end
|
809
|
+
buffer << '}'
|
810
|
+
end
|
811
|
+
if options[:graph]
|
812
|
+
buffer << '}' # GRAPH
|
813
|
+
end
|
814
|
+
|
815
|
+
options.fetch(:minuses, []).each do |query|
|
816
|
+
buffer += query.to_s_ggp.unshift('MINUS')
|
817
|
+
end
|
818
|
+
|
819
|
+
buffer << '}'
|
820
|
+
buffer
|
821
|
+
end
|
822
|
+
|
527
823
|
##
|
528
824
|
# Outputs a developer-friendly representation of this query to `stderr`.
|
529
825
|
#
|
@@ -540,5 +836,16 @@ module SPARQL; class Client
|
|
540
836
|
def inspect
|
541
837
|
sprintf("#<%s:%#0x(%s)>", self.class.name, __id__, to_s)
|
542
838
|
end
|
839
|
+
|
840
|
+
# Allow Filters to be
|
841
|
+
class Filter < SPARQL::Client::QueryElement
|
842
|
+
def initialize(*args)
|
843
|
+
super
|
844
|
+
end
|
845
|
+
|
846
|
+
def to_s
|
847
|
+
"FILTER(#{elements.join(' ')})"
|
848
|
+
end
|
849
|
+
end
|
543
850
|
end
|
544
|
-
end
|
851
|
+
end
|