sparql 1.0.8 → 1.1.0p0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +14 -6
  2. data/README.md +57 -25
  3. data/VERSION +1 -1
  4. data/lib/sinatra/sparql.rb +5 -3
  5. data/lib/sparql/algebra/aggregate.rb +67 -0
  6. data/lib/sparql/algebra/evaluatable.rb +49 -4
  7. data/lib/sparql/algebra/expression.rb +6 -4
  8. data/lib/sparql/algebra/extensions.rb +99 -9
  9. data/lib/sparql/algebra/operator/and.rb +7 -4
  10. data/lib/sparql/algebra/operator/asc.rb +6 -3
  11. data/lib/sparql/algebra/operator/avg.rb +36 -0
  12. data/lib/sparql/algebra/operator/bnode.rb +5 -4
  13. data/lib/sparql/algebra/operator/bound.rb +5 -2
  14. data/lib/sparql/algebra/operator/coalesce.rb +6 -3
  15. data/lib/sparql/algebra/operator/concat.rb +6 -3
  16. data/lib/sparql/algebra/operator/count.rb +30 -0
  17. data/lib/sparql/algebra/operator/exists.rb +39 -0
  18. data/lib/sparql/algebra/operator/exprlist.rb +6 -3
  19. data/lib/sparql/algebra/operator/extend.rb +3 -1
  20. data/lib/sparql/algebra/operator/filter.rb +2 -1
  21. data/lib/sparql/algebra/operator/group.rb +101 -0
  22. data/lib/sparql/algebra/operator/group_concat.rb +55 -0
  23. data/lib/sparql/algebra/operator/if.rb +5 -5
  24. data/lib/sparql/algebra/operator/in.rb +7 -4
  25. data/lib/sparql/algebra/operator/max.rb +38 -0
  26. data/lib/sparql/algebra/operator/min.rb +38 -0
  27. data/lib/sparql/algebra/operator/minus.rb +51 -16
  28. data/lib/sparql/algebra/operator/negate.rb +31 -0
  29. data/lib/sparql/algebra/operator/notexists.rb +37 -0
  30. data/lib/sparql/algebra/operator/notin.rb +7 -4
  31. data/lib/sparql/algebra/operator/or.rb +7 -4
  32. data/lib/sparql/algebra/operator/order.rb +2 -2
  33. data/lib/sparql/algebra/operator/sample.rb +32 -0
  34. data/lib/sparql/algebra/operator/strlang.rb +1 -1
  35. data/lib/sparql/algebra/operator/sum.rb +36 -0
  36. data/lib/sparql/algebra/operator/table.rb +44 -0
  37. data/lib/sparql/algebra/operator.rb +37 -2
  38. data/lib/sparql/algebra.rb +1 -0
  39. data/lib/sparql/grammar/meta.rb +1143 -696
  40. data/lib/sparql/grammar/parser11.rb +178 -32
  41. data/lib/sparql/grammar/terminals11.rb +2 -2
  42. data/lib/sparql/grammar.rb +0 -2
  43. data/lib/sparql/results.rb +55 -0
  44. metadata +78 -81
checksums.yaml CHANGED
@@ -1,7 +1,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: eabf23290e66c519d04a3903fb9c10cab87236c5
4
- data.tar.gz: ccf93905bff11c897f0fb00b01113095c527aa9e
5
- SHA512:
6
- metadata.gz: de063e0c1d091dcb541f43d00942db7636c1e931f1504337cb9b0c8648085e30856bfbb332b08cfc392aece68c62595c1f6bd2924be9f715bc1fb2574b3b75a6
7
- data.tar.gz: d03282b7ed8ae2edadbd9050d8101a9195df45844baa4cf126fefefef38c0b5089a07a67f75902e6a54d3a8404a86358ab13445a161f0b9fdc7fd0ad10aa9ac1
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NWU0YWQ2MjFiNjU1MDZlZmNmMGI4ZGU5ZGRhMDA1ZTI2NDk0NTdmYQ==
5
+ data.tar.gz: !binary |-
6
+ ZmNlOTk2OGUzMjQwZDY5OGIxZjM1Yjc3NmFlYjdkMjIyNmZlOWRhNw==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ Mzc1ZmQ3NmNjYWJiOGU2ZDUzYjE4Mjk5NzM0NzIzZGU5MDQ4MjM3MzkxZjZh
10
+ YzIyYzJkNzJmNmMwMTJhNWE5ZjZjMzEyODlhNjkxYjk3MDE3YTg2YTQyZmEz
11
+ OGExNWMyZjhkYjBjZjQwZDFiOTQzNjFmODA0Y2ZlN2M1ZmYwMTI=
12
+ data.tar.gz: !binary |-
13
+ NzE5OTg5NDQ4MjEzOTEyY2E3NmMxYTM2ZDRlZmUwODMzZTAzNGE5ODUzMzVi
14
+ NGRiMzc0N2EzMTRiZTc0ZmJmZGQ1ZTgwNmY5YzQ5NDFmNGRmYWE1NjYzNTYz
15
+ ZjZhODkzN2E1NjQ3ZmIwNjUzZTRjZWJmMWIyZWQ1N2MwMDJkNzI=
data/README.md CHANGED
@@ -9,9 +9,11 @@ This is a [Ruby][] implementation of [SPARQL][] for [RDF.rb][].
9
9
  ## Features
10
10
 
11
11
  * 100% free and unencumbered [public domain](http://unlicense.org/) software.
12
- * [SPARQL 1.0][] query parsing and execution
13
- * Limited [SPARQL 1.1][] query parsing and execution
14
- * SPARQL results as [XML][SPARQL XML], [JSON][SPARQL JSON] or HTML.
12
+ * [SPARQL 1.1 Query][] parsing and execution (excluding [Property Paths][])
13
+ * SPARQL results as [XML][SPARQL XML], [JSON][SPARQL JSON],
14
+ [CSV][SPARQL 1.1 Query Results CSV and TSV Formats],
15
+ [TSV][SPARQL 1.1 Query Results CSV and TSV Formats]
16
+ or HTML.
15
17
  * SPARQL CONSTRUCT or DESCRIBE serialized based on Format, Extension of Mime Type
16
18
  using available RDF Writers (see [Linked Data][])
17
19
  * SPARQL Client for accessing remote SPARQL endpoints.
@@ -24,32 +26,48 @@ This is a [Ruby][] implementation of [SPARQL][] for [RDF.rb][].
24
26
 
25
27
  ## Description
26
28
 
27
- The {SPARQL} gem implements the [SPARQL 1.0][] using the [SPARQL 1.1][] grammar,
28
- and provides [Rack][] and [Sinatra][]
29
+ The {SPARQL} gem implements [SPARQL 1.1 Query][], and provides [Rack][] and [Sinatra][]
29
30
  middleware to provide results using [HTTP Content Negotiation][conneg].
30
31
 
31
- * {SPARQL::Grammar} implements a [SPARQL 1.1][] parser generating [SPARQL S-Expressions (SSE)][SSE].
32
- * [SPARQL 1.1][] capabilities beyond [SPARQL 1.0][] are not yet supported.
33
- See the section on [SPARQL 1.1][] extensions and limitations for further detail.
32
+ * {SPARQL::Grammar} implements a [SPARQL 1.1 Query][] parser generating [SPARQL S-Expressions (SSE)][SSE].
33
+ * Support for [Property Paths][] is excluded.
34
+ See the section on [SPARQL 1.1 Query][] extensions and limitations for further detail.
34
35
  * {SPARQL::Algebra} executes SSE against Any `RDF::Graph` or `RDF::Repository`, including
35
36
  compliant [RDF.rb][] repository adaptors such as [RDF::DO][] and [RDF::Mongo][].
36
37
  * {Rack::SPARQL} and {Sinatra::SPARQL} provide middleware components to format results
37
38
  using an appropriate format based on [HTTP content negotiation][conneg].
38
39
 
39
- ### [SPARQL 1.1][] Extensions and Limitations
40
- The {SPARQL} gem uses the [SPARQL 1.1][] {file:etc/sparql11.bnf EBNF grammar}, which provides
40
+ ### [SPARQL 1.1 Query][] Extensions and Limitations
41
+ The {SPARQL} gem uses the [SPARQL 1.1 Query][] {file:etc/sparql11.bnf EBNF grammar}, which provides
41
42
  much more capability than [SPARQL 1.0][], but has a few limitations:
42
43
 
43
44
  * The format for decimal datatypes has changed in [RDF 1.1][]; they may no
44
45
  longer have a trailing ".", although they do not need a leading digit.
45
- * BNodes may now include extended characters, including ".". As a consequence, BNodes
46
- used in the object position, where the statement or pattern is terminated by a "."
47
- must contain whitespace separating the BNode label, and the ".".
46
+ * BNodes may now include extended characters, including ".".
48
47
 
49
- The SPARQL gem now implements the following [SPARQL 1.1][] operations:
48
+ The SPARQL gem now implements the following [SPARQL 1.1 Query][] operations:
50
49
 
51
- * Support for all [functions](http://www.w3.org/TR/sparql11-query/#SparqlOps).
52
- * Support for [BIND](http://www.w3.org/TR/sparql11-query/#bind)
50
+ * [Functions](http://www.w3.org/TR/sparql11-query/#SparqlOps)
51
+ * [BIND](http://www.w3.org/TR/sparql11-query/#bind)
52
+ * [GROUP BY](http://www.w3.org/TR/sparql11-query/#groupby)
53
+ * [Aggregates](http://www.w3.org/TR/sparql11-query/#aggregates)
54
+ * [Subqueries](http://www.w3.org/TR/sparql11-query/#subqueries)
55
+ * [Inline Data](http://www.w3.org/TR/2013/REC-sparql11-query-20130321/#inline-data)
56
+ * [Inline Data](http://www.w3.org/TR/2013/REC-sparql11-query-20130321/#inline-data)
57
+ * [Exists](http://www.w3.org/TR/2013/REC-sparql11-query-20130321/#func-filter-exists)
58
+ * [Negation](http://www.w3.org/TR/2013/REC-sparql11-query-20130321/#negation)
59
+
60
+ The only major area of [SPARQL 1.1 Query][] missing is
61
+ [Property Paths][], which
62
+ will be in later release along with:
63
+
64
+ * [Update][SPARQL 1.1 Update],
65
+ * [Federated Query][SPARQL 1.1 Federated Query],
66
+ * [Entailment Regimes][SPARQL 1.1 Entailment Regimes],
67
+ * [Protocol][SPARQL 1.1 Protocol], and
68
+ * [Graph Store HTTP Protocol][SPARQL 1.1 Graph Store HTTP Protocol]
69
+
70
+ either in this, or related gems.
53
71
 
54
72
  ### SPARQL Extension Functions
55
73
  Extension functions may be defined, which will be invoked during query evaluation. For example:
@@ -71,18 +89,21 @@ Then, use the function in a query:
71
89
  BIND(rsp:crypt(?email) AS ?crypted)
72
90
  }
73
91
 
92
+ See {SPARQL::Algebra::Expression.register_extension} for details.
93
+
74
94
  ### Middleware
75
95
 
76
- `Rack::SPARQL` is a superset of [Rack::LinkedData][] to allow content negotiated results
96
+ {Rack::SPARQL} is a superset of [Rack::LinkedData][] to allow content negotiated results
77
97
  to be returned any `RDF::Enumerable` or `RDF::Query::Solutions` compatible results.
78
98
  You would typically return an instance of `RDF::Graph`, `RDF::Repository` or `RDF::Query::Solutions`
79
99
  from your Rack application, and let the `Rack::SPARQL::ContentNegotiation` middleware
80
100
  take care of serializing your response into whatever format the HTTP
81
101
  client requested and understands.
82
102
 
83
- `Sinatra::SPARQL` is a thin Sinatra-specific wrapper around the
103
+ {Sinatra::SPARQL} is a thin Sinatra-specific wrapper around the
84
104
  {Rack::SPARQL} middleware, which implements SPARQL
85
- content negotiation for Rack applications.
105
+ content negotiation for Rack applications. {Sinatra::SPARQL} also supports
106
+ [SPARQL 1.1 Service Description][].
86
107
 
87
108
  The middleware queries [RDF.rb][] for the MIME content types of known RDF
88
109
  serialization formats, so it will work with whatever serialization plugins
@@ -102,8 +123,8 @@ Queries using datasets are re-written to use the identified graphs for `FROM` an
102
123
  ### Result formats
103
124
 
104
125
  `SPARQL.serialize_results` may be used on it's own, or in conjunction with {Rack::SPARQL} or {Sinatra::SPARQL}
105
- to provide content-negotiated query results. For basic `SELECT` and `ASK` this includes HTML, XML and JSON formats.
106
- `DESCRIBE` and `CONSTRUCT` create an `RDF::Graph`, which can be serialized through [HTTP Content Netogiation][conneg]
126
+ to provide content-negotiated query results. For basic `SELECT` and `ASK` this includes HTML, XML, CSV, TSV and JSON formats.
127
+ `DESCRIBE` and `CONSTRUCT` create an `RDF::Graph`, which can be serialized through [HTTP Content Negotiation][conneg]
107
128
  using available RDF writers. For best results, require [Linked Data][] to enable
108
129
  a full set of RDF formats.
109
130
 
@@ -212,8 +233,8 @@ Full documentation available on [Rubydoc.info][SPARQL doc]
212
233
  ## Dependencies
213
234
 
214
235
  * [Ruby](http://ruby-lang.org/) (>= 1.9.2)
215
- * [RDF.rb](http://rubygems.org/gems/rdf) (>= 1.1)
216
- * [SPARQL::Client](https://rubygems.org/gems/sparql-client) (>= 1.0)
236
+ * [RDF.rb](http://rubygems.org/gems/rdf) (>= 1.0.7)
237
+ * [SPARQL::Client](https://rubygems.org/gems/sparql-client) (>= 1.0.3)
217
238
  * [SXP](https://rubygems.org/gems/sxp) (>= 0.1.0)
218
239
  * [Builder](https://rubygems.org/gems/builder) (>= 3.0.0)
219
240
  * [JSON](https://rubygems.org/gems/json) (>= 1.5.1)
@@ -281,7 +302,6 @@ A copy of the [SPARQL 1.0 tests][] and [SPARQL 1.1 tests][] are included in the
281
302
  [SPARQL 1.0]: http://www.w3.org/TR/rdf-sparql-query/
282
303
  [SPARQL 1.0 tests]:http://www.w3.org/2001/sw/DataAccess/tests/
283
304
  [SPARQL 1.1 tests]: http://www.w3.org/2009/sparql/docs/tests/
284
- [SPARQL 1.1]: http://www.w3.org/TR/sparql11-query/
285
305
  [SSE]: http://openjena.org/wiki/SSE
286
306
  [SXP]: http://sxp.rubyforge.org/
287
307
  [grammar]: http://www.w3.org/TR/rdf-sparql-query/#grammar
@@ -292,9 +312,21 @@ A copy of the [SPARQL 1.0 tests][] and [SPARQL 1.1 tests][] are included in the
292
312
  [SPARQL doc]: http://rubydoc.info/github/ruby-rdf/sparql/frames
293
313
  [SPARQL XML]: http://www.w3.org/TR/rdf-sparql-XMLres/
294
314
  [SPARQL JSON]: http://www.w3.org/TR/rdf-sparql-json-res/
295
- [SPARQL Protocol]: http://www.w3.org/TR/rdf-sparql-protocol/
315
+ [Property Paths]: http://www.w3.org/TR/2013/REC-sparql11-query-20130321/#propertypaths
316
+
296
317
  [SSD]: http://www.w3.org/TR/sparql11-service-description/
297
318
  [Rack]: http://rack.rubyforge.org/
298
319
  [Sinatra]: http://www.sinatrarb.com/
299
320
  [conneg]: http://en.wikipedia.org/wiki/Content_negotiation
300
321
 
322
+ [SPARQL 1.1 Query]: http://www.w3.org/TR/sparql11-query/
323
+ [SPARQL 1.1 Update]: http://www.w3.org/TR/sparql11-update/
324
+ [SPARQL 1.1 Service Description]: http://www.w3.org/TR/sparql11-service-description/
325
+ [SPARQL 1.1 Federated Query]: http://www.w3.org/TR/sparql11-federated-query/
326
+ [SPARQL 1.1 Query Results JSON Format]: http://www.w3.org/TR/sparql11-results-json/
327
+ [SPARQL 1.1 Query Results CSV and TSV Formats]: http://www.w3.org/TR/sparql11-results-csv-tsv/
328
+ [SPARQL Query Results XML Format]: http://www.w3.org/TR/rdf-sparql-XMLres/
329
+ [SPARQL 1.1 Entailment Regimes]: http://www.w3.org/TR/sparql11-entailment/
330
+ [SPARQL 1.1 Protocol]: http://www.w3.org/TR/sparql11-protocol/
331
+ [SPARQL 1.1 Graph Store HTTP Protocol]: http://www.w3.org/TR/sparql11-http-rdf-update/
332
+
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.8
1
+ 1.1.0p0
@@ -42,7 +42,7 @@ module Sinatra
42
42
  node = RDF::Node.new
43
43
  g << [node, RDF.type, sd.join("#Service")]
44
44
  g << [node, sd.join("#endpoint"), options[:endpoint] || url("/sparql")]
45
- g << [node, sd.join("#supportedLanguage"), sd.join("#SPARQL10Query")]
45
+ g << [node, sd.join("#supportedLanguage"), sd.join("#SPARQL11Query")]
46
46
 
47
47
  # Result formats, both RDF and SPARQL Results.
48
48
  # FIXME: We should get this from the avaliable serializers
@@ -50,8 +50,10 @@ module Sinatra
50
50
  g << [node, sd.join("#resultFormat"), RDF::URI("http://www.w3.org/ns/formats/Turtle")]
51
51
  g << [node, sd.join("#resultFormat"), RDF::URI("http://www.w3.org/ns/formats/RDFa")]
52
52
  g << [node, sd.join("#resultFormat"), RDF::URI("http://www.w3.org/ns/formats/N-Triples")]
53
- g << [node, sd.join("#resultFormat"), RDF::URI("http://www.w3.org/ns/formats/SPARQL_RESULTS_XML")]
54
- g << [node, sd.join("#resultFormat"), RDF::URI("http://www.w3.org/ns/formats/SPARQL_RESULTS_JSON")]
53
+ g << [node, sd.join("#resultFormat"), RDF::URI("http://www.w3.org/ns/formats/SPARQL_Results_XML")]
54
+ g << [node, sd.join("#resultFormat"), RDF::URI("http://www.w3.org/ns/formats/SPARQL_Results_JSON")]
55
+ g << [node, sd.join("#resultFormat"), RDF::URI("http://www.w3.org/ns/formats/SPARQL_Results_CSV")]
56
+ g << [node, sd.join("#resultFormat"), RDF::URI("http://www.w3.org/ns/formats/SPARQL_Results_TSV")]
55
57
 
56
58
  # Features
57
59
  g << [node, sd.join("#feature"), sd.join("#DereferencesURIs")]
@@ -0,0 +1,67 @@
1
+ module SPARQL; module Algebra
2
+ ##
3
+ # A SPARQL algebra aggregate.
4
+ #
5
+ # Aggregates are for SPARQL set functions. Aggregates take one
6
+ # or more operands which are `Enumerable` lists of `RDF::Term`
7
+ # and return a single `RDF::Term` or `TypeError`.
8
+ #
9
+ # @see http://www.w3.org/TR/sparql11-query/#setFunctions
10
+ # @see http://www.w3.org/TR/sparql11-query/#aggregates
11
+ #
12
+ # @abstract
13
+ module Aggregate
14
+ ##
15
+ # Aggregates this operator accross its operands using
16
+ # a solutions enumerable.
17
+ #
18
+ # @param [Enumerable<RDF::Query::Solution>] solutions ([])
19
+ # an enumerable set of query solutions
20
+ # @param [Hash{Symbol => Object}] options ({})
21
+ # options passed from query
22
+ # @return [RDF::Term]
23
+ # @raise [TypeError]
24
+ # @abstract
25
+ def aggregate(solutions = [], options = {})
26
+ args_enum = solutions.map do |solution|
27
+ operands.map do |operand|
28
+ begin
29
+ operand.evaluate(solution, options.merge(:depth => options[:depth].to_i + 1))
30
+ rescue TypeError
31
+ # Ignore errors
32
+ nil
33
+ end
34
+ end.compact
35
+ end
36
+ apply(args_enum)
37
+ end
38
+
39
+ ##
40
+ # @param [Enumerable<Array<RDF::Term>>] enum
41
+ # Enumerable yielding evaluated operands
42
+ # @return [RDF::Term]
43
+ # @abstract
44
+ def apply(enum)
45
+ raise NotImplementedError, "#{self.class}#apply(#{operands.map(&:class).join(', ')})"
46
+ end
47
+
48
+ ##
49
+ # This is a no-op for Aggregates.
50
+ #
51
+ # @return [SPARQL::Algebra::Evaluatable] self
52
+ def replace_vars!(&block)
53
+ self
54
+ end
55
+
56
+ ##
57
+ # Replace ourselves with a variable returned from the block
58
+ #
59
+ # @yield agg
60
+ # @yieldparam [SPARQL::Algebra::Aggregate] agg
61
+ # @yieldreturn [RDF::Query::Variable]
62
+ # @return [RDF::Query::Variable] the returned variable
63
+ def replace_aggregate!(&block)
64
+ yield self
65
+ end
66
+ end # Aggregate
67
+ end; end # SPARQL::Algebra
@@ -7,12 +7,14 @@ module SPARQL; module Algebra
7
7
  ##
8
8
  # Evaluates this operator using the given variable `bindings`.
9
9
  #
10
- # @param [RDF::Query::Solution, #[]] bindings
10
+ # @param [RDF::Query::Solution] bindings
11
11
  # a query solution containing zero or more variable bindings
12
+ # @param [Hash{Symbol => Object}] options ({})
13
+ # options passed from query
12
14
  # @return [RDF::Term]
13
15
  # @abstract
14
- def evaluate(bindings = {})
15
- args = operands.map { |operand| operand.evaluate(bindings) }
16
+ def evaluate(bindings, options = {})
17
+ args = operands.map { |operand| operand.evaluate(bindings, options.merge(:depth => options[:depth].to_i + 1)) }
16
18
  options[:memoize] ? memoize(*args) : apply(*args)
17
19
  end
18
20
 
@@ -33,5 +35,48 @@ module SPARQL; module Algebra
33
35
  def apply(*operands)
34
36
  raise NotImplementedError, "#{self.class}#apply(#{operands.map(&:class).join(', ')})"
35
37
  end
36
- end # Query
38
+
39
+ ##
40
+ # Replace operators which are variables with the result of the block
41
+ # descending into operators which are also evaluatable
42
+ #
43
+ # @yield var
44
+ # @yieldparam [RDF::Query::Variable] var
45
+ # @yieldreturn [RDF::Query::Variable, SPARQL::Algebra::Evaluatable]
46
+ # @return [SPARQL::Algebra::Evaluatable] self
47
+ def replace_vars!(&block)
48
+ @operands.map! do |op|
49
+ case
50
+ when op.is_a?(RDF::Query::Variable)
51
+ yield op
52
+ when op.respond_to?(:replace_vars!)
53
+ op.replace_vars!(&block)
54
+ else
55
+ op
56
+ end
57
+ end
58
+ self
59
+ end
60
+
61
+ ##
62
+ # Recursively re-map operators to replace aggregates with temporary variables returned from the block
63
+ #
64
+ # @yield agg
65
+ # @yieldparam [SPARQL::Algebra::Aggregate] agg
66
+ # @yieldreturn [RDF::Query::Variable]
67
+ # @return [SPARQL::Algebra::Evaluatable, RDF::Query::Variable] self
68
+ def replace_aggregate!(&block)
69
+ @operands.map! do |op|
70
+ case
71
+ when op.aggregate?
72
+ yield op
73
+ when op.respond_to?(:replace_aggregate!)
74
+ op.replace_aggregate!(&block)
75
+ else
76
+ op
77
+ end
78
+ end
79
+ self
80
+ end
81
+ end # Evaluatable
37
82
  end; end # SPARQL::Algebra
@@ -149,7 +149,6 @@ module SPARQL; module Algebra
149
149
  # @yield *args
150
150
  # @yieldparam [Array<RDF::Term>] *args
151
151
  # @yieldreturn [RDF::Term]
152
- # @param [Proc] function
153
152
  # @return [void]
154
153
  # @raise [TypeError] if `uri` is not an RDF::URI or no block is given
155
154
  def self.register_extension(uri, &block)
@@ -174,7 +173,7 @@ module SPARQL; module Algebra
174
173
  # that.
175
174
  #
176
175
  # @param [RDF::URI] function
177
- # @param [Array<RDF::Term>] *args
176
+ # @param [Array<RDF::Term>] args splat of args to function
178
177
  # @return [RDF::Term]
179
178
  # @see http://www.w3.org/TR/sparql11-query/#extensionFunctions
180
179
  # @see http://www.w3.org/TR/sparql11-query/#FunctionMapping
@@ -289,9 +288,12 @@ module SPARQL; module Algebra
289
288
  # Subclasses can override this method in order to implement something
290
289
  # more useful.
291
290
  #
292
- # @param [RDF::Query::Solution, #[]] bindings
291
+ # @param [RDF::Query::Solution] bindings
292
+ # a query solution containing zero or more variable bindings
293
+ # @param [Hash{Symbol => Object}] options ({})
294
+ # options passed from query
293
295
  # @return [Expression] `self`
294
- def evaluate(bindings = {})
296
+ def evaluate(bindings, options = {})
295
297
  self
296
298
  end
297
299
 
@@ -20,7 +20,7 @@ class Object
20
20
  end
21
21
 
22
22
  ##
23
- # Extensions for Ruby's `Object` class.
23
+ # Extensions for Ruby's `Array` class.
24
24
  class Array
25
25
  ##
26
26
  # Returns the SXP representation of this object, defaults to `self`.
@@ -29,7 +29,7 @@ class Array
29
29
  def to_sxp_bin
30
30
  map {|x| x.to_sxp_bin}
31
31
  end
32
-
32
+
33
33
  ##
34
34
  # Evaluates the array using the given variable `bindings`.
35
35
  #
@@ -37,12 +37,14 @@ class Array
37
37
  # an XSD datatype, and the second is the expression to be evaluated.
38
38
  # The result is cast as a literal of the appropriate type
39
39
  #
40
- # @param [RDF::Query::Solution, #[]] bindings
40
+ # @param [RDF::Query::Solution] bindings
41
41
  # a query solution containing zero or more variable bindings
42
+ # @param [Hash{Symbol => Object}] options ({})
43
+ # options passed from query
42
44
  # @return [RDF::Term]
43
- def evaluate(bindings)
44
- dt, val = self.map {|o| o.evaluate(bindings)}
45
- SPARQL::Algebra::Expression.extension(*self.map {|o| o.evaluate(bindings)})
45
+ # @see {SPARQL::Algebra::Expression.evaluate}
46
+ def evaluate(bindings, options = {})
47
+ SPARQL::Algebra::Expression.extension(*self.map {|o| o.evaluate(bindings, options)})
46
48
  end
47
49
 
48
50
  ##
@@ -58,6 +60,79 @@ class Array
58
60
  def execute(queryable, options = {})
59
61
  raise NotImplementedError, "SPARQL::Algebra '#{first}' operator not implemented"
60
62
  end
63
+
64
+ ##
65
+ # Returns `true` if any of the operands are variables, `false`
66
+ # otherwise.
67
+ #
68
+ # @return [Boolean] `true` or `false`
69
+ # @see #constant?
70
+ def variable?
71
+ any? do |operand|
72
+ operand.is_a?(Variable) ||
73
+ (operand.respond_to?(:variable?) && operand.variable?)
74
+ end
75
+ end
76
+ def constant?; !(variable?); end
77
+ def evaluatable?; true; end
78
+ def executable?; false; end
79
+ def aggregate?; false; end
80
+
81
+ ##
82
+ # Replace operators which are variables with the result of the block
83
+ # descending into operators which are also evaluatable
84
+ #
85
+ # @yield var
86
+ # @yieldparam [RDF::Query::Variable] var
87
+ # @yieldreturn [RDF::Query::Variable, SPARQL::Algebra::Evaluatable]
88
+ # @return [SPARQL::Algebra::Evaluatable] self
89
+ def replace_vars!(&block)
90
+ map! do |op|
91
+ case
92
+ when op.respond_to?(:variable?) && op.variable?
93
+ yield op
94
+ when op.respond_to?(:replace_vars!)
95
+ op.replace_vars!(&block)
96
+ else
97
+ op
98
+ end
99
+ end
100
+ self
101
+ end
102
+
103
+ ##
104
+ # Recursively re-map operators to replace aggregates with temporary variables returned from the block
105
+ #
106
+ # @yield agg
107
+ # @yieldparam [SPARQL::Algebra::Aggregate] agg
108
+ # @yieldreturn [RDF::Query::Variable]
109
+ # @return [SPARQL::Algebra::Evaluatable, RDF::Query::Variable] self
110
+ def replace_aggregate!(&block)
111
+ map! do |op|
112
+ case
113
+ when op.respond_to?(:aggregate?) && op.aggregate?
114
+ yield op
115
+ when op.respond_to?(:replace_aggregate!)
116
+ op.replace_aggregate!(&block)
117
+ else
118
+ op
119
+ end
120
+ end
121
+ self
122
+ end
123
+ end
124
+
125
+ ##
126
+ # Extensions for Ruby's `Hash` class.
127
+ class Hash
128
+ ##
129
+ # Returns the SXP representation of this object, defaults to `self`.
130
+ #
131
+ # @return [String]
132
+ def to_sxp_bin
133
+ to_a.to_sxp_bin
134
+ end
135
+ def to_sxp; to_sxp_bin; end
61
136
  end
62
137
 
63
138
  ##
@@ -65,10 +140,17 @@ end
65
140
  module RDF::Term
66
141
  include SPARQL::Algebra::Expression
67
142
 
68
- def evaluate(bindings)
143
+ # @param [RDF::Query::Solution] bindings
144
+ # a query solution containing zero or more variable bindings
145
+ # @param [Hash{Symbol => Object}] options ({})
146
+ # options passed from query
147
+ # @return [RDF::Term]
148
+ def evaluate(bindings, options = {})
69
149
  self
70
150
  end
71
151
 
152
+ def aggregate?; false; end
153
+
72
154
  # Term compatibility according to SPARQL
73
155
  #
74
156
  # Compatibility of two arguments is defined as:
@@ -185,13 +267,21 @@ class RDF::Query::Variable
185
267
  ##
186
268
  # Returns the value of this variable in the given `bindings`.
187
269
  #
188
- # @param [RDF::Query::Solution, #[]] bindings
270
+ # @param [RDF::Query::Solution] bindings
271
+ # a query solution containing zero or more variable bindings
272
+ # @param [Hash{Symbol => Object}] options ({})
273
+ # options passed from query
189
274
  # @return [RDF::Term] the value of this variable
190
275
  # @raise [TypeError] if the variable is not bound
191
- def evaluate(bindings = {})
276
+ def evaluate(bindings, options = {})
192
277
  raise TypeError if bindings.respond_to?(:bound?) && !bindings.bound?(self)
193
278
  bindings[name.to_sym]
194
279
  end
280
+
281
+ def to_s
282
+ prefix = distinguished? || name.to_s[0,1] == '.' ? '?' : "??"
283
+ unbound? ? "#{prefix}#{name}" : "#{prefix}#{name}=#{value}"
284
+ end
195
285
  end # RDF::Query::Variable
196
286
 
197
287
  ##
@@ -34,18 +34,21 @@ module SPARQL; module Algebra
34
34
  # Note that this operator operates on the effective boolean value
35
35
  # (EBV) of its operands.
36
36
  #
37
- # @param [RDF::Query::Solution, #[]] bindings
37
+ # @param [RDF::Query::Solution] bindings
38
+ # a query solution containing zero or more variable bindings
39
+ # @param [Hash{Symbol => Object}] options ({})
40
+ # options passed from query
38
41
  # @return [RDF::Literal::Boolean] `true` or `false`
39
42
  # @raise [TypeError] if the operands could not be coerced to boolean literals
40
- def evaluate(bindings = {})
43
+ def evaluate(bindings, options = {})
41
44
  begin
42
- left = boolean(operand(0).evaluate(bindings)).true?
45
+ left = boolean(operand(0).evaluate(bindings, options.merge(:depth => options[:depth].to_i + 1))).true?
43
46
  rescue TypeError
44
47
  left = nil
45
48
  end
46
49
 
47
50
  begin
48
- right = boolean(operand(1).evaluate(bindings)).true?
51
+ right = boolean(operand(1).evaluate(bindings, options.merge(:depth => options[:depth].to_i + 1))).true?
49
52
  rescue TypeError
50
53
  right = nil
51
54
  end
@@ -19,10 +19,13 @@ module SPARQL; module Algebra
19
19
  # Returns the evaluation of it's operand. Default comparison is in
20
20
  # ascending order. Ordering is applied in {Order}.
21
21
  #
22
- # @param [RDF::Query::Solution, #[]] bindings
22
+ # @param [RDF::Query::Solution] bindings
23
+ # a query solution containing zero or more variable bindings
24
+ # @param [Hash{Symbol => Object}] options ({})
25
+ # options passed from query
23
26
  # @return [RDF::Term]
24
- def evaluate(bindings = {})
25
- operand(0).evaluate(bindings)
27
+ def evaluate(bindings, options = {})
28
+ operand(0).evaluate(bindings, options.merge(:depth => options[:depth].to_i + 1))
26
29
  end
27
30
  end # Asc
28
31
  end # Operator
@@ -0,0 +1,36 @@
1
+ module SPARQL; module Algebra
2
+ class Operator
3
+ ##
4
+ # The SPARQL `avg` set function.
5
+ #
6
+ # @example
7
+ # (prefix ((: <http://www.example.org/>))
8
+ # (project (?avg)
9
+ # (extend ((?avg ?.0))
10
+ # (group () ((?.0 (avg ?o)))
11
+ # (bgp (triple ?s :dec ?o))))))
12
+ #
13
+ # @see http://www.w3.org/TR/sparql11-query/#defn_aggAvg
14
+ class Avg < Operator::Unary
15
+ include Aggregate
16
+
17
+ NAME = :avg
18
+
19
+ ##
20
+ # The Avg set function calculates the average value for an expression over a group. It is defined in terms of Sum and Count.
21
+ #
22
+ # @param [Enumerable<Array<RDF::Term>>] enum
23
+ # enum of evaluated operand
24
+ # @return [RDF::Literal::Numeric] The numeric average of the terms
25
+ def apply(enum)
26
+ if enum.empty?
27
+ RDF::Literal(0)
28
+ elsif enum.flatten.all? {|n| n.is_a?(RDF::Literal::Numeric)}
29
+ enum.flatten.reduce(:+) / RDF::Literal::Decimal.new(enum.length)
30
+ else
31
+ raise TypeError, "Averaging non-numeric types: #{enum.flatten}"
32
+ end
33
+ end
34
+ end # Avg
35
+ end # Operator
36
+ end; end # SPARQL::Algebra
@@ -35,12 +35,13 @@ module SPARQL; module Algebra
35
35
  ##
36
36
  # Evaluates this operator using the given variable `bindings`.
37
37
  #
38
- # @param [RDF::Query::Solution, #[]] bindings
38
+ # @param [RDF::Query::Solution] bindings
39
39
  # a query solution containing zero or more variable bindings
40
+ # @param [Hash{Symbol => Object}] options ({})
41
+ # options passed from query
40
42
  # @return [RDF::Term]
41
- # @abstract
42
- def evaluate(bindings = {})
43
- args = operands.map { |operand| operand.evaluate(bindings) }
43
+ def evaluate(bindings, options = {})
44
+ args = operands.map { |operand| operand.evaluate(bindings, options.merge(:depth => options[:depth].to_i + 1)) }
44
45
  apply(args.first, bindings)
45
46
  end
46
47
 
@@ -33,10 +33,13 @@ module SPARQL; module Algebra
33
33
  # Returns `true` if the operand is a variable that is bound to a
34
34
  # value in the given `bindings`, `false` otherwise.
35
35
  #
36
- # @param [RDF::Query::Solution, #[]] bindings
36
+ # @param [RDF::Query::Solution] bindings
37
+ # a query solution containing zero or more variable bindings
38
+ # @param [Hash{Symbol => Object}] options ({})
39
+ # options passed from query
37
40
  # @return [RDF::Literal::Boolean] `true` or `false`
38
41
  # @raise [TypeError] if the operand is not a variable
39
- def evaluate(bindings = {})
42
+ def evaluate(bindings, options = {})
40
43
  case var = operand
41
44
  when Variable
42
45
  bindings.respond_to?(:bound?) && bindings.bound?(var) ?
@@ -38,13 +38,16 @@ module SPARQL; module Algebra
38
38
  # COALESCE(?y, 3) #=> 3
39
39
  # COALESCE(?y) #=> raises an error because y is not bound.
40
40
  #
41
- # @param [RDF::Query::Solution, #[]] bindings
41
+ # @param [RDF::Query::Solution] bindings
42
+ # a query solution containing zero or more variable bindings
43
+ # @param [Hash{Symbol => Object}] options ({})
44
+ # options passed from query
42
45
  # @return [RDF::Term]
43
46
  # @raise [TypeError] if none of the operands succeeds
44
- def evaluate(bindings = {})
47
+ def evaluate(bindings, options = {})
45
48
  operands.each do |op|
46
49
  begin
47
- return op.evaluate(bindings)
50
+ return op.evaluate(bindings, options.merge(:depth => options[:depth].to_i + 1))
48
51
  rescue
49
52
  end
50
53
  end