sparql 1.1.6 → 1.1.7

Sign up to get free protection for your applications and to get access to all the features.
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
  #