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
@@ -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