sparql 1.1.6 → 1.1.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7a1d4a5797ead6402462b84ed480cca4a748f2cb
4
- data.tar.gz: 86cae5d9912179a3e8c08ac5d0b92ac3949787fe
3
+ metadata.gz: f8e188130c333370c626f037f5c2312a222b5f3d
4
+ data.tar.gz: 61c0398d56e8e2891dc97d4a8843411c5912e0d6
5
5
  SHA512:
6
- metadata.gz: 69ef15c1ef1a15a051fb7f9837d2a4bb584c4d557ce4d52958cd214752eae33c80d0e87e5cdbf98527b4c8bb73041969ec3f2b2d2790386459a08b7c3a7baa60
7
- data.tar.gz: 088b6db9ad1d2e7c1019956bd112be2abafbf1fcf4004c4a410ac5eb6a402a0c36e7a80b4f8d73b66ac0fa584683b9406494a1ba7552139a7dfd1d245d39b155
6
+ metadata.gz: 9398d997c21707cfaeef51fb8fd5dd11008e0d23bcc81ee8f9027d98ee37297b19735b4c4a6202cbf241c7faf811510af0dbf588742bcaf2389cfdda1488027a
7
+ data.tar.gz: 03a7de12fa45056026eff0a2d93bfc92d375173c6afabfc462e223f10f5ea75f4ef943d22a9621aa84836b07703be4d4ee2206b917206edba70648af30f79723
data/README.md CHANGED
@@ -9,7 +9,7 @@ This is a [Ruby][] implementation of [SPARQL][] for [RDF.rb][].
9
9
  ## Features
10
10
 
11
11
  * 100% free and unencumbered [public domain](http://unlicense.org/) software.
12
- * [SPARQL 1.1 Query][] parsing and execution (excluding [Property Paths][])
12
+ * Complete [SPARQL 1.1 Query][] parsing and execution
13
13
  * SPARQL results as [XML][SPARQL XML], [JSON][SPARQL JSON],
14
14
  [CSV][SPARQL 1.1 Query Results CSV and TSV Formats],
15
15
  [TSV][SPARQL 1.1 Query Results CSV and TSV Formats]
@@ -21,6 +21,7 @@ This is a [Ruby][] implementation of [SPARQL][] for [RDF.rb][].
21
21
  * [Rack][] and [Sinatra][] middleware to perform [HTTP content negotiation][conneg] for result formats
22
22
  * Compatible with any [Rack][] or [Sinatra][] application and any Rack-based framework.
23
23
  * Helper method for describing [SPARQL Service Description][SSD]
24
+ * Implementation Report: {file:etc/earl.html EARL}
24
25
  * Compatible with Ruby >= 1.9.3.
25
26
  * Compatible with older Ruby versions with the help of the [Backports][] gem.
26
27
  * Supports Unicode query strings both on all versions of Ruby.
@@ -30,8 +31,6 @@ This is a [Ruby][] implementation of [SPARQL][] for [RDF.rb][].
30
31
  The {SPARQL} gem implements [SPARQL 1.1 Query][], and [SPARQL 1.1 Update][], and provides [Rack][] and [Sinatra][] middleware to provide results using [HTTP Content Negotiation][conneg].
31
32
 
32
33
  * {SPARQL::Grammar} implements a [SPARQL 1.1 Query][] and [SPARQL 1.1 Update][] parser generating [SPARQL S-Expressions (SSE)][SSE].
33
- * Support for [Property Paths][] is excluded.
34
- See the section on [SPARQL 1.1 Query][] extensions and limitations for further detail.
35
34
  * {SPARQL::Algebra} executes SSE against Any `RDF::Graph` or `RDF::Repository`, including compliant [RDF.rb][] repository adaptors such as [RDF::DO][] and [RDF::Mongo][].
36
35
  * {Rack::SPARQL} and {Sinatra::SPARQL} provide middleware components to format results using an appropriate format based on [HTTP content negotiation][conneg].
37
36
 
@@ -53,13 +52,13 @@ The SPARQL gem now implements the following [SPARQL 1.1 Query][] operations:
53
52
  * [Inline Data](http://www.w3.org/TR/2013/REC-sparql11-query-20130321/#inline-data)
54
53
  * [Exists](http://www.w3.org/TR/2013/REC-sparql11-query-20130321/#func-filter-exists)
55
54
  * [Negation](http://www.w3.org/TR/2013/REC-sparql11-query-20130321/#negation)
55
+ * [Property Paths](http://www.w3.org/TR/2013/REC-sparql11-query-20130321/#propertypaths)
56
56
 
57
57
  The gem also includes the following [SPARQL 1.1 Update][] operations:
58
58
  * [Graph Update](http://www.w3.org/TR/sparql11-update/#graphUpdate)
59
59
  * [Graph Management](http://www.w3.org/TR/sparql11-update/#graphManagement)
60
60
 
61
- The only major area of [SPARQL 1.1 Query][] missing is
62
- [Property Paths][], which will be in later release along with:
61
+ Not supported:
63
62
 
64
63
  * [Federated Query][SPARQL 1.1 Federated Query],
65
64
  * [Entailment Regimes][SPARQL 1.1 Entailment Regimes],
@@ -337,7 +336,6 @@ A copy of the [SPARQL 1.0 tests][] and [SPARQL 1.1 tests][] are also included in
337
336
  [SPARQL XML]: http://www.w3.org/TR/rdf-sparql-XMLres/
338
337
  [SPARQL JSON]: http://www.w3.org/TR/rdf-sparql-json-res/
339
338
  [SPARQL EBNF]: http://www.w3.org/TR/sparql11-query/#sparqlGrammar
340
- [Property Paths]: http://www.w3.org/TR/2013/REC-sparql11-query-20130321/#propertypaths
341
339
 
342
340
  [SSD]: http://www.w3.org/TR/sparql11-service-description/
343
341
  [Rack]: http://rack.rubyforge.org/
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.6
1
+ 1.1.7
@@ -120,7 +120,7 @@ module SPARQL; module Algebra
120
120
  end
121
121
 
122
122
  debug(options) {"#{operator.inspect}(#{operands.map(&:inspect).join(',')})"}
123
- options.delete_if {|k, v| [:debug, :depth, :prefixes, :base_uri, :update].include?(k) }
123
+ options.delete_if {|k, v| [:debug, :depth, :prefixes, :base_uri, :update, :validate].include?(k) }
124
124
  operands << options unless options.empty?
125
125
  operator.new(*operands)
126
126
  end
@@ -317,7 +317,33 @@ module SPARQL; module Algebra
317
317
  def to_sxp_bin
318
318
  self
319
319
  end
320
-
320
+
321
+ ##
322
+ # Is this value valid, and composed only of valid components?
323
+ #
324
+ # @return [Boolean] `true` or `false`
325
+ def valid?
326
+ true
327
+ end
328
+
329
+ ##
330
+ # Is this value invalid, or is it composed of any invalid components?
331
+ #
332
+ # @return [Boolean] `true` or `false`
333
+ def invalid?
334
+ !valid?
335
+ end
336
+
337
+ ##
338
+ # Default validate! implementation, overridden in concrete classes
339
+ # @return [SPARQL::Algebra::Expression] `self`
340
+ # @raise [ArgumentError] if the value is invalid
341
+ def validate!
342
+ raise ArgumentError if invalid?
343
+ self
344
+ end
345
+ alias_method :validate, :validate!
346
+
321
347
  private
322
348
  # @overload: May be called with node, message and an option hash
323
349
  # @param [String] node processing node
@@ -135,6 +135,37 @@ class Array
135
135
  end
136
136
  self
137
137
  end
138
+
139
+ ##
140
+ # Return the non-destinguished variables contained within this Array
141
+ # @return [Array<RDF::Query::Variable>]
142
+ def ndvars
143
+ vars.reject(&:distinguished?)
144
+ end
145
+
146
+ ##
147
+ # Return the variables contained within this Array
148
+ # @return [Array<RDF::Query::Variable>]
149
+ def vars
150
+ select {|o| o.respond_to?(:vars)}.map(&:vars).flatten.compact
151
+ end
152
+
153
+ ##
154
+ # Is this value composed only of valid components?
155
+ #
156
+ # @return [Boolean] `true` or `false`
157
+ def valid?
158
+ all? {|e| e.respond_to?(:valid?) ? e.valid? : true}
159
+ end
160
+
161
+ ##
162
+ # Validate all components.
163
+ # @return [Array] `self`
164
+ # @raise [ArgumentError] if the value is invalid
165
+ def validate!
166
+ each {|e| e.validate! if e.respond_to?(:validate!)}
167
+ self
168
+ end
138
169
  end
139
170
 
140
171
  ##
@@ -199,6 +230,20 @@ module RDF::Term
199
230
  (language == other.language || dtr == RDF::XSD.string) :
200
231
  dtr == RDF::XSD.string
201
232
  end
233
+
234
+ ##
235
+ # Return the non-destinguished variables contained within this operator
236
+ # @return [Array<RDF::Query::Variable>]
237
+ def ndvars
238
+ vars.reject(&:distinguished?)
239
+ end
240
+
241
+ ##
242
+ # Return the variables contained within this operator
243
+ # @return [Array<RDF::Query::Variable>]
244
+ def vars
245
+ variable? ? [self] : []
246
+ end
202
247
  end # RDF::Term
203
248
 
204
249
  # Override RDF::Queryable to execute against SPARQL::Algebra::Query elements as well as RDF::Query and RDF::Pattern
@@ -311,6 +356,20 @@ class RDF::Query
311
356
  def query_yields_solutions?
312
357
  true
313
358
  end
359
+
360
+ ##
361
+ # Return the non-destinguished variables contained within patterns
362
+ # @return [Array<RDF::Query::Variable>]
363
+ def ndvars
364
+ patterns.map(&:ndvars).flatten
365
+ end
366
+
367
+ ##
368
+ # Return the variables contained within patterns
369
+ # @return [Array<RDF::Query::Variable>]
370
+ def vars
371
+ patterns.map(&:vars).flatten
372
+ end
314
373
  end
315
374
 
316
375
  class RDF::Query::Pattern
@@ -323,6 +382,20 @@ class RDF::Query::Pattern
323
382
  [:triple, subject, predicate, object]
324
383
  end
325
384
  end
385
+
386
+ ##
387
+ # Return the non-destinguished variables contained within this pattern
388
+ # @return [Array<RDF::Query::Variable>]
389
+ def ndvars
390
+ vars.reject(&:distinguished?)
391
+ end
392
+
393
+ ##
394
+ # Return the variables contained within this pattern
395
+ # @return [Array<RDF::Query::Variable>]
396
+ def vars
397
+ variables.values
398
+ end
326
399
  end
327
400
 
328
401
  ##
@@ -85,6 +85,17 @@ module SPARQL; module Algebra
85
85
  autoload :Subtract, 'sparql/algebra/operator/subtract'
86
86
  autoload :UCase, 'sparql/algebra/operator/ucase'
87
87
 
88
+ # Property Paths
89
+ autoload :Alt, 'sparql/algebra/operator/alt'
90
+ autoload :NotOneOf, 'sparql/algebra/operator/notoneof'
91
+ autoload :PathOpt, 'sparql/algebra/operator/path_opt'
92
+ autoload :PathPlus, 'sparql/algebra/operator/path_plus'
93
+ autoload :PathStar, 'sparql/algebra/operator/path_star'
94
+ autoload :Path, 'sparql/algebra/operator/path'
95
+ autoload :Reverse, 'sparql/algebra/operator/reverse'
96
+ autoload :Seq, 'sparql/algebra/operator/seq'
97
+ autoload :Sequence, 'sparql/algebra/operator/sequence'
98
+
88
99
  # Miscellaneous
89
100
  autoload :Asc, 'sparql/algebra/operator/asc'
90
101
  autoload :Coalesce, 'sparql/algebra/operator/coalesce'
@@ -159,6 +170,7 @@ module SPARQL; module Algebra
159
170
  when :>= then GreaterThanOrEqual
160
171
  when :abs then Abs
161
172
  when :add then Add
173
+ when :alt then Alt
162
174
  when :and, :'&&' then And
163
175
  when :avg then Avg
164
176
  when :bnode then BNode
@@ -195,18 +207,26 @@ module SPARQL; module Algebra
195
207
  when :month then Month
196
208
  when :multiply then Multiply
197
209
  when :not, :'!' then Not
198
- when :notexists then NotExists
210
+ when :notexists then NotExists
199
211
  when :notin then NotIn
212
+ when :notoneof then NotOneOf
200
213
  when :now then Now
201
214
  when :or, :'||' then Or
215
+ when :path then Path
216
+ when :path? then PathOpt
217
+ when :"path*" then PathStar
218
+ when :"path+" then PathPlus
202
219
  when :plus then Plus
203
220
  when :rand then Rand
204
221
  when :regex then Regex
205
222
  when :replace then Replace
223
+ when :reverse then Reverse
206
224
  when :round then Round
207
225
  when :sameterm then SameTerm
208
226
  when :sample then Sample
209
227
  when :seconds then Seconds
228
+ when :seq then Seq
229
+ when :sequence then Sequence
210
230
  when :sha1 then SHA1
211
231
  when :sha256 then SHA256
212
232
  when :sha512 then SHA512
@@ -322,7 +342,11 @@ module SPARQL; module Algebra
322
342
  @options = operands.last.is_a?(Hash) ? operands.pop.dup : {}
323
343
  @operands = operands.map! do |operand|
324
344
  case operand
345
+ when Array
346
+ operand.each {|op| op.parent = self if operand.respond_to?(:parent=)}
347
+ operand
325
348
  when Operator, Variable, RDF::Term, RDF::Query, RDF::Query::Pattern, Array, Symbol
349
+ operand.parent = self if operand.respond_to?(:parent=)
326
350
  operand
327
351
  when TrueClass, FalseClass, Numeric, String, DateTime, Date, Time
328
352
  RDF::Literal(operand)
@@ -387,12 +411,6 @@ module SPARQL; module Algebra
387
411
  @prefixes = hash
388
412
  end
389
413
 
390
- ##
391
- # Any additional options for this operator.
392
- #
393
- # @return [Hash]
394
- attr_reader :options
395
-
396
414
  ##
397
415
  # The operands to this operator.
398
416
  #
@@ -540,7 +558,7 @@ module SPARQL; module Algebra
540
558
  #
541
559
  # @return [String]
542
560
  def inspect
543
- sprintf("#<%s:%#0x(%s)>", self.class.name, __id__, operands.map(&:inspect).join(', '))
561
+ sprintf("#<%s:%#0x(%s)>", self.class.name, __id__, operands.to_sse.gsub(/\s+/m, ' '))
544
562
  end
545
563
 
546
564
  ##
@@ -552,7 +570,21 @@ module SPARQL; module Algebra
552
570
  alias_method :==, :eql?
553
571
 
554
572
  ##
555
- # Iterate via deapth-first recursive descent over operands, yielding each operator
573
+ # Return the non-destinguished variables contained within this operator
574
+ # @return [Array<RDF::Query::Variable>]
575
+ def ndvars
576
+ vars.reject(&:distinguished?)
577
+ end
578
+
579
+ ##
580
+ # Return the variables contained within this operator
581
+ # @return [Array<RDF::Query::Variable>]
582
+ def vars
583
+ operands.select {|o| o.respond_to?(:vars)}.map(&:vars).flatten
584
+ end
585
+
586
+ ##
587
+ # Iterate via depth-first recursive descent over operands, yielding each operator
556
588
  # @yield operator
557
589
  # @yieldparam [Object] operator
558
590
  def descendants(&block)
@@ -569,6 +601,38 @@ module SPARQL; module Algebra
569
601
  block.call(operand)
570
602
  end
571
603
  end
604
+
605
+ ##
606
+ # Parent expression, if any
607
+ #
608
+ # @return [Operator]
609
+ def parent; @options[:parent]; end
610
+
611
+ ##
612
+ # Parent operator, if any
613
+ #
614
+ # @return [Operator]
615
+ def parent=(operator)
616
+ @options[:parent]= operator
617
+ end
618
+
619
+ ##
620
+ # First ancestor operator of type `klass`
621
+ #
622
+ # @param [Class] klass
623
+ # @return [Operator]
624
+ def first_ancestor(klass)
625
+ parent.is_a?(klass) ? parent : parent.first_ancestor(klass) if parent
626
+ end
627
+
628
+ ##
629
+ # Validate all operands, operator specific classes should override for operator-specific validation
630
+ # @return [SPARQL::Algebra::Expression] `self`
631
+ # @raise [ArgumentError] if the value is invalid
632
+ def validate!
633
+ operands.each {|op| op.validate! if op.respond_to?(:validate!)}
634
+ self
635
+ end
572
636
  protected
573
637
 
574
638
  ##
@@ -0,0 +1,58 @@
1
+ module SPARQL; module Algebra
2
+ class Operator
3
+ ##
4
+ # The SPARQL Property Path `alt` (Alternative Property Path) operator.
5
+ #
6
+ # @example
7
+ # (alt a b)
8
+ #
9
+ # @see http://www.w3.org/TR/sparql11-query/#defn_evalPP_alternative
10
+ class Alt < Operator::Binary
11
+ include Query
12
+
13
+ NAME = :alt
14
+
15
+ ##
16
+ # Equivalent to:
17
+ #
18
+ # (path x (alt :p :q) y)
19
+ # => (union (bgp (x :p y)) (bgp (x :q y)))
20
+ #
21
+ # @param [RDF::Queryable] queryable
22
+ # the graph or repository to query
23
+ # @param [Hash{Symbol => Object}] options
24
+ # any additional keyword options
25
+ # @option options [RDF::Term, RDF::Variable] :subject
26
+ # @option options [RDF::Term, RDF::Variable] :object
27
+ # @yield [solution]
28
+ # each matching solution
29
+ # @yieldparam [RDF::Query::Solution] solution
30
+ # @yieldreturn [void] ignored
31
+ # @see http://www.w3.org/TR/rdf-sparql-query/#sparqlAlgebra
32
+ def execute(queryable, options = {}, &block)
33
+ subject, object = options[:subject], options[:object]
34
+ debug(options) {"Alt #{[subject, operands, object].to_sse}"}
35
+
36
+ # Solutions where predicate exists
37
+ qa = if operand(0).is_a?(RDF::Term)
38
+ RDF::Query.new do |q|
39
+ q.pattern [subject, operand(0), object]
40
+ end
41
+ else
42
+ operand(0)
43
+ end
44
+
45
+ qb = if operand(1).is_a?(RDF::Term)
46
+ RDF::Query.new do |q|
47
+ q.pattern [subject, operand(1), object]
48
+ end
49
+ else
50
+ operand(1)
51
+ end
52
+
53
+ query = Union.new(qa, qb)
54
+ queryable.query(query, options.merge(depth: options[:depth].to_i + 1), &block)
55
+ end
56
+ end # Alt
57
+ end # Operator
58
+ end; end # SPARQL::Algebra
@@ -41,10 +41,10 @@ module SPARQL; module Algebra
41
41
  # @see http://www.w3.org/TR/rdf-sparql-query/#evaluation
42
42
  def execute(queryable, options = {}, &block)
43
43
  debug(options) {"Extend"}
44
- @solutions = operands.last.execute(queryable, options.merge(depth: options[:depth].to_i + 1))
44
+ @solutions = operand(1).execute(queryable, options.merge(depth: options[:depth].to_i + 1))
45
45
  @solutions.each do |solution|
46
46
  debug(options) {"===> soln #{solution.to_hash.inspect}"}
47
- operands.first.each do |(var, expr)|
47
+ operand(0).each do |(var, expr)|
48
48
  begin
49
49
  val = expr.evaluate(solution, options.merge(
50
50
  queryable: queryable,
@@ -60,7 +60,19 @@ module SPARQL; module Algebra
60
60
  @solutions.each(&block) if block_given?
61
61
  @solutions
62
62
  end
63
-
63
+
64
+ # The variable introduced by the BIND clause must not have been used in the group graph pattern up to the point of use in BIND
65
+ def validate!
66
+ bind_vars = operand(0).map(&:first)
67
+ query_vars = operand(1).vars
68
+
69
+ unless (bind_vars.compact & query_vars.compact).empty?
70
+ raise ArgumentError,
71
+ "bound variable used in query: #{(bind_vars.compact & query_vars.compact).to_sse}"
72
+ end
73
+ super
74
+ end
75
+
64
76
  ##
65
77
  # Returns an optimized version of this query.
66
78
  #
@@ -52,7 +52,24 @@ module SPARQL; module Algebra
52
52
  @solutions.each(&block) if block_given?
53
53
  @solutions
54
54
  end
55
-
55
+
56
+ # If filtering a join of two BGPs (having the same graph name), don't worry about validating, for shared ndvars, anyway,
57
+ #
58
+ # (filter (regex ?homepage "^http://example.org/" "")
59
+ # (join
60
+ # (bgp (triple ??who :homepage ?homepage))
61
+ # (bgp (triple ??who :schoolHomepage ?schoolPage))))))
62
+ #
63
+ # is legitimate
64
+ def validate!
65
+ unless (join = operands.last).is_a?(Join) &&
66
+ join.operands.all? {|op| op.is_a?(RDF::Query)} &&
67
+ join.operands.map(&:context).uniq.length == 1
68
+ operands.last.validate!
69
+ end
70
+ self
71
+ end
72
+
56
73
  ##
57
74
  # Returns an optimized version of this query.
58
75
  #