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