sparql 1.0.8 → 1.1.0p0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +14 -6
- data/README.md +57 -25
- data/VERSION +1 -1
- data/lib/sinatra/sparql.rb +5 -3
- data/lib/sparql/algebra/aggregate.rb +67 -0
- data/lib/sparql/algebra/evaluatable.rb +49 -4
- data/lib/sparql/algebra/expression.rb +6 -4
- data/lib/sparql/algebra/extensions.rb +99 -9
- data/lib/sparql/algebra/operator/and.rb +7 -4
- data/lib/sparql/algebra/operator/asc.rb +6 -3
- data/lib/sparql/algebra/operator/avg.rb +36 -0
- data/lib/sparql/algebra/operator/bnode.rb +5 -4
- data/lib/sparql/algebra/operator/bound.rb +5 -2
- data/lib/sparql/algebra/operator/coalesce.rb +6 -3
- data/lib/sparql/algebra/operator/concat.rb +6 -3
- data/lib/sparql/algebra/operator/count.rb +30 -0
- data/lib/sparql/algebra/operator/exists.rb +39 -0
- data/lib/sparql/algebra/operator/exprlist.rb +6 -3
- data/lib/sparql/algebra/operator/extend.rb +3 -1
- data/lib/sparql/algebra/operator/filter.rb +2 -1
- data/lib/sparql/algebra/operator/group.rb +101 -0
- data/lib/sparql/algebra/operator/group_concat.rb +55 -0
- data/lib/sparql/algebra/operator/if.rb +5 -5
- data/lib/sparql/algebra/operator/in.rb +7 -4
- data/lib/sparql/algebra/operator/max.rb +38 -0
- data/lib/sparql/algebra/operator/min.rb +38 -0
- data/lib/sparql/algebra/operator/minus.rb +51 -16
- data/lib/sparql/algebra/operator/negate.rb +31 -0
- data/lib/sparql/algebra/operator/notexists.rb +37 -0
- data/lib/sparql/algebra/operator/notin.rb +7 -4
- data/lib/sparql/algebra/operator/or.rb +7 -4
- data/lib/sparql/algebra/operator/order.rb +2 -2
- data/lib/sparql/algebra/operator/sample.rb +32 -0
- data/lib/sparql/algebra/operator/strlang.rb +1 -1
- data/lib/sparql/algebra/operator/sum.rb +36 -0
- data/lib/sparql/algebra/operator/table.rb +44 -0
- data/lib/sparql/algebra/operator.rb +37 -2
- data/lib/sparql/algebra.rb +1 -0
- data/lib/sparql/grammar/meta.rb +1143 -696
- data/lib/sparql/grammar/parser11.rb +178 -32
- data/lib/sparql/grammar/terminals11.rb +2 -2
- data/lib/sparql/grammar.rb +0 -2
- data/lib/sparql/results.rb +55 -0
- metadata +78 -81
checksums.yaml
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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.
|
13
|
-
*
|
14
|
-
|
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
|
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
|
-
*
|
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 ".".
|
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
|
-
*
|
52
|
-
*
|
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
|
-
|
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
|
-
|
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
|
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.
|
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
|
-
[
|
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.
|
1
|
+
1.1.0p0
|
data/lib/sinatra/sparql.rb
CHANGED
@@ -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("#
|
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/
|
54
|
-
g << [node, sd.join("#resultFormat"), RDF::URI("http://www.w3.org/ns/formats/
|
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
|
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
|
-
|
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>]
|
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
|
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 `
|
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
|
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
|
-
|
44
|
-
|
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
|
-
|
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
|
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
|
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
|
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
|
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
|
-
|
42
|
-
|
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
|
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
|
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
|