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
@@ -6,6 +6,19 @@ module SPARQL; module Algebra
6
6
  module Expression
7
7
  include RDF::Util::Logger
8
8
 
9
+ # Operators for which `:triple` denotes a pattern, not a builtin
10
+ PATTERN_PARENTS = [
11
+ Operator::BGP,
12
+ Operator::Construct,
13
+ Operator::Delete,
14
+ Operator::DeleteData,
15
+ Operator::DeleteWhere,
16
+ Operator::Graph,
17
+ Operator::Insert,
18
+ Operator::InsertData,
19
+ Operator::Path,
20
+ ].freeze
21
+
9
22
  ##
10
23
  # @example
11
24
  # Expression.parse('(isLiteral 3.1415)')
@@ -66,9 +79,11 @@ module SPARQL; module Algebra
66
79
  #
67
80
  # @param [Array] sse
68
81
  # a SPARQL S-Expression (SSE) form
82
+ # @param [Hash{Symbol => Object}] options
83
+ # any additional options (see {Operator#initialize})
69
84
  # @return [Expression]
70
- def self.for(*sse)
71
- self.new(sse)
85
+ def self.for(*sse, **options)
86
+ self.new(sse, **options)
72
87
  end
73
88
  class << self; alias_method :[], :for; end
74
89
 
@@ -82,20 +97,27 @@ module SPARQL; module Algebra
82
97
  # any additional options (see {Operator#initialize})
83
98
  # @return [Expression]
84
99
  # @raise [TypeError] if any of the operands is invalid
85
- def self.new(sse, **options)
100
+ def self.new(sse, parent_operator: nil, **options)
86
101
  raise ArgumentError, "invalid SPARQL::Algebra::Expression form: #{sse.inspect}" unless sse.is_a?(Array)
87
102
 
88
103
  operator = Operator.for(sse.first, sse.length - 1)
104
+
105
+ # If we don't find an operator, and sse.first is an extension IRI, use a function call
106
+ if !operator && sse.first.is_a?(RDF::URI) && self.extension?(sse.first)
107
+ operator = Operator.for(:function_call, sse.length)
108
+ sse.unshift(:function_call)
109
+ end
110
+
89
111
  unless operator
90
112
  return case sse.first
91
113
  when Array
92
114
  debug(options) {"Map array elements #{sse}"}
93
- sse.map {|s| self.new(s, depth: options[:depth].to_i + 1, **options)}
115
+ sse.map {|s| self.new(s, parent_operator: parent_operator, depth: options[:depth].to_i + 1, **options)}
94
116
  else
95
117
  debug(options) {"No operator found for #{sse.first}"}
96
118
  sse.map do |s|
97
119
  s.is_a?(Array) ?
98
- self.new(s, depth: options[:depth].to_i + 1) :
120
+ self.new(s, parent_operator: parent_operator, depth: options[:depth].to_i + 1) :
99
121
  s
100
122
  end
101
123
  end
@@ -105,7 +127,7 @@ module SPARQL; module Algebra
105
127
  debug(options) {"Operator=#{operator.inspect}, Operand=#{operand.inspect}"}
106
128
  case operand
107
129
  when Array
108
- self.new(operand, depth: options[:depth].to_i + 1, **options)
130
+ self.new(operand, parent_operator: operator, depth: options[:depth].to_i + 1, **options)
109
131
  when Operator, Variable, RDF::Term, RDF::Query, Symbol
110
132
  operand
111
133
  when TrueClass, FalseClass, Numeric, String, DateTime, Date, Time
@@ -115,11 +137,22 @@ module SPARQL; module Algebra
115
137
  end
116
138
 
117
139
  debug(options) {"#{operator.inspect}(#{operands.map(&:inspect).join(',')})"}
140
+ logger = options[:logger]
118
141
  options.delete_if {|k, v| [:debug, :logger, :depth, :prefixes, :base_uri, :update, :validate].include?(k) }
119
142
  begin
120
- operator.new(*operands, **options)
143
+ # Due to confusiong over (triple) and special-case for (qtriple)
144
+ if operator == RDF::Query::Pattern
145
+ options = options.merge(quoted: true) if sse.first == :qtriple
146
+ elsif operator == Operator::Triple && PATTERN_PARENTS.include?(parent_operator)
147
+ operator = RDF::Query::Pattern
148
+ end
149
+ operator.new(*operands, parent_operator: operator, **options)
121
150
  rescue ArgumentError => e
122
- error(options) {"Operator=#{operator.inspect}: #{e}"}
151
+ if logger
152
+ logger.error("Operator=#{operator.inspect}: #{e}")
153
+ else
154
+ raise "Operator=#{operator.inspect}: #{e}"
155
+ end
123
156
  end
124
157
  end
125
158
 
@@ -163,6 +196,17 @@ module SPARQL; module Algebra
163
196
  @extensions ||= {}
164
197
  end
165
198
 
199
+ ##
200
+ # Is an extension function available?
201
+ #
202
+ # It's either a registered extension, or an XSD casting function
203
+ #
204
+ # @param [RDF::URI] function
205
+ # @return [Boolean]
206
+ def self.extension?(function)
207
+ function.to_s.start_with?(RDF::XSD.to_s) || self.extensions[function]
208
+ end
209
+
166
210
  ##
167
211
  # Invoke an extension function.
168
212
  #
@@ -190,7 +234,7 @@ module SPARQL; module Algebra
190
234
  #
191
235
  # @param [RDF::URI] datatype
192
236
  # Datatype to evaluate, one of:
193
- # xsd:integer, xsd:decimal xsd:float, xsd:double, xsd:string, xsd:boolean, or xsd:dateTime
237
+ # xsd:integer, xsd:decimal xsd:float, xsd:double, xsd:string, xsd:boolean, xsd:dateTime, xsd:duration, xsd:dayTimeDuration, xsd:yearMonthDuration
194
238
  # @param [RDF::Term] value
195
239
  # Value, which should be a typed literal, where the type must be that specified
196
240
  # @raise [TypeError] if datatype is not a URI or value cannot be cast to datatype
@@ -198,7 +242,7 @@ module SPARQL; module Algebra
198
242
  # @see https://www.w3.org/TR/sparql11-query/#FunctionMapping
199
243
  def self.cast(datatype, value)
200
244
  case datatype
201
- when RDF::XSD.dateTime
245
+ when RDF::XSD.date, RDF::XSD.time, RDF::XSD.dateTime
202
246
  case value
203
247
  when RDF::Literal::DateTime, RDF::Literal::Date, RDF::Literal::Time
204
248
  RDF::Literal.new(value, datatype: datatype)
@@ -207,6 +251,15 @@ module SPARQL; module Algebra
207
251
  else
208
252
  RDF::Literal.new(value.value, datatype: datatype, validate: true)
209
253
  end
254
+ when RDF::XSD.duration, RDF::XSD.dayTimeDuration, RDF::XSD.yearMonthDuration
255
+ case value
256
+ when RDF::Literal::Duration, RDF::Literal::DayTimeDuration, RDF::Literal::YearMonthDuration
257
+ RDF::Literal.new(value, datatype: datatype, validate: true, canonicalize: true)
258
+ when RDF::Literal::Numeric, RDF::Literal::Boolean, RDF::URI, RDF::Node
259
+ raise TypeError, "Value #{value.inspect} cannot be cast as #{datatype}"
260
+ else
261
+ RDF::Literal.new(value.value, datatype: datatype, validate: true, canonicalize: true)
262
+ end
210
263
  when RDF::XSD.float, RDF::XSD.double
211
264
  case value
212
265
  when RDF::Literal::Boolean
@@ -43,6 +43,15 @@ class Object
43
43
  def deep_dup
44
44
  dup
45
45
  end
46
+
47
+ ##
48
+ #
49
+ # Returns a partial SPARQL grammar for this term.
50
+ #
51
+ # @return [String]
52
+ def to_sparql(**options)
53
+ to_sxp(**options)
54
+ end
46
55
  end
47
56
 
48
57
  ##
@@ -57,20 +66,14 @@ class Array
57
66
  end
58
67
 
59
68
  ##
60
- # Evaluates the array using the given variable `bindings`.
61
69
  #
62
- # In this case, the Array has two elements, the first of which is
63
- # an XSD datatype, and the second is the expression to be evaluated.
64
- # The result is cast as a literal of the appropriate type
70
+ # Returns a partial SPARQL grammar for this array.
65
71
  #
66
- # @param [RDF::Query::Solution] bindings
67
- # a query solution containing zero or more variable bindings
68
- # @param [Hash{Symbol => Object}] options ({})
69
- # options passed from query
70
- # @return [RDF::Term]
71
- # @see SPARQL::Algebra::Expression.evaluate
72
- def evaluate(bindings, **options)
73
- SPARQL::Algebra::Expression.extension(*self.map {|o| o.evaluate(bindings, **options)})
72
+ # @param [String] delimiter (" ")
73
+ # If the first element is an IRI, treat it as an extension function
74
+ # @return [String]
75
+ def to_sparql(delimiter: " ", **options)
76
+ map {|e| e.to_sparql(**options)}.join(delimiter)
74
77
  end
75
78
 
76
79
  ##
@@ -121,6 +124,14 @@ class Array
121
124
  end
122
125
  def constant?; !(variable?); end
123
126
 
127
+ ##
128
+ # The variables used in this array.
129
+ #
130
+ # @return [Hash{Symbol => RDF::Query::Variable}]
131
+ def variables
132
+ self.inject({}) {|hash, o| o.respond_to?(:variables) ? hash.merge(o.variables) : hash}
133
+ end
134
+
124
135
  ##
125
136
  # Does this contain any nodes?
126
137
  #
@@ -216,15 +227,6 @@ end
216
227
  ##
217
228
  # Extensions for Ruby's `Hash` class.
218
229
  class Hash
219
- ##
220
- # Returns the SXP representation of this object, defaults to `self`.
221
- #
222
- # @return [String]
223
- def to_sxp_bin
224
- to_a.to_sxp_bin
225
- end
226
- def to_sxp; to_sxp_bin; end
227
-
228
230
  ##
229
231
  # A duplicate of this hash.
230
232
  #
@@ -278,24 +280,19 @@ module RDF::Term
278
280
  # @see SPARQL::Algebra::Expression#optimize
279
281
  def optimize(**options)
280
282
  optimized = self.deep_dup
281
- optimized.lexical = nil if optimized.respond_to?(:lexical=)
282
- optimized
283
+ #optimized.lexical = nil if optimized.respond_to?(:lexical=)
284
+ #optimized
283
285
  end
284
- end # RDF::Term
285
286
 
286
- class RDF::Literal::Double
287
287
  ##
288
- # Returns the SXP representation of this object.
288
+ #
289
+ # Returns a partial SPARQL grammar for this term.
289
290
  #
290
291
  # @return [String]
291
- def to_sxp
292
- case
293
- when nan? then 'nan.0'
294
- when infinite? then (infinite? > 0 ? '+inf.0' : '-inf.0')
295
- else canonicalize.to_s.downcase
296
- end
292
+ def to_sparql(**options)
293
+ to_sxp(**options)
297
294
  end
298
- end
295
+ end # RDF::Term
299
296
 
300
297
  # Override RDF::Queryable to execute against SPARQL::Algebra::Query elements as well as RDF::Query and RDF::Pattern
301
298
  module RDF::Queryable
@@ -343,13 +340,22 @@ module RDF::Queryable
343
340
  query_without_sparql(pattern, **options, &block)
344
341
  end
345
342
  end
343
+
344
+ ##
345
+ #
346
+ # Returns a partial SPARQL grammar for this term.
347
+ #
348
+ # @return [String]
349
+ def to_sparql(**options)
350
+ raise NotImplementedError, "SPARQL::Algebra '#{first}' operator not implemented"
351
+ end
346
352
  end
347
353
 
348
354
  class RDF::Statement
349
355
  # Transform Statement Pattern into an SXP
350
356
  # @return [Array]
351
357
  def to_sxp_bin
352
- [ (has_graph? ? :quad : :triple),
358
+ [ (has_graph? ? :quad : (quoted? ? :qtriple : :triple)),
353
359
  (:inferred if inferred?),
354
360
  subject,
355
361
  predicate,
@@ -361,9 +367,21 @@ class RDF::Statement
361
367
  ##
362
368
  # Returns an S-Expression (SXP) representation
363
369
  #
370
+ # @param [Hash{Symbol => RDF::URI}] prefixes (nil)
371
+ # @param [RDF::URI] base_uri (nil)
372
+ # @return [String]
373
+ def to_sxp(prefixes: nil, base_uri: nil)
374
+ to_sxp_bin.to_sxp(prefixes: prefixes, base_uri: base_uri)
375
+ end
376
+
377
+ ##
378
+ #
379
+ # Returns a partial SPARQL grammar for this term.
380
+ #
364
381
  # @return [String]
365
- def to_sxp
366
- to_sxp_bin.to_sxp
382
+ def to_sparql(**options)
383
+ str = to_triple.map {|term| term.to_sparql(**options)}.join(" ")
384
+ quoted? ? ('<<' + str + '>>') : str
367
385
  end
368
386
 
369
387
  ##
@@ -411,6 +429,40 @@ class RDF::Query
411
429
  end
412
430
  end
413
431
 
432
+ ##
433
+ #
434
+ # Returns a partial SPARQL grammar for this query.
435
+ #
436
+ # @param [Boolean] top_level (true)
437
+ # Treat this as a top-level, generating SELECT ... WHERE {}
438
+ # @param [Array<Operator>] filter_ops ([])
439
+ # Filter Operations
440
+ # @return [String]
441
+ def to_sparql(top_level: true, filter_ops: [], **options)
442
+ str = @patterns.map do |e|
443
+ e.to_sparql(top_level: false, **options) + " . \n"
444
+ end.join("")
445
+ str = "GRAPH #{graph_name.to_sparql(**options)} {\n#{str}\n}\n" if graph_name
446
+ if top_level
447
+ SPARQL::Algebra::Operator.to_sparql(str, filter_ops: filter_ops, **options)
448
+ else
449
+ # Filters
450
+ filter_ops.each do |op|
451
+ str << "\nFILTER (#{op.to_sparql(**options)}) ."
452
+ end
453
+
454
+ # Extensons
455
+ extensions = options.fetch(:extensions, [])
456
+ extensions.each do |as, expression|
457
+ v = expression.to_sparql(**options)
458
+ pp = RDF::Query::Variable.new(as).to_sparql(**options)
459
+ str << "\nBIND (" << v << " AS " << pp << ") ."
460
+ end
461
+ str = "{#{str}}" unless filter_ops.empty? && extensions.empty?
462
+ str
463
+ end
464
+ end
465
+
414
466
  ##
415
467
  # Binds the pattern to a solution, making it no longer variable if all variables are resolved to bound variables
416
468
  #
@@ -462,11 +514,11 @@ class RDF::Query
462
514
  def optimize!(**options)
463
515
  @patterns = @patterns.map do |pattern|
464
516
  components = pattern.to_quad.map do |term|
465
- if term.respond_to?(:lexical=)
466
- term.dup.instance_eval {@lexical = nil; self}
467
- else
517
+ #if term.respond_to?(:lexical=)
518
+ # term.dup.instance_eval {@lexical = nil; self}
519
+ #else
468
520
  term
469
- end
521
+ #end
470
522
  end
471
523
  RDF::Query::Pattern.from(components, **pattern.options)
472
524
  end
@@ -530,11 +582,15 @@ class RDF::Query::Variable
530
582
  self
531
583
  end
532
584
 
533
- # Display variable as SXP
534
- # @return [Array]
535
- def to_sxp
536
- prefix = distinguished? ? (existential? ? '$' : '?') : (existential? ? '$$' : '??')
537
- unbound? ? "#{prefix}#{name}".to_sym.to_sxp : ["#{prefix}#{name}".to_sym, value].to_sxp
585
+ ##
586
+ #
587
+ # Returns a partial SPARQL grammar for this term.
588
+ #
589
+ # The Non-distinguished form (`??xxx`) is not part of the grammar, so replace with a blank-node
590
+ #
591
+ # @return [String]
592
+ def to_sparql(**options)
593
+ self.distinguished? ? super : "_:_nd#{self.name}"
538
594
  end
539
595
  end # RDF::Query::Variable
540
596
 
@@ -576,5 +632,13 @@ class RDF::Query::Solution
576
632
  def to_sxp_bin
577
633
  to_a.to_sxp_bin
578
634
  end
579
- def to_sxp; to_sxp_bin; end
635
+
636
+ # Transform Solution into an SXP
637
+ #
638
+ # @param [Hash{Symbol => RDF::URI}] prefixes (nil)
639
+ # @param [RDF::URI] base_uri (nil)
640
+ # @return [String]
641
+ def to_sxp(prefixes: nil, base_uri: nil)
642
+ to_sxp_bin.to_sxp(prefixes: prefixes, base_uri: base_uri)
643
+ end
580
644
  end # RDF::Query::Solution
@@ -3,8 +3,19 @@ module SPARQL; module Algebra
3
3
  ##
4
4
  # The SPARQL logical `abs` operator.
5
5
  #
6
- # @example
7
- # (abs ?x)
6
+ # [121] BuiltInCall ::= ... | 'ABS' '(' Expression ')'
7
+ #
8
+ # @example SPARQL Grammar
9
+ # PREFIX : <http://example.org/>
10
+ # SELECT * WHERE {
11
+ # ?s :num ?num
12
+ # FILTER(ABS(?num) >= 2)
13
+ # }
14
+ #
15
+ # @example SSE
16
+ # (prefix ((: <http://example.org/>))
17
+ # (filter (>= (abs ?num) 2)
18
+ # (bgp (triple ?s :num ?num))))
8
19
  #
9
20
  # @see https://www.w3.org/TR/sparql11-query/#func-abs
10
21
  # @see https://www.w3.org/TR/xpath-functions/#func-abs
@@ -26,6 +37,15 @@ module SPARQL; module Algebra
26
37
  else raise TypeError, "expected an RDF::Literal::Numeric, but got #{operand.inspect}"
27
38
  end
28
39
  end
40
+
41
+ ##
42
+ #
43
+ # Returns a partial SPARQL grammar for this operator.
44
+ #
45
+ # @return [String]
46
+ def to_sparql(**options)
47
+ "ABS(#{operands.first.to_sparql(**options)})"
48
+ end
29
49
  end # Abs
30
50
  end # Operator
31
51
  end; end # SPARQL::Algebra
@@ -6,8 +6,15 @@ module SPARQL; module Algebra
6
6
  #
7
7
  # The ADD operation is a shortcut for inserting all data from an input graph into a destination graph. Data from the input graph is not affected, and initial data from the destination graph, if any, is kept intact.
8
8
  #
9
- # @example
10
- # (add default <a>)
9
+ # [35] Add ::= "ADD" "SILENT"? GraphOrDefault "TO" GraphOrDefault
10
+ #
11
+ # @example SPARQL Update
12
+ # PREFIX : <http://example.org/>
13
+ # ADD DEFAULT TO :g1
14
+ #
15
+ # @example SSE
16
+ # (prefix ((: <http://example.org/>))
17
+ # (update (add default :g1)))
11
18
  #
12
19
  # @see https://www.w3.org/TR/sparql11-update/#add
13
20
  class Add < Operator
@@ -51,6 +58,18 @@ module SPARQL; module Algebra
51
58
  end
52
59
  queryable
53
60
  end
61
+
62
+ ##
63
+ #
64
+ # Returns a partial SPARQL grammar for this operator.
65
+ #
66
+ # @return [String]
67
+ def to_sparql(**options)
68
+ *args, last = operands.dup
69
+ args += [:TO, last]
70
+
71
+ "ADD " + args.to_sparql(**options)
72
+ end
54
73
  end # Add
55
74
  end # Operator
56
75
  end; end # SPARQL::Algebra
@@ -0,0 +1,69 @@
1
+ module SPARQL; module Algebra
2
+ class Operator
3
+ ##
4
+ # The SPARQL `adjust` operator.
5
+ #
6
+ # [121] BuiltInCall ::= ... | 'ADJUST' '(' Expression ',' Expression ')'
7
+ #
8
+ # @example SPARQL Grammar
9
+ # PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
10
+ # SELECT ?id (ADJUST(?d, ?tz) AS ?adjusted) WHERE {
11
+ # VALUES (?id ?tz ?d) {
12
+ # (1 "-PT10H"^^xsd:dayTimeDuration "2002-03-07"^^xsd:date)
13
+ # }
14
+ # }
15
+ #
16
+ # @example SSE
17
+ # (prefix ((xsd: <http://www.w3.org/2001/XMLSchema#>))
18
+ # (project (?id ?adjusted)
19
+ # (extend ((?adjusted (adjust ?d ?tz)))
20
+ # (table (vars ?id ?tz ?d)
21
+ # (row
22
+ # (?id 1)
23
+ # (?tz "-PT10H"^^xsd:dayTimeDuration)
24
+ # (?d "2002-03-07"^^xsd:date))))))
25
+ #
26
+ # @see https://www.w3.org/TR/sparql11-query/#func-abs
27
+ # @see https://www.w3.org/TR/xpath-functions/#func-abs
28
+ class Adjust < Operator::Binary
29
+ include Evaluatable
30
+
31
+ NAME = [:adjust]
32
+
33
+ ##
34
+ # Returns the first operand adjusted by the dayTimeDuration of the second operand
35
+ #
36
+ # @param [RDF::Literal::Temporal] operand
37
+ # the operand
38
+ # @param [RDF::Literal, String] duration
39
+ # the dayTimeDuration or an empty string.
40
+ # @return [RDF::Literal] literal of same type
41
+ # @raise [TypeError] if the operand is not a numeric value
42
+ def apply(operand, duration, **options)
43
+ case operand
44
+ when RDF::Literal::Temporal
45
+ case duration
46
+ when RDF::Literal::DayTimeDuration
47
+ operand.adjust_to_timezone(duration)
48
+ when RDF::Literal
49
+ raise TypeError, "expected second operand to be an empty literal, but got #{duration.inspect}" unless duration.to_s.empty?
50
+ operand.adjust_to_timezone(nil)
51
+ else
52
+ raise TypeError, "expected second operand to be an RDF::Literal::DayTimeDuration, but got #{duration.inspect}"
53
+ end
54
+ else
55
+ raise TypeError, "expected first operand to be an RDF::Literal::Temporal, but got #{operand.inspect}"
56
+ end
57
+ end
58
+
59
+ ##
60
+ #
61
+ # Returns a partial SPARQL grammar for this operator.
62
+ #
63
+ # @return [String]
64
+ def to_sparql(**options)
65
+ "ADJUST(#{operands.to_sparql(delimiter: ', ', **options)})"
66
+ end
67
+ end # Abs
68
+ end # Operator
69
+ end; end # SPARQL::Algebra
@@ -7,8 +7,23 @@ module SPARQL; module Algebra
7
7
  #
8
8
  # eval(Path(X, alt(P,Q), Y)) = Union(eval(Path(X, P, Y)), eval(Path(X, Q, Y)))
9
9
  #
10
- # @example
11
- # (alt a b)
10
+ # [89] PathAlternative ::= PathSequence ( '|' PathSequence )*
11
+ #
12
+ # @example SPARQL Query
13
+ # PREFIX : <http://www.example.org/>
14
+ # SELECT ?t
15
+ # WHERE {
16
+ # :a :p1|:p2/:p3|:p4 ?t
17
+ # }
18
+ #
19
+ # @example SSE
20
+ # (prefix ((: <http://www.example.org/>))
21
+ # (project (?t)
22
+ # (path :a
23
+ # (alt
24
+ # (alt :p1 (seq :p2 :p3))
25
+ # :p4)
26
+ # ?t)))
12
27
  #
13
28
  # @see https://www.w3.org/TR/sparql11-query/#defn_evalPP_alternative
14
29
  class Alt < Operator::Binary
@@ -57,6 +72,15 @@ module SPARQL; module Algebra
57
72
  query = Union.new(qa, qb)
58
73
  queryable.query(query, depth: options[:depth].to_i + 1, **options, &block)
59
74
  end
75
+
76
+ ##
77
+ #
78
+ # Returns a partial SPARQL grammar for this operator.
79
+ #
80
+ # @return [String]
81
+ def to_sparql(**options)
82
+ "(#{operands.first.to_sparql(**options)}|#{operands.last.to_sparql(**options)})"
83
+ end
60
84
  end # Alt
61
85
  end # Operator
62
86
  end; end # SPARQL::Algebra
@@ -3,9 +3,22 @@ module SPARQL; module Algebra
3
3
  ##
4
4
  # The SPARQL logical `and` operator.
5
5
  #
6
- # @example
7
- # (&& ?x ?y)
8
- # (and ?x ?y)
6
+ # [112] ConditionalAndExpression::= ValueLogical ( '&&' ValueLogical )*
7
+ #
8
+ # @example SPARQL Grammar
9
+ # PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
10
+ # PREFIX : <http://example.org/ns#>
11
+ # SELECT ?a
12
+ # WHERE { ?a :p ?v .
13
+ # FILTER ("true"^^xsd:boolean && ?v) .
14
+ # }
15
+ #
16
+ # @example SSE
17
+ # (prefix
18
+ # ((xsd: <http://www.w3.org/2001/XMLSchema#>) (: <http://example.org/ns#>))
19
+ # (project (?a)
20
+ # (filter (&& true ?v)
21
+ # (bgp (triple ?a :p ?v)))))
9
22
  #
10
23
  # @see https://www.w3.org/TR/sparql11-query/#func-logical-and
11
24
  # @see https://www.w3.org/TR/sparql11-query/#evaluation
@@ -60,6 +73,15 @@ module SPARQL; module Algebra
60
73
  else RDF::Literal(left && right)
61
74
  end
62
75
  end
76
+
77
+ ##
78
+ #
79
+ # Returns a partial SPARQL grammar for this operator.
80
+ #
81
+ # @return [String]
82
+ def to_sparql(**options)
83
+ "(#{operands.first.to_sparql(**options)} && #{operands.last.to_sparql(**options)})"
84
+ end
63
85
  end # And
64
86
  end # Operator
65
87
  end; end # SPARQL::Algebra
@@ -3,7 +3,15 @@ module SPARQL; module Algebra
3
3
  ##
4
4
  # The SPARQL ascending sort operator.
5
5
  #
6
- # @example
6
+ # [24] OrderCondition ::= ( ( 'ASC' | 'DESC' ) BrackettedExpression ) | ( Constraint | Var )
7
+ #
8
+ # @example SPARQL Query
9
+ # PREFIX foaf: <http://xmlns.com/foaf/0.1/>
10
+ # SELECT ?name
11
+ # WHERE { ?x foaf:name ?name }
12
+ # ORDER BY ASC(?name)
13
+ #
14
+ # @example SSE
7
15
  # (prefix ((foaf: <http://xmlns.com/foaf/0.1/>))
8
16
  # (project (?name)
9
17
  # (order ((asc ?name))
@@ -27,6 +35,17 @@ module SPARQL; module Algebra
27
35
  def evaluate(bindings, **options)
28
36
  operand(0).evaluate(bindings, depth: options[:depth].to_i + 1, **options)
29
37
  end
38
+
39
+ ##
40
+ #
41
+ # Returns a partial SPARQL grammar for this operator.
42
+ #
43
+ # Provides order to descendant query.
44
+ #
45
+ # @return [String]
46
+ def to_sparql(**options)
47
+ "ASC(#{operands.last.to_sparql(**options)})"
48
+ end
30
49
  end # Asc
31
50
  end # Operator
32
51
  end; end # SPARQL::Algebra
@@ -5,7 +5,13 @@ module SPARQL; module Algebra
5
5
  #
6
6
  # Applications can use the ASK form to test whether or not a query pattern has a solution. No information is returned about the possible query solutions, just whether or not a solution exists.
7
7
  #
8
- # @example
8
+ # [12] AskQuery ::= 'ASK' DatasetClause* WhereClause ValuesClause
9
+ #
10
+ # @example SPARQL Query
11
+ # PREFIX : <http://example/>
12
+ # ASK { :x :p ?x }
13
+ #
14
+ # @example SSE
9
15
  # (prefix ((: <http://example/>))
10
16
  # (ask
11
17
  # (bgp (triple :x :p ?x))))
@@ -41,6 +47,16 @@ module SPARQL; module Algebra
41
47
  def query_yields_boolean?
42
48
  true
43
49
  end
50
+
51
+ ##
52
+ #
53
+ # Returns a partial SPARQL grammar for this term.
54
+ #
55
+ # @return [String]
56
+ def to_sparql(**options)
57
+ "ASK\n" +
58
+ operands.first.to_sparql(top_level: true, project: nil, **options)
59
+ end
44
60
  end # Ask
45
61
  end # Operator
46
62
  end; end # SPARQL::Algebra