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
@@ -0,0 +1,64 @@
1
+
2
+ module SPARQL; module Algebra
3
+ class Operator
4
+ ##
5
+ # The SPARQL `function_call` operator.
6
+ #
7
+ # [70] FunctionCall ::= iri ArgList
8
+ #
9
+ # @example SPARQL Grammar
10
+ # PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
11
+ # SELECT *
12
+ # WHERE { ?s ?p ?o . FILTER xsd:integer(?o) }
13
+ #
14
+ # @example SSE
15
+ # (prefix
16
+ # ((xsd: <http://www.w3.org/2001/XMLSchema#>))
17
+ # (filter (xsd:integer ?o)
18
+ # (bgp (triple ?s ?p ?o))))
19
+ #
20
+ # @see https://www.w3.org/TR/sparql11-query/#funcex-regex
21
+ # @see https://www.w3.org/TR/xpath-functions/#func-matches
22
+ class FunctionCall < Operator
23
+ include Evaluatable
24
+
25
+ NAME = :function_call
26
+
27
+ ##
28
+ # Invokes the function with the passed arguments.
29
+ #
30
+ # @param [RDF::IRI] iri
31
+ # Identifies the function
32
+ # @param [Array<RDF::Term>] args
33
+ # @return [RDF::Term]
34
+ def apply(iri, *args, **options)
35
+ args = RDF.nil == args.last ? args[0..-2] : args
36
+ SPARQL::Algebra::Expression.extension(iri, *args, **options)
37
+ end
38
+
39
+ ##
40
+ # Returns the SPARQL S-Expression (SSE) representation of this expression.
41
+ #
42
+ # Remove the optional argument.
43
+ #
44
+ # @return [Array] `self`
45
+ # @see https://openjena.org/wiki/SSE
46
+ def to_sxp_bin
47
+ @operands.map(&:to_sxp_bin)
48
+ end
49
+
50
+ ##
51
+ #
52
+ # Returns a partial SPARQL grammar for this operator.
53
+ #
54
+ # @return [String]
55
+ def to_sparql(**options)
56
+ iri, args = operands
57
+ iri.to_sparql(**options) +
58
+ '(' +
59
+ args.to_sparql(delimiter: ', ', **options) +
60
+ ')'
61
+ end
62
+ end # FunctionCall
63
+ end # Operator
64
+ end; end # SPARQL::Algebra
@@ -7,7 +7,7 @@ module SPARQL; module Algebra
7
7
  #
8
8
  # [58] GraphGraphPattern ::= 'GRAPH' VarOrIri GroupGraphPattern
9
9
  #
10
- # @example SPARQL Grammar
10
+ # @example SPARQL Grammar (query)
11
11
  # PREFIX : <http://example/>
12
12
  # SELECT * {
13
13
  # GRAPH ?g { ?s ?p ?o }
@@ -18,15 +18,50 @@ module SPARQL; module Algebra
18
18
  # (graph ?g
19
19
  # (bgp (triple ?s ?p ?o))))
20
20
  #
21
- # @example of a query
21
+ # @example SPARQL Grammar (named set of statements)
22
+ # PREFIX : <http://example/>
23
+ # SELECT * {
24
+ # GRAPH :g { :s :p :o }
25
+ # }
26
+ #
27
+ # @example SSE (named set of statements)
22
28
  # (prefix ((: <http://example/>))
29
+ # (graph :g
30
+ # (bgp (triple :s :p :o))))
31
+ #
32
+ # @example SPARQL Grammar (syntax-graph-05.rq)
33
+ # PREFIX : <http://example.org/>
34
+ # SELECT *
35
+ # WHERE
36
+ # {
37
+ # :x :p :z
38
+ # GRAPH ?g { :x :b ?a . GRAPH ?g2 { :x :p ?x } }
39
+ # }
40
+ #
41
+ # @example SSE (syntax-graph-05.rq)
42
+ # (prefix ((: <http://example.org/>))
43
+ # (join
44
+ # (bgp (triple :x :p :z))
23
45
  # (graph ?g
24
- # (bgp (triple ?s ?p ?o))))
46
+ # (join
47
+ # (bgp (triple :x :b ?a))
48
+ # (graph ?g2
49
+ # (bgp (triple :x :p ?x)))))))
25
50
  #
26
- # @example named set of statements
27
- # (prefix ((: <http://example/>))
28
- # (graph :g
29
- # ((triple :s :p :o))))
51
+ # @example SPARQL Grammar (pp06.rq)
52
+ # prefix ex: <http://www.example.org/schema#>
53
+ # prefix in: <http://www.example.org/instance#>
54
+ #
55
+ # select ?x where {
56
+ # graph ?g {in:a ex:p1/ex:p2 ?x}
57
+ # }
58
+ #
59
+ # @example SSE (syntax-graph-05.rq)
60
+ # (prefix ((ex: <http://www.example.org/schema#>)
61
+ # (in: <http://www.example.org/instance#>))
62
+ # (project (?x)
63
+ # (graph ?g
64
+ # (path in:a (seq ex:p1 ex:p2) ?x))))
30
65
  #
31
66
  # @see https://www.w3.org/TR/sparql11-query/#sparqlAlgebra
32
67
  class Graph < Operator::Binary
@@ -89,6 +124,21 @@ module SPARQL; module Algebra
89
124
  def rewrite(&block)
90
125
  self
91
126
  end
127
+
128
+ ##
129
+ #
130
+ # Returns a partial SPARQL grammar for this operator.
131
+ #
132
+ # @param [Boolean] top_level (true)
133
+ # Treat this as a top-level, generating SELECT ... WHERE {}
134
+ # @return [String]
135
+ def to_sparql(top_level: true, **options)
136
+ query = operands.last.to_sparql(top_level: false, **options)
137
+ # Paths don't automatically get braces.
138
+ query = "{\n#{query}\n}" unless query.start_with?('{')
139
+ str = "GRAPH #{operands.first.to_sparql(**options)} " + query
140
+ top_level ? Operator.to_sparql(str, **options) : str
141
+ end
92
142
  end # Graph
93
143
  end # Operator
94
144
  end; end # SPARQL::Algebra
@@ -25,6 +25,37 @@ module SPARQL; module Algebra
25
25
  # (group (?P) ((??.0 (count ?O)))
26
26
  # (bgp (triple ?S ?P ?O))))))
27
27
  #
28
+ # @example SPARQL Grammar (HAVING aggregate)
29
+ # PREFIX : <http://www.example.org/>
30
+ # SELECT ?s (AVG(?o) AS ?avg)
31
+ # WHERE { ?s ?p ?o }
32
+ # GROUP BY ?s
33
+ # HAVING (AVG(?o) <= 2.0)
34
+ #
35
+ # @example SSE (HAVING aggregate)
36
+ # (prefix ((: <http://www.example.org/>))
37
+ # (project (?s ?avg)
38
+ # (filter (<= ??.0 2.0)
39
+ # (extend ((?avg ??.0))
40
+ # (group (?s) ((??.0 (avg ?o)))
41
+ # (bgp (triple ?s ?p ?o)))))) )
42
+ #
43
+ # @example SPARQL Grammar (non-triveal filters)
44
+ # PREFIX : <http://example.com/data/#>
45
+ # SELECT ?g (AVG(?p) AS ?avg) ((MIN(?p) + MAX(?p)) / 2 AS ?c)
46
+ # WHERE { ?g :p ?p . }
47
+ # GROUP BY ?g
48
+ #
49
+ # @example SSE (non-triveal filters)
50
+ # (prefix ((: <http://example.com/data/#>))
51
+ # (project (?g ?avg ?c)
52
+ # (extend ((?avg ??.0) (?c (/ (+ ??.1 ??.2) 2)))
53
+ # (group (?g)
54
+ # ((??.0 (avg ?p))
55
+ # (??.1 (min ?p))
56
+ # (??.2 (max ?p)))
57
+ # (bgp (triple ?g :p ?p)))) ))
58
+ #
28
59
  # @see https://www.w3.org/TR/sparql11-query/#sparqlAlgebra
29
60
  class Group < Operator
30
61
  include Query
@@ -137,16 +168,56 @@ module SPARQL; module Algebra
137
168
  #
138
169
  # @param [Hash{Symbol => Operator}] extensions
139
170
  # Variable bindings
171
+ # @param [Array<Operator>] filter_ops ([])
172
+ # Filter Operations
140
173
  # @return [String]
141
- def to_sparql(extensions: {}, **options)
174
+ def to_sparql(extensions: {}, filter_ops: [], **options)
175
+ having_ops = []
142
176
  if operands.length > 2
177
+ temp_bindings = operands[1].inject({}) {|memo, (var, op)| memo.merge(var => op)}
143
178
  # Replace extensions from temporary bindings
144
- operands[1].each do |var, op|
145
- ext_var = extensions.invert.fetch(var)
146
- extensions[ext_var] = op
179
+ temp_bindings.each do |var, op|
180
+ # Update extensions using a temporarily bound variable with its binding
181
+ extensions = extensions.inject({}) do |memo, (ext_var, ext_op)|
182
+ if ext_op.is_a?(Operator)
183
+ # Try to recursivley replace variable within operator
184
+ new_op = ext_op.deep_dup.rewrite do |operand|
185
+ if operand.is_a?(Variable) && operand.to_sym == var.to_sym
186
+ op.dup
187
+ else
188
+ operand
189
+ end
190
+ end
191
+ memo.merge(ext_var => new_op)
192
+ elsif ext_op.is_a?(Variable) && ext_op.to_sym == var.to_sym
193
+ memo.merge(ext_var => op)
194
+ else
195
+ # Doesn't match this variable, so don't change
196
+ memo.merge(ext_var => ext_op)
197
+ end
198
+ end
199
+
200
+ # Filter ops using temporary bindinds are used for HAVING clauses
201
+ filter_ops.each do |fop|
202
+ having_ops << fop if fop.descendants.include?(var) && !having_ops.include?(fop)
203
+ end
204
+ end
205
+
206
+ # If used in a HAVING clause, it's not also a filter
207
+ filter_ops -= having_ops
208
+
209
+ # Replace each operand in having using var with it's corresponding operation
210
+ having_ops = having_ops.map do |op|
211
+ op.dup.rewrite do |operand|
212
+ # Rewrite based on temporary bindings
213
+ temp_bindings.fetch(operand, operand)
214
+ end
147
215
  end
148
216
  end
149
- operands.last.to_sparql(extensions: extensions, group_ops: operands.first, **options)
217
+ operands.last.to_sparql(extensions: extensions,
218
+ group_ops: operands.first,
219
+ having_ops: having_ops,
220
+ **options)
150
221
  end
151
222
  end # Group
152
223
  end # Operator
@@ -16,6 +16,24 @@ module SPARQL; module Algebra
16
16
  # (group () ((??.0 (group_concat ?x)))
17
17
  # (bgp))))
18
18
  #
19
+ # @example SPARQL Grammar (DISTINCT)
20
+ # SELECT (GROUP_CONCAT(DISTINCT ?x) AS ?y) {}
21
+ #
22
+ # @example SSE (DISTINCT)
23
+ # (project (?y)
24
+ # (extend ((?y ??.0))
25
+ # (group () ((??.0 (group_concat distinct ?x)))
26
+ # (bgp))))
27
+ #
28
+ # @example SPARQL Grammar (SEPARATOR)
29
+ # SELECT (GROUP_CONCAT(?x; SEPARATOR=';') AS ?y) {}
30
+ #
31
+ # @example SSE (SEPARATOR)
32
+ # (project (?y)
33
+ # (extend ((?y ??.0))
34
+ # (group () ((??.0 (group_concat (separator ";") ?x)))
35
+ # (bgp))))
36
+ #
19
37
  # @see https://www.w3.org/TR/sparql11-query/#defn_aggGroupConcat
20
38
  class GroupConcat < Operator
21
39
  include Aggregate
@@ -63,7 +81,13 @@ module SPARQL; module Algebra
63
81
  #
64
82
  # @return [String]
65
83
  def to_sparql(**options)
66
- "GROUP_CONCAT(#{operands.to_sparql(delimiter: ', ', **options)})"
84
+ distinct = operands.first == :distinct
85
+ args = distinct ? operands[1..-1] : operands
86
+ separator = args.first.last if args.first.is_a?(Array) && args.first.first == :separator
87
+ args = args[1..-1] if separator
88
+ str = "GROUP_CONCAT(#{'DISTINCT ' if distinct}#{args.to_sparql(delimiter: ', ', **options)}"
89
+ str << "; SEPARATOR=#{separator.to_sparql}" if separator
90
+ str << ")"
67
91
  end
68
92
  end # GroupConcat
69
93
  end # Operator
@@ -48,15 +48,15 @@ module SPARQL; module Algebra
48
48
  rescue
49
49
  raise TypeError
50
50
  end
51
- end # If
52
51
 
53
- ##
54
- #
55
- # Returns a partial SPARQL grammar for this operator.
56
- #
57
- # @return [String]
58
- def to_sparql(**options)
59
- "IF(" + operands.to_sparql(delimiter: ', ', **options) + ")"
60
- end
61
- end # If
52
+ ##
53
+ #
54
+ # Returns a partial SPARQL grammar for this operator.
55
+ #
56
+ # @return [String]
57
+ def to_sparql(**options)
58
+ "IF(" + operands.to_sparql(delimiter: ', ', **options) + ")"
59
+ end
60
+ end # If
61
+ end # Operator
62
62
  end; end # SPARQL::Algebra
@@ -69,7 +69,9 @@ module SPARQL; module Algebra
69
69
  #
70
70
  # @return [String]
71
71
  def to_sparql(**options)
72
- "INSERT {\n" + operands.first.to_sparql(as_statement: true, **options) + "\n}"
72
+ "INSERT {\n" +
73
+ operands.first.to_sparql(as_statement: true, delimiter: " .\n", **options) +
74
+ "\n}"
73
75
  end
74
76
  end # Insert
75
77
  end # Operator
@@ -54,7 +54,7 @@ module SPARQL; module Algebra
54
54
  # @return [String]
55
55
  def to_sparql(**options)
56
56
  "INSERT DATA {\n" +
57
- operands.first.to_sparql(as_statement: true, top_level: false, delimiter: "\n", **options) +
57
+ operands.first.to_sparql(as_statement: true, top_level: false, delimiter: ". \n", **options) +
58
58
  "\n}"
59
59
  end
60
60
  end # InsertData
@@ -13,8 +13,7 @@ module SPARQL; module Algebra
13
13
  # }
14
14
  #
15
15
  # @example SSE
16
- # (prefix ((xsd: <http://www.w3.org/2001/XMLSchema#>)
17
- # (: <http://example.org/things#>))
16
+ # (prefix ((: <http://example.org/things#>))
18
17
  # (project (?x ?v)
19
18
  # (filter (isBlank ?v)
20
19
  # (bgp (triple ?x :p ?v)))))
@@ -13,8 +13,7 @@ module SPARQL; module Algebra
13
13
  # }
14
14
  #
15
15
  # @example SSE
16
- # (prefix ((xsd: <http://www.w3.org/2001/XMLSchema#>)
17
- # (: <http://example.org/things#>))
16
+ # (prefix ((: <http://example.org/things#>))
18
17
  # (project (?x ?v)
19
18
  # (filter (isIRI ?v)
20
19
  # (bgp (triple ?x :p ?v)))))
@@ -13,8 +13,7 @@ module SPARQL; module Algebra
13
13
  # }
14
14
  #
15
15
  # @example SSE
16
- # (prefix ((xsd: <http://www.w3.org/2001/XMLSchema#>)
17
- # (: <http://example.org/things#>))
16
+ # (prefix ((: <http://example.org/things#>))
18
17
  # (project (?x ?v)
19
18
  # (filter (isLiteral ?v)
20
19
  # (bgp (triple ?x :p ?v)))))
@@ -15,8 +15,7 @@ module SPARQL; module Algebra
15
15
  # }
16
16
  #
17
17
  # @example SSE
18
- # (prefix ((xsd: <http://www.w3.org/2001/XMLSchema#>)
19
- # (: <http://example.org/things#>))
18
+ # (prefix ((: <http://example.org/things#>))
20
19
  # (project (?x ?v)
21
20
  # (filter (isNumeric ?v)
22
21
  # (bgp (triple ?x :p ?v)))))
@@ -19,6 +19,22 @@ module SPARQL; module Algebra
19
19
  # (graph ?g
20
20
  # (bgp (triple ?s ?q ?v)))))
21
21
  #
22
+ # @example SPARQL Grammar (inline filter)
23
+ # PREFIX : <http://xmlns.com/foaf/0.1/>
24
+ # ASK {
25
+ # :who :homepage ?homepage
26
+ # FILTER REGEX(?homepage, "^http://example.org/")
27
+ # :who :schoolHomepage ?schoolPage
28
+ # }
29
+ #
30
+ # @example SSE (inline filter)
31
+ # (prefix ((: <http://xmlns.com/foaf/0.1/>))
32
+ # (ask
33
+ # (filter (regex ?homepage "^http://example.org/")
34
+ # (join
35
+ # (bgp (triple :who :homepage ?homepage))
36
+ # (bgp (triple :who :schoolHomepage ?schoolPage))))))
37
+ #
22
38
  # @see https://www.w3.org/TR/sparql11-query/#sparqlAlgebra
23
39
  class Join < Operator::Binary
24
40
  include Query
@@ -98,10 +114,28 @@ module SPARQL; module Algebra
98
114
  #
99
115
  # @param [Boolean] top_level (true)
100
116
  # Treat this as a top-level, generating SELECT ... WHERE {}
117
+ # @param [Hash{Symbol => Operator}] extensions
118
+ # Variable bindings
119
+ # @param [Array<Operator>] filter_ops ([])
120
+ # Filter Operations
101
121
  # @return [String]
102
- def to_sparql(top_level: true, **options)
103
- str = operands.to_sparql(top_level: false, delimiter: "\n", **options)
104
- top_level ? Operator.to_sparql(str, **options) : str
122
+ def to_sparql(top_level: true, filter_ops: [], extensions: {}, **options)
123
+ # If this is top-level, and the last operand is a Table (values), put the values at the outer-level
124
+ str = "{\n" + operands.first.to_sparql(top_level: false, extensions: {}, **options)
125
+
126
+ # Any accrued filters go here.
127
+ filter_ops.each do |op|
128
+ str << "\nFILTER (#{op.to_sparql(**options)}) ."
129
+ end
130
+
131
+ if top_level && operands.last.is_a?(Table)
132
+ str << "\n}"
133
+ options = options.merge(values_clause: operands.last)
134
+ else
135
+ str << "\n{\n" + operands.last.to_sparql(top_level: false, extensions: {}, **options) + "\n}\n}"
136
+ end
137
+
138
+ top_level ? Operator.to_sparql(str, extensions: extensions, **options) : str
105
139
  end
106
140
  end # Join
107
141
  end # Operator
@@ -12,9 +12,8 @@ module SPARQL; module Algebra
12
12
  # }
13
13
  #
14
14
  # @example SSE
15
- # (prefix
16
- # ((: <http://example.org/>))
17
- # (project (?str ?lstr)
15
+ # (prefix ((: <http://example.org/>))
16
+ # (project (?s ?lstr)
18
17
  # (extend ((?lstr (lcase ?str)))
19
18
  # (bgp (triple ?s :str ?str)))))
20
19
  #
@@ -131,14 +131,27 @@ module SPARQL; module Algebra
131
131
  #
132
132
  # @param [Boolean] top_level (true)
133
133
  # Treat this as a top-level, generating SELECT ... WHERE {}
134
+ # @param [Hash{Symbol => Operator}] extensions
135
+ # Variable bindings
136
+ # @param [Array<Operator>] filter_ops ([])
137
+ # Filter Operations
134
138
  # @return [String]
135
- def to_sparql(top_level: true, **options)
136
- str = operands[0].to_sparql(top_level: false, **options) +
137
- "\nOPTIONAL { \n" +
138
- operands[1].to_sparql(top_level: false, **options) + "\n"
139
- str << 'FILTER (' + operands[2].to_sparql(**options) + ") \n" if operands[2]
140
- str << '}'
141
- top_level ? Operator.to_sparql(str, **options) : str
139
+ def to_sparql(top_level: true, filter_ops: [], extensions: {}, **options)
140
+ str = "{\n" + operands[0].to_sparql(top_level: false, extensions: {}, **options)
141
+ str <<
142
+ "\nOPTIONAL {\n" +
143
+ operands[1].to_sparql(top_level: false, extensions: {}, **options)
144
+ case operands[2]
145
+ when SPARQL::Algebra::Operator::Exprlist
146
+ operands[2].operands.each do |op|
147
+ str << "\nFILTER (" + op.to_sparql(**options) + ")"
148
+ end
149
+ when nil
150
+ else
151
+ str << "\nFILTER (" + operands[2].to_sparql(**options) + ")"
152
+ end
153
+ str << "\n}}"
154
+ top_level ? Operator.to_sparql(str, filter_ops: filter_ops, extensions: extensions, **options) : str
142
155
  end
143
156
  end # LeftJoin
144
157
  end # Operator
@@ -56,7 +56,9 @@ module SPARQL; module Algebra
56
56
  #
57
57
  # @return [String]
58
58
  def to_sparql(**options)
59
- "MAX(" + operands.to_sparql(**options) + ")"
59
+ distinct = operands.first == :distinct
60
+ args = distinct ? operands[1..-1] : operands
61
+ "MAX(#{'DISTINCT ' if distinct}#{args.to_sparql(**options)})"
60
62
  end
61
63
  end # Max
62
64
  end # Operator
@@ -15,7 +15,7 @@ module SPARQL; module Algebra
15
15
  # (project (?min)
16
16
  # (extend ((?min ??.0))
17
17
  # (group () ((??.0 (min ?o)))
18
- # (bgp (triple ?s ?p ?o))))))
18
+ # (bgp (triple ?s :dec ?o))))))
19
19
  #
20
20
  # @see https://www.w3.org/TR/sparql11-query/#defn_aggMin
21
21
  class Min < Operator
@@ -56,7 +56,9 @@ module SPARQL; module Algebra
56
56
  #
57
57
  # @return [String]
58
58
  def to_sparql(**options)
59
- "MIN(" + operands.to_sparql(**options) + ")"
59
+ distinct = operands.first == :distinct
60
+ args = distinct ? operands[1..-1] : operands
61
+ "MIN(#{'DISTINCT ' if distinct}#{args.to_sparql(**options)})"
60
62
  end
61
63
  end # Min
62
64
  end # Operator
@@ -14,6 +14,32 @@ module SPARQL; module Algebra
14
14
  # (triple ?s ?p ?o))
15
15
  # (bgp (triple ?s ?q ?v)))
16
16
  #
17
+ # @example SPARQL Grammar (inline filter)
18
+ # PREFIX : <http://example/>
19
+ # SELECT (?s1 AS ?subset) (?s2 AS ?superset)
20
+ # WHERE {
21
+ # ?s2 a :Set .
22
+ # ?s1 a :Set .
23
+ # FILTER(?s1 != ?s2)
24
+ # MINUS {
25
+ # ?s1 a :Set .
26
+ # ?s2 a :Set .
27
+ # FILTER(?s1 != ?s2)
28
+ # }
29
+ # }
30
+ #
31
+ # @example SSE (inline filter)
32
+ # (prefix ((: <http://example/>))
33
+ # (project (?subset ?superset)
34
+ # (extend ((?subset ?s1) (?superset ?s2))
35
+ # (filter (!= ?s1 ?s2)
36
+ # (minus
37
+ # (bgp (triple ?s2 a :Set) (triple ?s1 a :Set))
38
+ # (filter (!= ?s1 ?s2)
39
+ # (bgp
40
+ # (triple ?s1 a :Set)
41
+ # (triple ?s2 a :Set))))))))
42
+ #
17
43
  # @see https://www.w3.org/TR/xpath-functions/#func-numeric-unary-minus
18
44
  # @see https://www.w3.org/TR/sparql11-query/#sparqlAlgebra
19
45
  class Minus < Operator::Binary
@@ -74,15 +100,29 @@ module SPARQL; module Algebra
74
100
  #
75
101
  # Returns a partial SPARQL grammar for this operator.
76
102
  #
103
+ # @param [Hash{Symbol => Operator}] extensions
104
+ # Variable bindings
105
+ # @param [Array<Operator>] filter_ops ([])
106
+ # Filter Operations
77
107
  # @param [Boolean] top_level (true)
78
108
  # Treat this as a top-level, generating SELECT ... WHERE {}
79
109
  # @return [String]
80
- def to_sparql(top_level: true, **options)
81
- str = operands.first.to_sparql(top_level: false, **options) + "\n"
82
- str << "MINUS {\n"
83
- str << operands.last.to_sparql(top_level: false, **options)
84
- str << "\n}"
85
- top_level ? Operator.to_sparql(str, **options) : str
110
+ def to_sparql(top_level: true, filter_ops: [], extensions: {}, **options)
111
+ lhs, *rhs = operands
112
+ str = "{\n" + lhs.to_sparql(top_level: false, extensions: {}, **options)
113
+
114
+ # Any accrued filters go here.
115
+ filter_ops.each do |op|
116
+ str << "\nFILTER (#{op.to_sparql(**options)}) ."
117
+ end
118
+
119
+ rhs.each do |minus|
120
+ str << "\nMINUS {\n"
121
+ str << minus.to_sparql(top_level: false, extensions: {}, **options)
122
+ str << "\n}"
123
+ end
124
+ str << "}"
125
+ top_level ? Operator.to_sparql(str, extensions: extensions, **options) : str
86
126
  end
87
127
  end # Minus
88
128
  end # Operator
@@ -50,7 +50,7 @@ module SPARQL; module Algebra
50
50
  #
51
51
  # @return [String]
52
52
  def to_sparql(**options)
53
- "#{operands.first.to_sparql(**options)} * #{operands.last.to_sparql(**options)}"
53
+ "(#{operands.first.to_sparql(**options)} * #{operands.last.to_sparql(**options)})"
54
54
  end
55
55
  end # Multiply
56
56
  end # Operator
@@ -8,13 +8,13 @@ module SPARQL; module Algebra
8
8
  # @example SPARQL Grammar
9
9
  # PREFIX ex: <http://www.example.org/schema#>
10
10
  # PREFIX in: <http://www.example.org/instance#>
11
- # ASK { in:b ^ex:p in:a }
11
+ # ASK { in:a !(ex:p1|ex:p2) ?x }
12
12
  #
13
13
  # @example SSE
14
14
  # (prefix ((ex: <http://www.example.org/schema#>)
15
- # (in: <http://www.example.org/instance#>))
15
+ # (in: <http://www.example.org/instance#>))
16
16
  # (ask
17
- # (path in:b (reverse ex:p) in:a)))
17
+ # (path in:a (notoneof ex:p1 ex:p2) ?x)))
18
18
  #
19
19
  # @see https://www.w3.org/TR/sparql11-query/#eval_negatedPropertySet
20
20
  class NotOneOf < Operator
@@ -56,6 +56,15 @@ module SPARQL; module Algebra
56
56
  block.call(solution)
57
57
  end
58
58
  end
59
+
60
+ ##
61
+ #
62
+ # Returns a partial SPARQL grammar for this operator.
63
+ #
64
+ # @return [String]
65
+ def to_sparql(**options)
66
+ "!(" + operands.to_sparql(delimiter: ' | ', **options) + ')'
67
+ end
59
68
  end # NotOneOf
60
69
  end # Operator
61
70
  end; end # SPARQL::Algebra