sparql-client 2.1.0 → 3.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,6 @@
1
- module SPARQL; class Client
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 http://www.w3.org/TR/sparql11-query/#QueryForms
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 http://www.w3.org/TR/sparql11-query/#ask
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 http://www.w3.org/TR/sparql11-query/#select
53
- def self.select(*variables)
54
- options = variables.last.is_a?(Hash) ? variables.pop : {}
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 http://www.w3.org/TR/sparql11-query/#describe
72
- def self.describe(*variables)
73
- options = variables.last.is_a?(Hash) ? variables.pop : {}
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 http://www.w3.org/TR/sparql11-query/#construct
91
- def self.construct(*patterns)
92
- options = patterns.last.is_a?(Hash) ? patterns.pop : {}
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 = {}, &block)
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 http://www.w3.org/TR/sparql11-query/#ask
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
- # @param [Array<Symbol>] variables
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 http://www.w3.org/TR/sparql11-query/#select
135
+ # @see https://www.w3.org/TR/sparql11-query/#select
127
136
  def select(*variables)
128
- @values = variables.map { |var| [var, RDF::Query::Variable.new(var)] }
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 http://www.w3.org/TR/sparql11-query/#describe
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 http://www.w3.org/TR/sparql11-query/#construct
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 http://www.w3.org/TR/sparql11-query/#specifyingDataset
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 http://www.w3.org/TR/sparql11-query/#GraphPattern
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(:o => :asc, :p => :desc)
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 http://www.w3.org/TR/sparql11-query/#modOrderBy
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 http://www.w3.org/TR/sparql11-query/#modOrderBy
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 http://www.w3.org/TR/sparql11-query/#modOrderBy
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 http://www.w3.org/TR/sparql11-query/#groupby
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 http://www.w3.org/TR/sparql11-query/#modDuplicates
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 http://www.w3.org/TR/sparql11-query/#modDuplicates
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 http://www.w3.org/TR/sparql11-query/#queryDataset
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 http://www.w3.org/TR/sparql11-query/#modOffset
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 http://www.w3.org/TR/sparql11-query/#modResultLimit
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
- # @example PREFIX dc: <http://purl.org/dc/elements/1.1/> PREFIX foaf: <http://xmlns.com/foaf/0.1/> SELECT * WHERE \{ ?s ?p ?o . \}
324
- # query.select.
325
- # prefix(dc: RDF::URI("http://purl.org/dc/elements/1.1/")).
326
- # prefix(foaf: RDF::URI("http://xmlns.com/foaf/0.1/")).
327
- # where([:s, :p, :o])
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
- # @return [Query]
330
- # @see http://www.w3.org/TR/sparql11-query/#prefNames
331
- def prefix(string)
332
- (options[:prefixes] ||= []) << string
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 http://www.w3.org/TR/sparql11-query/#optionals
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
- # @private
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 << 'WHERE {'
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
- buffer << '}' # WHERE
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({ :var1 => :asc, :var2 => :desc})
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; end
851
+ end