sparql 3.2.0 → 3.2.1

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