sparql 3.1.8 → 3.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +88 -60
- data/VERSION +1 -1
- data/bin/sparql +15 -35
- data/lib/rack/sparql/conneg.rb +22 -1
- data/lib/sinatra/sparql/extensions.rb +1 -1
- data/lib/sinatra/sparql.rb +57 -12
- data/lib/sparql/algebra/expression.rb +63 -10
- data/lib/sparql/algebra/extensions.rb +110 -46
- data/lib/sparql/algebra/operator/abs.rb +22 -2
- data/lib/sparql/algebra/operator/add.rb +21 -2
- data/lib/sparql/algebra/operator/adjust.rb +69 -0
- data/lib/sparql/algebra/operator/alt.rb +26 -2
- data/lib/sparql/algebra/operator/and.rb +25 -3
- data/lib/sparql/algebra/operator/asc.rb +20 -1
- data/lib/sparql/algebra/operator/ask.rb +17 -1
- data/lib/sparql/algebra/operator/avg.rb +19 -1
- data/lib/sparql/algebra/operator/base.rb +18 -1
- data/lib/sparql/algebra/operator/bgp.rb +13 -1
- data/lib/sparql/algebra/operator/bnode.rb +33 -10
- data/lib/sparql/algebra/operator/bound.rb +22 -1
- data/lib/sparql/algebra/operator/ceil.rb +25 -2
- data/lib/sparql/algebra/operator/clear.rb +26 -2
- data/lib/sparql/algebra/operator/coalesce.rb +33 -11
- data/lib/sparql/algebra/operator/compare.rb +9 -0
- data/lib/sparql/algebra/operator/concat.rb +26 -2
- data/lib/sparql/algebra/operator/construct.rb +29 -6
- data/lib/sparql/algebra/operator/contains.rb +24 -2
- data/lib/sparql/algebra/operator/copy.rb +19 -2
- data/lib/sparql/algebra/operator/count.rb +52 -6
- data/lib/sparql/algebra/operator/create.rb +20 -2
- data/lib/sparql/algebra/operator/dataset.rb +37 -2
- data/lib/sparql/algebra/operator/datatype.rb +25 -6
- data/lib/sparql/algebra/operator/day.rb +25 -7
- data/lib/sparql/algebra/operator/delete.rb +29 -2
- data/lib/sparql/algebra/operator/delete_data.rb +23 -2
- data/lib/sparql/algebra/operator/delete_where.rb +24 -2
- data/lib/sparql/algebra/operator/desc.rb +20 -1
- data/lib/sparql/algebra/operator/describe.rb +27 -4
- data/lib/sparql/algebra/operator/distinct.rb +20 -3
- data/lib/sparql/algebra/operator/divide.rb +26 -2
- data/lib/sparql/algebra/operator/drop.rb +27 -3
- data/lib/sparql/algebra/operator/encode_for_uri.rb +22 -2
- data/lib/sparql/algebra/operator/equal.rb +12 -2
- data/lib/sparql/algebra/operator/exists.rb +28 -4
- data/lib/sparql/algebra/operator/exprlist.rb +15 -2
- data/lib/sparql/algebra/operator/extend.rb +95 -7
- data/lib/sparql/algebra/operator/filter.rb +27 -5
- data/lib/sparql/algebra/operator/floor.rb +25 -2
- data/lib/sparql/algebra/operator/function_call.rb +64 -0
- data/lib/sparql/algebra/operator/graph.rb +69 -6
- data/lib/sparql/algebra/operator/greater_than.rb +12 -3
- data/lib/sparql/algebra/operator/greater_than_or_equal.rb +12 -2
- data/lib/sparql/algebra/operator/group.rb +133 -8
- data/lib/sparql/algebra/operator/group_concat.rb +43 -7
- data/lib/sparql/algebra/operator/hours.rb +25 -7
- data/lib/sparql/algebra/operator/if.rb +20 -3
- data/lib/sparql/algebra/operator/in.rb +18 -1
- data/lib/sparql/algebra/operator/insert.rb +24 -2
- data/lib/sparql/algebra/operator/insert_data.rb +23 -2
- data/lib/sparql/algebra/operator/iri.rb +21 -4
- data/lib/sparql/algebra/operator/is_blank.rb +20 -3
- data/lib/sparql/algebra/operator/is_iri.rb +20 -3
- data/lib/sparql/algebra/operator/is_literal.rb +20 -3
- data/lib/sparql/algebra/operator/is_numeric.rb +22 -5
- data/lib/sparql/algebra/operator/is_triple.rb +32 -0
- data/lib/sparql/algebra/operator/join.rb +58 -3
- data/lib/sparql/algebra/operator/lang.rb +25 -0
- data/lib/sparql/algebra/operator/lang_matches.rb +22 -1
- data/lib/sparql/algebra/operator/lcase.rb +22 -2
- data/lib/sparql/algebra/operator/left_join.rb +44 -3
- data/lib/sparql/algebra/operator/less_than.rb +12 -3
- data/lib/sparql/algebra/operator/less_than_or_equal.rb +12 -2
- data/lib/sparql/algebra/operator/load.rb +25 -2
- data/lib/sparql/algebra/operator/max.rb +19 -1
- data/lib/sparql/algebra/operator/md5.rb +22 -5
- data/lib/sparql/algebra/operator/min.rb +21 -3
- data/lib/sparql/algebra/operator/minus.rb +65 -7
- data/lib/sparql/algebra/operator/minutes.rb +25 -7
- data/lib/sparql/algebra/operator/modify.rb +62 -5
- data/lib/sparql/algebra/operator/month.rb +25 -7
- data/lib/sparql/algebra/operator/move.rb +20 -2
- data/lib/sparql/algebra/operator/multiply.rb +26 -3
- data/lib/sparql/algebra/operator/negate.rb +23 -3
- data/lib/sparql/algebra/operator/not.rb +24 -3
- data/lib/sparql/algebra/operator/not_equal.rb +13 -0
- data/lib/sparql/algebra/operator/notexists.rb +30 -6
- data/lib/sparql/algebra/operator/notin.rb +20 -3
- data/lib/sparql/algebra/operator/notoneof.rb +21 -2
- data/lib/sparql/algebra/operator/now.rb +24 -5
- data/lib/sparql/algebra/operator/object.rb +32 -0
- data/lib/sparql/algebra/operator/or.rb +26 -3
- data/lib/sparql/algebra/operator/order.rb +64 -1
- data/lib/sparql/algebra/operator/path.rb +29 -2
- data/lib/sparql/algebra/operator/path_opt.rb +28 -65
- data/lib/sparql/algebra/operator/path_plus.rb +37 -10
- data/lib/sparql/algebra/operator/path_range.rb +178 -0
- data/lib/sparql/algebra/operator/path_star.rb +25 -4
- data/lib/sparql/algebra/operator/path_zero.rb +110 -0
- data/lib/sparql/algebra/operator/plus.rb +49 -8
- data/lib/sparql/algebra/operator/predicate.rb +32 -0
- data/lib/sparql/algebra/operator/prefix.rb +24 -3
- data/lib/sparql/algebra/operator/project.rb +111 -6
- data/lib/sparql/algebra/operator/rand.rb +30 -2
- data/lib/sparql/algebra/operator/reduced.rb +20 -3
- data/lib/sparql/algebra/operator/regex.rb +26 -18
- data/lib/sparql/algebra/operator/replace.rb +26 -6
- data/lib/sparql/algebra/operator/reverse.rb +31 -2
- data/lib/sparql/algebra/operator/round.rb +25 -2
- data/lib/sparql/algebra/operator/same_term.rb +24 -6
- data/lib/sparql/algebra/operator/sample.rb +32 -8
- data/lib/sparql/algebra/operator/seconds.rb +25 -7
- data/lib/sparql/algebra/operator/seq.rb +23 -5
- data/lib/sparql/algebra/operator/sequence.rb +14 -11
- data/lib/sparql/algebra/operator/sha1.rb +18 -1
- data/lib/sparql/algebra/operator/sha256.rb +18 -1
- data/lib/sparql/algebra/operator/sha384.rb +18 -1
- data/lib/sparql/algebra/operator/sha512.rb +18 -1
- data/lib/sparql/algebra/operator/slice.rb +27 -5
- data/lib/sparql/algebra/operator/str.rb +21 -1
- data/lib/sparql/algebra/operator/strafter.rb +25 -2
- data/lib/sparql/algebra/operator/strbefore.rb +25 -2
- data/lib/sparql/algebra/operator/strdt.rb +22 -1
- data/lib/sparql/algebra/operator/strends.rb +25 -3
- data/lib/sparql/algebra/operator/strlang.rb +24 -6
- data/lib/sparql/algebra/operator/strlen.rb +23 -2
- data/lib/sparql/algebra/operator/strstarts.rb +25 -2
- data/lib/sparql/algebra/operator/struuid.rb +29 -9
- data/lib/sparql/algebra/operator/subject.rb +32 -0
- data/lib/sparql/algebra/operator/substr.rb +23 -2
- data/lib/sparql/algebra/operator/subtract.rb +37 -7
- data/lib/sparql/algebra/operator/sum.rb +24 -6
- data/lib/sparql/algebra/operator/table.rb +85 -4
- data/lib/sparql/algebra/operator/timezone.rb +25 -7
- data/lib/sparql/algebra/operator/triple.rb +24 -0
- data/lib/sparql/algebra/operator/tz.rb +24 -7
- data/lib/sparql/algebra/operator/ucase.rb +23 -2
- data/lib/sparql/algebra/operator/union.rb +29 -6
- data/lib/sparql/algebra/operator/update.rb +46 -4
- data/lib/sparql/algebra/operator/using.rb +49 -2
- data/lib/sparql/algebra/operator/uuid.rb +27 -8
- data/lib/sparql/algebra/operator/with.rb +38 -4
- data/lib/sparql/algebra/operator/year.rb +25 -7
- data/lib/sparql/algebra/operator.rb +150 -12
- data/lib/sparql/algebra/query.rb +5 -3
- data/lib/sparql/algebra/sxp_extensions.rb +3 -3
- data/lib/sparql/algebra.rb +42 -6
- data/lib/sparql/grammar/meta.rb +1367 -267
- data/lib/sparql/grammar/parser11.rb +829 -331
- data/lib/sparql/grammar/terminals11.rb +2 -2
- data/lib/sparql/grammar.rb +6 -4
- data/lib/sparql/results.rb +3 -2
- data/lib/sparql/server.rb +93 -0
- data/lib/sparql.rb +8 -5
- metadata +57 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 853c52b99bab0b9455b25a81c8c946e5bcb1429dddadf010b66315f1dbafda51
|
4
|
+
data.tar.gz: 90d89877c031476efdaa1d0b119b2a2404925186b4e0b3de99818d75ead76bff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 98816c81664147aa0fa4ccd90ed14461eb51b652b011395f38f044934c2ff335197fbc3d2d7f447771a5b1e46fa24042aef45fd1f95356b91f52bafee48d1a72
|
7
|
+
data.tar.gz: 5545d7ddd245d73382229ed6f9996b24bd66a4285ee18d6295f697202ad5901eb4221cbf2533ef1539b98ccc0c095519e531b541db3e1c4757d813ab888cb9fd
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
# SPARQL for
|
1
|
+
# SPARQL Query and Update library for Ruby
|
2
2
|
|
3
|
-
|
3
|
+
An implementation of [SPARQL][] for [RDF.rb][].
|
4
4
|
|
5
5
|
[](https://badge.fury.io/rb/sparql)
|
6
6
|
[](https://github.com/ruby-rdf/sparql/actions?query=workflow%3ACI)
|
@@ -17,24 +17,26 @@ This is a [Ruby][] implementation of [SPARQL][] for [RDF.rb][].
|
|
17
17
|
or HTML.
|
18
18
|
* SPARQL CONSTRUCT or DESCRIBE serialized based on Format, Extension of Mime Type
|
19
19
|
using available RDF Writers (see [Linked Data][])
|
20
|
-
* SPARQL Client for accessing remote SPARQL endpoints.
|
21
|
-
* SPARQL
|
20
|
+
* SPARQL Client for accessing remote SPARQL endpoints (via [sparql-client](https://github.com/ruby-rdf/sparql-client)).
|
21
|
+
* [SPARQL 1.1 Protocol][] (via {SPARQL::Server}).
|
22
|
+
* [SPARQL 1.1 Update][]
|
22
23
|
* [Rack][] and [Sinatra][] middleware to perform [HTTP content negotiation][conneg] for result formats
|
23
24
|
* Compatible with any [Rack][] or [Sinatra][] application and any Rack-based framework.
|
24
25
|
* Helper method for describing [SPARQL Service Description][SSD]
|
26
|
+
* Helper method for setting up datasets as part of the [SPARQL 1.1 Protocol][].
|
25
27
|
* Implementation Report: {file:etc/earl.html EARL}
|
26
|
-
* Compatible with Ruby >= 2.
|
27
|
-
* Compatible with older Ruby versions with the help of the [Backports][] gem.
|
28
|
+
* Compatible with Ruby >= 2.6.
|
28
29
|
* Supports Unicode query strings both on all versions of Ruby.
|
29
30
|
* Provisional support for [SPARQL-star][].
|
30
31
|
|
31
32
|
## Description
|
32
33
|
|
33
|
-
The {SPARQL} gem implements [SPARQL 1.1 Query][], and [SPARQL 1.1 Update][], and provides [Rack][] and [Sinatra][] middleware to provide results using [HTTP Content Negotiation][conneg].
|
34
|
+
The {SPARQL} gem implements [SPARQL 1.1 Query][], and [SPARQL 1.1 Update][], and provides [Rack][] and [Sinatra][] middleware to provide results using [HTTP Content Negotiation][conneg] and to support [SPARQL 1.1 Protocol][].
|
34
35
|
|
35
36
|
* {SPARQL::Grammar} implements a [SPARQL 1.1 Query][] and [SPARQL 1.1 Update][] parser generating [SPARQL S-Expressions (SSE)][SSE].
|
36
37
|
* {SPARQL::Algebra} executes SSE against Any `RDF::Graph` or `RDF::Repository`, including compliant [RDF.rb][] repository adaptors such as [RDF::DO][] and [RDF::Mongo][].
|
37
38
|
* {Rack::SPARQL} and {Sinatra::SPARQL} provide middleware components to format results using an appropriate format based on [HTTP content negotiation][conneg].
|
39
|
+
* {SPARQL::Server} implements the [SPARQL 1.1 Protocol][] using {Sinatra::SPARQL}.
|
38
40
|
|
39
41
|
### [SPARQL 1.1 Query][] Extensions and Limitations
|
40
42
|
The {SPARQL} gem uses the [SPARQL 1.1 Query][] {file:etc/sparql11.html EBNF grammar}, which provides much more capability than [SPARQL 1.0][], but has a few limitations:
|
@@ -63,17 +65,22 @@ The gem also includes the following [SPARQL 1.1 Update][] operations:
|
|
63
65
|
Not supported:
|
64
66
|
|
65
67
|
* [Federated Query][SPARQL 1.1 Federated Query],
|
66
|
-
* [Entailment Regimes][SPARQL 1.1 Entailment Regimes],
|
67
|
-
* [Protocol][SPARQL 1.1 Protocol]
|
68
|
-
* [Graph Store HTTP Protocol][SPARQL 1.1 Graph Store HTTP Protocol]
|
69
|
-
|
70
|
-
either in this, or related gems.
|
68
|
+
* [Entailment Regimes][SPARQL 1.1 Entailment Regimes], and
|
69
|
+
* [Graph Store HTTP Protocol][SPARQL 1.1 Graph Store HTTP Protocol] but the closely related [Linked Data Platform][] implemented in [rdf-ldp](https://github.com/ruby-rdf/rdf-ldp) supports these use cases.
|
71
70
|
|
72
71
|
### Updates for RDF 1.1
|
73
72
|
Starting with version 1.1.2, the SPARQL gem uses the 1.1 version of the [RDF.rb][], which adheres to [RDF 1.1 Concepts](https://www.w3.org/TR/rdf11-concepts/) rather than [RDF 1.0](https://www.w3.org/TR/rdf-concepts/). The main difference is that there is now no difference between a _Simple Literal_ (a literal with no datatype or language) and a Literal with datatype _xsd:string_; this causes some minor differences in the way in which queries are understood, and when expecting different results.
|
74
73
|
|
75
74
|
Additionally, queries now take a block, or return an `Enumerator`; this is in keeping with much of the behavior of [RDF.rb][] methods, including `Queryable#query`, and with version 1.1 or [RDF.rb][], Query#execute. As a consequence, all queries which used to be of the form `query.execute(repository)` may equally be called as `repository.query(query)`. Previously, results were returned as a concrete class implementing `RDF::Queryable` or `RDF::Query::Solutions`, these are now `Enumerators`.
|
76
75
|
|
76
|
+
### SPARQL 1.2
|
77
|
+
The gem supports some of the extensions proposed by the [SPARQL 1.2 Community Group](https://github.com/w3c/sparql-12). In particular, the following extensions are now implemented:
|
78
|
+
|
79
|
+
* [SEP-0002: better support for Durations, Dates, and Times](https://github.com/w3c/sparql-12/blob/main/SEP/SEP-0002/sep-0002.md)
|
80
|
+
* This includes full support for `xsd:date`, `xsd:time`, `xsd:duration`, `xsd:dayTimeDuration`, and `xsd:yearMonthDuration` along with associated XPath/XQuery functions including a new `ADJUST` builtin. (**Note: This feature is subject to change or elimination as the standards process progresses.**)
|
81
|
+
* [SEP-0003: Property paths with a min/max hop](https://github.com/w3c/sparql-12/blob/main/SEP/SEP-0003/sep-0003.md)
|
82
|
+
* This includes support for non-counting path forms such as `rdf:rest{1,3}` to match the union of paths `rdf:rest`, `rdf:rest/rdf:rest`, and `rdf:rest/rdf:rest/rdf:rest`. (**Note: This feature is subject to change or elimination as the standards process progresses.**)
|
83
|
+
|
77
84
|
### SPARQL Extension Functions
|
78
85
|
Extension functions may be defined, which will be invoked during query evaluation. For example:
|
79
86
|
|
@@ -96,6 +103,10 @@ Then, use the function in a query:
|
|
96
103
|
|
97
104
|
See {SPARQL::Algebra::Expression.register_extension} for details.
|
98
105
|
|
106
|
+
### Variable Pre-binding
|
107
|
+
|
108
|
+
A call to execute a parsed query can include pre-bound variables, which cause queries to be executed with matching variables bound as defined. Variable pre-binding can be done using a Hash structure, or a Query Solution. See [Query with Binding example](#query-with-binding) and {SPARQL::Algebra::Query#execute}.
|
109
|
+
|
99
110
|
### SPARQLStar (SPARQL-star)
|
100
111
|
|
101
112
|
The gem supports [SPARQL-star][] where patterns may include sub-patterns recursively, for a kind of Reification.
|
@@ -163,44 +174,31 @@ Note that results can be serialized only when the format supports [RDF-star][].
|
|
163
174
|
|
164
175
|
#### SPARQL results
|
165
176
|
|
166
|
-
The SPARQL results formats are extended to serialize
|
177
|
+
The SPARQL results formats are extended to serialize quoted triples as described for [RDF4J](https://rdf4j.org/documentation/programming/rdfstar/):
|
167
178
|
|
168
179
|
{
|
169
180
|
"head" : {
|
170
|
-
"vars" : [
|
171
|
-
"a",
|
172
|
-
"b",
|
173
|
-
"c"
|
174
|
-
]
|
181
|
+
"vars" : ["a", "b", "c"]
|
175
182
|
},
|
176
183
|
"results" : {
|
177
184
|
"bindings": [
|
178
185
|
{ "a" : {
|
179
186
|
"type" : "triple",
|
180
187
|
"value" : {
|
181
|
-
"s" : {
|
182
|
-
|
183
|
-
"value" : "http://example.org/bob"
|
184
|
-
},
|
185
|
-
"p" : {
|
186
|
-
"type" : "uri",
|
187
|
-
"value" : "http://xmlns.com/foaf/0.1/name"
|
188
|
-
},
|
188
|
+
"s" : {"value" : "http://example.org/bob", "type": "uri"},
|
189
|
+
"p" : {"value" : "http://xmlns.com/foaf/0.1/name", "type": "uri"},
|
189
190
|
"o" : {
|
190
|
-
"
|
191
|
+
"value" : "23",
|
191
192
|
"type" : "literal",
|
192
|
-
"
|
193
|
+
"datatype" : "http://www.w3.org/2001/XMLSchema#integer"
|
193
194
|
}
|
194
195
|
}
|
195
196
|
},
|
196
|
-
"b": {
|
197
|
-
"type": "uri",
|
198
|
-
"value": "http://example.org/certainty"
|
199
|
-
},
|
197
|
+
"b": {"value": "http://example.org/certainty", "type": "uri"},
|
200
198
|
"c" : {
|
201
|
-
"
|
199
|
+
"value" : "0.9",
|
202
200
|
"type" : "literal",
|
203
|
-
"
|
201
|
+
"datatype" : "http://www.w3.org/2001/XMLSchema#decimal"
|
204
202
|
}
|
205
203
|
}
|
206
204
|
]
|
@@ -215,17 +213,23 @@ You would typically return an instance of `RDF::Graph`, `RDF::Repository` or an
|
|
215
213
|
from your Rack application, and let the `Rack::SPARQL::ContentNegotiation` middleware
|
216
214
|
take care of serializing your response into whatever format the HTTP
|
217
215
|
client requested and understands.
|
216
|
+
Content negotiation also transforms `application/x-www-form-urlencoded` to either `application/sparql-query`
|
217
|
+
or `application/sparql-update` as appropriate for [SPARQL 1.1 Protocol][].
|
218
218
|
|
219
219
|
{Sinatra::SPARQL} is a thin Sinatra-specific wrapper around the
|
220
220
|
{Rack::SPARQL} middleware, which implements SPARQL
|
221
221
|
content negotiation for Rack applications. {Sinatra::SPARQL} also supports
|
222
|
-
[SPARQL 1.1 Service Description][].
|
222
|
+
[SPARQL 1.1 Service Description][] (via {Sinatra::SPARQL::Helpers.service_description} and protocol-based dataset mangement via {Sinatra::SPARQL::Helpers.dataset} for `default-graph-uri` and `named-graph-uri` The `using-graph-uri` and `using-named-graph-uri` query parameters are managed through {SPARQL::Algebra::Operator::Modify#execute}.
|
223
223
|
|
224
224
|
The middleware queries [RDF.rb][] for the MIME content types of known RDF
|
225
225
|
serialization formats, so it will work with whatever serialization extensions
|
226
226
|
that are currently available for RDF.rb. (At present, this includes support
|
227
227
|
for N-Triples, N-Quads, Turtle, RDF/XML, RDF/JSON, JSON-LD, RDFa, TriG and TriX.)
|
228
228
|
|
229
|
+
### Server
|
230
|
+
|
231
|
+
A simple [Sinatra][]-based server is implemented in {SPARQL::Server.application} using {Rack::SPARQL} and {Sinatra::SPARQL} completes the implementation of [SPARQL 1.1 Protocol][] and can be used to compose a server including other capabilities.
|
232
|
+
|
229
233
|
### Remote datasets
|
230
234
|
|
231
235
|
A SPARQL query containing `FROM` or `FROM NAMED` (also `UPDATE` or `UPDATE NAMED`) will load the referenced IRI unless the repository already contains a graph with that same IRI. This is performed using [RDF.rb][] `RDF::Util::File.open_file` passing HTTP Accept headers for various available RDF formats. For best results, require [Linked Data][] to enable a full set of RDF formats in the `GET` request. Also, consider overriding `RDF::Util::File.open_file` with an implementation with support for HTTP Get headers (such as `Net::HTTP`).
|
@@ -248,27 +252,27 @@ a full set of RDF formats.
|
|
248
252
|
### Querying a repository with a SPARQL query
|
249
253
|
|
250
254
|
queryable = RDF::Repository.load("etc/doap.ttl")
|
251
|
-
|
252
|
-
queryable.query(
|
255
|
+
query = SPARQL.parse("SELECT * WHERE { ?s ?p ?o }")
|
256
|
+
queryable.query(query) do |result|
|
253
257
|
result.inspect
|
254
258
|
end
|
255
259
|
|
256
260
|
### Executing a SPARQL query against a repository
|
257
261
|
|
258
262
|
queryable = RDF::Repository.load("etc/doap.ttl")
|
259
|
-
|
260
|
-
|
263
|
+
query = SPARQL.parse("SELECT * WHERE { ?s ?p ?o }")
|
264
|
+
query.execute(queryable) do |result|
|
261
265
|
result.inspect
|
262
266
|
end
|
263
267
|
|
264
268
|
### Updating a repository
|
265
269
|
|
266
270
|
queryable = RDF::Repository.load("etc/doap.ttl")
|
267
|
-
|
271
|
+
update = SPARQL.parse(%(
|
268
272
|
PREFIX doap: <http://usefulinc.com/ns/doap#>
|
269
273
|
INSERT DATA { <https://rubygems> doap:implements <http://www.w3.org/TR/sparql11-update/>}
|
270
274
|
), update: true)
|
271
|
-
|
275
|
+
update.execute(queryable)
|
272
276
|
|
273
277
|
### Rendering solutions as JSON, XML, CSV, TSV or HTML
|
274
278
|
queryable = RDF::Repository.load("etc/doap.ttl")
|
@@ -277,8 +281,29 @@ a full set of RDF formats.
|
|
277
281
|
|
278
282
|
### Parsing a SPARQL query string to SSE
|
279
283
|
|
280
|
-
|
281
|
-
|
284
|
+
query = SPARQL.parse("SELECT * WHERE { ?s ?p ?o }")
|
285
|
+
query.to_sxp #=> (bgp (triple ?s ?p ?o))
|
286
|
+
|
287
|
+
### Parsing a SSE to SPARQL query or update string to SPARQL
|
288
|
+
|
289
|
+
# Note: if the SSE uses extension functions, they either must be XSD casting functions, or custom functions which are registered extensions. (See [SPARQL Extension Functions](#sparql-extension-functions))
|
290
|
+
|
291
|
+
query = SPARQL::Algebra.parse(%{(bgp (triple ?s ?p ?o))})
|
292
|
+
sparql = query.to_sparql #=> "SELECT * WHERE { ?s ?p ?o }"
|
293
|
+
|
294
|
+
### Query with Binding
|
295
|
+
|
296
|
+
bindings = {page: RDF::URI("https://greggkellogg.net/")}
|
297
|
+
queryable = RDF::Repository.load("etc/doap.ttl")
|
298
|
+
query = SPARQL.parse(%(
|
299
|
+
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
|
300
|
+
SELECT ?person
|
301
|
+
WHERE {
|
302
|
+
?person foaf:homepage ?page .
|
303
|
+
}
|
304
|
+
))
|
305
|
+
solutions = query.execute(queryable, bindings: bindings)
|
306
|
+
solutions.to_sxp #=> (((person <https://greggkellogg.net/foaf#me>)))
|
282
307
|
|
283
308
|
### Command line processing
|
284
309
|
|
@@ -289,6 +314,10 @@ a full set of RDF formats.
|
|
289
314
|
sparql parse etc/input.rq
|
290
315
|
sparql parse -e "SELECT * WHERE { ?s ?p ?o }"
|
291
316
|
|
317
|
+
# Generate SPARQL Query from SSE
|
318
|
+
sparql parse --sse etc/input.sse --format sparql
|
319
|
+
sparql parse --sse --format sparql -e "(dataset (<http://usefulinc.com/ns/doap>) (bgp (triple ?s ?p ?o))))"
|
320
|
+
|
292
321
|
# Run query using SSE input
|
293
322
|
sparql execute --dataset etc/doap.ttl --sse etc/input.sse
|
294
323
|
sparql execute --sse -e "(dataset (<etc/doap.ttl>) (bgp (triple ?s ?p ?o))))"
|
@@ -368,19 +397,19 @@ Full documentation available on [Rubydoc.info][SPARQL doc]
|
|
368
397
|
|
369
398
|
## Dependencies
|
370
399
|
|
371
|
-
* [Ruby](https://ruby-lang.org/) (>= 2.
|
372
|
-
* [RDF.rb](https://rubygems.org/gems/rdf) (~> 3.
|
373
|
-
* [SPARQL::Client](https://rubygems.org/gems/sparql-client) (~> 3.
|
374
|
-
* [SXP](https://rubygems.org/gems/sxp) (~> 1.
|
375
|
-
* [Builder](https://rubygems.org/gems/builder) (
|
376
|
-
* [JSON](https://rubygems.org/gems/json) (
|
377
|
-
* Soft dependency on [Linked Data][] (>= 3.
|
378
|
-
* Soft dependency on [Nokogiri](https://rubygems.org/gems/nokogiri) (
|
400
|
+
* [Ruby](https://ruby-lang.org/) (>= 2.6)
|
401
|
+
* [RDF.rb](https://rubygems.org/gems/rdf) (~> 3.2)
|
402
|
+
* [SPARQL::Client](https://rubygems.org/gems/sparql-client) (~> 3.1)
|
403
|
+
* [SXP](https://rubygems.org/gems/sxp) (~> 1.2)
|
404
|
+
* [Builder](https://rubygems.org/gems/builder) (~> 3.2)
|
405
|
+
* [JSON](https://rubygems.org/gems/json) (~> 2.6)
|
406
|
+
* Soft dependency on [Linked Data][] (>= 3.1)
|
407
|
+
* Soft dependency on [Nokogiri](https://rubygems.org/gems/nokogiri) (~> 1.12)
|
379
408
|
Falls back to REXML for XML parsing Builder for XML serializing. Nokogiri is much more efficient
|
380
|
-
* Soft dependency on [Equivalent XML](https://rubygems.org/gems/equivalent-xml) (>= 0.
|
409
|
+
* Soft dependency on [Equivalent XML](https://rubygems.org/gems/equivalent-xml) (>= 0.6)
|
381
410
|
Equivalent XML performs more efficient comparisons of XML Literals when Nokogiri is included
|
382
|
-
* Soft dependency on [Rack][] (
|
383
|
-
* Soft dependency on [Sinatra][] (
|
411
|
+
* Soft dependency on [Rack][] (~> 2.2)
|
412
|
+
* Soft dependency on [Sinatra][] (~> 2.1)
|
384
413
|
|
385
414
|
## Installation
|
386
415
|
|
@@ -425,7 +454,7 @@ This repository uses [Git Flow](https://github.com/nvie/gitflow) to mange develo
|
|
425
454
|
## License
|
426
455
|
|
427
456
|
This is free and unencumbered public domain software. For more information,
|
428
|
-
see <https://unlicense.org/> or the accompanying {file:UNLICENSE}
|
457
|
+
see <https://unlicense.org/> or the accompanying {file:UNLICENSE}.
|
429
458
|
|
430
459
|
A copy of the [SPARQL EBNF][] and derived parser files are included in the repository, which are not covered under the UNLICENSE. These files are covered via the [W3C Document License](https://www.w3.org/Consortium/Legal/2002/copyright-documents-20021231).
|
431
460
|
|
@@ -444,15 +473,14 @@ A copy of the [SPARQL 1.0 tests][] and [SPARQL 1.1 tests][] are also included in
|
|
444
473
|
[SPARQL 1.0 tests]:https://www.w3.org/2001/sw/DataAccess/tests/
|
445
474
|
[SPARQL 1.1 tests]: https://www.w3.org/2009/sparql/docs/tests/
|
446
475
|
[SSE]: https://jena.apache.org/documentation/notes/sse.html
|
447
|
-
[SXP]: https://
|
476
|
+
[SXP]: https://dryruby.github.io/sxp
|
448
477
|
[grammar]: https://www.w3.org/TR/sparql11-query/#grammar
|
449
478
|
[RDF 1.1]: https://www.w3.org/TR/rdf11-concepts
|
450
|
-
[RDF.rb]: https://
|
479
|
+
[RDF.rb]: https://ruby-rdf.github.io/rdf
|
451
480
|
[RDF-star]: https://w3c.github.io/rdf-star/rdf-star-cg-spec.html
|
452
481
|
[SPARQL-star]: https://w3c.github.io/rdf-star/rdf-star-cg-spec.html#sparql-query-language
|
453
|
-
[Backports]: https://rubygems.org/gems/backports
|
454
482
|
[Linked Data]: https://rubygems.org/gems/linkeddata
|
455
|
-
[SPARQL doc]: https://
|
483
|
+
[SPARQL doc]: https://ruby-rdf.github.io/sparql/frames
|
456
484
|
[SPARQL XML]: https://www.w3.org/TR/rdf-sparql-XMLres/
|
457
485
|
[SPARQL JSON]: https://www.w3.org/TR/rdf-sparql-json-res/
|
458
486
|
[SPARQL EBNF]: https://www.w3.org/TR/sparql11-query/#sparqlGrammar
|
@@ -472,4 +500,4 @@ A copy of the [SPARQL 1.0 tests][] and [SPARQL 1.1 tests][] are also included in
|
|
472
500
|
[SPARQL 1.1 Entailment Regimes]: https://www.w3.org/TR/sparql11-entailment/
|
473
501
|
[SPARQL 1.1 Protocol]: https://www.w3.org/TR/sparql11-protocol/
|
474
502
|
[SPARQL 1.1 Graph Store HTTP Protocol]: https://www.w3.org/TR/sparql11-http-rdf-update/
|
475
|
-
|
503
|
+
[Linked Data Platform]: https://www.w3.org/TR/ldp/
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.
|
1
|
+
3.2.3
|
data/bin/sparql
CHANGED
@@ -45,48 +45,28 @@ def run(input, **options)
|
|
45
45
|
SPARQL::Grammar.parse(input, **options)
|
46
46
|
end
|
47
47
|
|
48
|
-
puts ("\nSSE:\n" + query.to_sse) if options[:debug]
|
48
|
+
puts ("\nSSE:\n" + query.to_sse) if options[:debug]
|
49
49
|
|
50
|
-
|
50
|
+
if options[:parse_only]
|
51
|
+
case options[:format]
|
52
|
+
when :sparql
|
53
|
+
puts ("\nSPARQL:\n" + query.to_sparql)
|
54
|
+
when nil, :sse
|
55
|
+
puts ("\nSSE:\n" + query.to_sse)
|
56
|
+
else
|
57
|
+
$stderr.puts "Unknown output format for parsing: #{options[:format]}. Use 'sse' or 'sparql'"
|
58
|
+
end
|
59
|
+
else
|
51
60
|
res = query.execute(options[:dataset], logger: options[:logger])
|
52
61
|
display_results(res, **options)
|
53
62
|
end
|
54
63
|
end
|
55
64
|
|
56
65
|
def server(options)
|
57
|
-
|
58
|
-
repository = options.fetch(:dataset, RDF::Repository.new)
|
59
|
-
|
60
|
-
app = Sinatra.new do
|
61
|
-
register Sinatra::SPARQL
|
62
|
-
set :repository, repository
|
63
|
-
|
64
|
-
before do
|
65
|
-
options[:logger].info "#{request.request_method} [#{request.path_info}], " +
|
66
|
-
params.merge(Accept: request.accept.map(&:to_s)).map {|k,v| "#{k}=#{v}" unless k.to_s == "content"}.join(" ")
|
67
|
-
end
|
68
|
-
|
69
|
-
get '/' do
|
70
|
-
if params["query"]
|
71
|
-
query = params["query"].to_s.match(/^http:/) ? RDF::Util::File.open_file(params["query"]) : ::CGI.unescape(params["query"].to_s)
|
72
|
-
SPARQL.execute(query, settings.repository)
|
73
|
-
else
|
74
|
-
settings.sparql_options.replace(standard_prefixes: true)
|
75
|
-
settings.sparql_options.merge!(:prefixes => {
|
76
|
-
ssd: "http://www.w3.org/ns/sparql-service-description#",
|
77
|
-
void: "http://rdfs.org/ns/void#"
|
78
|
-
})
|
79
|
-
service_description(repo: settings.repository, endpoint: url)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
post '/' do
|
84
|
-
SPARQL.execute(params['query'], settings.repository)
|
85
|
-
end
|
86
|
-
end
|
66
|
+
app = SPARQL::Server.application(**options)
|
87
67
|
Rack::Server.start(app: app, Port: options.fetch(:port, 9292))
|
88
68
|
rescue LoadError
|
89
|
-
$stderr.puts "Running SPARQL server requires Rack to be in environment: #{$!.message}"
|
69
|
+
$stderr.puts "Running SPARQL server requires Rack and Sinatra to be in environment: #{$!.message}"
|
90
70
|
end
|
91
71
|
|
92
72
|
def usage
|
@@ -98,7 +78,7 @@ def usage
|
|
98
78
|
puts " --dataset: File containing RDF graph or dataset"
|
99
79
|
puts " --debug: Display detailed debug output"
|
100
80
|
puts " --execute,-e: Use option argument as the SPARQL input if no query-file given"
|
101
|
-
puts " --format: Output format for results"
|
81
|
+
puts " --format: Output format for results (json, xml, csv, tsv, html, sparql, sse, or another RDF format)"
|
102
82
|
puts " --port,-p Port on which to run server; defaults to 9292"
|
103
83
|
puts " --sse: Query input is in SSE format"
|
104
84
|
puts " --update: Process query as a SPARQL Update"
|
@@ -151,7 +131,7 @@ end
|
|
151
131
|
|
152
132
|
case cmd
|
153
133
|
when 'execute', 'parse'
|
154
|
-
options[:
|
134
|
+
options[:parse_only] = true if cmd == 'parse'
|
155
135
|
input ||= ARGV.empty? ? $stdin.read : RDF::Util::File.open_file(ARGV.first).read
|
156
136
|
run(input, **options)
|
157
137
|
when 'query'
|
data/lib/rack/sparql/conneg.rb
CHANGED
@@ -39,15 +39,36 @@ module Rack; module SPARQL
|
|
39
39
|
# If result is `RDF::Literal::Boolean`, `RDF::Query::Results`, or `RDF::Enumerable`
|
40
40
|
# The result is serialized using {SPARQL::Results}
|
41
41
|
#
|
42
|
-
# Inserts ordered content types into the environment as `ORDERED_CONTENT_TYPES` if an Accept header is present
|
42
|
+
# Inserts ordered content types into the environment as `ORDERED_CONTENT_TYPES` if an Accept header is present.
|
43
|
+
#
|
44
|
+
# Normalizes `application/x-www-form-urlencoded` to either `application/sparql-query` or `application/sparql-update` forms.
|
43
45
|
#
|
44
46
|
# @param [Hash{String => String}] env
|
45
47
|
# @return [Array(Integer, Hash, #each)]
|
46
48
|
# @see https://www.rubydoc.info/github/rack/rack/Rack/Runtime#call-instance_method
|
47
49
|
def call(env)
|
48
50
|
env['ORDERED_CONTENT_TYPES'] = parse_accept_header(env['HTTP_ACCEPT']) if env.has_key?('HTTP_ACCEPT')
|
51
|
+
# Normalize application/x-www-form-urlencoded to application/sparql-query or application/sparql-update
|
52
|
+
if env['REQUEST_METHOD'] == 'POST' && env.fetch('CONTENT_TYPE', 'application/x-www-form-urlencoded').to_s.start_with?('application/x-www-form-urlencoded')
|
53
|
+
content = env['rack.input'].read
|
54
|
+
params = Rack::Utils.parse_query(content)
|
55
|
+
if query = params.delete('query')
|
56
|
+
return [406, {"Content-Type" => "text/plain"}, ["Multiple query parameters"]] unless query.is_a?(String)
|
57
|
+
env['rack.input'] = StringIO.new(query)
|
58
|
+
env['CONTENT_TYPE'] = 'application/sparql-query'
|
59
|
+
env['QUERY_STRING'] = Rack::Utils.build_query(params)
|
60
|
+
elsif update = params.delete('update')
|
61
|
+
return [406, {"Content-Type" => "text/plain"}, ["Multiple update parameters"]] unless update.is_a?(String)
|
62
|
+
env['rack.input'] = StringIO.new(update)
|
63
|
+
env['CONTENT_TYPE'] = 'application/sparql-update'
|
64
|
+
env['QUERY_STRING'] = Rack::Utils.build_query(params)
|
65
|
+
else
|
66
|
+
env['rack.input'].rewind # never mind
|
67
|
+
end
|
68
|
+
end
|
49
69
|
response = app.call(env)
|
50
70
|
body = response[2].respond_to?(:body) ? response[2].body : response[2]
|
71
|
+
body = body.first if body.is_a?(Array) && body.length == 1 && body.first.is_a?(RDF::Literal::Boolean)
|
51
72
|
case body
|
52
73
|
when RDF::Enumerable, RDF::Query::Solutions, RDF::Literal::Boolean
|
53
74
|
response[2] = body # Put it back in the response, it might have been a proxy
|
@@ -11,7 +11,7 @@ class Sinatra::Response
|
|
11
11
|
|
12
12
|
# Rack::Response#finish sometimes returns self as response body. We don't want that.
|
13
13
|
status, headers, result = super
|
14
|
-
result = body if
|
14
|
+
result = body if self == result
|
15
15
|
[status, headers, result]
|
16
16
|
end
|
17
17
|
end
|
data/lib/sinatra/sparql.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'sinatra/base'
|
2
2
|
require 'sinatra/sparql/extensions'
|
3
3
|
require 'rack/sparql'
|
4
|
+
require 'rdf/aggregate_repo'
|
4
5
|
|
5
6
|
module Sinatra
|
6
7
|
##
|
@@ -13,6 +14,7 @@ module Sinatra
|
|
13
14
|
##
|
14
15
|
# Helper methods.
|
15
16
|
module Helpers
|
17
|
+
|
16
18
|
##
|
17
19
|
# This is useful when a GET request is performed against a SPARQL endpoint and no query is performed. Provide a set of datasets, including a default dataset along with optional triple count, dump location, and description of the dataset.
|
18
20
|
#
|
@@ -36,23 +38,36 @@ module Sinatra
|
|
36
38
|
|
37
39
|
node = RDF::Node.new
|
38
40
|
g << [node, RDF.type, sd.join("#Service")]
|
39
|
-
g << [node, sd.join("#endpoint"), options
|
41
|
+
g << [node, sd.join("#endpoint"), RDF::URI(url(options.fetch(:endpoint, "/sparql")))]
|
42
|
+
g << [node, sd.join("#supportedLanguage"), sd.join("#SPARQL10Query")]
|
40
43
|
g << [node, sd.join("#supportedLanguage"), sd.join("#SPARQL11Query")]
|
44
|
+
g << [node, sd.join("#supportedLanguage"), sd.join("#SPARQL11Update")]
|
45
|
+
g << [node, sd.join("#supportedLanguage"), RDF::URI('http://www.w3.org/ns/rdf-star#SPARQLStarQuery')]
|
46
|
+
g << [node, sd.join("#supportedLanguage"), RDF::URI('http://www.w3.org/ns/rdf-star#SPARQLStarUpdate')]
|
41
47
|
|
48
|
+
# Input formats
|
49
|
+
RDF::Reader.map(&:format).select(&:to_uri).each do |format|
|
50
|
+
g << [node, sd.join("#inputFormat"), format.to_uri]
|
51
|
+
end
|
52
|
+
|
42
53
|
# Result formats, both RDF and SPARQL Results.
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
54
|
+
%w(
|
55
|
+
http://www.w3.org/ns/formats/SPARQL_Results_XML
|
56
|
+
http://www.w3.org/ns/formats/SPARQL_Results_JSON
|
57
|
+
http://www.w3.org/ns/formats/SPARQL_Results_CSV
|
58
|
+
http://www.w3.org/ns/formats/SPARQL_Results_TSV
|
59
|
+
).each do |uri|
|
60
|
+
g << [node, sd.join("#resultFormat"), uri]
|
61
|
+
end
|
62
|
+
|
63
|
+
RDF::Writer.map(&:format).select(&:to_uri).each do |format|
|
64
|
+
g << [node, sd.join("#resultFormat"), format.to_uri]
|
65
|
+
end
|
66
|
+
|
53
67
|
# Features
|
54
68
|
g << [node, sd.join("#feature"), sd.join("#DereferencesURIs")]
|
55
|
-
|
69
|
+
#g << [node, sd.join("#feature"), sd.join("#BasicFederatedQuery")]
|
70
|
+
|
56
71
|
# Datasets
|
57
72
|
ds = RDF::Node.new
|
58
73
|
g << [node, sd.join("#defaultDataset"), ds]
|
@@ -86,6 +101,36 @@ module Sinatra
|
|
86
101
|
end
|
87
102
|
g
|
88
103
|
end
|
104
|
+
|
105
|
+
##
|
106
|
+
# This either creates a merge repo, or uses the standard repository for performing the query, based on the parameters passed (`default-graph-uri` and `named-graph-uri`).
|
107
|
+
# Loads from the datasource, unless a graph named by
|
108
|
+
# the datasource URI already exists in the repository.
|
109
|
+
#
|
110
|
+
# @return [RDF::Dataset]
|
111
|
+
# @see Algebra::Operator::Dataset
|
112
|
+
def dataset(**options)
|
113
|
+
logger = options.fetch(:logger, ::Logger.new(false))
|
114
|
+
repo = settings.repository
|
115
|
+
if %i(default-graph-uri named-graph-uri).any? {|k| options.key?(k)}
|
116
|
+
default_datasets = Array(options[:"default-graph-uri"]).map {|u| RDF::URI(u)}
|
117
|
+
named_datasets = Array(options[:"named-graph-uri"]).map {|u| RDF::URI(u)}
|
118
|
+
|
119
|
+
(default_datasets + named_datasets).each do |uri|
|
120
|
+
load_opts = {logger: logger, graph_name: uri, base_uri: uri}
|
121
|
+
unless repo.has_graph?(uri)
|
122
|
+
logger.debug(options) {"=> load #{uri}"}
|
123
|
+
repo.load(uri.to_s, **load_opts)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# Create an aggregate based on queryable having just the bits we want
|
128
|
+
aggregate = RDF::AggregateRepo.new(repo)
|
129
|
+
named_datasets.each {|name| aggregate.named(name) if repo.has_graph?(name)}
|
130
|
+
aggregate.default(*default_datasets.select {|name| repo.has_graph?(name)})
|
131
|
+
aggregate
|
132
|
+
end || settings.repository
|
133
|
+
end
|
89
134
|
end
|
90
135
|
|
91
136
|
##
|