sparql 3.1.8 → 3.2.3

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 (155) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +88 -60
  3. data/VERSION +1 -1
  4. data/bin/sparql +15 -35
  5. data/lib/rack/sparql/conneg.rb +22 -1
  6. data/lib/sinatra/sparql/extensions.rb +1 -1
  7. data/lib/sinatra/sparql.rb +57 -12
  8. data/lib/sparql/algebra/expression.rb +63 -10
  9. data/lib/sparql/algebra/extensions.rb +110 -46
  10. data/lib/sparql/algebra/operator/abs.rb +22 -2
  11. data/lib/sparql/algebra/operator/add.rb +21 -2
  12. data/lib/sparql/algebra/operator/adjust.rb +69 -0
  13. data/lib/sparql/algebra/operator/alt.rb +26 -2
  14. data/lib/sparql/algebra/operator/and.rb +25 -3
  15. data/lib/sparql/algebra/operator/asc.rb +20 -1
  16. data/lib/sparql/algebra/operator/ask.rb +17 -1
  17. data/lib/sparql/algebra/operator/avg.rb +19 -1
  18. data/lib/sparql/algebra/operator/base.rb +18 -1
  19. data/lib/sparql/algebra/operator/bgp.rb +13 -1
  20. data/lib/sparql/algebra/operator/bnode.rb +33 -10
  21. data/lib/sparql/algebra/operator/bound.rb +22 -1
  22. data/lib/sparql/algebra/operator/ceil.rb +25 -2
  23. data/lib/sparql/algebra/operator/clear.rb +26 -2
  24. data/lib/sparql/algebra/operator/coalesce.rb +33 -11
  25. data/lib/sparql/algebra/operator/compare.rb +9 -0
  26. data/lib/sparql/algebra/operator/concat.rb +26 -2
  27. data/lib/sparql/algebra/operator/construct.rb +29 -6
  28. data/lib/sparql/algebra/operator/contains.rb +24 -2
  29. data/lib/sparql/algebra/operator/copy.rb +19 -2
  30. data/lib/sparql/algebra/operator/count.rb +52 -6
  31. data/lib/sparql/algebra/operator/create.rb +20 -2
  32. data/lib/sparql/algebra/operator/dataset.rb +37 -2
  33. data/lib/sparql/algebra/operator/datatype.rb +25 -6
  34. data/lib/sparql/algebra/operator/day.rb +25 -7
  35. data/lib/sparql/algebra/operator/delete.rb +29 -2
  36. data/lib/sparql/algebra/operator/delete_data.rb +23 -2
  37. data/lib/sparql/algebra/operator/delete_where.rb +24 -2
  38. data/lib/sparql/algebra/operator/desc.rb +20 -1
  39. data/lib/sparql/algebra/operator/describe.rb +27 -4
  40. data/lib/sparql/algebra/operator/distinct.rb +20 -3
  41. data/lib/sparql/algebra/operator/divide.rb +26 -2
  42. data/lib/sparql/algebra/operator/drop.rb +27 -3
  43. data/lib/sparql/algebra/operator/encode_for_uri.rb +22 -2
  44. data/lib/sparql/algebra/operator/equal.rb +12 -2
  45. data/lib/sparql/algebra/operator/exists.rb +28 -4
  46. data/lib/sparql/algebra/operator/exprlist.rb +15 -2
  47. data/lib/sparql/algebra/operator/extend.rb +95 -7
  48. data/lib/sparql/algebra/operator/filter.rb +27 -5
  49. data/lib/sparql/algebra/operator/floor.rb +25 -2
  50. data/lib/sparql/algebra/operator/function_call.rb +64 -0
  51. data/lib/sparql/algebra/operator/graph.rb +69 -6
  52. data/lib/sparql/algebra/operator/greater_than.rb +12 -3
  53. data/lib/sparql/algebra/operator/greater_than_or_equal.rb +12 -2
  54. data/lib/sparql/algebra/operator/group.rb +133 -8
  55. data/lib/sparql/algebra/operator/group_concat.rb +43 -7
  56. data/lib/sparql/algebra/operator/hours.rb +25 -7
  57. data/lib/sparql/algebra/operator/if.rb +20 -3
  58. data/lib/sparql/algebra/operator/in.rb +18 -1
  59. data/lib/sparql/algebra/operator/insert.rb +24 -2
  60. data/lib/sparql/algebra/operator/insert_data.rb +23 -2
  61. data/lib/sparql/algebra/operator/iri.rb +21 -4
  62. data/lib/sparql/algebra/operator/is_blank.rb +20 -3
  63. data/lib/sparql/algebra/operator/is_iri.rb +20 -3
  64. data/lib/sparql/algebra/operator/is_literal.rb +20 -3
  65. data/lib/sparql/algebra/operator/is_numeric.rb +22 -5
  66. data/lib/sparql/algebra/operator/is_triple.rb +32 -0
  67. data/lib/sparql/algebra/operator/join.rb +58 -3
  68. data/lib/sparql/algebra/operator/lang.rb +25 -0
  69. data/lib/sparql/algebra/operator/lang_matches.rb +22 -1
  70. data/lib/sparql/algebra/operator/lcase.rb +22 -2
  71. data/lib/sparql/algebra/operator/left_join.rb +44 -3
  72. data/lib/sparql/algebra/operator/less_than.rb +12 -3
  73. data/lib/sparql/algebra/operator/less_than_or_equal.rb +12 -2
  74. data/lib/sparql/algebra/operator/load.rb +25 -2
  75. data/lib/sparql/algebra/operator/max.rb +19 -1
  76. data/lib/sparql/algebra/operator/md5.rb +22 -5
  77. data/lib/sparql/algebra/operator/min.rb +21 -3
  78. data/lib/sparql/algebra/operator/minus.rb +65 -7
  79. data/lib/sparql/algebra/operator/minutes.rb +25 -7
  80. data/lib/sparql/algebra/operator/modify.rb +62 -5
  81. data/lib/sparql/algebra/operator/month.rb +25 -7
  82. data/lib/sparql/algebra/operator/move.rb +20 -2
  83. data/lib/sparql/algebra/operator/multiply.rb +26 -3
  84. data/lib/sparql/algebra/operator/negate.rb +23 -3
  85. data/lib/sparql/algebra/operator/not.rb +24 -3
  86. data/lib/sparql/algebra/operator/not_equal.rb +13 -0
  87. data/lib/sparql/algebra/operator/notexists.rb +30 -6
  88. data/lib/sparql/algebra/operator/notin.rb +20 -3
  89. data/lib/sparql/algebra/operator/notoneof.rb +21 -2
  90. data/lib/sparql/algebra/operator/now.rb +24 -5
  91. data/lib/sparql/algebra/operator/object.rb +32 -0
  92. data/lib/sparql/algebra/operator/or.rb +26 -3
  93. data/lib/sparql/algebra/operator/order.rb +64 -1
  94. data/lib/sparql/algebra/operator/path.rb +29 -2
  95. data/lib/sparql/algebra/operator/path_opt.rb +28 -65
  96. data/lib/sparql/algebra/operator/path_plus.rb +37 -10
  97. data/lib/sparql/algebra/operator/path_range.rb +178 -0
  98. data/lib/sparql/algebra/operator/path_star.rb +25 -4
  99. data/lib/sparql/algebra/operator/path_zero.rb +110 -0
  100. data/lib/sparql/algebra/operator/plus.rb +49 -8
  101. data/lib/sparql/algebra/operator/predicate.rb +32 -0
  102. data/lib/sparql/algebra/operator/prefix.rb +24 -3
  103. data/lib/sparql/algebra/operator/project.rb +111 -6
  104. data/lib/sparql/algebra/operator/rand.rb +30 -2
  105. data/lib/sparql/algebra/operator/reduced.rb +20 -3
  106. data/lib/sparql/algebra/operator/regex.rb +26 -18
  107. data/lib/sparql/algebra/operator/replace.rb +26 -6
  108. data/lib/sparql/algebra/operator/reverse.rb +31 -2
  109. data/lib/sparql/algebra/operator/round.rb +25 -2
  110. data/lib/sparql/algebra/operator/same_term.rb +24 -6
  111. data/lib/sparql/algebra/operator/sample.rb +32 -8
  112. data/lib/sparql/algebra/operator/seconds.rb +25 -7
  113. data/lib/sparql/algebra/operator/seq.rb +23 -5
  114. data/lib/sparql/algebra/operator/sequence.rb +14 -11
  115. data/lib/sparql/algebra/operator/sha1.rb +18 -1
  116. data/lib/sparql/algebra/operator/sha256.rb +18 -1
  117. data/lib/sparql/algebra/operator/sha384.rb +18 -1
  118. data/lib/sparql/algebra/operator/sha512.rb +18 -1
  119. data/lib/sparql/algebra/operator/slice.rb +27 -5
  120. data/lib/sparql/algebra/operator/str.rb +21 -1
  121. data/lib/sparql/algebra/operator/strafter.rb +25 -2
  122. data/lib/sparql/algebra/operator/strbefore.rb +25 -2
  123. data/lib/sparql/algebra/operator/strdt.rb +22 -1
  124. data/lib/sparql/algebra/operator/strends.rb +25 -3
  125. data/lib/sparql/algebra/operator/strlang.rb +24 -6
  126. data/lib/sparql/algebra/operator/strlen.rb +23 -2
  127. data/lib/sparql/algebra/operator/strstarts.rb +25 -2
  128. data/lib/sparql/algebra/operator/struuid.rb +29 -9
  129. data/lib/sparql/algebra/operator/subject.rb +32 -0
  130. data/lib/sparql/algebra/operator/substr.rb +23 -2
  131. data/lib/sparql/algebra/operator/subtract.rb +37 -7
  132. data/lib/sparql/algebra/operator/sum.rb +24 -6
  133. data/lib/sparql/algebra/operator/table.rb +85 -4
  134. data/lib/sparql/algebra/operator/timezone.rb +25 -7
  135. data/lib/sparql/algebra/operator/triple.rb +24 -0
  136. data/lib/sparql/algebra/operator/tz.rb +24 -7
  137. data/lib/sparql/algebra/operator/ucase.rb +23 -2
  138. data/lib/sparql/algebra/operator/union.rb +29 -6
  139. data/lib/sparql/algebra/operator/update.rb +46 -4
  140. data/lib/sparql/algebra/operator/using.rb +49 -2
  141. data/lib/sparql/algebra/operator/uuid.rb +27 -8
  142. data/lib/sparql/algebra/operator/with.rb +38 -4
  143. data/lib/sparql/algebra/operator/year.rb +25 -7
  144. data/lib/sparql/algebra/operator.rb +150 -12
  145. data/lib/sparql/algebra/query.rb +5 -3
  146. data/lib/sparql/algebra/sxp_extensions.rb +3 -3
  147. data/lib/sparql/algebra.rb +42 -6
  148. data/lib/sparql/grammar/meta.rb +1367 -267
  149. data/lib/sparql/grammar/parser11.rb +829 -331
  150. data/lib/sparql/grammar/terminals11.rb +2 -2
  151. data/lib/sparql/grammar.rb +6 -4
  152. data/lib/sparql/results.rb +3 -2
  153. data/lib/sparql/server.rb +93 -0
  154. data/lib/sparql.rb +8 -5
  155. metadata +57 -35
@@ -5,11 +5,52 @@ module SPARQL; module Algebra
5
5
  #
6
6
  # Extends a solution
7
7
  #
8
- # @example
9
- # (select (?z)
10
- # (project (?z)
11
- # (extend ((?z (+ ?o 10)))
12
- # (bgp (triple ?s <http://example/p> ?o)))))
8
+ # [60] Bind ::= 'BIND' '(' Expression 'AS' Var ')'
9
+ #
10
+ # @example SPARQL Grammar
11
+ # SELECT ?z
12
+ # {
13
+ # ?x <http://example.org/p> ?o
14
+ # BIND(?o+10 AS ?z)
15
+ # }
16
+ #
17
+ # @example SSE
18
+ # (project (?z)
19
+ # (extend ((?z (+ ?o 10)))
20
+ # (bgp (triple ?x <http://example.org/p> ?o))))
21
+ #
22
+ # @example SPARQL Grammar (cast as boolean)
23
+ # PREFIX : <http://example.org/>
24
+ # PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
25
+ # PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
26
+ # SELECT ?a ?v (xsd:boolean(?v) AS ?boolean)
27
+ # WHERE { ?a :p ?v . }
28
+ #
29
+ # @example SSE (cast as boolean)
30
+ # (prefix ((: <http://example.org/>)
31
+ # (rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>)
32
+ # (xsd: <http://www.w3.org/2001/XMLSchema#>))
33
+ # (project (?a ?v ?boolean)
34
+ # (extend ((?boolean (xsd:boolean ?v)))
35
+ # (bgp (triple ?a :p ?v)))))
36
+ #
37
+ # @example SPARQL Grammar (inner bind)
38
+ # PREFIX : <http://example.org/>
39
+ #
40
+ # SELECT ?z ?s1
41
+ # {
42
+ # ?s ?p ?o .
43
+ # BIND(?o+1 AS ?z)
44
+ # ?s1 ?p1 ?z
45
+ # }
46
+ #
47
+ # @example SSE (inner bind)
48
+ # (prefix ((: <http://example.org/>))
49
+ # (project (?z ?s1)
50
+ # (join
51
+ # (extend ((?z (+ ?o 1)))
52
+ # (bgp (triple ?s ?p ?o)))
53
+ # (bgp (triple ?s1 ?p1 ?z)))))
13
54
  #
14
55
  # @see https://www.w3.org/TR/sparql11-query/#evaluation
15
56
  class Extend < Operator::Binary
@@ -63,16 +104,63 @@ module SPARQL; module Algebra
63
104
  end
64
105
 
65
106
  # The variable introduced by the BIND clause must not have been used in the group graph pattern up to the point of use in BIND
107
+ #
108
+ # Also, variables used in a binding expression must be projected by the query.
66
109
  def validate!
67
110
  bind_vars = operand(0).map(&:first).map(&:name)
68
- query_vars = operand(1).vars.map(&:name)
111
+ query_vars = operand(1).variables.keys
69
112
 
70
113
  unless (bind_vars.compact & query_vars.compact).empty?
71
114
  raise ArgumentError,
72
115
  "bound variable used in query: #{(bind_vars.compact & query_vars.compact).to_sse}"
73
116
  end
117
+
118
+ # Special case for group variables
119
+ if operands.last.is_a?(Group)
120
+ bind_expr_vars = operand(0).map(&:last).variables.keys
121
+ group_vars = operands.last.variables.keys
122
+ group_internal_vars = operands.last.internal_variables.keys
123
+
124
+ bind_expr_vars.each do |v|
125
+ raise ArgumentError,
126
+ "extension expression uses variable not in scope: #{v}" if
127
+ group_internal_vars.include?(v) &&
128
+ !group_vars.include?(v)
129
+ end
130
+ end
131
+
74
132
  super
75
133
  end
76
- end # Filter
134
+
135
+ ##
136
+ # The variables used in the extension.
137
+ # Includes extended variables.
138
+ #
139
+ # @return [Hash{Symbol => RDF::Query::Variable}]
140
+ def variables
141
+ operands.first.
142
+ map(&:first).
143
+ map(&:variables).
144
+ inject(operands.last.variables) {|memo, h| memo.merge(h)}
145
+ end
146
+
147
+ ##
148
+ #
149
+ # Returns a partial SPARQL grammar for this operator.
150
+ #
151
+ # Extracts bindings.
152
+ #
153
+ # @return [String]
154
+ def to_sparql(**options)
155
+ extensions = operands.first.inject({}) do |memo, (as, expression)|
156
+ # Use string/name of variable "as" to aid in later matching
157
+ memo.merge(as.to_s => expression)
158
+ end
159
+
160
+ # Merge any inherited extensions from options
161
+ extensions = options.delete(:extensions).merge(extensions) if options.key?(:extensions)
162
+ operands.last.to_sparql(extensions: extensions, **options)
163
+ end
164
+ end # Extend
77
165
  end # Operator
78
166
  end; end # SPARQL::Algebra
@@ -3,11 +3,19 @@ module SPARQL; module Algebra
3
3
  ##
4
4
  # The SPARQL GraphPattern `filter` operator.
5
5
  #
6
- # @example
7
- # (select (?v)
8
- # (project (?v)
9
- # (filter (= ?v 2)
10
- # (bgp (triple ?s <http://example/p> ?v)))))
6
+ # [68] Filter ::= 'FILTER' Constraint
7
+ #
8
+ # @example SPARQL Grammar
9
+ # SELECT ?v
10
+ # {
11
+ # ?s <http://example/p> ?v
12
+ # FILTER(?v = 2)
13
+ # }
14
+ #
15
+ # @example SSE
16
+ # (project (?v)
17
+ # (filter (= ?v 2)
18
+ # (bgp (triple ?s <http://example/p> ?v))))
11
19
  #
12
20
  # @see https://www.w3.org/TR/sparql11-query/#evaluation
13
21
  class Filter < Operator::Binary
@@ -69,6 +77,20 @@ module SPARQL; module Algebra
69
77
  end
70
78
  self
71
79
  end
80
+
81
+ ##
82
+ #
83
+ # Returns a partial SPARQL grammar for this operator.
84
+ #
85
+ # Provides filters to descendant query.
86
+ #
87
+ # If filter operation is an Exprlist, then separate into multiple filter ops.
88
+ #
89
+ # @return [String]
90
+ def to_sparql(**options)
91
+ filter_ops = operands.first.is_a?(Operator::Exprlist) ? operands.first.operands : [operands.first]
92
+ str = operands.last.to_sparql(filter_ops: filter_ops, **options)
93
+ end
72
94
  end # Filter
73
95
  end # Operator
74
96
  end; end # SPARQL::Algebra
@@ -3,8 +3,22 @@ module SPARQL; module Algebra
3
3
  ##
4
4
  # The SPARQL logical `floor` operator.
5
5
  #
6
- # @example
7
- # (floor ?x)
6
+ # [121] BuiltInCall ::= ... 'FLOOR' '(' Expression ')'
7
+ #
8
+ # @example SPARQL Grammar
9
+ # PREFIX : <http://example.org/>
10
+ # PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
11
+ # SELECT ?s ?num (FLOOR(?num) AS ?floor) WHERE {
12
+ # ?s :num ?num
13
+ # }
14
+ #
15
+ # @example SSE
16
+ # (prefix
17
+ # ((: <http://example.org/>)
18
+ # (xsd: <http://www.w3.org/2001/XMLSchema#>))
19
+ # (project (?s ?num ?floor)
20
+ # (extend ((?floor (floor ?num)))
21
+ # (bgp (triple ?s :num ?num)))))
8
22
  #
9
23
  # @see https://www.w3.org/TR/sparql11-query/#func-floor
10
24
  # @see https://www.w3.org/TR/xpath-functions/#func-floor
@@ -30,6 +44,15 @@ module SPARQL; module Algebra
30
44
  else raise TypeError, "expected an RDF::Literal::Numeric, but got #{operand.inspect}"
31
45
  end
32
46
  end
47
+
48
+ ##
49
+ #
50
+ # Returns a partial SPARQL grammar for this operator.
51
+ #
52
+ # @return [String]
53
+ def to_sparql(**options)
54
+ "FLOOR(#{operands.to_sparql(**options)})"
55
+ end
33
56
  end # Floor
34
57
  end # Operator
35
58
  end; end # SPARQL::Algebra
@@ -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
@@ -5,15 +5,63 @@ module SPARQL; module Algebra
5
5
  #
6
6
  # This is a wrapper to add a `graph_name` to the query, or an array of statements.
7
7
  #
8
- # @example of a query
8
+ # [58] GraphGraphPattern ::= 'GRAPH' VarOrIri GroupGraphPattern
9
+ #
10
+ # @example SPARQL Grammar (query)
11
+ # PREFIX : <http://example/>
12
+ # SELECT * {
13
+ # GRAPH ?g { ?s ?p ?o }
14
+ # }
15
+ #
16
+ # @example SSE
9
17
  # (prefix ((: <http://example/>))
10
- # (graph ?g
11
- # (bgp (triple ?s ?p ?o))))
18
+ # (graph ?g
19
+ # (bgp (triple ?s ?p ?o))))
20
+ #
21
+ # @example SPARQL Grammar (named set of statements)
22
+ # PREFIX : <http://example/>
23
+ # SELECT * {
24
+ # GRAPH :g { :s :p :o }
25
+ # }
12
26
  #
13
- # @example named set of statements
27
+ # @example SSE (named set of statements)
14
28
  # (prefix ((: <http://example/>))
15
- # (graph :g
16
- # ((triple :s :p :o))))
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))
45
+ # (graph ?g
46
+ # (join
47
+ # (bgp (triple :x :b ?a))
48
+ # (graph ?g2
49
+ # (bgp (triple :x :p ?x)))))))
50
+ #
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))))
17
65
  #
18
66
  # @see https://www.w3.org/TR/sparql11-query/#sparqlAlgebra
19
67
  class Graph < Operator::Binary
@@ -76,6 +124,21 @@ module SPARQL; module Algebra
76
124
  def rewrite(&block)
77
125
  self
78
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
79
142
  end # Graph
80
143
  end # Operator
81
144
  end; end # SPARQL::Algebra
@@ -3,8 +3,18 @@ module SPARQL; module Algebra
3
3
  ##
4
4
  # The SPARQL relational `>` (greater than) comparison operator.
5
5
  #
6
- # @example
7
- # (> ?x ?y)
6
+ # [114] RelationalExpression ::= NumericExpression ('>' NumericExpression)?
7
+ #
8
+ # @example SPARQL Grammar
9
+ # PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
10
+ # PREFIX : <http://example.org/things#>
11
+ # SELECT ?x
12
+ # WHERE { ?x :p ?v . FILTER ( ?v > 1 ) }
13
+ #
14
+ # @example SSE
15
+ # (prefix
16
+ # ((xsd: <http://www.w3.org/2001/XMLSchema#>) (: <http://example.org/things#>))
17
+ # (project (?x) (filter (> ?v 1) (bgp (triple ?x :p ?v)))))
8
18
  #
9
19
  # @see https://www.w3.org/TR/sparql11-query/#OperatorMapping
10
20
  # @see https://www.w3.org/TR/xpath-functions/#func-compare
@@ -25,7 +35,6 @@ module SPARQL; module Algebra
25
35
  # @return [RDF::Literal::Boolean] `true` or `false`
26
36
  # @raise [TypeError] if either operand is not a literal
27
37
  def apply(left, right, **options)
28
- #require 'byebug'; byebug if super == RDF::Literal(1)
29
38
  RDF::Literal(super == RDF::Literal(1))
30
39
  end
31
40
  end # GreaterThan
@@ -4,8 +4,18 @@ module SPARQL; module Algebra
4
4
  # The SPARQL relational `>=` (greater than or equal) comparison
5
5
  # operator.
6
6
  #
7
- # @example
8
- # (>= ?x ?y)
7
+ # [114] RelationalExpression ::= NumericExpression ('>=' NumericExpression)?
8
+ #
9
+ # @example SPARQL Grammar
10
+ # PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
11
+ # PREFIX : <http://example.org/things#>
12
+ # SELECT ?x
13
+ # WHERE { ?x :p ?v . FILTER ( ?v >= 1 ) }
14
+ #
15
+ # @example SSE
16
+ # (prefix
17
+ # ((xsd: <http://www.w3.org/2001/XMLSchema#>) (: <http://example.org/things#>))
18
+ # (project (?x) (filter (>= ?v 1) (bgp (triple ?x :p ?v)))))
9
19
  #
10
20
  # @see https://www.w3.org/TR/sparql11-query/#OperatorMapping
11
21
  # @see https://www.w3.org/TR/xpath-functions/#func-compare
@@ -8,14 +8,53 @@ module SPARQL; module Algebra
8
8
  # query to be grouped. If three operands are provided,
9
9
  # the second is an array of temporary bindings.
10
10
  #
11
- # @example
12
- # (prefix ((: <http://example/>))
13
- # (project (?w ?S)
14
- # (extend ((?S ??.0))
15
- # (group (?w) ((??.0 (sample ?v)))
16
- # (leftjoin
17
- # (bgp (triple ?s :p ?v))
18
- # (bgp (triple ?s :q ?w)))))))
11
+ # [19] GroupClause ::= 'GROUP' 'BY' GroupCondition+
12
+ #
13
+ # @example SPARQL Grammar
14
+ # PREFIX : <http://www.example.org>
15
+ #
16
+ # SELECT ?P (COUNT(?O) AS ?C)
17
+ # WHERE { ?S ?P ?O }
18
+ # GROUP BY ?P
19
+ #
20
+ # @example SSE
21
+ # (prefix
22
+ # ((: <http://www.example.org>))
23
+ # (project (?P ?C)
24
+ # (extend ((?C ??.0))
25
+ # (group (?P) ((??.0 (count ?O)))
26
+ # (bgp (triple ?S ?P ?O))))))
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-trivial 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-trivial 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)))) ))
19
58
  #
20
59
  # @see https://www.w3.org/TR/sparql11-query/#sparqlAlgebra
21
60
  class Group < Operator
@@ -122,6 +161,92 @@ module SPARQL; module Algebra
122
161
  end
123
162
  super
124
163
  end
164
+
165
+ ##
166
+ # The variables used in the extension.
167
+ # Includes grouped variables and temporary, but not those in the query, itself
168
+ #
169
+ # @return [Hash{Symbol => RDF::Query::Variable}]
170
+ def variables
171
+ group_vars = operands.first
172
+
173
+ aggregate_vars = (operands.length == 3 ? operand(1) : [])
174
+
175
+ # Extract first element of each and merge it's variables
176
+ (group_vars + aggregate_vars).
177
+ map do |o|
178
+ v = Array(o).first
179
+ v if v.is_a?(RDF::Query::Variable)
180
+ end.compact.
181
+ map(&:variables).
182
+ inject({}) {|memo, h| memo.merge(h)}
183
+ end
184
+
185
+ ##
186
+ # The variables used within the query
187
+ #
188
+ # @return [Hash{Symbol => RDF::Query::Variable}]
189
+ def internal_variables
190
+ operands.last.variables
191
+ end
192
+
193
+ ##
194
+ #
195
+ # Returns a partial SPARQL grammar for this operator.
196
+ #
197
+ # @param [Hash{String => Operator}] extensions
198
+ # Variable bindings
199
+ # @param [Array<Operator>] filter_ops ([])
200
+ # Filter Operations
201
+ # @return [String]
202
+ def to_sparql(extensions: {}, filter_ops: [], **options)
203
+ having_ops = []
204
+ if operands.length > 2
205
+ temp_bindings = operands[1].inject({}) {|memo, (var, op)| memo.merge(var => op)}
206
+ # Replace extensions from temporary bindings
207
+ temp_bindings.each do |var, op|
208
+ # Update extensions using a temporarily bound variable with its binding
209
+ extensions = extensions.inject({}) do |memo, (ext_var, ext_op)|
210
+ if ext_op.is_a?(Operator)
211
+ # Try to recursivley replace variable within operator
212
+ new_op = ext_op.deep_dup.rewrite do |operand|
213
+ if operand.is_a?(Variable) && operand.to_sym == var.to_sym
214
+ op.dup
215
+ else
216
+ operand
217
+ end
218
+ end
219
+ memo.merge(ext_var.to_s => new_op)
220
+ elsif ext_op.is_a?(Variable) && ext_op.to_sym == var.to_sym
221
+ memo.merge(ext_var.to_s => op)
222
+ else
223
+ # Doesn't match this variable, so don't change
224
+ memo.merge(ext_var.to_s => ext_op)
225
+ end
226
+ end
227
+
228
+ # Filter ops using temporary bindinds are used for HAVING clauses
229
+ filter_ops.each do |fop|
230
+ having_ops << fop if fop.descendants.include?(var) && !having_ops.include?(fop)
231
+ end
232
+ end
233
+
234
+ # If used in a HAVING clause, it's not also a filter
235
+ filter_ops -= having_ops
236
+
237
+ # Replace each operand in having using var with it's corresponding operation
238
+ having_ops = having_ops.map do |op|
239
+ op.dup.rewrite do |operand|
240
+ # Rewrite based on temporary bindings
241
+ temp_bindings.fetch(operand, operand)
242
+ end
243
+ end
244
+ end
245
+ operands.last.to_sparql(extensions: extensions,
246
+ group_ops: operands.first,
247
+ having_ops: having_ops,
248
+ **options)
249
+ end
125
250
  end # Group
126
251
  end # Operator
127
252
  end; end # SPARQL::Algebra
@@ -5,13 +5,34 @@ module SPARQL; module Algebra
5
5
  #
6
6
  # GroupConcat is a set function which performs a string concatenation across the values of an expression with a group. The order of the strings is not specified. The separator character used in the concatenation may be given with the scalar argument SEPARATOR.
7
7
  #
8
- # @example
9
- # (prefix ((: <http://www.example.org/>))
10
- # (filter (|| (= ?g "1 22") (= ?g "22 1"))
11
- # (project (?g)
12
- # (extend ((?g ??.0))
13
- # (group () ((??.0 (group_concat ?o)))
14
- # (bgp (triple ??.0 :p1 ?o)))))))
8
+ # [127] Aggregate::= ... | 'GROUP_CONCAT' '(' 'DISTINCT'? Expression ( ';' 'SEPARATOR' '=' String )? ')'
9
+ #
10
+ # @example SPARQL Grammar
11
+ # SELECT (GROUP_CONCAT(?x) AS ?y) {}
12
+ #
13
+ # @example SSE
14
+ # (project (?y)
15
+ # (extend ((?y ??.0))
16
+ # (group () ((??.0 (group_concat ?x)))
17
+ # (bgp))))
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))))
15
36
  #
16
37
  # @see https://www.w3.org/TR/sparql11-query/#defn_aggGroupConcat
17
38
  class GroupConcat < Operator
@@ -53,6 +74,21 @@ module SPARQL; module Algebra
53
74
  def apply(enum, separator, **options)
54
75
  RDF::Literal(enum.flatten.map(&:to_s).join(separator.to_s))
55
76
  end
77
+
78
+ ##
79
+ #
80
+ # Returns a partial SPARQL grammar for this operator.
81
+ #
82
+ # @return [String]
83
+ def to_sparql(**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 << ")"
91
+ end
56
92
  end # GroupConcat
57
93
  end # Operator
58
94
  end; end # SPARQL::Algebra
@@ -3,11 +3,20 @@ module SPARQL; module Algebra
3
3
  ##
4
4
  # The SPARQL logical `hours` operator.
5
5
  #
6
- # @example
7
- # (prefix ((: <http://example.org/>))
8
- # (project (?s ?x)
9
- # (extend ((?x (hours ?date)))
10
- # (bgp (triple ?s :date ?date)))))
6
+ # [121] BuiltInCall ::= ... | 'HOURS' '(' Expression ')'
7
+ #
8
+ # @example SPARQL Grammar
9
+ # PREFIX : <http://example.org/>
10
+ # SELECT ?s (HOURS(?date) AS ?x) WHERE {
11
+ # ?s :date ?date
12
+ # }
13
+ #
14
+ # @example SSE
15
+ # (prefix
16
+ # ((: <http://example.org/>))
17
+ # (project (?s ?x)
18
+ # (extend ((?x (hours ?date)))
19
+ # (bgp (triple ?s :date ?date)))))
11
20
  #
12
21
  # @see https://www.w3.org/TR/sparql11-query/#func-hours
13
22
  class Hours < Operator::Unary
@@ -20,12 +29,21 @@ module SPARQL; module Algebra
20
29
  #
21
30
  # @param [RDF::Literal] operand
22
31
  # the operand
23
- # @return [RDF::Literal]
32
+ # @return [RDF::Literal::Temporal]
24
33
  # @raise [TypeError] if the operand is not a simple literal
25
34
  def apply(operand, **options)
26
- raise TypeError, "expected an RDF::Literal::DateTime, but got #{operand.inspect}" unless operand.is_a?(RDF::Literal::DateTime)
35
+ raise TypeError, "expected an RDF::Literal::Temporal, but got #{operand.inspect}" unless operand.is_a?(RDF::Literal::Temporal)
27
36
  RDF::Literal(operand.object.hour)
28
37
  end
38
+
39
+ ##
40
+ #
41
+ # Returns a partial SPARQL grammar for this operator.
42
+ #
43
+ # @return [String]
44
+ def to_sparql(**options)
45
+ "HOURS(#{operands.last.to_sparql(**options)})"
46
+ end
29
47
  end # Hours
30
48
  end # Operator
31
49
  end; end # SPARQL::Algebra