sparql 1.0.8 → 1.1.0p0
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.
- 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
|