sparql 3.1.8 → 3.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (155) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +88 -60
  3. data/VERSION +1 -1
  4. data/bin/sparql +15 -35
  5. data/lib/rack/sparql/conneg.rb +22 -1
  6. data/lib/sinatra/sparql/extensions.rb +1 -1
  7. data/lib/sinatra/sparql.rb +57 -12
  8. data/lib/sparql/algebra/expression.rb +63 -10
  9. data/lib/sparql/algebra/extensions.rb +110 -46
  10. data/lib/sparql/algebra/operator/abs.rb +22 -2
  11. data/lib/sparql/algebra/operator/add.rb +21 -2
  12. data/lib/sparql/algebra/operator/adjust.rb +69 -0
  13. data/lib/sparql/algebra/operator/alt.rb +26 -2
  14. data/lib/sparql/algebra/operator/and.rb +25 -3
  15. data/lib/sparql/algebra/operator/asc.rb +20 -1
  16. data/lib/sparql/algebra/operator/ask.rb +17 -1
  17. data/lib/sparql/algebra/operator/avg.rb +19 -1
  18. data/lib/sparql/algebra/operator/base.rb +18 -1
  19. data/lib/sparql/algebra/operator/bgp.rb +13 -1
  20. data/lib/sparql/algebra/operator/bnode.rb +33 -10
  21. data/lib/sparql/algebra/operator/bound.rb +22 -1
  22. data/lib/sparql/algebra/operator/ceil.rb +25 -2
  23. data/lib/sparql/algebra/operator/clear.rb +26 -2
  24. data/lib/sparql/algebra/operator/coalesce.rb +33 -11
  25. data/lib/sparql/algebra/operator/compare.rb +9 -0
  26. data/lib/sparql/algebra/operator/concat.rb +26 -2
  27. data/lib/sparql/algebra/operator/construct.rb +29 -6
  28. data/lib/sparql/algebra/operator/contains.rb +24 -2
  29. data/lib/sparql/algebra/operator/copy.rb +19 -2
  30. data/lib/sparql/algebra/operator/count.rb +52 -6
  31. data/lib/sparql/algebra/operator/create.rb +20 -2
  32. data/lib/sparql/algebra/operator/dataset.rb +37 -2
  33. data/lib/sparql/algebra/operator/datatype.rb +25 -6
  34. data/lib/sparql/algebra/operator/day.rb +25 -7
  35. data/lib/sparql/algebra/operator/delete.rb +29 -2
  36. data/lib/sparql/algebra/operator/delete_data.rb +23 -2
  37. data/lib/sparql/algebra/operator/delete_where.rb +24 -2
  38. data/lib/sparql/algebra/operator/desc.rb +20 -1
  39. data/lib/sparql/algebra/operator/describe.rb +27 -4
  40. data/lib/sparql/algebra/operator/distinct.rb +20 -3
  41. data/lib/sparql/algebra/operator/divide.rb +26 -2
  42. data/lib/sparql/algebra/operator/drop.rb +27 -3
  43. data/lib/sparql/algebra/operator/encode_for_uri.rb +22 -2
  44. data/lib/sparql/algebra/operator/equal.rb +12 -2
  45. data/lib/sparql/algebra/operator/exists.rb +28 -4
  46. data/lib/sparql/algebra/operator/exprlist.rb +15 -2
  47. data/lib/sparql/algebra/operator/extend.rb +95 -7
  48. data/lib/sparql/algebra/operator/filter.rb +27 -5
  49. data/lib/sparql/algebra/operator/floor.rb +25 -2
  50. data/lib/sparql/algebra/operator/function_call.rb +64 -0
  51. data/lib/sparql/algebra/operator/graph.rb +69 -6
  52. data/lib/sparql/algebra/operator/greater_than.rb +12 -3
  53. data/lib/sparql/algebra/operator/greater_than_or_equal.rb +12 -2
  54. data/lib/sparql/algebra/operator/group.rb +133 -8
  55. data/lib/sparql/algebra/operator/group_concat.rb +43 -7
  56. data/lib/sparql/algebra/operator/hours.rb +25 -7
  57. data/lib/sparql/algebra/operator/if.rb +20 -3
  58. data/lib/sparql/algebra/operator/in.rb +18 -1
  59. data/lib/sparql/algebra/operator/insert.rb +24 -2
  60. data/lib/sparql/algebra/operator/insert_data.rb +23 -2
  61. data/lib/sparql/algebra/operator/iri.rb +21 -4
  62. data/lib/sparql/algebra/operator/is_blank.rb +20 -3
  63. data/lib/sparql/algebra/operator/is_iri.rb +20 -3
  64. data/lib/sparql/algebra/operator/is_literal.rb +20 -3
  65. data/lib/sparql/algebra/operator/is_numeric.rb +22 -5
  66. data/lib/sparql/algebra/operator/is_triple.rb +32 -0
  67. data/lib/sparql/algebra/operator/join.rb +58 -3
  68. data/lib/sparql/algebra/operator/lang.rb +25 -0
  69. data/lib/sparql/algebra/operator/lang_matches.rb +22 -1
  70. data/lib/sparql/algebra/operator/lcase.rb +22 -2
  71. data/lib/sparql/algebra/operator/left_join.rb +44 -3
  72. data/lib/sparql/algebra/operator/less_than.rb +12 -3
  73. data/lib/sparql/algebra/operator/less_than_or_equal.rb +12 -2
  74. data/lib/sparql/algebra/operator/load.rb +25 -2
  75. data/lib/sparql/algebra/operator/max.rb +19 -1
  76. data/lib/sparql/algebra/operator/md5.rb +22 -5
  77. data/lib/sparql/algebra/operator/min.rb +21 -3
  78. data/lib/sparql/algebra/operator/minus.rb +65 -7
  79. data/lib/sparql/algebra/operator/minutes.rb +25 -7
  80. data/lib/sparql/algebra/operator/modify.rb +62 -5
  81. data/lib/sparql/algebra/operator/month.rb +25 -7
  82. data/lib/sparql/algebra/operator/move.rb +20 -2
  83. data/lib/sparql/algebra/operator/multiply.rb +26 -3
  84. data/lib/sparql/algebra/operator/negate.rb +23 -3
  85. data/lib/sparql/algebra/operator/not.rb +24 -3
  86. data/lib/sparql/algebra/operator/not_equal.rb +13 -0
  87. data/lib/sparql/algebra/operator/notexists.rb +30 -6
  88. data/lib/sparql/algebra/operator/notin.rb +20 -3
  89. data/lib/sparql/algebra/operator/notoneof.rb +21 -2
  90. data/lib/sparql/algebra/operator/now.rb +24 -5
  91. data/lib/sparql/algebra/operator/object.rb +32 -0
  92. data/lib/sparql/algebra/operator/or.rb +26 -3
  93. data/lib/sparql/algebra/operator/order.rb +64 -1
  94. data/lib/sparql/algebra/operator/path.rb +29 -2
  95. data/lib/sparql/algebra/operator/path_opt.rb +28 -65
  96. data/lib/sparql/algebra/operator/path_plus.rb +37 -10
  97. data/lib/sparql/algebra/operator/path_range.rb +178 -0
  98. data/lib/sparql/algebra/operator/path_star.rb +25 -4
  99. data/lib/sparql/algebra/operator/path_zero.rb +110 -0
  100. data/lib/sparql/algebra/operator/plus.rb +49 -8
  101. data/lib/sparql/algebra/operator/predicate.rb +32 -0
  102. data/lib/sparql/algebra/operator/prefix.rb +24 -3
  103. data/lib/sparql/algebra/operator/project.rb +111 -6
  104. data/lib/sparql/algebra/operator/rand.rb +30 -2
  105. data/lib/sparql/algebra/operator/reduced.rb +20 -3
  106. data/lib/sparql/algebra/operator/regex.rb +26 -18
  107. data/lib/sparql/algebra/operator/replace.rb +26 -6
  108. data/lib/sparql/algebra/operator/reverse.rb +31 -2
  109. data/lib/sparql/algebra/operator/round.rb +25 -2
  110. data/lib/sparql/algebra/operator/same_term.rb +24 -6
  111. data/lib/sparql/algebra/operator/sample.rb +32 -8
  112. data/lib/sparql/algebra/operator/seconds.rb +25 -7
  113. data/lib/sparql/algebra/operator/seq.rb +23 -5
  114. data/lib/sparql/algebra/operator/sequence.rb +14 -11
  115. data/lib/sparql/algebra/operator/sha1.rb +18 -1
  116. data/lib/sparql/algebra/operator/sha256.rb +18 -1
  117. data/lib/sparql/algebra/operator/sha384.rb +18 -1
  118. data/lib/sparql/algebra/operator/sha512.rb +18 -1
  119. data/lib/sparql/algebra/operator/slice.rb +27 -5
  120. data/lib/sparql/algebra/operator/str.rb +21 -1
  121. data/lib/sparql/algebra/operator/strafter.rb +25 -2
  122. data/lib/sparql/algebra/operator/strbefore.rb +25 -2
  123. data/lib/sparql/algebra/operator/strdt.rb +22 -1
  124. data/lib/sparql/algebra/operator/strends.rb +25 -3
  125. data/lib/sparql/algebra/operator/strlang.rb +24 -6
  126. data/lib/sparql/algebra/operator/strlen.rb +23 -2
  127. data/lib/sparql/algebra/operator/strstarts.rb +25 -2
  128. data/lib/sparql/algebra/operator/struuid.rb +29 -9
  129. data/lib/sparql/algebra/operator/subject.rb +32 -0
  130. data/lib/sparql/algebra/operator/substr.rb +23 -2
  131. data/lib/sparql/algebra/operator/subtract.rb +37 -7
  132. data/lib/sparql/algebra/operator/sum.rb +24 -6
  133. data/lib/sparql/algebra/operator/table.rb +85 -4
  134. data/lib/sparql/algebra/operator/timezone.rb +25 -7
  135. data/lib/sparql/algebra/operator/triple.rb +24 -0
  136. data/lib/sparql/algebra/operator/tz.rb +24 -7
  137. data/lib/sparql/algebra/operator/ucase.rb +23 -2
  138. data/lib/sparql/algebra/operator/union.rb +29 -6
  139. data/lib/sparql/algebra/operator/update.rb +46 -4
  140. data/lib/sparql/algebra/operator/using.rb +49 -2
  141. data/lib/sparql/algebra/operator/uuid.rb +27 -8
  142. data/lib/sparql/algebra/operator/with.rb +38 -4
  143. data/lib/sparql/algebra/operator/year.rb +25 -7
  144. data/lib/sparql/algebra/operator.rb +150 -12
  145. data/lib/sparql/algebra/query.rb +5 -3
  146. data/lib/sparql/algebra/sxp_extensions.rb +3 -3
  147. data/lib/sparql/algebra.rb +42 -6
  148. data/lib/sparql/grammar/meta.rb +1367 -267
  149. data/lib/sparql/grammar/parser11.rb +829 -331
  150. data/lib/sparql/grammar/terminals11.rb +2 -2
  151. data/lib/sparql/grammar.rb +6 -4
  152. data/lib/sparql/results.rb +3 -2
  153. data/lib/sparql/server.rb +93 -0
  154. data/lib/sparql.rb +8 -5
  155. metadata +57 -35
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0fc6d6745c325bd23d83fe640eb238a22406b567cf06223703afd90ea5733be1
4
- data.tar.gz: 056cde1c14633aa791f492be673eb023bab95d7af4e5b19fb94c99540fc22908
3
+ metadata.gz: 853c52b99bab0b9455b25a81c8c946e5bcb1429dddadf010b66315f1dbafda51
4
+ data.tar.gz: 90d89877c031476efdaa1d0b119b2a2404925186b4e0b3de99818d75ead76bff
5
5
  SHA512:
6
- metadata.gz: 32187664d7d06bd49cffa8354987ce3f11ba44b35960a92ac80353a538a15489b342f1d3e66a0ad792971489e83cc57ab05b936e93f8e04fe2f5df4a527d3d39
7
- data.tar.gz: 9d8a75fd40c9ccbac6a9753d9b7a04b9e26a4504a55636d61d3d34c1d4dde77c6838d5ac45828a40d06b7c4658971c1f435da0c6812e7b2eda4aa5dfcfaa9cda
6
+ metadata.gz: 98816c81664147aa0fa4ccd90ed14461eb51b652b011395f38f044934c2ff335197fbc3d2d7f447771a5b1e46fa24042aef45fd1f95356b91f52bafee48d1a72
7
+ data.tar.gz: 5545d7ddd245d73382229ed6f9996b24bd66a4285ee18d6295f697202ad5901eb4221cbf2533ef1539b98ccc0c095519e531b541db3e1c4757d813ab888cb9fd
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # SPARQL for RDF.rb
1
+ # SPARQL Query and Update library for Ruby
2
2
 
3
- This is a [Ruby][] implementation of [SPARQL][] for [RDF.rb][].
3
+ An implementation of [SPARQL][] for [RDF.rb][].
4
4
 
5
5
  [![Gem Version](https://badge.fury.io/rb/sparql.png)](https://badge.fury.io/rb/sparql)
6
6
  [![Build Status](https://github.com/ruby-rdf/sparql/workflows/CI/badge.svg?branch=develop)](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 Update
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.2.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], and
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 embedded triples as described for [RDF4J](https://rdf4j.org/documentation/programming/rdfstar/):
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
- "type" : "uri",
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
- "datatype" : "http://www.w3.org/2001/XMLSchema#integer",
191
+ "value" : "23",
191
192
  "type" : "literal",
192
- "value" : "23"
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
- "datatype" : "http://www.w3.org/2001/XMLSchema#decimal",
199
+ "value" : "0.9",
202
200
  "type" : "literal",
203
- "value" : "0.9"
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
- sse = SPARQL.parse("SELECT * WHERE { ?s ?p ?o }")
252
- queryable.query(sse) do |result|
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
- sse = SPARQL.parse("SELECT * WHERE { ?s ?p ?o }")
260
- sse.execute(queryable) do |result|
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
- sse = SPARQL.parse(%(
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
- sse.execute(queryable)
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
- sse = SPARQL.parse("SELECT * WHERE { ?s ?p ?o }")
281
- sse.to_sxp #=> (bgp (triple ?s ?p ?o))
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.2.2)
372
- * [RDF.rb](https://rubygems.org/gems/rdf) (~> 3.0)
373
- * [SPARQL::Client](https://rubygems.org/gems/sparql-client) (~> 3.0)
374
- * [SXP](https://rubygems.org/gems/sxp) (~> 1.0)
375
- * [Builder](https://rubygems.org/gems/builder) (>= 3.0.0)
376
- * [JSON](https://rubygems.org/gems/json) (>= 1.8.2)
377
- * Soft dependency on [Linked Data][] (>= 3.0)
378
- * Soft dependency on [Nokogiri](https://rubygems.org/gems/nokogiri) (>= 1.7)
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.3.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][] (>= 2.0)
383
- * Soft dependency on [Sinatra][] (>= 2.0)
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} file.
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://www.rubydoc.info/github/dryruby/sxp
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://rubydoc.info/github/ruby-rdf/rdf
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://rubydoc.info/github/ruby-rdf/sparql/frames
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.8
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] || options[:to_sse]
48
+ puts ("\nSSE:\n" + query.to_sse) if options[:debug]
49
49
 
50
- unless options[:to_sse]
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
- require 'sinatra/sparql'
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[:to_sse] = true if cmd == 'parse'
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'
@@ -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 result == self
14
+ result = body if self == result
15
15
  [status, headers, result]
16
16
  end
17
17
  end
@@ -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[:endpoint] || url("/sparql")]
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
- # FIXME: We should get this from the avaliable serializers
44
- g << [node, sd.join("#resultFormat"), RDF::URI("http://www.w3.org/ns/formats/RDF_XML")]
45
- g << [node, sd.join("#resultFormat"), RDF::URI("http://www.w3.org/ns/formats/Turtle")]
46
- g << [node, sd.join("#resultFormat"), RDF::URI("http://www.w3.org/ns/formats/RDFa")]
47
- g << [node, sd.join("#resultFormat"), RDF::URI("http://www.w3.org/ns/formats/N-Triples")]
48
- g << [node, sd.join("#resultFormat"), RDF::URI("http://www.w3.org/ns/formats/SPARQL_Results_XML")]
49
- g << [node, sd.join("#resultFormat"), RDF::URI("http://www.w3.org/ns/formats/SPARQL_Results_JSON")]
50
- g << [node, sd.join("#resultFormat"), RDF::URI("http://www.w3.org/ns/formats/SPARQL_Results_CSV")]
51
- g << [node, sd.join("#resultFormat"), RDF::URI("http://www.w3.org/ns/formats/SPARQL_Results_TSV")]
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
  ##