sparql 1.0.6 → 1.0.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.
Files changed (57) hide show
  1. data/README.md +11 -4
  2. data/VERSION +1 -1
  3. data/lib/sparql/algebra/extensions.rb +36 -0
  4. data/lib/sparql/algebra/operator.rb +197 -87
  5. data/lib/sparql/algebra/operator/abs.rb +31 -0
  6. data/lib/sparql/algebra/operator/base.rb +1 -0
  7. data/lib/sparql/algebra/operator/bnode.rb +88 -0
  8. data/lib/sparql/algebra/operator/bound.rb +2 -1
  9. data/lib/sparql/algebra/operator/ceil.rb +31 -0
  10. data/lib/sparql/algebra/operator/coalesce.rb +65 -0
  11. data/lib/sparql/algebra/operator/concat.rb +49 -0
  12. data/lib/sparql/algebra/operator/contains.rb +44 -0
  13. data/lib/sparql/algebra/operator/dataset.rb +11 -48
  14. data/lib/sparql/algebra/operator/datatype.rb +4 -2
  15. data/lib/sparql/algebra/operator/day.rb +31 -0
  16. data/lib/sparql/algebra/operator/encode_for_uri.rb +38 -0
  17. data/lib/sparql/algebra/operator/extend.rb +31 -2
  18. data/lib/sparql/algebra/operator/floor.rb +33 -0
  19. data/lib/sparql/algebra/operator/hours.rb +31 -0
  20. data/lib/sparql/algebra/operator/if.rb +55 -0
  21. data/lib/sparql/algebra/operator/in.rb +68 -0
  22. data/lib/sparql/algebra/operator/iri.rb +40 -0
  23. data/lib/sparql/algebra/operator/is_numeric.rb +41 -0
  24. data/lib/sparql/algebra/operator/lang_matches.rb +2 -2
  25. data/lib/sparql/algebra/operator/lcase.rb +31 -0
  26. data/lib/sparql/algebra/operator/md5.rb +34 -0
  27. data/lib/sparql/algebra/operator/minutes.rb +31 -0
  28. data/lib/sparql/algebra/operator/month.rb +31 -0
  29. data/lib/sparql/algebra/operator/not.rb +2 -2
  30. data/lib/sparql/algebra/operator/notin.rb +70 -0
  31. data/lib/sparql/algebra/operator/now.rb +29 -0
  32. data/lib/sparql/algebra/operator/order.rb +9 -13
  33. data/lib/sparql/algebra/operator/rand.rb +24 -0
  34. data/lib/sparql/algebra/operator/replace.rb +81 -0
  35. data/lib/sparql/algebra/operator/round.rb +31 -0
  36. data/lib/sparql/algebra/operator/seconds.rb +31 -0
  37. data/lib/sparql/algebra/operator/sha1.rb +34 -0
  38. data/lib/sparql/algebra/operator/sha256.rb +34 -0
  39. data/lib/sparql/algebra/operator/sha384.rb +34 -0
  40. data/lib/sparql/algebra/operator/sha512.rb +34 -0
  41. data/lib/sparql/algebra/operator/strafter.rb +57 -0
  42. data/lib/sparql/algebra/operator/strbefore.rb +59 -0
  43. data/lib/sparql/algebra/operator/strdt.rb +33 -0
  44. data/lib/sparql/algebra/operator/strends.rb +46 -0
  45. data/lib/sparql/algebra/operator/strlang.rb +34 -0
  46. data/lib/sparql/algebra/operator/strlen.rb +34 -0
  47. data/lib/sparql/algebra/operator/strstarts.rb +46 -0
  48. data/lib/sparql/algebra/operator/struuid.rb +32 -0
  49. data/lib/sparql/algebra/operator/substr.rb +80 -0
  50. data/lib/sparql/algebra/operator/timezone.rb +34 -0
  51. data/lib/sparql/algebra/operator/tz.rb +31 -0
  52. data/lib/sparql/algebra/operator/ucase.rb +31 -0
  53. data/lib/sparql/algebra/operator/uuid.rb +32 -0
  54. data/lib/sparql/algebra/operator/year.rb +31 -0
  55. data/lib/sparql/grammar/parser11.rb +128 -70
  56. data/lib/sparql/grammar/terminals11.rb +4 -5
  57. metadata +62 -7
@@ -0,0 +1,31 @@
1
+ module SPARQL; module Algebra
2
+ class Operator
3
+ ##
4
+ # The SPARQL logical `abs` operator.
5
+ #
6
+ # @example
7
+ # (abs ?x)
8
+ #
9
+ # @see http://www.w3.org/TR/sparql11-query/#func-abs
10
+ # @see http://www.w3.org/TR/xpath-functions/#func-abs
11
+ class Abs < Operator::Unary
12
+ include Evaluatable
13
+
14
+ NAME = [:abs]
15
+
16
+ ##
17
+ # Returns the absolute value of the operand. An error is raised if operand is not a numeric value
18
+ #
19
+ # @param [RDF::Literal] operand
20
+ # the operand
21
+ # @return [RDF::Literal] literal of same type
22
+ # @raise [TypeError] if the operand is not a numeric value
23
+ def apply(operand)
24
+ case operand
25
+ when RDF::Literal::Numeric then operand.abs
26
+ else raise TypeError, "expected an RDF::Literal::Numeric, but got #{operand.inspect}"
27
+ end
28
+ end
29
+ end # Abs
30
+ end # Operator
31
+ end; end # SPARQL::Algebra
@@ -27,6 +27,7 @@ module SPARQL; module Algebra
27
27
  # @see http://www.w3.org/TR/rdf-sparql-query/#sparqlAlgebra
28
28
  def execute(queryable, options = {})
29
29
  debug(options) {"Base #{operands.first}"}
30
+ Operator.base_uri = operands.first
30
31
  @solutions = operands.last.execute(queryable, options.merge(:depth => options[:depth].to_i + 1))
31
32
  debug(options) {"=> #{@solutions.inspect}"}
32
33
  @solutions
@@ -0,0 +1,88 @@
1
+ module SPARQL; module Algebra
2
+ class Operator
3
+ ##
4
+ # The SPARQL `bnode` operator.
5
+ #
6
+ # @example
7
+ # (prefix ((: <http://example.org/>)
8
+ # (xsd: <http://www.w3.org/2001/XMLSchema#>))
9
+ # (project (?s1 ?s2 ?b1 ?b2)
10
+ # (extend ((?b1 (bnode ?s1)) (?b2 (bnode ?s2)))
11
+ # (filter (exprlist (|| (= ?a :s1) (= ?a :s3)) (|| (= ?b :s1) (= ?b :s3)))
12
+ # (bgp
13
+ # (triple ?a :str ?s1)
14
+ # (triple ?b :str ?s2)
15
+ # )))))
16
+ #
17
+ # @see http://www.w3.org/TR/sparql11-query/#func-bnode
18
+ class BNode < Operator::Unary
19
+ include Evaluatable
20
+
21
+ NAME = :bnode
22
+
23
+
24
+ ##
25
+ # Initializes a new operator instance.
26
+ #
27
+ # @param [RDF::Literal] literal (false)
28
+ # @param [Hash{Symbol => Object}] options
29
+ # any additional options (see {Operator#initialize})
30
+ # @raise [TypeError] if any operand is invalid
31
+ def initialize(literal = false, options = {})
32
+ super
33
+ end
34
+
35
+ ##
36
+ # Evaluates this operator using the given variable `bindings`.
37
+ #
38
+ # @param [RDF::Query::Solution, #[]] bindings
39
+ # a query solution containing zero or more variable bindings
40
+ # @return [RDF::Term]
41
+ # @abstract
42
+ def evaluate(bindings = {})
43
+ args = operands.map { |operand| operand.evaluate(bindings) }
44
+ apply(args.first, bindings)
45
+ end
46
+
47
+ ##
48
+ # The BNODE function constructs a blank node that is distinct from all blank nodes in the dataset being queried and distinct from all blank nodes created by calls to this constructor for other query solutions. If the no argument form is used, every call results in a distinct blank node. If the form with a simple literal is used, every call results in distinct blank nodes for different simple literals, and the same blank node for calls with the same simple literal within expressions for one solution mapping.
49
+ #
50
+ # This functionality is compatible with the treatment of blank nodes in SPARQL CONSTRUCT templates.
51
+ #
52
+ # @param [RDF::Literal] literal (nil)
53
+ # @param [RDF::Query::Solution, #[]] bindings
54
+ # a query solution containing zero or more variable bindings
55
+ # @return [RDF::Node]
56
+ # @raise [TypeError] if the operand is not a simple literal or nil
57
+ def apply(literal, bindings)
58
+ @@bnode_base ||= "b0"
59
+ @@bindings ||= bindings
60
+ @@bnodes ||= {}
61
+
62
+ if literal == RDF::Literal::FALSE
63
+ l, @@bnode_base = @@bnode_base, @@bnode_base.succ
64
+ RDF::Node.new(l)
65
+ else
66
+ raise TypeError, "expected an simple literal, but got #{literal.inspect}" unless literal.literal? && literal.simple?
67
+ # Return the same BNode if used with the same binding
68
+ @@bnodes, @@bindings = {}, bindings unless @@bindings == bindings
69
+ @@bnodes[literal.to_s.to_sym] ||= begin
70
+ l, @@bnode_base = @@bnode_base, @@bnode_base.succ
71
+ RDF::Node.new(l)
72
+ end
73
+ end
74
+ end
75
+
76
+ ##
77
+ # Returns the SPARQL S-Expression (SSE) representation of this expression.
78
+ #
79
+ # Remove the optional argument.
80
+ #
81
+ # @return [Array] `self`
82
+ # @see http://openjena.org/wiki/SSE
83
+ def to_sxp_bin
84
+ [NAME] + operands.reject {|o| o == false}
85
+ end
86
+ end # BNode
87
+ end # Operator
88
+ end; end # SPARQL::Algebra
@@ -39,7 +39,8 @@ module SPARQL; module Algebra
39
39
  def evaluate(bindings = {})
40
40
  case var = operand
41
41
  when Variable
42
- operand.evaluate(bindings) ? RDF::Literal::TRUE : RDF::Literal::FALSE
42
+ bindings.respond_to?(:bound?) && bindings.bound?(var) ?
43
+ RDF::Literal::TRUE : RDF::Literal::FALSE
43
44
  else raise TypeError, "expected an RDF::Query::Variable, but got #{var.inspect}"
44
45
  end
45
46
  end
@@ -0,0 +1,31 @@
1
+ module SPARQL; module Algebra
2
+ class Operator
3
+ ##
4
+ # The SPARQL logical `ceil` operator.
5
+ #
6
+ # @example
7
+ # (ceil ?x)
8
+ #
9
+ # @see http://www.w3.org/TR/sparql11-query/#func-ceil
10
+ # @see http://www.w3.org/TR/xpath-functions/#func-ceil
11
+ class Ceil < Operator::Unary
12
+ include Evaluatable
13
+
14
+ NAME = [:ceil]
15
+
16
+ ##
17
+ # Returns the smallest (closest to negative infinity) number with no fractional part that is not less than the value of arg. An error is raised if arg is not a numeric value.
18
+ #
19
+ # @param [RDF::Literal] operand
20
+ # the operand
21
+ # @return [RDF::Literal] literal of same type
22
+ # @raise [TypeError] if the operand is not a numeric value
23
+ def apply(operand)
24
+ case operand
25
+ when RDF::Literal::Numeric then operand.ceil
26
+ else raise TypeError, "expected an RDF::Literal::Numeric, but got #{operand.inspect}"
27
+ end
28
+ end
29
+ end # Ceil
30
+ end # Operator
31
+ end; end # SPARQL::Algebra
@@ -0,0 +1,65 @@
1
+ module SPARQL; module Algebra
2
+ class Operator
3
+ ##
4
+ # The SPARQL `coalesce` function.
5
+ #
6
+ # Used for filters with more than one expression.
7
+ #
8
+ # @example
9
+ # (prefix ((: <http://example.org/>)
10
+ # (xsd: <http://www.w3.org/2001/XMLSchema#>))
11
+ # (project (?cx ?div ?def ?err)
12
+ # (extend ((?cx (coalesce ?x -1))
13
+ # (?div (coalesce (/ ?o ?x) -2))
14
+ # (?def (coalesce ?z -3))
15
+ # (?err (coalesce ?z)))
16
+ # (leftjoin
17
+ # (bgp (triple ?s :p ?o))
18
+ # (bgp (triple ?s :q ?x))))))
19
+ #
20
+ # @see http://www.w3.org/TR/sparql11-query/#func-coalesce
21
+ class Coalesce < Operator
22
+ include Evaluatable
23
+
24
+ NAME = :coalesce
25
+
26
+ ##
27
+ # The COALESCE function form returns the RDF term value of the first expression that evaluates without error. In SPARQL, evaluating an unbound variable raises an error.
28
+ #
29
+ # If none of the arguments evaluates to an RDF term, an error is raised. If no expressions are evaluated without error, an error is raised.
30
+ #
31
+ #
32
+ # @example
33
+ # Suppose ?x = 2 and ?y is not bound in some query solution:
34
+ #
35
+ # COALESCE(?x, 1/0) #=> 2, the value of x
36
+ # COALESCE(1/0, ?x) #=> 2
37
+ # COALESCE(5, ?x) #=> 5
38
+ # COALESCE(?y, 3) #=> 3
39
+ # COALESCE(?y) #=> raises an error because y is not bound.
40
+ #
41
+ # @param [RDF::Query::Solution, #[]] bindings
42
+ # @return [RDF::Term]
43
+ # @raise [TypeError] if none of the operands succeeds
44
+ def evaluate(bindings = {})
45
+ operands.each do |op|
46
+ begin
47
+ return op.evaluate(bindings)
48
+ rescue
49
+ end
50
+ end
51
+ raise TypeError, "None of the operands evaluated"
52
+ end
53
+
54
+ ##
55
+ # Returns an optimized version of this query.
56
+ #
57
+ # Return optimized query
58
+ #
59
+ # @return [Union, RDF::Query] `self`
60
+ def optimize
61
+ operands = operands.map(&:optimize)
62
+ end
63
+ end # Coalesce
64
+ end # Operator
65
+ end; end # SPARQL::Algebra
@@ -0,0 +1,49 @@
1
+ module SPARQL; module Algebra
2
+ class Operator
3
+ ##
4
+ # A SPARQL `concat` operator.
5
+ #
6
+ # @example
7
+ # (concat ?a ?b)
8
+ #
9
+ # @see http://www.w3.org/TR/sparql11-query/#func-concat
10
+ # @see http://www.w3.org/TR/xpath-functions/#func-concat
11
+ class Concat < Operator::Binary
12
+ include Evaluatable
13
+
14
+ NAME = :concat
15
+
16
+ ##
17
+ # The lexical form of the returned literal is obtained by concatenating the lexical forms of its inputs. If all input literals are typed literals of type xsd:string, then the returned literal is also of type xsd:string, if all input literals are plain literals with identical language tag, then the returned literal is a plain literal with the same language tag, in all other cases, the returned literal is a simple literal.
18
+ #
19
+ # @example
20
+ # concat("foo", "bar") #=> "foobar"
21
+ # concat("foo"@en, "bar"@en) #=> "foobar"@en
22
+ # concat("foo"^^xsd:string, "bar"^^xsd:string) #=> "foobar"^^xsd:string
23
+ # concat("foo", "bar"^^xsd:string) #=> "foobar"
24
+ # concat("foo"@en, "bar") #=> "foobar"
25
+ # concat("foo"@en, "bar"^^xsd:string) #=> "foobar"
26
+ #
27
+ # @param [RDF::Literal] left
28
+ # a literal
29
+ # @param [RDF::Literal] right
30
+ # a literal
31
+ # @return [RDF::Literal]
32
+ # @raise [TypeError] if either operand is not a literal
33
+ def apply(left, right)
34
+ case
35
+ when !left.literal? || !right.literal?
36
+ raise TypeError, "expected two plain literal operands, but got #{left.inspect} and #{right.inspect}"
37
+ when ![left.datatype, right.datatype].compact.all? {|dt| dt == RDF::XSD.string}
38
+ raise TypeError, "expected two plain literal operands, but got #{left.inspect} and #{right.inspect}"
39
+ when left.datatype == RDF::XSD.string && right.datatype == RDF::XSD.string
40
+ RDF::Literal.new("#{left}#{right}", :datatype => RDF::XSD.string)
41
+ when left.has_language? && left.language == right.language
42
+ RDF::Literal.new("#{left}#{right}", :language => left.language)
43
+ else
44
+ RDF::Literal.new("#{left}#{right}")
45
+ end
46
+ end
47
+ end # Concat
48
+ end # Operator
49
+ end; end # SPARQL::Algebra
@@ -0,0 +1,44 @@
1
+ module SPARQL; module Algebra
2
+ class Operator
3
+ ##
4
+ # A SPARQL `contains` operator.
5
+ #
6
+ # @example
7
+ # (contains ?x ?y)
8
+ #
9
+ # @see http://www.w3.org/TR/sparql11-query/#func-contains
10
+ # @see http://www.w3.org/TR/xpath-functions/#func-contains
11
+ class Contains < Operator::Binary
12
+ include Evaluatable
13
+
14
+ NAME = :contains
15
+
16
+ ##
17
+ # The CONTAINS function corresponds to the XPath fn:contains. The arguments must be argument compatible otherwise an error is raised.
18
+ #
19
+ # @example
20
+ # contains("foobar", "bar") #=> true
21
+ # contains("foobar"@en, "foo"@en) #=> true
22
+ # contains("foobar"^^xsd:string, "bar"^^xsd:string) #=> true
23
+ # contains("foobar"^^xsd:string, "foo") #=> true
24
+ # contains("foobar", "bar"^^xsd:string) #=> true
25
+ # contains("foobar"@en, "foo") #=> true
26
+ # contains("foobar"@en, "bar"^^xsd:string) #=> true
27
+ #
28
+ # @param [RDF::Literal] left
29
+ # a literal
30
+ # @param [RDF::Literal] right
31
+ # a literal
32
+ # @return [RDF::Literal::Boolean]
33
+ # @raise [TypeError] if operands are not compatible
34
+ def apply(left, right)
35
+ case
36
+ when !left.compatible?(right)
37
+ raise TypeError, "expected two RDF::Literal operands, but got #{left.inspect} and #{right.inspect}"
38
+ when left.to_s.include?(right.to_s) then RDF::Literal::TRUE
39
+ else RDF::Literal::FALSE
40
+ end
41
+ end
42
+ end # Contains
43
+ end # Operator
44
+ end; end # SPARQL::Algebra
@@ -3,9 +3,8 @@ begin
3
3
  rescue LoadError => e
4
4
  require 'rdf/ntriples'
5
5
  end
6
+ require 'rdf/aggregate_repo'
6
7
 
7
- # FIXME: This version uses named graphs for default graphs, which violates the condition in RDF::Repository#query_pattern, where it specifically does not match variables against the default graph. To work properly, RDF.rb will need to allow some way to specify a set of graphs as being default, and affect the matching within #query_pattern so that variables don't match against this.
8
- # Note that a graph may be both default and named, so the context of the query is significant.
9
8
  module SPARQL; module Algebra
10
9
  class Operator
11
10
  ##
@@ -168,53 +167,17 @@ module SPARQL; module Algebra
168
167
  queryable.load(uri.to_s, load_opts)
169
168
  end
170
169
  end
171
- require 'rdf/nquads'
172
- debug(options) { queryable.dump(:nquads) }
170
+ debug(options) {
171
+ require 'rdf/nquads'
172
+ queryable.dump(:nquads)
173
+ }
173
174
 
174
- # Re-write the operand:
175
- operator = self.rewrite do |op|
176
- case op
177
- when Operator::Graph
178
- if named_datasets.empty?
179
- # * If there are no named datasets, remove all (graph)
180
- # operations.
181
- debug(options) {"=> #{op.to_sxp} => (bgp)"}
182
- Operator::BGP.new
183
- elsif (name = op.operand(0)).is_a?(RDF::Resource)
184
- # It must match one of the named_datasets
185
- debug(options) {"=> #{op.to_sxp} => (bgp)"}
186
- named_datasets.include?(name) ? op : Operator::BGP.new
187
- else
188
- # Name is a variable, replace op with a filter on that
189
- # variable and op
190
- filter_expressions = named_datasets.map {|u| Operator::Equal.new(name, u)}
191
- debug(options) {"=> #{op.to_sxp} => (filter (...) #{op.to_sxp})"}
192
- filt = to_binary(Operator::Or, *filter_expressions)
193
- Operator::Filter.new(filt, op)
194
- end
195
- when RDF::Query # Operator::BGP
196
- case default_datasets.length
197
- when 0
198
- # No Default Datasets, no query to run
199
- debug(options) {"=> #{op.to_sxp} => (bgp)"}
200
- Operator::BGP.new
201
- when 1
202
- # A single dataset, write as (graph <dataset> (bgp))
203
- debug(options) {"=> #{op.to_sxp} => (graph <#{default_datasets.first}> #{op.to_sxp})"}
204
- Operator::Graph.new(default_datasets.first, op)
205
- else
206
- # Several, rewrite as Union
207
- debug(options) {"=> #{op.to_sxp} => (union ...)"}
208
- to_binary(Operator::Union, *default_datasets.map {|u| Operator::Graph.new(u, op.dup)})
209
- end
210
- else
211
- nil
212
- end
213
- end
214
- executable = operator.operands.last
215
- debug(options) {"=> rewritten: #{executable.to_sxp}"}
216
-
217
- @solutions = executable.execute(queryable, options.merge(:depth => options[:depth].to_i + 1))
175
+ # Create an aggregate based on queryable having just the bits we want
176
+ aggregate = RDF::AggregateRepo.new(queryable)
177
+ named_datasets.each {|name| aggregate.named(name) if queryable.has_context?(name)}
178
+ aggregate.default(*default_datasets.select {|name| queryable.has_context?(name)})
179
+ executable = operands.last
180
+ @solutions = executable.execute(aggregate, options.merge(:depth => options[:depth].to_i + 1))
218
181
  end
219
182
 
220
183
  ##
@@ -30,9 +30,11 @@ module SPARQL; module Algebra
30
30
  def apply(literal)
31
31
  case literal
32
32
  when RDF::Literal then case
33
- when literal.typed? then RDF::URI(literal.datatype)
33
+ when RDF::VERSION.to_s >= "1.1" then literal.datatype
34
34
  when literal.simple? then RDF::XSD.string
35
- else raise TypeError, "expected a typed or simple RDF::Literal, but got #{literal.inspect}"
35
+ when literal.datatype == RDF::XSD.string then RDF::XSD.string
36
+ when literal.plain? then RDF.langString
37
+ else RDF::URI(literal.datatype)
36
38
  end
37
39
  else raise TypeError, "expected an RDF::Literal, but got #{literal.inspect}"
38
40
  end
@@ -0,0 +1,31 @@
1
+ module SPARQL; module Algebra
2
+ class Operator
3
+ ##
4
+ # The SPARQL logical `day` operator.
5
+ #
6
+ # @example
7
+ # (prefix ((: <http://example.org/>))
8
+ # (project (?s ?x)
9
+ # (extend ((?x (day ?date)))
10
+ # (bgp (triple ?s :date ?date)))))
11
+ #
12
+ # @see http://www.w3.org/TR/sparql11-query/#func-day
13
+ class Day < Operator::Unary
14
+ include Evaluatable
15
+
16
+ NAME = :day
17
+
18
+ ##
19
+ # Returns the day part of arg as an integer.
20
+ #
21
+ # @param [RDF::Literal] operand
22
+ # the operand
23
+ # @return [RDF::Literal]
24
+ # @raise [TypeError] if the operand is not a simple literal
25
+ def apply(operand)
26
+ raise TypeError, "expected an RDF::Literal::DateTime, but got #{operand.inspect}" unless operand.is_a?(RDF::Literal::DateTime)
27
+ RDF::Literal(operand.object.day)
28
+ end
29
+ end # Day
30
+ end # Operator
31
+ end; end # SPARQL::Algebra