sparql 3.0.1 → 3.1.4
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 +188 -73
- data/UNLICENSE +1 -1
- data/VERSION +1 -1
- data/bin/sparql +44 -24
- data/lib/rack/sparql.rb +1 -1
- data/lib/rack/sparql/conneg.rb +3 -3
- data/lib/sinatra/sparql.rb +5 -5
- data/lib/sparql.rb +17 -16
- data/lib/sparql/algebra.rb +15 -23
- data/lib/sparql/algebra/aggregate.rb +4 -4
- data/lib/sparql/algebra/evaluatable.rb +2 -2
- data/lib/sparql/algebra/expression.rb +63 -40
- data/lib/sparql/algebra/extensions.rb +180 -33
- data/lib/sparql/algebra/operator.rb +37 -16
- data/lib/sparql/algebra/operator/abs.rb +2 -2
- data/lib/sparql/algebra/operator/add.rb +3 -3
- data/lib/sparql/algebra/operator/alt.rb +4 -4
- data/lib/sparql/algebra/operator/and.rb +7 -7
- data/lib/sparql/algebra/operator/asc.rb +3 -3
- data/lib/sparql/algebra/operator/ask.rb +4 -14
- data/lib/sparql/algebra/operator/avg.rb +6 -4
- data/lib/sparql/algebra/operator/base.rb +10 -10
- data/lib/sparql/algebra/operator/bgp.rb +1 -1
- data/lib/sparql/algebra/operator/bnode.rb +5 -5
- data/lib/sparql/algebra/operator/bound.rb +3 -3
- data/lib/sparql/algebra/operator/ceil.rb +2 -2
- data/lib/sparql/algebra/operator/clear.rb +3 -3
- data/lib/sparql/algebra/operator/coalesce.rb +3 -13
- data/lib/sparql/algebra/operator/compare.rb +8 -8
- data/lib/sparql/algebra/operator/concat.rb +4 -4
- data/lib/sparql/algebra/operator/construct.rb +4 -14
- data/lib/sparql/algebra/operator/contains.rb +2 -2
- data/lib/sparql/algebra/operator/copy.rb +3 -3
- data/lib/sparql/algebra/operator/count.rb +3 -3
- data/lib/sparql/algebra/operator/create.rb +3 -3
- data/lib/sparql/algebra/operator/dataset.rb +6 -17
- data/lib/sparql/algebra/operator/datatype.rb +1 -1
- data/lib/sparql/algebra/operator/day.rb +1 -1
- data/lib/sparql/algebra/operator/delete.rb +9 -7
- data/lib/sparql/algebra/operator/delete_data.rb +3 -3
- data/lib/sparql/algebra/operator/delete_where.rb +6 -6
- data/lib/sparql/algebra/operator/desc.rb +1 -1
- data/lib/sparql/algebra/operator/describe.rb +3 -13
- data/lib/sparql/algebra/operator/distinct.rb +4 -14
- data/lib/sparql/algebra/operator/divide.rb +1 -1
- data/lib/sparql/algebra/operator/drop.rb +3 -3
- data/lib/sparql/algebra/operator/encode_for_uri.rb +3 -3
- data/lib/sparql/algebra/operator/equal.rb +2 -2
- data/lib/sparql/algebra/operator/exists.rb +5 -5
- data/lib/sparql/algebra/operator/exprlist.rb +3 -13
- data/lib/sparql/algebra/operator/extend.rb +19 -18
- data/lib/sparql/algebra/operator/filter.rb +6 -16
- data/lib/sparql/algebra/operator/floor.rb +2 -2
- data/lib/sparql/algebra/operator/graph.rb +6 -17
- data/lib/sparql/algebra/operator/greater_than.rb +5 -5
- data/lib/sparql/algebra/operator/greater_than_or_equal.rb +5 -5
- data/lib/sparql/algebra/operator/group.rb +25 -25
- data/lib/sparql/algebra/operator/group_concat.rb +6 -6
- data/lib/sparql/algebra/operator/hours.rb +1 -1
- data/lib/sparql/algebra/operator/if.rb +5 -15
- data/lib/sparql/algebra/operator/in.rb +4 -14
- data/lib/sparql/algebra/operator/insert.rb +7 -5
- data/lib/sparql/algebra/operator/insert_data.rb +3 -3
- data/lib/sparql/algebra/operator/iri.rb +1 -1
- data/lib/sparql/algebra/operator/is_blank.rb +1 -1
- data/lib/sparql/algebra/operator/is_iri.rb +1 -1
- data/lib/sparql/algebra/operator/is_literal.rb +1 -1
- data/lib/sparql/algebra/operator/is_numeric.rb +1 -1
- data/lib/sparql/algebra/operator/join.rb +12 -10
- data/lib/sparql/algebra/operator/lang.rb +1 -1
- data/lib/sparql/algebra/operator/lang_matches.rb +3 -3
- data/lib/sparql/algebra/operator/lcase.rb +2 -2
- data/lib/sparql/algebra/operator/left_join.rb +15 -12
- data/lib/sparql/algebra/operator/less_than.rb +5 -5
- data/lib/sparql/algebra/operator/less_than_or_equal.rb +5 -5
- data/lib/sparql/algebra/operator/load.rb +3 -3
- data/lib/sparql/algebra/operator/max.rb +6 -4
- data/lib/sparql/algebra/operator/md5.rb +1 -1
- data/lib/sparql/algebra/operator/min.rb +6 -4
- data/lib/sparql/algebra/operator/minus.rb +12 -11
- data/lib/sparql/algebra/operator/minutes.rb +1 -1
- data/lib/sparql/algebra/operator/modify.rb +4 -4
- data/lib/sparql/algebra/operator/month.rb +1 -1
- data/lib/sparql/algebra/operator/move.rb +3 -3
- data/lib/sparql/algebra/operator/multiply.rb +1 -1
- data/lib/sparql/algebra/operator/negate.rb +1 -1
- data/lib/sparql/algebra/operator/not.rb +1 -1
- data/lib/sparql/algebra/operator/not_equal.rb +2 -2
- data/lib/sparql/algebra/operator/notexists.rb +4 -4
- data/lib/sparql/algebra/operator/notin.rb +4 -14
- data/lib/sparql/algebra/operator/notoneof.rb +5 -6
- data/lib/sparql/algebra/operator/now.rb +1 -1
- data/lib/sparql/algebra/operator/or.rb +7 -7
- data/lib/sparql/algebra/operator/order.rb +6 -16
- data/lib/sparql/algebra/operator/path.rb +6 -5
- data/lib/sparql/algebra/operator/path_opt.rb +16 -16
- data/lib/sparql/algebra/operator/path_plus.rb +8 -8
- data/lib/sparql/algebra/operator/path_star.rb +4 -4
- data/lib/sparql/algebra/operator/plus.rb +3 -3
- data/lib/sparql/algebra/operator/prefix.rb +10 -10
- data/lib/sparql/algebra/operator/project.rb +4 -14
- data/lib/sparql/algebra/operator/rand.rb +1 -1
- data/lib/sparql/algebra/operator/reduced.rb +4 -14
- data/lib/sparql/algebra/operator/regex.rb +6 -6
- data/lib/sparql/algebra/operator/replace.rb +4 -4
- data/lib/sparql/algebra/operator/reverse.rb +4 -4
- data/lib/sparql/algebra/operator/round.rb +2 -2
- data/lib/sparql/algebra/operator/same_term.rb +8 -5
- data/lib/sparql/algebra/operator/sample.rb +3 -3
- data/lib/sparql/algebra/operator/seconds.rb +1 -1
- data/lib/sparql/algebra/operator/seq.rb +5 -6
- data/lib/sparql/algebra/operator/sequence.rb +4 -4
- data/lib/sparql/algebra/operator/sha1.rb +1 -1
- data/lib/sparql/algebra/operator/sha256.rb +1 -1
- data/lib/sparql/algebra/operator/sha384.rb +1 -1
- data/lib/sparql/algebra/operator/sha512.rb +1 -1
- data/lib/sparql/algebra/operator/slice.rb +4 -14
- data/lib/sparql/algebra/operator/str.rb +1 -1
- data/lib/sparql/algebra/operator/strafter.rb +2 -2
- data/lib/sparql/algebra/operator/strbefore.rb +2 -2
- data/lib/sparql/algebra/operator/strdt.rb +2 -2
- data/lib/sparql/algebra/operator/strends.rb +2 -2
- data/lib/sparql/algebra/operator/strlang.rb +2 -2
- data/lib/sparql/algebra/operator/strlen.rb +2 -2
- data/lib/sparql/algebra/operator/strstarts.rb +2 -2
- data/lib/sparql/algebra/operator/struuid.rb +1 -1
- data/lib/sparql/algebra/operator/substr.rb +4 -4
- data/lib/sparql/algebra/operator/subtract.rb +1 -1
- data/lib/sparql/algebra/operator/sum.rb +6 -4
- data/lib/sparql/algebra/operator/table.rb +3 -3
- data/lib/sparql/algebra/operator/timezone.rb +1 -1
- data/lib/sparql/algebra/operator/tz.rb +1 -1
- data/lib/sparql/algebra/operator/ucase.rb +2 -2
- data/lib/sparql/algebra/operator/union.rb +9 -8
- data/lib/sparql/algebra/operator/update.rb +4 -4
- data/lib/sparql/algebra/operator/using.rb +4 -4
- data/lib/sparql/algebra/operator/uuid.rb +1 -1
- data/lib/sparql/algebra/operator/with.rb +7 -7
- data/lib/sparql/algebra/operator/year.rb +1 -1
- data/lib/sparql/algebra/query.rb +2 -2
- data/lib/sparql/algebra/update.rb +2 -2
- data/lib/sparql/algebra/version.rb +1 -1
- data/lib/sparql/extensions.rb +5 -5
- data/lib/sparql/grammar.rb +111 -11
- data/lib/sparql/grammar/meta.rb +2340 -907
- data/lib/sparql/grammar/parser11.rb +69 -69
- data/lib/sparql/grammar/terminals11.rb +2 -0
- data/lib/sparql/results.rb +73 -46
- data/lib/sparql/version.rb +1 -1
- metadata +38 -65
data/lib/rack/sparql.rb
CHANGED
@@ -18,7 +18,7 @@ module Rack
|
|
18
18
|
# @param [Hash{Symbol => Object}] options
|
19
19
|
# @option options [Boolean] :overwrite (false)
|
20
20
|
# @return [void]
|
21
|
-
def self.register_mime_types!(options
|
21
|
+
def self.register_mime_types!(**options)
|
22
22
|
if defined?(Rack::Mime::MIME_TYPES)
|
23
23
|
RDF::Format.each do |format|
|
24
24
|
if !Rack::Mime::MIME_TYPES.has_key?(file_ext = ".#{format.to_sym}") || options[:overwrite]
|
data/lib/rack/sparql/conneg.rb
CHANGED
@@ -43,7 +43,7 @@ module Rack; module SPARQL
|
|
43
43
|
#
|
44
44
|
# @param [Hash{String => String}] env
|
45
45
|
# @return [Array(Integer, Hash, #each)]
|
46
|
-
# @see
|
46
|
+
# @see https://www.rubydoc.info/github/rack/rack/Rack/Runtime#call-instance_method
|
47
47
|
def call(env)
|
48
48
|
env['ORDERED_CONTENT_TYPES'] = parse_accept_header(env['HTTP_ACCEPT']) if env.has_key?('HTTP_ACCEPT')
|
49
49
|
response = app.call(env)
|
@@ -71,7 +71,7 @@ module Rack; module SPARQL
|
|
71
71
|
serialize_options = {}
|
72
72
|
serialize_options[:content_types] = env['ORDERED_CONTENT_TYPES'] if env['ORDERED_CONTENT_TYPES']
|
73
73
|
serialize_options.merge!(@options)
|
74
|
-
results = ::SPARQL.serialize_results(body, serialize_options)
|
74
|
+
results = ::SPARQL.serialize_results(body, **serialize_options)
|
75
75
|
raise RDF::WriterError, "can't serialize results" unless results
|
76
76
|
headers = headers.merge(VARY).merge('Content-Type' => results.content_type) # FIXME: don't overwrite existing Vary headers
|
77
77
|
[status, headers, [results]]
|
@@ -89,7 +89,7 @@ module Rack; module SPARQL
|
|
89
89
|
#
|
90
90
|
# @param [String, #to_s] header
|
91
91
|
# @return [Array<String>]
|
92
|
-
# @see
|
92
|
+
# @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
|
93
93
|
def parse_accept_header(header)
|
94
94
|
entries = header.to_s.split(',')
|
95
95
|
entries.map { |e| accept_entry(e) }.sort_by(&:last).map(&:first)
|
data/lib/sinatra/sparql.rb
CHANGED
@@ -8,7 +8,7 @@ module Sinatra
|
|
8
8
|
#
|
9
9
|
# To override negotiation on Content-Type, set :format in `sparql_options` to a RDF Format class, or symbol identifying a format.
|
10
10
|
#
|
11
|
-
# @see
|
11
|
+
# @see https://www.sinatrarb.com/extensions.html
|
12
12
|
module SPARQL
|
13
13
|
##
|
14
14
|
# Helper methods.
|
@@ -25,9 +25,9 @@ module Sinatra
|
|
25
25
|
# URI of the service endpoint, defaults to "/sparql" in the current realm.
|
26
26
|
# @return [RDF::Graph]
|
27
27
|
#
|
28
|
-
# @see
|
29
|
-
# @see
|
30
|
-
def service_description(options
|
28
|
+
# @see https://www.w3.org/TR/sparql11-service-description
|
29
|
+
# @see https://www.w3.org/TR/void/
|
30
|
+
def service_description(**options)
|
31
31
|
repository = options[:repository]
|
32
32
|
|
33
33
|
g = RDF::Graph.new
|
@@ -103,7 +103,7 @@ module Sinatra
|
|
103
103
|
app.helpers(Sinatra::SPARQL::Helpers)
|
104
104
|
app.send(:include, ::SPARQL)
|
105
105
|
app.send(:include, ::RDF)
|
106
|
-
app.send(:include, ::LinkedData)
|
106
|
+
app.send(:include, ::LinkedData) if defined?(::LinkedData)
|
107
107
|
end
|
108
108
|
end
|
109
109
|
end
|
data/lib/sparql.rb
CHANGED
@@ -1,16 +1,18 @@
|
|
1
|
+
require 'sxp'
|
1
2
|
require 'sparql/extensions'
|
3
|
+
require 'sparql/algebra/sxp_extensions'
|
2
4
|
|
3
5
|
##
|
4
6
|
# A SPARQL for RDF.rb.
|
5
7
|
#
|
6
|
-
# @see
|
8
|
+
# @see https://www.w3.org/TR/sparql11-query
|
7
9
|
module SPARQL
|
8
10
|
autoload :Algebra, 'sparql/algebra'
|
9
11
|
autoload :Grammar, 'sparql/grammar'
|
10
12
|
autoload :Results, 'sparql/results'
|
11
13
|
autoload :VERSION, 'sparql/version'
|
12
14
|
|
13
|
-
# @see
|
15
|
+
# @see https://rubygems-client
|
14
16
|
autoload :Client, 'sparql/client'
|
15
17
|
|
16
18
|
##
|
@@ -28,8 +30,8 @@ module SPARQL
|
|
28
30
|
# a `queryable` object such as an RDF::Graph
|
29
31
|
# or RDF::Repository.
|
30
32
|
# @raise [Parser::Error] on invalid input
|
31
|
-
def self.parse(query, options
|
32
|
-
query = Grammar::Parser.new(query, options).parse(options[:update] ? :UpdateUnit : :QueryUnit)
|
33
|
+
def self.parse(query, **options)
|
34
|
+
query = Grammar::Parser.new(query, **options).parse(options[:update] ? :UpdateUnit : :QueryUnit)
|
33
35
|
end
|
34
36
|
|
35
37
|
##
|
@@ -52,12 +54,13 @@ module SPARQL
|
|
52
54
|
# results = SPARQL.execute("SELECT * WHERE { ?s ?p ?o }", repository)
|
53
55
|
#
|
54
56
|
# @param [IO, StringIO, String, #to_s] query
|
57
|
+
# @param [RDF::Queryable] queryable
|
55
58
|
# @param [Hash{Symbol => Object}] options
|
56
|
-
# @option options
|
57
|
-
#
|
58
|
-
# One or more URIs used to initialize a new instance of `queryable` in the default graph.
|
59
|
+
# @option options [Boolean] :optimize
|
60
|
+
# Optimize query before execution.
|
59
61
|
# @option options [RDF::URI, String, Array<RDF::URI, String>] :default_graph_uri
|
60
|
-
#
|
62
|
+
# @option options [RDF::URI, String, Array<RDF::URI, String>] :load_datasets
|
63
|
+
# One or more URIs used to initialize a new instance of `queryable` in the default graph. One or more URIs used to initialize a new instance of `queryable` in the default graph.
|
61
64
|
# @option options [RDF::URI, String, Array<RDF::URI, String>] :named_graph_uri
|
62
65
|
# One or more URIs used to initialize the `queryable` as a named graph.
|
63
66
|
# @yield [solution]
|
@@ -67,15 +70,13 @@ module SPARQL
|
|
67
70
|
# @return [RDF::Graph, Boolean, RDF::Query::Solutions::Enumerator]
|
68
71
|
# Note, results may be used with {SPARQL.serialize_results} to obtain appropriate output encoding.
|
69
72
|
# @raise [SPARQL::MalformedQuery] on invalid input
|
70
|
-
def self.execute(query, queryable, options
|
71
|
-
query = self.parse(query, options)
|
73
|
+
def self.execute(query, queryable, **options, &block)
|
74
|
+
query = self.parse(query, **options)
|
75
|
+
query = query.optimize(**options) if options[:optimize]
|
72
76
|
queryable = queryable || RDF::Repository.new
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
puts query.to_sxp
|
77
|
-
when Array
|
78
|
-
options[:debug] << query.to_sxp
|
77
|
+
|
78
|
+
if options[:logger]
|
79
|
+
options[:logger].debug("SPARQL.execute") {SXP::Generator.string query.to_sxp_bin}
|
79
80
|
end
|
80
81
|
|
81
82
|
if options.has_key?(:load_datasets)
|
data/lib/sparql/algebra.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'rdf' # @see
|
1
|
+
require 'rdf' # @see https://rubygems.org/gems/rdf
|
2
2
|
require 'rdf/xsd'
|
3
3
|
|
4
4
|
module SPARQL
|
@@ -141,21 +141,21 @@ module SPARQL
|
|
141
141
|
# ## Constructing operator expressions manually
|
142
142
|
#
|
143
143
|
# Operator(:isBlank).new(RDF::Node(:foobar)).to_sxp #=> "(isBlank _:foobar)"
|
144
|
-
# Operator(:isIRI).new(RDF::URI('
|
145
|
-
# Operator(:isLiteral).new(RDF::Literal(3.1415)).to_sxp #=> "(isLiteral 3.
|
146
|
-
# Operator(:str).new(Operator(:datatype).new(RDF::Literal(3.1415))).to_sxp #=> "(str (datatype 3.
|
144
|
+
# Operator(:isIRI).new(RDF::URI('https://rubygems.org/gems/rdf/')).to_sxp #=> "(isIRI <https://rubygems.org/gems/rdf/>)"
|
145
|
+
# Operator(:isLiteral).new(RDF::Literal(3.1415)).to_sxp #=> "(isLiteral 3.1415e0)"
|
146
|
+
# Operator(:str).new(Operator(:datatype).new(RDF::Literal(3.1415))).to_sxp #=> "(str (datatype 3.1415e0))"
|
147
147
|
#
|
148
148
|
# ## Constructing operator expressions using SSE forms
|
149
149
|
#
|
150
150
|
# SPARQL::Algebra::Expression[:isBlank, RDF::Node(:foobar)].to_sxp #=> "(isBlank _:foobar)"
|
151
|
-
# SPARQL::Algebra::Expression[:isIRI, RDF::URI('
|
152
|
-
# SPARQL::Algebra::Expression[:isLiteral, RDF::Literal(3.1415)].to_sxp #=> "(isLiteral 3.
|
153
|
-
# SPARQL::Algebra::Expression[:str, [:datatype, RDF::Literal(3.1415)]].to_sxp #=> "(str (datatype 3.
|
151
|
+
# SPARQL::Algebra::Expression[:isIRI, RDF::URI('https://rubygems.org/gems/rdf/')].to_sxp #=> "(isIRI <https://rubygems.org/gems/rdf/>)"
|
152
|
+
# SPARQL::Algebra::Expression[:isLiteral, RDF::Literal(3.1415)].to_sxp #=> "(isLiteral 3.1415e0)"
|
153
|
+
# SPARQL::Algebra::Expression[:str, [:datatype, RDF::Literal(3.1415)]].to_sxp #=> "(str (datatype 3.1415e0))"
|
154
154
|
#
|
155
155
|
# ## Constructing operator expressions using SSE strings
|
156
156
|
#
|
157
157
|
# SPARQL::Algebra::Expression.parse('(isBlank _:foobar)')
|
158
|
-
# SPARQL::Algebra::Expression.parse('(isIRI <
|
158
|
+
# SPARQL::Algebra::Expression.parse('(isIRI <https://rubygems.org/gems/rdf/>)')
|
159
159
|
# SPARQL::Algebra::Expression.parse('(isLiteral 3.1415)')
|
160
160
|
# SPARQL::Algebra::Expression.parse('(str (datatype 3.1415))')
|
161
161
|
#
|
@@ -165,11 +165,6 @@ module SPARQL
|
|
165
165
|
# Operator(:isIRI).evaluate(RDF::Vocab::DC.title) #=> RDF::Literal::TRUE
|
166
166
|
# Operator(:isLiteral).evaluate(RDF::Literal(3.1415)) #=> RDF::Literal::TRUE
|
167
167
|
#
|
168
|
-
# ## Optimizing expressions containing constant subexpressions
|
169
|
-
#
|
170
|
-
# SPARQL::Algebra::Expression.parse('(sameTerm ?var ?var)').optimize #=> RDF::Literal::TRUE
|
171
|
-
# SPARQL::Algebra::Expression.parse('(* -2 (- (* (+ 1 2) (+ 3 4))))').optimize #=> RDF::Literal(42)
|
172
|
-
#
|
173
168
|
# ## Evaluating expressions on a solution sequence
|
174
169
|
#
|
175
170
|
# # Find all people and their names & e-mail addresses:
|
@@ -372,8 +367,8 @@ module SPARQL
|
|
372
367
|
# @param [Hash{Symbol => Object}] options
|
373
368
|
# any additional options (see {Operator#initialize})
|
374
369
|
# @return [SPARQL::Algebra::Operator]
|
375
|
-
def parse(sse, options
|
376
|
-
Expression.parse(sse, options)
|
370
|
+
def parse(sse, **options)
|
371
|
+
Expression.parse(sse, **options)
|
377
372
|
end
|
378
373
|
module_function :parse
|
379
374
|
|
@@ -390,8 +385,8 @@ module SPARQL
|
|
390
385
|
# @yieldparam [SPARQL::Algebra::Expression] expression
|
391
386
|
# @yieldreturn [void] ignored
|
392
387
|
# @return [Expression]
|
393
|
-
def open(sse, options
|
394
|
-
Expression.open(sse, options)
|
388
|
+
def open(sse, **options)
|
389
|
+
Expression.open(sse, **options)
|
395
390
|
end
|
396
391
|
module_function :open
|
397
392
|
|
@@ -405,8 +400,7 @@ module SPARQL
|
|
405
400
|
def Expression(*sse)
|
406
401
|
Expression.for(*sse)
|
407
402
|
end
|
408
|
-
|
409
|
-
module_function :Expr, :Expression
|
403
|
+
module_function :Expression
|
410
404
|
|
411
405
|
##
|
412
406
|
# @example
|
@@ -417,8 +411,7 @@ module SPARQL
|
|
417
411
|
def Operator(name, arity = nil)
|
418
412
|
Operator.for(name, arity)
|
419
413
|
end
|
420
|
-
|
421
|
-
module_function :Op, :Operator
|
414
|
+
module_function :Operator
|
422
415
|
|
423
416
|
##
|
424
417
|
# @example
|
@@ -430,8 +423,7 @@ module SPARQL
|
|
430
423
|
def Variable(name)
|
431
424
|
Variable.new(name)
|
432
425
|
end
|
433
|
-
|
434
|
-
module_function :Var, :Variable
|
426
|
+
module_function :Variable
|
435
427
|
|
436
428
|
Variable = RDF::Query::Variable
|
437
429
|
end # Algebra
|
@@ -6,8 +6,8 @@ module SPARQL; module Algebra
|
|
6
6
|
# or more operands which are `Enumerable` lists of `RDF::Term`
|
7
7
|
# and return a single `RDF::Term` or `TypeError`.
|
8
8
|
#
|
9
|
-
# @see
|
10
|
-
# @see
|
9
|
+
# @see https://www.w3.org/TR/sparql11-query/#setFunctions
|
10
|
+
# @see https://www.w3.org/TR/sparql11-query/#aggregates
|
11
11
|
#
|
12
12
|
# @abstract
|
13
13
|
module Aggregate
|
@@ -24,12 +24,12 @@ module SPARQL; module Algebra
|
|
24
24
|
# @return [RDF::Term]
|
25
25
|
# @raise [TypeError]
|
26
26
|
# @abstract
|
27
|
-
def aggregate(solutions = [], options
|
27
|
+
def aggregate(solutions = [], **options)
|
28
28
|
operands.shift if distinct = (operands.first == :distinct)
|
29
29
|
args_enum = solutions.map do |solution|
|
30
30
|
operands.map do |operand|
|
31
31
|
begin
|
32
|
-
operand.evaluate(solution,
|
32
|
+
operand.evaluate(solution, depth: options[:depth].to_i + 1, **options)
|
33
33
|
rescue TypeError
|
34
34
|
# Ignore errors
|
35
35
|
nil
|
@@ -13,8 +13,8 @@ module SPARQL; module Algebra
|
|
13
13
|
# options passed from query
|
14
14
|
# @return [RDF::Term]
|
15
15
|
# @abstract
|
16
|
-
def evaluate(bindings, options
|
17
|
-
args = operands.map { |operand| operand.evaluate(bindings,
|
16
|
+
def evaluate(bindings, **options)
|
17
|
+
args = operands.map { |operand| operand.evaluate(bindings, depth: options[:depth].to_i + 1, **options) }
|
18
18
|
options[:memoize] ? memoize(*args) : apply(*args)
|
19
19
|
end
|
20
20
|
|
@@ -19,14 +19,7 @@ module SPARQL; module Algebra
|
|
19
19
|
# @yieldparam [SPARQL::Algebra::Expression] expression
|
20
20
|
# @yieldreturn [void] ignored
|
21
21
|
# @return [Expression]
|
22
|
-
def self.parse(sse, options
|
23
|
-
begin
|
24
|
-
require 'sxp' # @see http://rubygems.org/gems/sxp
|
25
|
-
rescue LoadError
|
26
|
-
abort "SPARQL::Algebra::Expression.parse requires the SXP gem (hint: `gem install sxp')."
|
27
|
-
end
|
28
|
-
require 'sparql/algebra/sxp_extensions'
|
29
|
-
|
22
|
+
def self.parse(sse, **options, &block)
|
30
23
|
sse = sse.encode(Encoding::UTF_8)
|
31
24
|
sxp = SXP::Reader::SPARQL.new(sse) do |reader|
|
32
25
|
# Set base_uri if we have one
|
@@ -38,7 +31,7 @@ module SPARQL; module Algebra
|
|
38
31
|
Operator.base_uri = options.delete(:base_uri) if options.has_key?(:base_uri)
|
39
32
|
Operator.prefixes = sxp.prefixes || {}
|
40
33
|
|
41
|
-
expression = self.new(sxp_result, options)
|
34
|
+
expression = self.new(sxp_result, **options)
|
42
35
|
|
43
36
|
yield(expression) if block_given?
|
44
37
|
expression
|
@@ -57,8 +50,8 @@ module SPARQL; module Algebra
|
|
57
50
|
# @yieldparam [SPARQL::Algebra::Expression] expression
|
58
51
|
# @yieldreturn [void] ignored
|
59
52
|
# @return [Expression]
|
60
|
-
def self.open(filename, options
|
61
|
-
RDF::Util::File.open_file(filename, options) do |file|
|
53
|
+
def self.open(filename, **options, &block)
|
54
|
+
RDF::Util::File.open_file(filename, **options) do |file|
|
62
55
|
options[:base_uri] ||= filename
|
63
56
|
Expression.parse(file, options, &block)
|
64
57
|
end
|
@@ -87,7 +80,7 @@ module SPARQL; module Algebra
|
|
87
80
|
# any additional options (see {Operator#initialize})
|
88
81
|
# @return [Expression]
|
89
82
|
# @raise [TypeError] if any of the operands is invalid
|
90
|
-
def self.new(sse, options
|
83
|
+
def self.new(sse, **options)
|
91
84
|
raise ArgumentError, "invalid SPARQL::Algebra::Expression form: #{sse.inspect}" unless sse.is_a?(Array)
|
92
85
|
|
93
86
|
operator = Operator.for(sse.first, sse.length - 1)
|
@@ -95,7 +88,7 @@ module SPARQL; module Algebra
|
|
95
88
|
return case sse.first
|
96
89
|
when Array
|
97
90
|
debug(options) {"Map array elements #{sse}"}
|
98
|
-
sse.map {|s| self.new(s,
|
91
|
+
sse.map {|s| self.new(s, depth: options[:depth].to_i + 1, **options)}
|
99
92
|
else
|
100
93
|
debug(options) {"No operator found for #{sse.first}"}
|
101
94
|
sse.map do |s|
|
@@ -110,7 +103,7 @@ module SPARQL; module Algebra
|
|
110
103
|
debug(options) {"Operator=#{operator.inspect}, Operand=#{operand.inspect}"}
|
111
104
|
case operand
|
112
105
|
when Array
|
113
|
-
self.new(operand,
|
106
|
+
self.new(operand, depth: options[:depth].to_i + 1, **options)
|
114
107
|
when Operator, Variable, RDF::Term, RDF::Query, Symbol
|
115
108
|
operand
|
116
109
|
when TrueClass, FalseClass, Numeric, String, DateTime, Date, Time
|
@@ -120,7 +113,7 @@ module SPARQL; module Algebra
|
|
120
113
|
end
|
121
114
|
|
122
115
|
debug(options) {"#{operator.inspect}(#{operands.map(&:inspect).join(',')})"}
|
123
|
-
options.delete_if {|k, v| [:debug, :depth, :prefixes, :base_uri, :update, :validate].include?(k) }
|
116
|
+
options.delete_if {|k, v| [:debug, :logger, :depth, :prefixes, :base_uri, :update, :validate].include?(k) }
|
124
117
|
operands << options unless options.empty?
|
125
118
|
operator.new(*operands)
|
126
119
|
end
|
@@ -175,8 +168,8 @@ module SPARQL; module Algebra
|
|
175
168
|
# @param [RDF::URI] function
|
176
169
|
# @param [Array<RDF::Term>] args splat of args to function
|
177
170
|
# @return [RDF::Term]
|
178
|
-
# @see
|
179
|
-
# @see
|
171
|
+
# @see https://www.w3.org/TR/sparql11-query/#extensionFunctions
|
172
|
+
# @see https://www.w3.org/TR/sparql11-query/#FunctionMapping
|
180
173
|
def self.extension(function, *args)
|
181
174
|
if function.to_s.start_with?(RDF::XSD.to_s)
|
182
175
|
self.cast(function, args.first)
|
@@ -197,7 +190,7 @@ module SPARQL; module Algebra
|
|
197
190
|
# Value, which should be a typed literal, where the type must be that specified
|
198
191
|
# @raise [TypeError] if datatype is not a URI or value cannot be cast to datatype
|
199
192
|
# @return [RDF::Term]
|
200
|
-
# @see
|
193
|
+
# @see https://www.w3.org/TR/sparql11-query/#FunctionMapping
|
201
194
|
def self.cast(datatype, value)
|
202
195
|
case datatype
|
203
196
|
when RDF::XSD.dateTime
|
@@ -229,21 +222,45 @@ module SPARQL; module Algebra
|
|
229
222
|
when RDF::Literal::DateTime, RDF::Literal::Date, RDF::Literal::Time, RDF::URI, RDF::Node
|
230
223
|
raise TypeError, "Value #{value.inspect} cannot be cast as #{datatype}"
|
231
224
|
else
|
232
|
-
RDF::Literal.new(
|
225
|
+
RDF::Literal::Boolean.new(value.value, datatype: datatype, validate: true)
|
233
226
|
end
|
234
227
|
when RDF::XSD.decimal, RDF::XSD.integer
|
235
228
|
case value
|
236
229
|
when RDF::Literal::Boolean
|
237
230
|
RDF::Literal.new(value.object ? 1 : 0, datatype: datatype)
|
238
|
-
when RDF::Literal::
|
239
|
-
RDF::Literal.new(value, datatype: datatype)
|
231
|
+
when RDF::Literal::Numeric
|
232
|
+
RDF::Literal.new(value.object, datatype: datatype)
|
240
233
|
when RDF::Literal::DateTime, RDF::Literal::Date, RDF::Literal::Time, RDF::URI, RDF::Node
|
241
234
|
raise TypeError, "Value #{value.inspect} cannot be cast as #{datatype}"
|
242
235
|
else
|
243
236
|
RDF::Literal.new(value.value, datatype: datatype, validate: true)
|
244
237
|
end
|
245
238
|
when RDF::XSD.string
|
246
|
-
|
239
|
+
# Cast to string rules based on https://www.w3.org/TR/xpath-functions/#casting-to-string
|
240
|
+
case value
|
241
|
+
when RDF::Literal::Integer
|
242
|
+
RDF::Literal.new(value.canonicalize.to_s, datatype: datatype)
|
243
|
+
when RDF::Literal::Decimal
|
244
|
+
if value == value.ceil
|
245
|
+
RDF::Literal.new(value.ceil, datatype: datatype)
|
246
|
+
else
|
247
|
+
RDF::Literal.new(value.canonicalize.to_s, datatype: datatype)
|
248
|
+
end
|
249
|
+
when RDF::Literal::Float, RDF::Literal::Double
|
250
|
+
if value.abs >= 0.000001 && value.abs < 1000000
|
251
|
+
# If SV has an absolute value that is greater than or equal to 0.000001 (one millionth) and less than 1000000 (one million), then the value is converted to an xs:decimal and the resulting xs:decimal is converted to an xs:string according to the rules above, as though using an implementation of xs:decimal that imposes no limits on the totalDigits or fractionDigits facets.
|
252
|
+
cast(datatype, RDF::Literal::Decimal.new(value.object))
|
253
|
+
elsif value.object.zero?
|
254
|
+
# If SV has the value positive or negative zero, TV is "0" or "-0" respectively.
|
255
|
+
RDF::Literal.new(value.to_s.start_with?('-') ? '-0' : '0', datatype: datatype)
|
256
|
+
else
|
257
|
+
# If SV is positive or negative infinity, TV is the string "INF" or "-INF" respectively.
|
258
|
+
# In other cases, the result consists of a mantissa, which has the lexical form of an xs:decimal, followed by the letter "E", followed by an exponent which has the lexical form of an xs:integer. Leading zeroes and "+" signs are prohibited in the exponent. For the mantissa, there must be a decimal point, and there must be exactly one digit before the decimal point, which must be non-zero. The "+" sign is prohibited. There must be at least one digit after the decimal point. Apart from this mandatory digit, trailing zero digits are prohibited.
|
259
|
+
RDF::Literal.new(value.canonicalize.to_s, datatype: datatype)
|
260
|
+
end
|
261
|
+
else
|
262
|
+
RDF::Literal.new(value.canonicalize.to_s, datatype: datatype)
|
263
|
+
end
|
247
264
|
else
|
248
265
|
raise TypeError, "Expected datatype (#{datatype}) to be a recognized XPath function"
|
249
266
|
end
|
@@ -280,12 +297,26 @@ module SPARQL; module Algebra
|
|
280
297
|
##
|
281
298
|
# Returns an optimized version of this expression.
|
282
299
|
#
|
283
|
-
# This is the default implementation, which simply returns `self`.
|
300
|
+
# This is the default implementation, which simply returns a copy of `self`.
|
284
301
|
# Subclasses can override this method in order to implement something
|
285
302
|
# more useful.
|
286
303
|
#
|
287
|
-
# @
|
288
|
-
|
304
|
+
# @param [Hash{Symbol => Object}] options
|
305
|
+
# any additional options for optimization
|
306
|
+
# @return [Expression] a copy of `self`
|
307
|
+
# @see RDF::Query#optimize
|
308
|
+
def optimize(**options)
|
309
|
+
self.deep_dup.optimize!(**options)
|
310
|
+
end
|
311
|
+
|
312
|
+
##
|
313
|
+
# Optimizes this query.
|
314
|
+
#
|
315
|
+
# @param [Hash{Symbol => Object}] options
|
316
|
+
# any additional options for optimization
|
317
|
+
# @return [self]
|
318
|
+
# @see RDF::Query#optimize!
|
319
|
+
def optimize!(**options)
|
289
320
|
self
|
290
321
|
end
|
291
322
|
|
@@ -313,7 +344,7 @@ module SPARQL; module Algebra
|
|
313
344
|
# more useful.
|
314
345
|
#
|
315
346
|
# @return [Array] `self`
|
316
|
-
# @see
|
347
|
+
# @see https://openjena.org/wiki/SSE
|
317
348
|
def to_sxp_bin
|
318
349
|
self
|
319
350
|
end
|
@@ -349,7 +380,7 @@ module SPARQL; module Algebra
|
|
349
380
|
# @param [String] node processing node
|
350
381
|
# @param [String] message
|
351
382
|
# @param [Hash{Symbol => Object}] options
|
352
|
-
# @option options [
|
383
|
+
# @option options [Logger] :logger for logging progress
|
353
384
|
# @option options [Integer] :depth (@productions.length)
|
354
385
|
# Processing depth for indenting message output.
|
355
386
|
# @yieldreturn [String] appended to message, to allow for lazy-evaulation of message
|
@@ -357,7 +388,7 @@ module SPARQL; module Algebra
|
|
357
388
|
# @overload: May be called with node and an option hash
|
358
389
|
# @param [String] node processing node
|
359
390
|
# @param [Hash{Symbol => Object}] options
|
360
|
-
# @option options [
|
391
|
+
# @option options [Logger] :logger for logging progress
|
361
392
|
# @option options [Integer] :depth (@productions.length)
|
362
393
|
# Processing depth for indenting message output.
|
363
394
|
# @yieldreturn [String] appended to message, to allow for lazy-evaulation of message
|
@@ -365,22 +396,14 @@ module SPARQL; module Algebra
|
|
365
396
|
# @overload: May be called with only options, in which case the block is used to return the output message
|
366
397
|
# @param [String] node processing node
|
367
398
|
# @param [Hash{Symbol => Object}] options
|
368
|
-
# @option options [
|
399
|
+
# @option options [Logger] :logger for logging progress
|
369
400
|
# @option options [Integer] :depth (@productions.length)
|
370
401
|
# Processing depth for indenting message output.
|
371
402
|
# @yieldreturn [String] appended to message, to allow for lazy-evaulation of message
|
372
|
-
def self.debug(*args)
|
403
|
+
def self.debug(*args, &block)
|
373
404
|
options = args.last.is_a?(Hash) ? args.pop : {}
|
374
|
-
return unless options[:
|
375
|
-
|
376
|
-
message = message + yield if block_given?
|
377
|
-
depth = options[:depth] || 0
|
378
|
-
case options[:debug]
|
379
|
-
when Array
|
380
|
-
options[:debug] << "#{' ' * depth}#{message}"
|
381
|
-
else
|
382
|
-
$stderr.puts("#{' ' * depth}#{message}")
|
383
|
-
end
|
405
|
+
return unless options[:logger]
|
406
|
+
options[:logger].debug(*args, **options, &block)
|
384
407
|
end
|
385
408
|
|
386
409
|
def debug(*args, &block)
|