sparql 3.1.0 → 3.1.2

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 (149) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +183 -69
  3. data/UNLICENSE +1 -1
  4. data/VERSION +1 -1
  5. data/bin/sparql +20 -9
  6. data/lib/rack/sparql/conneg.rb +2 -2
  7. data/lib/sinatra/sparql.rb +3 -3
  8. data/lib/sparql.rb +8 -6
  9. data/lib/sparql/algebra.rb +7 -15
  10. data/lib/sparql/algebra/aggregate.rb +2 -2
  11. data/lib/sparql/algebra/expression.rb +22 -8
  12. data/lib/sparql/algebra/extensions.rb +135 -3
  13. data/lib/sparql/algebra/operator.rb +37 -9
  14. data/lib/sparql/algebra/operator/abs.rb +2 -2
  15. data/lib/sparql/algebra/operator/add.rb +2 -2
  16. data/lib/sparql/algebra/operator/alt.rb +2 -2
  17. data/lib/sparql/algebra/operator/and.rb +3 -3
  18. data/lib/sparql/algebra/operator/asc.rb +1 -1
  19. data/lib/sparql/algebra/operator/ask.rb +2 -12
  20. data/lib/sparql/algebra/operator/avg.rb +1 -1
  21. data/lib/sparql/algebra/operator/base.rb +8 -8
  22. data/lib/sparql/algebra/operator/bgp.rb +1 -1
  23. data/lib/sparql/algebra/operator/bnode.rb +2 -2
  24. data/lib/sparql/algebra/operator/bound.rb +1 -1
  25. data/lib/sparql/algebra/operator/ceil.rb +2 -2
  26. data/lib/sparql/algebra/operator/clear.rb +2 -2
  27. data/lib/sparql/algebra/operator/coalesce.rb +1 -11
  28. data/lib/sparql/algebra/operator/compare.rb +8 -8
  29. data/lib/sparql/algebra/operator/concat.rb +2 -2
  30. data/lib/sparql/algebra/operator/construct.rb +2 -12
  31. data/lib/sparql/algebra/operator/contains.rb +2 -2
  32. data/lib/sparql/algebra/operator/copy.rb +2 -2
  33. data/lib/sparql/algebra/operator/count.rb +1 -1
  34. data/lib/sparql/algebra/operator/create.rb +2 -2
  35. data/lib/sparql/algebra/operator/dataset.rb +2 -13
  36. data/lib/sparql/algebra/operator/datatype.rb +1 -1
  37. data/lib/sparql/algebra/operator/day.rb +1 -1
  38. data/lib/sparql/algebra/operator/delete.rb +4 -4
  39. data/lib/sparql/algebra/operator/delete_data.rb +2 -2
  40. data/lib/sparql/algebra/operator/delete_where.rb +2 -2
  41. data/lib/sparql/algebra/operator/desc.rb +1 -1
  42. data/lib/sparql/algebra/operator/describe.rb +2 -12
  43. data/lib/sparql/algebra/operator/distinct.rb +2 -12
  44. data/lib/sparql/algebra/operator/divide.rb +1 -1
  45. data/lib/sparql/algebra/operator/drop.rb +2 -2
  46. data/lib/sparql/algebra/operator/encode_for_uri.rb +2 -2
  47. data/lib/sparql/algebra/operator/equal.rb +2 -2
  48. data/lib/sparql/algebra/operator/exists.rb +1 -1
  49. data/lib/sparql/algebra/operator/exprlist.rb +1 -11
  50. data/lib/sparql/algebra/operator/extend.rb +14 -13
  51. data/lib/sparql/algebra/operator/filter.rb +3 -13
  52. data/lib/sparql/algebra/operator/floor.rb +2 -2
  53. data/lib/sparql/algebra/operator/graph.rb +3 -14
  54. data/lib/sparql/algebra/operator/greater_than.rb +5 -5
  55. data/lib/sparql/algebra/operator/greater_than_or_equal.rb +5 -5
  56. data/lib/sparql/algebra/operator/group.rb +14 -14
  57. data/lib/sparql/algebra/operator/group_concat.rb +1 -1
  58. data/lib/sparql/algebra/operator/hours.rb +1 -1
  59. data/lib/sparql/algebra/operator/if.rb +1 -11
  60. data/lib/sparql/algebra/operator/in.rb +1 -11
  61. data/lib/sparql/algebra/operator/insert.rb +3 -3
  62. data/lib/sparql/algebra/operator/insert_data.rb +2 -2
  63. data/lib/sparql/algebra/operator/iri.rb +1 -1
  64. data/lib/sparql/algebra/operator/is_blank.rb +1 -1
  65. data/lib/sparql/algebra/operator/is_iri.rb +1 -1
  66. data/lib/sparql/algebra/operator/is_literal.rb +1 -1
  67. data/lib/sparql/algebra/operator/is_numeric.rb +1 -1
  68. data/lib/sparql/algebra/operator/join.rb +9 -7
  69. data/lib/sparql/algebra/operator/lang.rb +1 -1
  70. data/lib/sparql/algebra/operator/lang_matches.rb +3 -3
  71. data/lib/sparql/algebra/operator/lcase.rb +2 -2
  72. data/lib/sparql/algebra/operator/left_join.rb +12 -9
  73. data/lib/sparql/algebra/operator/less_than.rb +5 -5
  74. data/lib/sparql/algebra/operator/less_than_or_equal.rb +5 -5
  75. data/lib/sparql/algebra/operator/load.rb +2 -2
  76. data/lib/sparql/algebra/operator/max.rb +1 -1
  77. data/lib/sparql/algebra/operator/md5.rb +1 -1
  78. data/lib/sparql/algebra/operator/min.rb +1 -1
  79. data/lib/sparql/algebra/operator/minus.rb +9 -8
  80. data/lib/sparql/algebra/operator/minutes.rb +1 -1
  81. data/lib/sparql/algebra/operator/modify.rb +1 -1
  82. data/lib/sparql/algebra/operator/month.rb +1 -1
  83. data/lib/sparql/algebra/operator/move.rb +2 -2
  84. data/lib/sparql/algebra/operator/multiply.rb +1 -1
  85. data/lib/sparql/algebra/operator/negate.rb +1 -1
  86. data/lib/sparql/algebra/operator/not.rb +1 -1
  87. data/lib/sparql/algebra/operator/not_equal.rb +2 -2
  88. data/lib/sparql/algebra/operator/notexists.rb +2 -2
  89. data/lib/sparql/algebra/operator/notin.rb +1 -11
  90. data/lib/sparql/algebra/operator/notoneof.rb +2 -2
  91. data/lib/sparql/algebra/operator/now.rb +1 -1
  92. data/lib/sparql/algebra/operator/or.rb +3 -3
  93. data/lib/sparql/algebra/operator/order.rb +2 -12
  94. data/lib/sparql/algebra/operator/path.rb +2 -2
  95. data/lib/sparql/algebra/operator/path_opt.rb +2 -2
  96. data/lib/sparql/algebra/operator/path_plus.rb +2 -2
  97. data/lib/sparql/algebra/operator/path_star.rb +2 -2
  98. data/lib/sparql/algebra/operator/plus.rb +2 -2
  99. data/lib/sparql/algebra/operator/prefix.rb +8 -8
  100. data/lib/sparql/algebra/operator/project.rb +2 -12
  101. data/lib/sparql/algebra/operator/rand.rb +1 -1
  102. data/lib/sparql/algebra/operator/reduced.rb +2 -12
  103. data/lib/sparql/algebra/operator/regex.rb +5 -5
  104. data/lib/sparql/algebra/operator/replace.rb +3 -3
  105. data/lib/sparql/algebra/operator/reverse.rb +2 -2
  106. data/lib/sparql/algebra/operator/round.rb +2 -2
  107. data/lib/sparql/algebra/operator/same_term.rb +8 -5
  108. data/lib/sparql/algebra/operator/sample.rb +1 -1
  109. data/lib/sparql/algebra/operator/seconds.rb +1 -1
  110. data/lib/sparql/algebra/operator/seq.rb +1 -1
  111. data/lib/sparql/algebra/operator/sequence.rb +1 -1
  112. data/lib/sparql/algebra/operator/sha1.rb +1 -1
  113. data/lib/sparql/algebra/operator/sha256.rb +1 -1
  114. data/lib/sparql/algebra/operator/sha384.rb +1 -1
  115. data/lib/sparql/algebra/operator/sha512.rb +1 -1
  116. data/lib/sparql/algebra/operator/slice.rb +2 -12
  117. data/lib/sparql/algebra/operator/str.rb +1 -1
  118. data/lib/sparql/algebra/operator/strafter.rb +2 -2
  119. data/lib/sparql/algebra/operator/strbefore.rb +2 -2
  120. data/lib/sparql/algebra/operator/strdt.rb +2 -2
  121. data/lib/sparql/algebra/operator/strends.rb +2 -2
  122. data/lib/sparql/algebra/operator/strlang.rb +2 -2
  123. data/lib/sparql/algebra/operator/strlen.rb +2 -2
  124. data/lib/sparql/algebra/operator/strstarts.rb +2 -2
  125. data/lib/sparql/algebra/operator/struuid.rb +1 -1
  126. data/lib/sparql/algebra/operator/substr.rb +3 -3
  127. data/lib/sparql/algebra/operator/subtract.rb +1 -1
  128. data/lib/sparql/algebra/operator/sum.rb +1 -1
  129. data/lib/sparql/algebra/operator/table.rb +2 -2
  130. data/lib/sparql/algebra/operator/timezone.rb +1 -1
  131. data/lib/sparql/algebra/operator/tz.rb +1 -1
  132. data/lib/sparql/algebra/operator/ucase.rb +2 -2
  133. data/lib/sparql/algebra/operator/union.rb +7 -6
  134. data/lib/sparql/algebra/operator/update.rb +2 -2
  135. data/lib/sparql/algebra/operator/using.rb +2 -2
  136. data/lib/sparql/algebra/operator/uuid.rb +1 -1
  137. data/lib/sparql/algebra/operator/with.rb +2 -2
  138. data/lib/sparql/algebra/operator/year.rb +1 -1
  139. data/lib/sparql/algebra/query.rb +1 -1
  140. data/lib/sparql/algebra/update.rb +1 -1
  141. data/lib/sparql/algebra/version.rb +1 -1
  142. data/lib/sparql/extensions.rb +1 -1
  143. data/lib/sparql/grammar.rb +104 -3
  144. data/lib/sparql/grammar/meta.rb +1372 -333
  145. data/lib/sparql/grammar/parser11.rb +27 -6
  146. data/lib/sparql/grammar/terminals11.rb +1 -0
  147. data/lib/sparql/results.rb +60 -41
  148. data/lib/sparql/version.rb +1 -1
  149. metadata +14 -8
@@ -8,7 +8,7 @@ module Sinatra
8
8
  #
9
9
  # To override negotiation on Content-Type, set :format in `sparql_options` to a RDF Format class, or symbol identifying a format.
10
10
  #
11
- # @see http://www.sinatrarb.com/extensions.html
11
+ # @see https://www.sinatrarb.com/extensions.html
12
12
  module SPARQL
13
13
  ##
14
14
  # Helper methods.
@@ -25,8 +25,8 @@ module Sinatra
25
25
  # URI of the service endpoint, defaults to "/sparql" in the current realm.
26
26
  # @return [RDF::Graph]
27
27
  #
28
- # @see http://www.w3.org/TR/sparql11-service-description
29
- # @see http://www.w3.org/TR/void/
28
+ # @see https://www.w3.org/TR/sparql11-service-description
29
+ # @see https://www.w3.org/TR/void/
30
30
  def service_description(**options)
31
31
  repository = options[:repository]
32
32
 
data/lib/sparql.rb CHANGED
@@ -3,14 +3,14 @@ require 'sparql/extensions'
3
3
  ##
4
4
  # A SPARQL for RDF.rb.
5
5
  #
6
- # @see http://www.w3.org/TR/sparql11-query
6
+ # @see https://www.w3.org/TR/sparql11-query
7
7
  module SPARQL
8
8
  autoload :Algebra, 'sparql/algebra'
9
9
  autoload :Grammar, 'sparql/grammar'
10
10
  autoload :Results, 'sparql/results'
11
11
  autoload :VERSION, 'sparql/version'
12
12
 
13
- # @see http://rubygems.org/gems/sparql-client
13
+ # @see https://rubygems-client
14
14
  autoload :Client, 'sparql/client'
15
15
 
16
16
  ##
@@ -52,12 +52,13 @@ module SPARQL
52
52
  # results = SPARQL.execute("SELECT * WHERE { ?s ?p ?o }", repository)
53
53
  #
54
54
  # @param [IO, StringIO, String, #to_s] query
55
+ # @param [RDF::Queryable] queryable
55
56
  # @param [Hash{Symbol => Object}] options
56
- # @option options [RDF::Queryable] :queryable
57
- # @option options [RDF::URI, String, Array<RDF::URI, String>] :load_datasets
58
- # One or more URIs used to initialize a new instance of `queryable` in the default graph.
57
+ # @option options [Boolean] :optimize
58
+ # Optimize query before execution.
59
59
  # @option options [RDF::URI, String, Array<RDF::URI, String>] :default_graph_uri
60
- # One or more URIs used to initialize a new instance of `queryable` in the default graph.
60
+ # @option options [RDF::URI, String, Array<RDF::URI, String>] :load_datasets
61
+ # One or more URIs used to initialize a new instance of `queryable` in the default graph. One or more URIs used to initialize a new instance of `queryable` in the default graph.
61
62
  # @option options [RDF::URI, String, Array<RDF::URI, String>] :named_graph_uri
62
63
  # One or more URIs used to initialize the `queryable` as a named graph.
63
64
  # @yield [solution]
@@ -69,6 +70,7 @@ module SPARQL
69
70
  # @raise [SPARQL::MalformedQuery] on invalid input
70
71
  def self.execute(query, queryable, **options, &block)
71
72
  query = self.parse(query, **options)
73
+ query = query.optimize(**options) if options[:optimize]
72
74
  queryable = queryable || RDF::Repository.new
73
75
 
74
76
  case options.fetch(:debug, nil)
@@ -1,4 +1,4 @@
1
- require 'rdf' # @see http://rubygems.org/gems/rdf
1
+ require 'rdf' # @see https://rubygems.org/gems/rdf
2
2
  require 'rdf/xsd'
3
3
 
4
4
  module SPARQL
@@ -141,21 +141,21 @@ module SPARQL
141
141
  # ## Constructing operator expressions manually
142
142
  #
143
143
  # Operator(:isBlank).new(RDF::Node(:foobar)).to_sxp #=> "(isBlank _:foobar)"
144
- # Operator(:isIRI).new(RDF::URI('http://rubygems.org/gems/rdf/')).to_sxp #=> "(isIRI <http://rubygems.org/gems/rdf/>)"
144
+ # Operator(:isIRI).new(RDF::URI('https://rubygems.org/gems/rdf/')).to_sxp #=> "(isIRI <https://rubygems.org/gems/rdf/>)"
145
145
  # Operator(:isLiteral).new(RDF::Literal(3.1415)).to_sxp #=> "(isLiteral 3.1415)"
146
146
  # Operator(:str).new(Operator(:datatype).new(RDF::Literal(3.1415))).to_sxp #=> "(str (datatype 3.1415))"
147
147
  #
148
148
  # ## Constructing operator expressions using SSE forms
149
149
  #
150
150
  # SPARQL::Algebra::Expression[:isBlank, RDF::Node(:foobar)].to_sxp #=> "(isBlank _:foobar)"
151
- # SPARQL::Algebra::Expression[:isIRI, RDF::URI('http://rubygems.org/gems/rdf/')].to_sxp #=> "(isIRI <http://rubygems.org/gems/rdf/>)"
151
+ # SPARQL::Algebra::Expression[:isIRI, RDF::URI('https://rubygems.org/gems/rdf/')].to_sxp #=> "(isIRI <https://rubygems.org/gems/rdf/>)"
152
152
  # SPARQL::Algebra::Expression[:isLiteral, RDF::Literal(3.1415)].to_sxp #=> "(isLiteral 3.1415)"
153
153
  # SPARQL::Algebra::Expression[:str, [:datatype, RDF::Literal(3.1415)]].to_sxp #=> "(str (datatype 3.1415))"
154
154
  #
155
155
  # ## Constructing operator expressions using SSE strings
156
156
  #
157
157
  # SPARQL::Algebra::Expression.parse('(isBlank _:foobar)')
158
- # SPARQL::Algebra::Expression.parse('(isIRI <http://rubygems.org/gems/rdf/>)')
158
+ # SPARQL::Algebra::Expression.parse('(isIRI <https://rubygems.org/gems/rdf/>)')
159
159
  # SPARQL::Algebra::Expression.parse('(isLiteral 3.1415)')
160
160
  # SPARQL::Algebra::Expression.parse('(str (datatype 3.1415))')
161
161
  #
@@ -165,11 +165,6 @@ module SPARQL
165
165
  # Operator(:isIRI).evaluate(RDF::Vocab::DC.title) #=> RDF::Literal::TRUE
166
166
  # Operator(:isLiteral).evaluate(RDF::Literal(3.1415)) #=> RDF::Literal::TRUE
167
167
  #
168
- # ## Optimizing expressions containing constant subexpressions
169
- #
170
- # SPARQL::Algebra::Expression.parse('(sameTerm ?var ?var)').optimize #=> RDF::Literal::TRUE
171
- # SPARQL::Algebra::Expression.parse('(* -2 (- (* (+ 1 2) (+ 3 4))))').optimize #=> RDF::Literal(42)
172
- #
173
168
  # ## Evaluating expressions on a solution sequence
174
169
  #
175
170
  # # Find all people and their names & e-mail addresses:
@@ -405,8 +400,7 @@ module SPARQL
405
400
  def Expression(*sse)
406
401
  Expression.for(*sse)
407
402
  end
408
- alias_method :Expr, :Expression
409
- module_function :Expr, :Expression
403
+ module_function :Expression
410
404
 
411
405
  ##
412
406
  # @example
@@ -417,8 +411,7 @@ module SPARQL
417
411
  def Operator(name, arity = nil)
418
412
  Operator.for(name, arity)
419
413
  end
420
- alias_method :Op, :Operator
421
- module_function :Op, :Operator
414
+ module_function :Operator
422
415
 
423
416
  ##
424
417
  # @example
@@ -430,8 +423,7 @@ module SPARQL
430
423
  def Variable(name)
431
424
  Variable.new(name)
432
425
  end
433
- alias_method :Var, :Variable
434
- module_function :Var, :Variable
426
+ module_function :Variable
435
427
 
436
428
  Variable = RDF::Query::Variable
437
429
  end # Algebra
@@ -6,8 +6,8 @@ module SPARQL; module Algebra
6
6
  # or more operands which are `Enumerable` lists of `RDF::Term`
7
7
  # and return a single `RDF::Term` or `TypeError`.
8
8
  #
9
- # @see http://www.w3.org/TR/sparql11-query/#setFunctions
10
- # @see http://www.w3.org/TR/sparql11-query/#aggregates
9
+ # @see https://www.w3.org/TR/sparql11-query/#setFunctions
10
+ # @see https://www.w3.org/TR/sparql11-query/#aggregates
11
11
  #
12
12
  # @abstract
13
13
  module Aggregate
@@ -21,7 +21,7 @@ module SPARQL; module Algebra
21
21
  # @return [Expression]
22
22
  def self.parse(sse, **options, &block)
23
23
  begin
24
- require 'sxp' # @see http://rubygems.org/gems/sxp
24
+ require 'sxp' # @see https://rubygems.org/gems/sxp
25
25
  rescue LoadError
26
26
  abort "SPARQL::Algebra::Expression.parse requires the SXP gem (hint: `gem install sxp')."
27
27
  end
@@ -175,8 +175,8 @@ module SPARQL; module Algebra
175
175
  # @param [RDF::URI] function
176
176
  # @param [Array<RDF::Term>] args splat of args to function
177
177
  # @return [RDF::Term]
178
- # @see http://www.w3.org/TR/sparql11-query/#extensionFunctions
179
- # @see http://www.w3.org/TR/sparql11-query/#FunctionMapping
178
+ # @see https://www.w3.org/TR/sparql11-query/#extensionFunctions
179
+ # @see https://www.w3.org/TR/sparql11-query/#FunctionMapping
180
180
  def self.extension(function, *args)
181
181
  if function.to_s.start_with?(RDF::XSD.to_s)
182
182
  self.cast(function, args.first)
@@ -197,7 +197,7 @@ module SPARQL; module Algebra
197
197
  # Value, which should be a typed literal, where the type must be that specified
198
198
  # @raise [TypeError] if datatype is not a URI or value cannot be cast to datatype
199
199
  # @return [RDF::Term]
200
- # @see http://www.w3.org/TR/sparql11-query/#FunctionMapping
200
+ # @see https://www.w3.org/TR/sparql11-query/#FunctionMapping
201
201
  def self.cast(datatype, value)
202
202
  case datatype
203
203
  when RDF::XSD.dateTime
@@ -280,12 +280,26 @@ module SPARQL; module Algebra
280
280
  ##
281
281
  # Returns an optimized version of this expression.
282
282
  #
283
- # This is the default implementation, which simply returns `self`.
283
+ # This is the default implementation, which simply returns a copy of `self`.
284
284
  # Subclasses can override this method in order to implement something
285
285
  # more useful.
286
286
  #
287
- # @return [Expression] `self`
288
- def optimize
287
+ # @param [Hash{Symbol => Object}] options
288
+ # any additional options for optimization
289
+ # @return [Expression] a copy of `self`
290
+ # @see RDF::Query#optimize
291
+ def optimize(**options)
292
+ self.deep_dup.optimize!(**options)
293
+ end
294
+
295
+ ##
296
+ # Optimizes this query.
297
+ #
298
+ # @param [Hash{Symbol => Object}] options
299
+ # any additional options for optimization
300
+ # @return [self]
301
+ # @see RDF::Query#optimize!
302
+ def optimize!(**options)
289
303
  self
290
304
  end
291
305
 
@@ -313,7 +327,7 @@ module SPARQL; module Algebra
313
327
  # more useful.
314
328
  #
315
329
  # @return [Array] `self`
316
- # @see http://openjena.org/wiki/SSE
330
+ # @see https://openjena.org/wiki/SSE
317
331
  def to_sxp_bin
318
332
  self
319
333
  end
@@ -27,6 +27,22 @@ class Object
27
27
  def to_sse
28
28
  SXP::Generator.string(self.to_sxp_bin)
29
29
  end
30
+
31
+ ##
32
+ # A duplicate of this object.
33
+ #
34
+ # @return [Object] a copy of `self`
35
+ # @see SPARQL::Algebra::Expression#optimize
36
+ def optimize(**options)
37
+ self.deep_dup
38
+ end
39
+
40
+ ##
41
+ # Default for deep_dup is shallow dup
42
+ # @return [Object]
43
+ def deep_dup
44
+ dup
45
+ end
30
46
  end
31
47
 
32
48
  ##
@@ -66,11 +82,34 @@ class Array
66
82
  # @param [Hash{Symbol => Object}] options
67
83
  # @raise [NotImplementedError]
68
84
  # If an attempt is made to perform an unsupported operation
69
- # @see http://www.w3.org/TR/sparql11-query/#sparqlAlgebra
85
+ # @see https://www.w3.org/TR/sparql11-query/#sparqlAlgebra
70
86
  def execute(queryable, **options)
71
87
  raise NotImplementedError, "SPARQL::Algebra '#{first}' operator not implemented"
72
88
  end
73
89
 
90
+ ##
91
+ # Return an optimized version of this array.
92
+ #
93
+ # @return [Array] a copy of `self`
94
+ # @see SPARQL::Algebra::Expression#optimize
95
+ def optimize(**options)
96
+ self.map do |op|
97
+ op.optimize(**options) if op.respond_to?(:optimize)
98
+ end
99
+ end
100
+
101
+ ##
102
+ # Binds the pattern to a solution, making it no longer variable if all variables are resolved to bound variables
103
+ #
104
+ # @param [RDF::Query::Solution] solution
105
+ # @return [self]
106
+ def bind(solution)
107
+ map! do |op|
108
+ op.respond_to?(:bind) ? op.bind(solution) : op
109
+ end
110
+ self
111
+ end
112
+
74
113
  ##
75
114
  # Returns `true` if any of the operands are variables, `false`
76
115
  # otherwise.
@@ -78,7 +117,7 @@ class Array
78
117
  # @return [Boolean] `true` or `false`
79
118
  # @see #constant?
80
119
  def variable?
81
- any?(&:variable?)
120
+ any? {|op| op.respond_to?(:variable?) && op.variable?}
82
121
  end
83
122
  def constant?; !(variable?); end
84
123
 
@@ -166,6 +205,12 @@ class Array
166
205
  each {|e| e.validate! if e.respond_to?(:validate!)}
167
206
  self
168
207
  end
208
+
209
+ ##
210
+ # Deep duplicate
211
+ def deep_dup
212
+ map(&:deep_dup)
213
+ end
169
214
  end
170
215
 
171
216
  ##
@@ -179,6 +224,21 @@ class Hash
179
224
  to_a.to_sxp_bin
180
225
  end
181
226
  def to_sxp; to_sxp_bin; end
227
+
228
+ ##
229
+ # A duplicate of this hash.
230
+ #
231
+ # @return [Hash] a copy of `self`
232
+ # @see SPARQL::Algebra::Expression#optimize
233
+ def optimize(**options)
234
+ self.deep_dup
235
+ end
236
+
237
+ ##
238
+ # Deep duplicate
239
+ def deep_dup
240
+ inject({}) {|memo, (k, v)| memo.merge(k => v.deep_dup)}
241
+ end
182
242
  end
183
243
 
184
244
  ##
@@ -210,6 +270,17 @@ module RDF::Term
210
270
  def vars
211
271
  variable? ? [self] : []
212
272
  end
273
+
274
+ ##
275
+ # A duplicate of this term.
276
+ #
277
+ # @return [RDF::Term] a copy of `self`
278
+ # @see SPARQL::Algebra::Expression#optimize
279
+ def optimize(**options)
280
+ optimized = self.deep_dup
281
+ optimized.lexical = nil if optimized.respond_to?(:lexical=)
282
+ optimized
283
+ end
213
284
  end # RDF::Term
214
285
 
215
286
  # Override RDF::Queryable to execute against SPARQL::Algebra::Query elements as well as RDF::Query and RDF::Pattern
@@ -258,7 +329,6 @@ module RDF::Queryable
258
329
  query_without_sparql(pattern, **options, &block)
259
330
  end
260
331
  end
261
-
262
332
  end
263
333
 
264
334
  class RDF::Statement
@@ -271,6 +341,15 @@ class RDF::Statement
271
341
  [:triple, subject, predicate, object]
272
342
  end
273
343
  end
344
+
345
+ ##
346
+ # A duplicate of this Statement.
347
+ #
348
+ # @return [RDF::Statement] a copy of `self`
349
+ # @see SPARQL::Algebra::Expression#optimize
350
+ def optimize(**options)
351
+ self.dup
352
+ end
274
353
  end
275
354
 
276
355
  class RDF::Query
@@ -306,6 +385,16 @@ class RDF::Query
306
385
  end
307
386
  end
308
387
 
388
+ ##
389
+ # Binds the pattern to a solution, making it no longer variable if all variables are resolved to bound variables
390
+ #
391
+ # @param [RDF::Query::Solution] solution
392
+ # @return [self]
393
+ def bind(solution)
394
+ patterns.each {|p| p.bind(solution)}
395
+ self
396
+ end
397
+
309
398
  # Query results in a boolean result (e.g., ASK)
310
399
  # @return [Boolean]
311
400
  def query_yields_boolean?
@@ -338,6 +427,27 @@ class RDF::Query
338
427
  variables.values
339
428
  end
340
429
 
430
+ ##
431
+ # Optimize the query, removing lexical shortcuts in URIs
432
+ #
433
+ # @return [self]
434
+ # @see SPARQL::Algebra::Expression#optimize!
435
+ def optimize!(**options)
436
+ @patterns = @patterns.map do |pattern|
437
+ components = pattern.to_a.map do |term|
438
+ if term.respond_to?(:lexical=)
439
+ term.dup.instance_eval {@lexical = nil; self}
440
+ else
441
+ term
442
+ end
443
+ end
444
+ RDF::Query::Pattern.from(components)
445
+ end.sort! do |a, b|
446
+ (a.cost || 0) <=> (b.cost || 0)
447
+ end
448
+ self
449
+ end
450
+
341
451
  ##
342
452
  # Returns `true` if this is executable (i.e., contains a graph patterns), `false`
343
453
  # otherwise.
@@ -390,6 +500,15 @@ class RDF::Query::Variable
390
500
  raise TypeError if bindings.respond_to?(:bound?) && !bindings.bound?(self)
391
501
  bindings[name.to_sym]
392
502
  end
503
+
504
+ ##
505
+ # Return self
506
+ #
507
+ # @return [RDF::Query::Variable] a copy of `self`
508
+ # @see SPARQL::Algebra::Expression#optimize
509
+ def optimize(**options)
510
+ self
511
+ end
393
512
  end # RDF::Query::Variable
394
513
 
395
514
  ##
@@ -419,3 +538,16 @@ class RDF::Query::Solutions
419
538
  end
420
539
  alias_method :filter!, :filter
421
540
  end # RDF::Query::Solutions
541
+
542
+ ##
543
+ # Extensions for `RDF::Query::Solution`.
544
+ class RDF::Query::Solution
545
+ ##
546
+ # Returns the SXP representation of this object, defaults to `self`.
547
+ #
548
+ # @return [String]
549
+ def to_sxp_bin
550
+ to_a.to_sxp_bin
551
+ end
552
+ def to_sxp; to_sxp_bin; end
553
+ end # RDF::Query::Solution
@@ -146,8 +146,6 @@ module SPARQL; module Algebra
146
146
  autoload :Using, 'sparql/algebra/operator/using'
147
147
  autoload :With, 'sparql/algebra/operator/with'
148
148
 
149
-
150
-
151
149
  ##
152
150
  # Returns an operator class for the given operator `name`.
153
151
  #
@@ -357,6 +355,22 @@ module SPARQL; module Algebra
357
355
  end
358
356
  end
359
357
 
358
+ ##
359
+ # Deep duplicate operands
360
+ def deep_dup
361
+ self.class.new(*operands.map(&:deep_dup))
362
+ end
363
+
364
+ ##
365
+ # Binds the pattern to a solution, making it no longer variable if all variables are resolved to bound variables
366
+ #
367
+ # @param [RDF::Query::Solution] solution
368
+ # @return [self]
369
+ def bind(solution)
370
+ @operands.each {|op| op.bind(solution)}
371
+ self
372
+ end
373
+
360
374
  ##
361
375
  # Base URI used for reading data sources with relative URIs
362
376
  #
@@ -434,7 +448,7 @@ module SPARQL; module Algebra
434
448
  # @return [Boolean] `true` or `false`
435
449
  # @see #constant?
436
450
  def variable?
437
- operands.any?(&:variable?)
451
+ operands.any? {|op| op.respond_to?(:variable?) && op.variable?}
438
452
  end
439
453
 
440
454
  ##
@@ -491,21 +505,35 @@ module SPARQL; module Algebra
491
505
  #
492
506
  # For constant expressions containing no variables, returns the result
493
507
  # of evaluating the expression with empty bindings; otherwise returns
494
- # `self`.
508
+ # a copy of `self`.
495
509
  #
496
510
  # Optimization is not possible if the expression raises an exception,
497
511
  # such as a `TypeError` or `ZeroDivisionError`, which must be conserved
498
512
  # at runtime.
499
513
  #
500
514
  # @return [SPARQL::Algebra::Expression]
501
- def optimize
515
+ # @see RDF::Query#optimize
516
+ def optimize(**options)
502
517
  if constant?
503
518
  # Note that if evaluation results in a `TypeError` or other error,
504
519
  # we must return `self` so that the error is conserved at runtime:
505
520
  evaluate(RDF::Query::Solution.new) rescue self
506
521
  else
507
- super # returns `self`
522
+ super # returns a copy of `self`
523
+ end
524
+ end
525
+
526
+ ##
527
+ # Optimizes this query by optimizing its constituent operands
528
+ # according to their cost estimates.
529
+ #
530
+ # @return [self]
531
+ # @see RDF::Query#optimize!
532
+ def optimize!(**options)
533
+ @operands.map! do |op|
534
+ op.optimize(**options) if op.respond_to?(:optimize)
508
535
  end
536
+ self
509
537
  end
510
538
 
511
539
  ##
@@ -532,7 +560,7 @@ module SPARQL; module Algebra
532
560
  # Returns the SPARQL S-Expression (SSE) representation of this operator.
533
561
  #
534
562
  # @return [Array]
535
- # @see http://openjena.org/wiki/SSE
563
+ # @see https://openjena.org/wiki/SSE
536
564
  def to_sxp_bin
537
565
  operator = [self.class.const_get(:NAME)].flatten.first
538
566
  [operator, *(operands || []).map(&:to_sxp_bin)]
@@ -544,7 +572,7 @@ module SPARQL; module Algebra
544
572
  # @return [String]
545
573
  def to_sxp
546
574
  begin
547
- require 'sxp' # @see http://rubygems.org/gems/sxp
575
+ require 'sxp' # @see https://rubygems.org/gems/sxp
548
576
  rescue LoadError
549
577
  abort "SPARQL::Algebra::Operator#to_sxp requires the SXP gem (hint: `gem install sxp')."
550
578
  end
@@ -647,7 +675,7 @@ module SPARQL; module Algebra
647
675
  # @param [RDF::Literal] literal
648
676
  # @return [RDF::Literal::Boolean] `true` or `false`
649
677
  # @raise [TypeError] if the literal could not be coerced to an `RDF::Literal::Boolean`
650
- # @see http://www.w3.org/TR/sparql11-query/#ebv
678
+ # @see https://www.w3.org/TR/sparql11-query/#ebv
651
679
  def boolean(literal)
652
680
  case literal
653
681
  when FalseClass then RDF::Literal::FALSE