sparql 1.0.6 → 1.0.7

Sign up to get free protection for your applications and to get access to all the features.
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,38 @@
1
+ require 'uri'
2
+
3
+ module SPARQL; module Algebra
4
+ class Operator
5
+ ##
6
+ # The SPARQL logical `abs` operator.
7
+ #
8
+ # @example
9
+ # (encode_for_uri ?x)
10
+ #
11
+ # @see http://www.w3.org/TR/sparql11-query/#func-encode
12
+ # @see http://www.w3.org/TR/xpath-functions/#func-abs
13
+ class EncodeForURI < Operator::Unary
14
+ include Evaluatable
15
+
16
+ NAME = :encode_for_uri
17
+
18
+ ##
19
+ # The ENCODE_FOR_URI function corresponds to the XPath fn:encode-for-uri function. It returns a simple literal with the lexical form obtained from the lexical form of its input after translating reserved characters according to the fn:encode-for-uri function.
20
+ #
21
+ # @example
22
+ # encode_for_uri("Los Angeles") "Los%20Angeles"
23
+ # encode_for_uri("Los Angeles"@en) "Los%20Angeles"
24
+ # encode_for_uri("Los Angeles"^^xsd:string) "Los%20Angeles"
25
+ #
26
+ # @param [RDF::Literal] operand
27
+ # the operand
28
+ # @return [RDF::Literal] literal of same type
29
+ # @raise [TypeError] if the operand is not a literal value
30
+ def apply(operand)
31
+ case operand
32
+ when RDF::Literal then RDF::Literal(::URI.encode(operand.to_s))
33
+ else raise TypeError, "expected an RDF::Literal, but got #{operand.inspect}"
34
+ end
35
+ end
36
+ end # EncodeForURI
37
+ end # Operator
38
+ end; end # SPARQL::Algebra
@@ -1,7 +1,9 @@
1
1
  module SPARQL; module Algebra
2
2
  class Operator
3
3
  ##
4
- # The SPARQL Bind `extend` operator.
4
+ # The SPARQL `Extend` operator.
5
+ #
6
+ # Extends a solution
5
7
  #
6
8
  # @example
7
9
  # (select (?z)
@@ -16,8 +18,35 @@ module SPARQL; module Algebra
16
18
  NAME = [:extend]
17
19
 
18
20
  ##
19
- # FIXME
21
+ # Let μ be a solution mapping, Ω a multiset of solution mappings, var a variable and expr be an expression, then we define:
22
+ #
23
+ # Extend(μ, var, expr) = μ ∪ { (var,value) | var not in dom(μ) and value = expr(μ) }
24
+ #
25
+ # Extend(μ, var, expr) = μ if var not in dom(μ) and expr(μ) is an error
26
+ #
27
+ # Extend is undefined when var in dom(μ).
28
+ #
29
+ # Extend(Ω, var, expr) = { Extend(μ, var, expr) | μ in Ω }
30
+ # @return [RDF::Query::Solutions]
31
+ # the resulting solution sequence
32
+ # @see http://www.w3.org/TR/rdf-sparql-query/#evaluation
20
33
  def execute(queryable, options = {})
34
+ debug(options) {"Extend"}
35
+ @solutions = operands.last.execute(queryable, options.merge(:depth => options[:depth].to_i + 1))
36
+ @solutions.each do |solution|
37
+ debug(options) {"===> soln #{solution.to_hash.inspect}"}
38
+ operands.first.each do |(var, expr)|
39
+ begin
40
+ val = expr.evaluate(solution)
41
+ debug(options) {"===> + #{var} => #{val.inspect}"}
42
+ solution.bindings[var.to_sym] = val
43
+ rescue TypeError => e
44
+ # Evaluates to error, ignore
45
+ debug(options) {"===> #{var} error: #{e.message}"}
46
+ end
47
+ end
48
+ end
49
+ @solutions
21
50
  end
22
51
 
23
52
  ##
@@ -0,0 +1,33 @@
1
+ module SPARQL; module Algebra
2
+ class Operator
3
+ ##
4
+ # The SPARQL logical `floor` operator.
5
+ #
6
+ # @example
7
+ # (floor ?x)
8
+ #
9
+ # @see http://www.w3.org/TR/sparql11-query/#func-floor
10
+ # @see http://www.w3.org/TR/xpath-functions/#func-floor
11
+ class Floor < Operator::Unary
12
+ include Evaluatable
13
+
14
+ NAME = [:floor]
15
+
16
+ ##
17
+ # Returns the largest (closest to positive infinity) number with no fractional part that is not greater than the value of $arg. If type of $arg is one of the four numeric types xs:float, xs:double, xs:decimal or xs:integer the type of the result is the same as the type of $arg. If the type of $arg is a type derived from one of the numeric types, the result is an instance of the base numeric type.
18
+ #
19
+ # For float and double arguments, if the argument is positive zero, then positive zero is returned. If the argument is negative zero, then negative zero is returned.
20
+ #
21
+ # @param [RDF::Literal] operand
22
+ # the operand
23
+ # @return [RDF::Literal] literal of same type
24
+ # @raise [TypeError] if the operand is not a numeric value
25
+ def apply(operand)
26
+ case operand
27
+ when RDF::Literal::Numeric then operand.floor
28
+ else raise TypeError, "expected an RDF::Literal::Numeric, but got #{operand.inspect}"
29
+ end
30
+ end
31
+ end # Floor
32
+ end # Operator
33
+ end; end # SPARQL::Algebra
@@ -0,0 +1,31 @@
1
+ module SPARQL; module Algebra
2
+ class Operator
3
+ ##
4
+ # The SPARQL logical `hours` operator.
5
+ #
6
+ # @example
7
+ # (prefix ((: <http://example.org/>))
8
+ # (project (?s ?x)
9
+ # (extend ((?x (hours ?date)))
10
+ # (bgp (triple ?s :date ?date)))))
11
+ #
12
+ # @see http://www.w3.org/TR/sparql11-query/#func-hours
13
+ class Hours < Operator::Unary
14
+ include Evaluatable
15
+
16
+ NAME = :hours
17
+
18
+ ##
19
+ # Returns the hours 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.hour)
28
+ end
29
+ end # Hours
30
+ end # Operator
31
+ end; end # SPARQL::Algebra
@@ -0,0 +1,55 @@
1
+ module SPARQL; module Algebra
2
+ class Operator
3
+ ##
4
+ # The SPARQL `if` function.
5
+ #
6
+ # @example
7
+ # (base <http://example.org/>
8
+ # (prefix ((xsd: <http://www.w3.org/2001/XMLSchema#>))
9
+ # (project (?o ?integer)
10
+ # (extend ((?integer (if (= (lang ?o) "ja") true false)))
11
+ # (bgp (triple ?s ?p ?o))))))
12
+ #
13
+ # @see http://www.w3.org/TR/sparql11-query/#func-if
14
+ class If < Operator::Ternary
15
+ include Evaluatable
16
+
17
+ NAME = :if
18
+
19
+ ##
20
+ # The IF function form evaluates the first argument, interprets it as a effective boolean value, then returns the value of expression2 if the EBV is true, otherwise it returns the value of expression3. Only one of expression2 and expression3 is evaluated. If evaluating the first argument raises an error, then an error is raised for the evaluation of the IF expression.
21
+ #
22
+ # @example
23
+ #
24
+ # IF(?x = 2, "yes", "no") #=> "yes"
25
+ # IF(bound(?y), "yes", "no") #=> "no"
26
+ # IF(?x=2, "yes", 1/?z) #=> "yes", the expression 1/?z is not evaluated
27
+ # IF(?x=1, "yes", 1/?z) #=> raises an error
28
+ # IF("2" > 1, "yes", "no") #=> raises an error
29
+ #
30
+ # Evaluates the first operand and returns the evaluation of either the second or third operands
31
+ #
32
+ # @param [RDF::Query::Solution, #[]] bindings
33
+ # a query solution containing zero or more variable bindings
34
+ # @return [RDF::Term]
35
+ # @raise [TypeError]
36
+ def evaluate(bindings = {})
37
+ operand(0).evaluate(bindings) == RDF::Literal::TRUE ?
38
+ operand(1).evaluate(bindings) :
39
+ operand(2).evaluate(bindings)
40
+ rescue
41
+ raise TypeError
42
+ end
43
+
44
+ ##
45
+ # Returns an optimized version of this query.
46
+ #
47
+ # Return optimized query
48
+ #
49
+ # @return [Union, RDF::Query] `self`
50
+ def optimize
51
+ operands = operands.map(&:optimize)
52
+ end
53
+ end # If
54
+ end # Operator
55
+ end; end # SPARQL::Algebra
@@ -0,0 +1,68 @@
1
+ module SPARQL; module Algebra
2
+ class Operator
3
+ ##
4
+ # The SPARQL GraphPattern `in` operator.
5
+ #
6
+ # Used for filters with more than one expression.
7
+ #
8
+ # @example
9
+ # (ask (filter (in 2 1 2 3) (bgp)))
10
+ #
11
+ # @see http://www.w3.org/TR/sparql11-query/#func-in
12
+ class In < Operator
13
+ include Evaluatable
14
+
15
+ NAME = :in
16
+
17
+ ##
18
+ # The IN operator tests whether the RDF term on the left-hand side is found in the values of list of expressions on the right-hand side. The test is done with "=" operator, which tests for the same value, as determined by the operator mapping.
19
+ #
20
+ # A list of zero terms on the right-hand side is legal.
21
+ #
22
+ # Errors in comparisons cause the IN expression to raise an error if the RDF term being tested is not found elsewhere in the list of terms.
23
+ #
24
+ # The IN operator is equivalent to the SPARQL expression:
25
+ #
26
+ # (lhs = expression1) || (lhs = expression2) || ...
27
+ #
28
+ # @example
29
+ #
30
+ # 2 IN (1, 2, 3) #=> true
31
+ # 2 IN () #=> false
32
+ # 2 IN (<http://example/iri>, "str", 2.0) #=> true
33
+ # 2 IN (1/0, 2) #=> true
34
+ # 2 IN (2, 1/0) #=> true
35
+ # 2 IN (3, 1/0) #=> raises an error
36
+ #
37
+ # @param [RDF::Query::Solution, #[]] bindings
38
+ # @return [RDF::Literal::Boolean] `true` or `false`
39
+ # @raise [TypeError] if term is not found and any operand raises an error
40
+ def evaluate(bindings = {})
41
+ lhs = operands.shift.evaluate(bindings)
42
+ error_found = false
43
+ found = operands.any? do |op|
44
+ begin
45
+ lhs == op.evaluate(bindings)
46
+ rescue TypeError
47
+ error_found = true
48
+ end
49
+ end
50
+ case
51
+ when found then RDF::Literal::TRUE
52
+ when error_found then raise TypeError
53
+ else RDF::Literal::FALSE
54
+ end
55
+ end
56
+
57
+ ##
58
+ # Returns an optimized version of this query.
59
+ #
60
+ # Return optimized query
61
+ #
62
+ # @return [Union, RDF::Query] `self`
63
+ def optimize
64
+ operands = operands.map(&:optimize)
65
+ end
66
+ end # In
67
+ end # Operator
68
+ end; end # SPARQL::Algebra
@@ -0,0 +1,40 @@
1
+ module SPARQL; module Algebra
2
+ class Operator
3
+ ##
4
+ # The SPARQL `iri` operator.
5
+ #
6
+ # @example
7
+ # (base <http://example.org/> (project (?uri ?iri)
8
+ # (extend ((?uri (uri "uri")) (?iri (iri "iri")))
9
+ # (bgp))))
10
+ #
11
+ # @see http://www.w3.org/TR/rdf-sparql-query/#func-iri
12
+ class IRI < Operator::Unary
13
+ include Evaluatable
14
+
15
+ NAME = [:iri, :uri]
16
+
17
+ ##
18
+ # The IRI function constructs an IRI by resolving the string argument (see RFC 3986 and RFC 3987 or any later RFC that superceeds RFC 3986 or RFC 3987). The IRI is resolved against the base IRI of the query and must result in an absolute IRI.
19
+ #
20
+ # The URI function is a synonym for IRI.
21
+ #
22
+ # If the function is passed an IRI, it returns the IRI unchanged.
23
+ #
24
+ # Passing any RDF term other than a simple literal, xsd:string or an IRI is an error.
25
+ #
26
+ # An implementation may normalize the IRI.
27
+ #
28
+ # @param [RDF::Term] literal
29
+ # a simple literal
30
+ # @return [RDF::URI]
31
+ # @raise [TypeError] if the operand is not a simple literal
32
+ def apply(literal)
33
+ raise TypeError, "expected an simple literal, but got #{literal.inspect}" unless literal.literal? && literal.simple?
34
+ Operator.base_uri.join(literal.to_s)
35
+ end
36
+
37
+ Operator::URI = IRI
38
+ end # IRI
39
+ end # Operator
40
+ end; end # SPARQL::Algebra
@@ -0,0 +1,41 @@
1
+ module SPARQL; module Algebra
2
+ class Operator
3
+ ##
4
+ # The SPARQL `isNumeric` operator.
5
+ #
6
+ # Note numeric denotes typed literals with datatypes xsd:integer, xsd:decimal, xsd:float, and xsd:double, not derived types.
7
+ #
8
+ # @example
9
+ # (prefix ((xsd: <http://www.w3.org/2001/XMLSchema#>)
10
+ # (: <http://example.org/things#>))
11
+ # (project (?x ?v)
12
+ # (filter (isNumeric ?v)
13
+ # (bgp (triple ?x :p ?v)))))
14
+ #
15
+ # @see http://www.w3.org/TR/sparql11-query/#func-isNumeric
16
+ class IsNumeric < Operator::Unary
17
+ include Evaluatable
18
+
19
+ NAME = :isnumeric
20
+
21
+ ##
22
+ # Returns `true` if the operand is an `RDF::Literal::Numeric`, `false`
23
+ # otherwise.
24
+ #
25
+ # @param [RDF::Term] term
26
+ # an RDF term
27
+ # @return [RDF::Literal::Boolean] `true` or `false`
28
+ # @raise [TypeError] if the operand is not an RDF term
29
+ def apply(term)
30
+ case term
31
+ when RDF::Literal::NonPositiveInteger then RDF::Literal::FALSE
32
+ when RDF::Literal::NonNegativeInteger then RDF::Literal::FALSE
33
+ when RDF::Literal::Long then RDF::Literal::FALSE
34
+ when RDF::Literal::Numeric then RDF::Literal::TRUE
35
+ when RDF::Term then RDF::Literal::FALSE
36
+ else raise TypeError, "expected an RDF::Term, but got #{term.inspect}"
37
+ end
38
+ end
39
+ end # IsNumeric
40
+ end # Operator
41
+ end; end # SPARQL::Algebra
@@ -28,10 +28,10 @@ module SPARQL; module Algebra
28
28
  # @raise [TypeError] if either operand is unbound
29
29
  # @raise [TypeError] if either operand is not a simple literal
30
30
  def apply(language_tag, language_range)
31
- raise TypeError, "expected a plain RDF::Literal for language_tag, but got #{language_tag.inspect}" unless language_tag.is_a?(RDF::Literal) && language_tag.plain?
31
+ raise TypeError, "expected a plain RDF::Literal for language_tag, but got #{language_tag.inspect}" unless language_tag.is_a?(RDF::Literal) && language_tag.simple?
32
32
  language_tag = language_tag.to_s.downcase
33
33
 
34
- raise TypeError, "expected a plain RDF::Literal for language_range, but got #{language_range.inspect}" unless language_range.is_a?(RDF::Literal) && language_range.plain?
34
+ raise TypeError, "expected a plain RDF::Literal for language_range, but got #{language_range.inspect}" unless language_range.is_a?(RDF::Literal) && language_range.simple?
35
35
  language_range = language_range.to_s.downcase
36
36
 
37
37
  case
@@ -0,0 +1,31 @@
1
+ module SPARQL; module Algebra
2
+ class Operator
3
+ ##
4
+ # The SPARQL logical `lcase` operator.
5
+ #
6
+ # @example
7
+ # (lcase ?x)
8
+ #
9
+ # @see http://www.w3.org/TR/sparql11-query/#func-lcase
10
+ # @see http://www.w3.org/TR/xpath-functions/#func-lcase
11
+ class LCase < Operator::Unary
12
+ include Evaluatable
13
+
14
+ NAME = :lcase
15
+
16
+ ##
17
+ # The LCASE function corresponds to the XPath fn:lower-case function. It returns a string literal whose lexical form is the lower case of the lexcial form of the argument.
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 literal value
23
+ def apply(operand)
24
+ case operand
25
+ when RDF::Literal then RDF::Literal(operand.to_s.downcase, :datatype => operand.datatype, :language => operand.language)
26
+ else raise TypeError, "expected an RDF::Literal::Numeric, but got #{operand.inspect}"
27
+ end
28
+ end
29
+ end # LCase
30
+ end # Operator
31
+ end; end # SPARQL::Algebra
@@ -0,0 +1,34 @@
1
+ require 'digest'
2
+
3
+ module SPARQL; module Algebra
4
+ class Operator
5
+ ##
6
+ # The SPARQL logical `md5` operator.
7
+ #
8
+ # @example
9
+ # (prefix ((: <http://example.org/>))
10
+ # (project (?hash)
11
+ # (extend ((?hash (md5 ?l)))
12
+ # (bgp (triple :s1 :str ?l)))))
13
+ #
14
+ # @see http://www.w3.org/TR/sparql11-query/#func-md5
15
+ class MD5 < Operator::Unary
16
+ include Evaluatable
17
+
18
+ NAME = :md5
19
+
20
+ ##
21
+ # Returns the MD5 checksum, as a hex digit string, calculated on the UTF-8 representation of the simple literal or lexical form of the xsd:string. Hex digits should be in lower case.
22
+ #
23
+ # @param [RDF::Literal] operand
24
+ # the operand
25
+ # @return [RDF::Literal]
26
+ # @raise [TypeError] if the operand is not a simple literal
27
+ def apply(operand)
28
+ raise TypeError, "expected an RDF::Literal, but got #{operand.inspect}" unless operand.literal?
29
+ raise TypeError, "expected simple literal or xsd:string, but got #{operand.inspect}" unless (operand.datatype || RDF::XSD.string) == RDF::XSD.string
30
+ RDF::Literal(Digest::MD5.new.hexdigest(operand.to_s))
31
+ end
32
+ end # MD5
33
+ end # Operator
34
+ end; end # SPARQL::Algebra