sparql 3.2.0 → 3.2.1

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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -0
  3. data/VERSION +1 -1
  4. data/lib/sparql/algebra/expression.rb +28 -3
  5. data/lib/sparql/algebra/extensions.rb +36 -32
  6. data/lib/sparql/algebra/operator/abs.rb +1 -1
  7. data/lib/sparql/algebra/operator/alt.rb +1 -1
  8. data/lib/sparql/algebra/operator/avg.rb +3 -1
  9. data/lib/sparql/algebra/operator/bgp.rb +9 -1
  10. data/lib/sparql/algebra/operator/clear.rb +13 -3
  11. data/lib/sparql/algebra/operator/construct.rb +1 -1
  12. data/lib/sparql/algebra/operator/count.rb +36 -6
  13. data/lib/sparql/algebra/operator/create.rb +5 -4
  14. data/lib/sparql/algebra/operator/dataset.rb +19 -11
  15. data/lib/sparql/algebra/operator/delete.rb +3 -1
  16. data/lib/sparql/algebra/operator/delete_data.rb +1 -1
  17. data/lib/sparql/algebra/operator/delete_where.rb +1 -1
  18. data/lib/sparql/algebra/operator/distinct.rb +2 -2
  19. data/lib/sparql/algebra/operator/divide.rb +1 -1
  20. data/lib/sparql/algebra/operator/drop.rb +15 -6
  21. data/lib/sparql/algebra/operator/encode_for_uri.rb +2 -4
  22. data/lib/sparql/algebra/operator/exprlist.rb +3 -1
  23. data/lib/sparql/algebra/operator/extend.rb +36 -3
  24. data/lib/sparql/algebra/operator/filter.rb +1 -1
  25. data/lib/sparql/algebra/operator/function_call.rb +64 -0
  26. data/lib/sparql/algebra/operator/graph.rb +57 -7
  27. data/lib/sparql/algebra/operator/group.rb +76 -5
  28. data/lib/sparql/algebra/operator/group_concat.rb +25 -1
  29. data/lib/sparql/algebra/operator/if.rb +10 -10
  30. data/lib/sparql/algebra/operator/insert.rb +3 -1
  31. data/lib/sparql/algebra/operator/insert_data.rb +1 -1
  32. data/lib/sparql/algebra/operator/is_blank.rb +1 -2
  33. data/lib/sparql/algebra/operator/is_iri.rb +1 -2
  34. data/lib/sparql/algebra/operator/is_literal.rb +1 -2
  35. data/lib/sparql/algebra/operator/is_numeric.rb +1 -2
  36. data/lib/sparql/algebra/operator/join.rb +37 -3
  37. data/lib/sparql/algebra/operator/lcase.rb +2 -3
  38. data/lib/sparql/algebra/operator/left_join.rb +20 -7
  39. data/lib/sparql/algebra/operator/max.rb +3 -1
  40. data/lib/sparql/algebra/operator/min.rb +4 -2
  41. data/lib/sparql/algebra/operator/minus.rb +46 -6
  42. data/lib/sparql/algebra/operator/multiply.rb +1 -1
  43. data/lib/sparql/algebra/operator/notoneof.rb +12 -3
  44. data/lib/sparql/algebra/operator/order.rb +44 -0
  45. data/lib/sparql/algebra/operator/plus.rb +1 -1
  46. data/lib/sparql/algebra/operator/project.rb +22 -4
  47. data/lib/sparql/algebra/operator/reduced.rb +3 -3
  48. data/lib/sparql/algebra/operator/regex.rb +1 -1
  49. data/lib/sparql/algebra/operator/reverse.rb +12 -1
  50. data/lib/sparql/algebra/operator/sample.rb +3 -1
  51. data/lib/sparql/algebra/operator/seq.rb +1 -1
  52. data/lib/sparql/algebra/operator/sequence.rb +4 -1
  53. data/lib/sparql/algebra/operator/strlang.rb +1 -2
  54. data/lib/sparql/algebra/operator/subtract.rb +1 -1
  55. data/lib/sparql/algebra/operator/sum.rb +9 -7
  56. data/lib/sparql/algebra/operator/table.rb +41 -7
  57. data/lib/sparql/algebra/operator/ucase.rb +1 -1
  58. data/lib/sparql/algebra/operator/update.rb +22 -1
  59. data/lib/sparql/algebra/operator/using.rb +18 -1
  60. data/lib/sparql/algebra/operator/with.rb +1 -1
  61. data/lib/sparql/algebra/operator.rb +46 -18
  62. data/lib/sparql/algebra.rb +20 -3
  63. data/lib/sparql/grammar/parser11.rb +3 -3
  64. metadata +15 -2
@@ -17,6 +17,50 @@ module SPARQL; module Algebra
17
17
  # (order ((asc ?name))
18
18
  # (bgp (triple ?x foaf:name ?name)))))
19
19
  #
20
+ # @example SPARQL Grammar (with builtin)
21
+ # PREFIX : <http://example.org/>
22
+ # SELECT ?s WHERE {
23
+ # ?s :p ?o .
24
+ # }
25
+ # ORDER BY str(?o)
26
+ #
27
+ # @example SSE (with builtin)
28
+ # (prefix ((: <http://example.org/>))
29
+ # (project (?s)
30
+ # (order ((str ?o))
31
+ # (bgp (triple ?s :p ?o)))))
32
+ #
33
+ # @example SPARQL Grammar (with bracketed expression)
34
+ # PREFIX : <http://example.org/>
35
+ # SELECT ?s WHERE {
36
+ # ?s :p ?o1 ; :q ?o2 .
37
+ # } ORDER BY (?o1 + ?o2)
38
+ #
39
+ # @example SSE (with bracketed expression)
40
+ # (prefix
41
+ # ((: <http://example.org/>))
42
+ # (project (?s)
43
+ # (order ((+ ?o1 ?o2))
44
+ # (bgp
45
+ # (triple ?s :p ?o1)
46
+ # (triple ?s :q ?o2)))))
47
+ #
48
+ # @example SPARQL Grammar (with function call)
49
+ # PREFIX : <http://example.org/ns#>
50
+ # PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
51
+ # SELECT *
52
+ # { ?s ?p ?o }
53
+ # ORDER BY
54
+ # DESC(?o+57) xsd:string(?o) ASC(?s)
55
+ #
56
+ # @example SSE (with function call)
57
+ # (prefix ((: <http://example.org/ns#>)
58
+ # (xsd: <http://www.w3.org/2001/XMLSchema#>))
59
+ # (order ((desc (+ ?o 57))
60
+ # (xsd:string ?o)
61
+ # (asc ?s))
62
+ # (bgp (triple ?s ?p ?o))))
63
+ #
20
64
  # @see https://www.w3.org/TR/sparql11-query/#modOrderBy
21
65
  class Order < Operator::Binary
22
66
  include Query
@@ -69,7 +69,7 @@ module SPARQL; module Algebra
69
69
  #
70
70
  # @return [String]
71
71
  def to_sparql(**options)
72
- "#{operands.first.to_sparql(**options)} + #{operands.last.to_sparql(**options)}"
72
+ "(#{operands.first.to_sparql(**options)} + #{operands.last.to_sparql(**options)})"
73
73
  end
74
74
  end # Plus
75
75
  end # Operator
@@ -20,20 +20,37 @@ module SPARQL; module Algebra
20
20
  # (filter (= ?v 2)
21
21
  # (bgp (triple ?s :p ?v)))))
22
22
  #
23
- # ## Sub select
24
- #
25
- # @example SPARQL Grammar
23
+ # @example SPARQL Grammar (Sub select)
26
24
  # SELECT (1 AS ?X ) {
27
25
  # SELECT (2 AS ?Y ) {}
28
26
  # }
29
27
  #
30
- # @example SSE
28
+ # @example SSE (Sub select)
31
29
  # (project (?X)
32
30
  # (extend ((?X 1))
33
31
  # (project (?Y)
34
32
  # (extend ((?Y 2))
35
33
  # (bgp)))))
36
34
  #
35
+ # @example SPARQL Grammar (filter projection)
36
+ # PREFIX : <http://www.example.org/>
37
+ # ASK {
38
+ # {SELECT (GROUP_CONCAT(?o) AS ?g) WHERE {
39
+ # :a :p1 ?o
40
+ # }}
41
+ # FILTER(?g = "1 22" || ?g = "22 1")
42
+ # }
43
+ #
44
+ # @example SSE (filter projection)
45
+ # (prefix ((: <http://www.example.org/>))
46
+ # (ask
47
+ # (filter
48
+ # (|| (= ?g "1 22") (= ?g "22 1"))
49
+ # (project (?g)
50
+ # (extend ((?g ??.0))
51
+ # (group () ((??.0 (group_concat ?o)))
52
+ # (bgp (triple :a :p1 ?o)))))) ))
53
+ #
37
54
  # @see https://www.w3.org/TR/sparql11-query/#modProjection
38
55
  class Project < Operator::Binary
39
56
  include Query
@@ -77,6 +94,7 @@ module SPARQL; module Algebra
77
94
  # Any of these options indicates we're in a sub-select
78
95
  opts = options.dup.delete_if {|k,v| %I{extensions filter_ops project}.include?(k)}
79
96
  content = operands.last.to_sparql(project: vars, **opts)
97
+ content = "{#{content}}" unless content.start_with?('{') && content.end_with?('}')
80
98
  Operator.to_sparql(content, **options)
81
99
  else
82
100
  operands.last.to_sparql(project: vars, **options)
@@ -8,12 +8,12 @@ module SPARQL; module Algebra
8
8
  # @example SPARQL Grammar
9
9
  # PREFIX : <http://example.org/>
10
10
  # PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
11
- # SELECT DISTINCT ?v
11
+ # SELECT REDUCED ?v
12
12
  # WHERE { ?x ?p ?v }
13
13
  #
14
14
  # @example SSE
15
- # (prefix ((xsd: <http://www.w3.org/2001/XMLSchema#>)
16
- # (: <http://example/>))
15
+ # (prefix ((: <http://example.org/>)
16
+ # (xsd: <http://www.w3.org/2001/XMLSchema#>))
17
17
  # (reduced
18
18
  # (project (?v)
19
19
  # (bgp (triple ?x ?p ?v)))))
@@ -6,8 +6,8 @@ module SPARQL; module Algebra
6
6
  # [122] RegexExpression ::= 'REGEX' '(' Expression ',' Expression ( ',' Expression )? ')'
7
7
  #
8
8
  # @example SPARQL Grammar
9
- # PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
10
9
  # PREFIX ex: <http://example.com/#>
10
+ # PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
11
11
  # SELECT ?val
12
12
  # WHERE {
13
13
  # ex:foo rdf:value ?val .
@@ -15,6 +15,17 @@ module SPARQL; module Algebra
15
15
  # (in: <http://www.example.org/instance#>))
16
16
  # (ask (path in:b (reverse ex:p) in:a)))
17
17
  #
18
+ # @example SPARQL Grammar
19
+ # prefix ex: <http://www.example.org/schema#>
20
+ # prefix in: <http://www.example.org/instance#>
21
+ #
22
+ # select * where { in:c ^(ex:p1/ex:p2) ?x }
23
+ #
24
+ # @example SSE
25
+ # (prefix ((ex: <http://www.example.org/schema#>)
26
+ # (in: <http://www.example.org/instance#>))
27
+ # (path in:c (reverse (seq ex:p1 ex:p2)) ?x))
28
+ #
18
29
  # @see https://www.w3.org/TR/sparql11-query/#defn_evalPP_inverse
19
30
  class Reverse < Operator::Unary
20
31
  include Query
@@ -65,7 +76,7 @@ module SPARQL; module Algebra
65
76
  #
66
77
  # @return [String]
67
78
  def to_sparql(**options)
68
- "^" + operands.first.to_sparql(**options)
79
+ "^(" + operands.first.to_sparql(**options) + ')'
69
80
  end
70
81
  end # Reverse
71
82
  end # Operator
@@ -54,7 +54,9 @@ module SPARQL; module Algebra
54
54
  #
55
55
  # @return [String]
56
56
  def to_sparql(**options)
57
- "SAMPLE(#{operands.to_sparql(**options)})"
57
+ distinct = operands.first == :distinct
58
+ args = distinct ? operands[1..-1] : operands
59
+ "SAMPLE(#{'DISTINCT ' if distinct}#{args.to_sparql(**options)})"
58
60
  end
59
61
  end # Sample
60
62
  end # Operator
@@ -83,7 +83,7 @@ module SPARQL; module Algebra
83
83
  #
84
84
  # @return [String]
85
85
  def to_sparql(**options)
86
- operands.to_sparql(delimiter: '/', **options)
86
+ '(' + operands.to_sparql(delimiter: '/', **options) + ')'
87
87
  end
88
88
  end # Seq
89
89
  end # Operator
@@ -4,8 +4,11 @@ module SPARQL; module Algebra
4
4
  ##
5
5
  # The SPARQL UPDATE `sequence` operator.
6
6
  #
7
- # Sequences through each operand
7
+ # Sequences through each operand.
8
8
  #
9
+ # [103] CollectionPath ::= '(' GraphNodePath+ ')'
10
+ #
11
+ # @see https://www.w3.org/TR/sparql11-query/#collections
9
12
  class Sequence < Operator
10
13
  include SPARQL::Algebra::Update
11
14
 
@@ -13,8 +13,7 @@ module SPARQL; module Algebra
13
13
  # }
14
14
  #
15
15
  # @example SSE
16
- # (prefix
17
- # ((: <http://example.org/>) (xsd: <http://www.w3.org/2001/XMLSchema#>))
16
+ # (prefix ((: <http://example.org/>))
18
17
  # (project (?s ?s2)
19
18
  # (extend ((?s2 (strlang ?str "en-US")))
20
19
  # (filter (langMatches (lang ?str) "en")
@@ -51,7 +51,7 @@ module SPARQL; module Algebra
51
51
  #
52
52
  # @return [String]
53
53
  def to_sparql(**options)
54
- "#{operands.first.to_sparql(**options)} - #{operands.last.to_sparql(**options)}"
54
+ "(#{operands.first.to_sparql(**options)} - #{operands.last.to_sparql(**options)})"
55
55
  end
56
56
  end # Subtract
57
57
  end # Operator
@@ -7,15 +7,15 @@ module SPARQL; module Algebra
7
7
  #
8
8
  # @example SPARQL Grammar
9
9
  # PREFIX : <http://www.example.org/>
10
- # SELECT (SUM(?O) AS ?sum)
10
+ # SELECT (SUM(?o) AS ?sum)
11
11
  # WHERE { ?s :dec ?o }
12
12
  #
13
13
  # @example SSE
14
- # (prefix ((: <http://www.example.org/>))
15
- # (project (?sum)
16
- # (extend ((?sum ??.0))
17
- # (group () ((??.0 (sum ?o)))
18
- # (bgp (triple ?s :dec ?o))))))
14
+ # (prefix ((: <http://www.example.org/>))
15
+ # (project (?sum)
16
+ # (extend ((?sum ??.0))
17
+ # (group () ((??.0 (sum ?o)))
18
+ # (bgp (triple ?s :dec ?o))))))
19
19
  #
20
20
  # @see https://www.w3.org/TR/sparql11-query/#defn_aggSum
21
21
  class Sum < Operator
@@ -47,7 +47,9 @@ module SPARQL; module Algebra
47
47
  #
48
48
  # @return [String]
49
49
  def to_sparql(**options)
50
- "SUM(" + operands.to_sparql(**options) + ")"
50
+ distinct = operands.first == :distinct
51
+ args = distinct ? operands[1..-1] : operands
52
+ "SUM(#{'DISTINCT ' if distinct}#{args.to_sparql(**options)})"
51
53
  end
52
54
  end # Sum
53
55
  end # Operator
@@ -8,7 +8,7 @@ module SPARQL; module Algebra
8
8
  #
9
9
  # [28] ValuesClause ::= ( 'VALUES' DataBlock )?
10
10
  #
11
- # @example SPARQL Grammar
11
+ # @example SPARQL Grammar (ValuesClause)
12
12
  # PREFIX dc: <http://purl.org/dc/elements/1.1/>
13
13
  # PREFIX : <http://example.org/book/>
14
14
  # PREFIX ns: <http://example.org/ns#>
@@ -18,7 +18,7 @@ module SPARQL; module Algebra
18
18
  # }
19
19
  # VALUES ?book { :book1 }
20
20
  #
21
- # @example SSE
21
+ # @example SSE (ValuesClause)
22
22
  # (prefix ((dc: <http://purl.org/dc/elements/1.1/>)
23
23
  # (: <http://example.org/book/>)
24
24
  # (ns: <http://example.org/ns#>))
@@ -27,8 +27,38 @@ module SPARQL; module Algebra
27
27
  # (bgp (triple ?book dc:title ?title) (triple ?book ns:price ?price))
28
28
  # (table (vars ?book) (row (?book :book1)))) ))
29
29
  #
30
+ # @example SPARQL Grammar (empty query no values)
31
+ # SELECT * { } VALUES () { }
32
+ #
33
+ # @example SSE (empty query no values)
34
+ # (join (bgp) (table empty))
35
+ #
36
+ # [61] InlineData ::= 'VALUES' DataBlock
37
+ #
38
+ # @example SPARQL Grammar (InlineData)
39
+ # PREFIX dc: <http://purl.org/dc/elements/1.1/>
40
+ # PREFIX : <http://example.org/book/>
41
+ # PREFIX ns: <http://example.org/ns#>
42
+ #
43
+ # SELECT ?book ?title ?price
44
+ # {
45
+ # VALUES ?book { :book1 }
46
+ # ?book dc:title ?title ;
47
+ # ns:price ?price .
48
+ # }
49
+ #
50
+ # @example SSE (InlineData)
51
+ # (prefix ((dc: <http://purl.org/dc/elements/1.1/>)
52
+ # (: <http://example.org/book/>)
53
+ # (ns: <http://example.org/ns#>))
54
+ # (project (?book ?title ?price)
55
+ # (join
56
+ # (table (vars ?book) (row (?book :book1)))
57
+ # (bgp (triple ?book dc:title ?title) (triple ?book ns:price ?price))) ))
58
+ #
30
59
  # @example empty table
31
60
  # (table unit)
61
+ #
32
62
  # @see https://www.w3.org/TR/2013/REC-sparql11-query-20130321/#inline-data
33
63
  class Table < Operator
34
64
  include Query
@@ -67,22 +97,26 @@ module SPARQL; module Algebra
67
97
  #
68
98
  # Returns a partial SPARQL grammar for this operator.
69
99
  #
100
+ # @param [Boolean] top_level (true)
101
+ # Treat this as a top-level, generating SELECT ... WHERE {}
70
102
  # @return [String]
71
- def to_sparql(**options)
72
- str = "VALUES (#{operands.first[1..-1].map { |e| e.to_sparql(**options) }.join(' ')}) {\n"
103
+ def to_sparql(top_level: true, **options)
104
+ str = "VALUES (#{Array(operands.first)[1..-1].map { |e| e.to_sparql(**options) }.join(' ')}) {\n"
73
105
  operands[1..-1].each do |row|
74
106
  line = '('
75
107
  row[1..-1].each do |col|
76
- line << "#{col[1].to_sparql(**options)} "
108
+ v = col[1].to_sparql(as_statement: true, **options)
109
+ v = "<< #{v} >>" if col[1].is_a?(RDF::Statement)
110
+ line << v + ' '
77
111
  end
78
- line = line.chop
112
+ line = line.chomp(' ')
79
113
  line << ")\n"
80
114
 
81
115
  str << line
82
116
  end
83
117
 
84
118
  str << "}\n"
85
- str
119
+ top_level ? Operator.to_sparql(str, **options) : str
86
120
  end
87
121
  end # Table
88
122
  end # Operator
@@ -14,7 +14,7 @@ module SPARQL; module Algebra
14
14
  # @example SSE
15
15
  # (prefix
16
16
  # ((: <http://example.org/>))
17
- # (project (?str ?ustr)
17
+ # (project (?s ?ustr)
18
18
  # (extend ((?ustr (ucase ?str)))
19
19
  # (bgp (triple ?s :str ?str)))))
20
20
  #
@@ -21,6 +21,27 @@ module SPARQL; module Algebra
21
21
  # (delete ((triple ?a foaf:knows ?b)))
22
22
  # (insert ((triple ?b foaf:knows ?a)))) ))
23
23
  #
24
+ # @example SPARQL Grammar (update multiple)
25
+ # PREFIX : <http://example.org/>
26
+ # PREFIX foaf: <http://xmlns.com/foaf/0.1/>
27
+ #
28
+ # DELETE { ?a foaf:knows ?b . }
29
+ # WHERE { ?a foaf:knows ?b . }
30
+ # ;
31
+ # INSERT { ?b foaf:knows ?a . }
32
+ # WHERE { ?a foaf:knows ?b .}
33
+ #
34
+ # @example SSE (update multiple)
35
+ # (prefix ((: <http://example.org/>)
36
+ # (foaf: <http://xmlns.com/foaf/0.1/>))
37
+ # (update
38
+ # (modify
39
+ # (bgp (triple ?a foaf:knows ?b))
40
+ # (delete ((triple ?a foaf:knows ?b))))
41
+ # (modify
42
+ # (bgp (triple ?a foaf:knows ?b))
43
+ # (insert ((triple ?b foaf:knows ?a))))))
44
+ #
24
45
  # @see https://www.w3.org/TR/sparql11-update/#graphUpdate
25
46
  class Update < Operator
26
47
  include SPARQL::Algebra::Update
@@ -58,7 +79,7 @@ module SPARQL; module Algebra
58
79
  #
59
80
  # @return [String]
60
81
  def to_sparql(**options)
61
- str = operands.map { |e| e.to_sparql(**options) }.join("\n")
82
+ str = operands.map { |e| e.to_sparql(**options) }.join(";\n")
62
83
  end
63
84
  end # Update
64
85
  end # Operator
@@ -28,6 +28,21 @@ module SPARQL; module Algebra
28
28
  # (bgp (triple :a foaf:knows ?s) (triple ?s ?p ?o)))
29
29
  # (delete ((triple ?s ?p ?o)))) ))
30
30
  #
31
+ # @example SPARQL Grammar (multiple clauses)
32
+ # PREFIX : <http://example.org/>
33
+ #
34
+ # INSERT { ?s ?p "q" }
35
+ # USING :g1
36
+ # USING :g2
37
+ # WHERE { ?s ?p ?o }
38
+ #
39
+ # @example SSE (multiple clauses)
40
+ # (prefix ((: <http://example.org/>))
41
+ # (update
42
+ # (modify (using (:g1 :g2)
43
+ # (bgp (triple ?s ?p ?o)))
44
+ # (insert ((triple ?s ?p "q"))))))
45
+ #
31
46
  # @see https://www.w3.org/TR/sparql11-update/#add
32
47
  class Using < Operator
33
48
  include SPARQL::Algebra::Query
@@ -61,7 +76,9 @@ module SPARQL; module Algebra
61
76
  #
62
77
  # @return [String]
63
78
  def to_sparql(**options)
64
- str = "USING #{operands.first.to_sparql(**options)}\n"
79
+ str = "\n" + operands.first.map do |op|
80
+ "USING #{op.to_sparql(**options)}\n"
81
+ end.join("")
65
82
  content = operands.last.to_sparql(top_level: false, **options)
66
83
  str << Operator.to_sparql(content, project: nil, **options)
67
84
  end
@@ -85,7 +85,7 @@ module SPARQL; module Algebra
85
85
  #
86
86
  # @return [String]
87
87
  def to_sparql(**options)
88
- with, where, ops = operands
88
+ with, where, *ops = operands
89
89
  str = "WITH #{with.to_sparql(**options)}\n"
90
90
 
91
91
  # The content of the WHERE clause, may be USING
@@ -106,6 +106,7 @@ module SPARQL; module Algebra
106
106
  autoload :Coalesce, 'sparql/algebra/operator/coalesce'
107
107
  autoload :Desc, 'sparql/algebra/operator/desc'
108
108
  autoload :Exprlist, 'sparql/algebra/operator/exprlist'
109
+ autoload :FunctionCall, 'sparql/algebra/operator/function_call'
109
110
  autoload :GroupConcat, 'sparql/algebra/operator/group_concat'
110
111
  autoload :In, 'sparql/algebra/operator/in'
111
112
  autoload :NotIn, 'sparql/algebra/operator/notin'
@@ -254,6 +255,7 @@ module SPARQL; module Algebra
254
255
  when :asc then Asc
255
256
  when :desc then Desc
256
257
  when :exprlist then Exprlist
258
+ when :function_call then FunctionCall
257
259
 
258
260
  # Datasets
259
261
  when :dataset then Dataset
@@ -336,31 +338,41 @@ module SPARQL; module Algebra
336
338
  # Generate a top-level Grammar, using collected options
337
339
  #
338
340
  # @param [String] content
341
+ # @param [Operator] datasets ([])
342
+ # @param [Operator] distinct (false)
339
343
  # @param [Hash{Symbol => Operator}] extensions
340
344
  # Variable bindings
341
- # @param [Operator] distinct (false)
342
345
  # @param [Array<Operator>] filter_ops ([])
343
346
  # Filter Operations
344
347
  # @param [Integer] limit (nil)
345
348
  # @param [Array<Operator>] group_ops ([])
349
+ # @param [Array<Operator>] having_ops ([])
346
350
  # @param [Integer] offset (nil)
347
351
  # @param [Array<Operator>] order_ops ([])
348
352
  # Order Operations
349
353
  # @param [Array<Symbol,Operator>] project (%i(*))
350
354
  # Terms to project
351
355
  # @param [Operator] reduced (false)
356
+ # @param [Operator] values_clause (nil)
357
+ # Top-level Values clause
358
+ # @param [Operator] where_clause (true)
359
+ # Emit 'WHERE' before GroupGraphPattern
352
360
  # @param [Hash{Symbol => Object}] options
353
361
  # @return [String]
354
362
  def self.to_sparql(content,
363
+ datasets: [],
355
364
  distinct: false,
356
365
  extensions: {},
357
366
  filter_ops: [],
358
367
  group_ops: [],
368
+ having_ops: [],
359
369
  limit: nil,
360
370
  offset: nil,
361
371
  order_ops: [],
362
372
  project: %i(*),
363
373
  reduced: false,
374
+ values_clause: nil,
375
+ where_clause: true,
364
376
  **options)
365
377
  str = ""
366
378
 
@@ -372,28 +384,41 @@ module SPARQL; module Algebra
372
384
 
373
385
  str << project.map do |p|
374
386
  if expr = extensions.delete(p)
387
+ v = expr.to_sparql(as_statement: true, **options)
388
+ v = "<< #{v} >>" if expr.is_a?(RDF::Statement)
389
+ pp = p.to_sparql(**options)
375
390
  # Replace projected variables with their extension, if any
376
- "(" + [expr, :AS, p].to_sparql(**options) + ")"
391
+ '(' + v + ' AS ' + pp + ')'
377
392
  else
378
393
  p.to_sparql(**options)
379
394
  end
380
395
  end.join(" ") + "\n"
381
396
  end
382
397
 
383
- # Extensions
398
+ # DatasetClause
399
+ datasets.each do |ds|
400
+ str << "FROM #{ds.to_sparql(**options)}\n"
401
+ end
402
+
403
+ # Bind
384
404
  extensions.each do |as, expression|
385
- content << "\nBIND (#{expression.to_sparql(**options)} AS #{as.to_sparql(**options)}) ."
405
+ v = expression.to_sparql(as_statement: true, **options)
406
+ v = "<< #{v} >>" if expression.is_a?(RDF::Statement)
407
+ content << "\nBIND (" << v << " AS " << as.to_sparql(**options) << ") ."
386
408
  end
387
409
 
388
- # Filters
410
+ # Filter
389
411
  filter_ops.each do |f|
390
- content << "\nFILTER #{f.to_sparql(**options)} ."
412
+ content << "\nFILTER (#{f.to_sparql(**options)}) ."
391
413
  end
392
414
 
393
- # Where clause
394
- str << "WHERE {\n#{content}\n}\n"
415
+ # WhereClause / GroupGraphPattern
416
+ str << (where_clause ? "WHERE {\n#{content}\n}\n" : "{\n#{content}\n}\n")
395
417
 
396
- # Group
418
+ ##
419
+ # SolutionModifier
420
+ #
421
+ # GroupClause
397
422
  unless group_ops.empty?
398
423
  ops = group_ops.map do |o|
399
424
  # Replace projected variables with their extension, if any
@@ -404,14 +429,22 @@ module SPARQL; module Algebra
404
429
  str << "GROUP BY #{ops.join(' ')}\n"
405
430
  end
406
431
 
407
- # Order
432
+ # HavingClause
433
+ unless having_ops.empty?
434
+ str << "HAVING #{having_ops.to_sparql(**options)}"
435
+ end
436
+
437
+ # OrderClause
408
438
  unless order_ops.empty?
409
439
  str << "ORDER BY #{order_ops.to_sparql(**options)}\n"
410
440
  end
411
441
 
412
- # Offset and Limmit
442
+ # LimitOffsetClauses
413
443
  str << "OFFSET #{offset}\n" unless offset.nil?
414
444
  str << "LIMIT #{limit}\n" unless limit.nil?
445
+
446
+ # Values Clause
447
+ str << values_clause.to_sparql(top_level: false, **options) if values_clause
415
448
  str
416
449
  end
417
450
 
@@ -640,12 +673,8 @@ module SPARQL; module Algebra
640
673
  # @return [SPARQL::Algebra::Expression] `self`
641
674
  def rewrite(&block)
642
675
  @operands = @operands.map do |op|
643
- # Rewrite the operand
644
- unless new_op = block.call(op)
645
- # Not re-written, rewrite
646
- new_op = op.respond_to?(:rewrite) ? op.rewrite(&block) : op
647
- end
648
- new_op
676
+ new_op = block.call(op)
677
+ new_op.respond_to?(:rewrite) ? new_op.rewrite(&block) : new_op
649
678
  end
650
679
  self
651
680
  end
@@ -671,7 +700,6 @@ module SPARQL; module Algebra
671
700
  end
672
701
 
673
702
  ##
674
- #
675
703
  # Returns a partial SPARQL grammar for the operator.
676
704
  #
677
705
  # @return [String]
@@ -19,6 +19,24 @@ module SPARQL
19
19
  #
20
20
  # {RDF::Query} and {RDF::Query::Pattern} are used as primitives for `bgp` and `triple` expressions.
21
21
  #
22
+ # # Background
23
+ #
24
+ # The SPARQL Algebra, and the S-Expressions used to represent it, are based on those of [Jena](https://jena.apache.org/documentation/notes/sse.html). Generally, an S-Expression generated by this Gem can be used as an SSE input to Jena, or an SSE output from Jena can also be used as input to this Gem.
25
+ #
26
+ # S-Expressions generally follow a standardized nesting resulting from parsing the original SPARQL Grammar. The individual operators map to SPARQL Grammar productions, and in most cases, the SPARQL Grammar can be reproduced by turning the S-Expression back into SPARQL (see {SPARQL::Algebra::Operator#to_sparql}). The order of operations will typically be as follows:
27
+ #
28
+ # * {SPARQL::Algebra::Operator::Base}
29
+ # * {SPARQL::Algebra::Operator::Prefix}
30
+ # * {SPARQL::Algebra::Operator::Slice}
31
+ # * {SPARQL::Algebra::Operator::Distinct}
32
+ # * {SPARQL::Algebra::Operator::Reduced}
33
+ # * {SPARQL::Algebra::Operator::Project}
34
+ # * {SPARQL::Algebra::Operator::Order}
35
+ # * {SPARQL::Algebra::Operator::Filter}
36
+ # * {SPARQL::Algebra::Operator::Extend}
37
+ # * {SPARQL::Algebra::Operator::Group}
38
+ # * {SPARQL::Algebra::Query} (many classes implement Query)
39
+ #
22
40
  # # Queries
23
41
  #
24
42
  # require 'sparql/algebra'
@@ -262,6 +280,7 @@ module SPARQL
262
280
  # * {SPARQL::Algebra::Operator::Extend}
263
281
  # * {SPARQL::Algebra::Operator::Filter}
264
282
  # * {SPARQL::Algebra::Operator::Floor}
283
+ # * {SPARQL::Algebra::Operator::FunctionCall}
265
284
  # * {SPARQL::Algebra::Operator::Graph}
266
285
  # * {SPARQL::Algebra::Operator::GreaterThan}
267
286
  # * {SPARQL::Algebra::Operator::GreaterThanOrEqual}
@@ -339,11 +358,9 @@ module SPARQL
339
358
  # * {SPARQL::Algebra::Operator::With}
340
359
  # * {SPARQL::Algebra::Operator::Year}
341
360
  #
342
- # TODO
343
- # ====
344
- # * Operator#optimize needs to be completed and tested.
345
361
  #
346
362
  # @see http://www.w3.org/TR/sparql11-query/#sparqlAlgebra
363
+ # @see https://jena.apache.org/documentation/notes/sse.html
347
364
  module Algebra
348
365
  include RDF
349
366
 
@@ -197,7 +197,7 @@ module SPARQL::Grammar
197
197
  # [2] Query ::= Prologue
198
198
  # ( SelectQuery | ConstructQuery | DescribeQuery | AskQuery )
199
199
  production(:Query) do |input, data, callback|
200
- query = data[:query].first
200
+ query = data[:query].first if data[:query]
201
201
 
202
202
  # Add prefix
203
203
  if data[:PrefixDecl]
@@ -806,7 +806,7 @@ module SPARQL::Grammar
806
806
 
807
807
  # [70] FunctionCall ::= iri ArgList
808
808
  production(:FunctionCall) do |input, data, callback|
809
- add_prod_data(:Function, Array(data[:iri]) + data[:ArgList])
809
+ add_prod_data(:Function, SPARQL::Algebra::Operator::FunctionCall.new(data[:iri], *data[:ArgList]))
810
810
  end
811
811
 
812
812
  # [71] ArgList ::= NIL
@@ -1437,7 +1437,7 @@ module SPARQL::Grammar
1437
1437
  production(:iriOrFunction) do |input, data, callback|
1438
1438
  if data.has_key?(:ArgList)
1439
1439
  # Function is (func arg1 arg2 ...)
1440
- add_prod_data(:Function, Array(data[:iri]) + data[:ArgList])
1440
+ add_prod_data(:Function, SPARQL::Algebra::Operator::FunctionCall.new(data[:iri], *data[:ArgList]))
1441
1441
  else
1442
1442
  input[:iri] = data[:iri]
1443
1443
  end