sparql 3.1.0 → 3.1.6
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 +37 -17
- data/lib/rack/sparql/conneg.rb +2 -2
- data/lib/sinatra/sparql.rb +4 -4
- data/lib/sparql.rb +13 -12
- data/lib/sparql/algebra.rb +11 -19
- data/lib/sparql/algebra/aggregate.rb +2 -2
- data/lib/sparql/algebra/expression.rb +67 -38
- data/lib/sparql/algebra/extensions.rb +182 -23
- data/lib/sparql/algebra/operator.rb +55 -22
- data/lib/sparql/algebra/operator/abs.rb +2 -2
- data/lib/sparql/algebra/operator/add.rb +2 -2
- data/lib/sparql/algebra/operator/alt.rb +2 -2
- data/lib/sparql/algebra/operator/and.rb +3 -3
- data/lib/sparql/algebra/operator/asc.rb +1 -1
- data/lib/sparql/algebra/operator/ask.rb +2 -12
- data/lib/sparql/algebra/operator/avg.rb +8 -1
- data/lib/sparql/algebra/operator/base.rb +8 -8
- data/lib/sparql/algebra/operator/bgp.rb +2 -2
- data/lib/sparql/algebra/operator/bnode.rb +2 -2
- data/lib/sparql/algebra/operator/bound.rb +1 -1
- data/lib/sparql/algebra/operator/ceil.rb +2 -2
- data/lib/sparql/algebra/operator/clear.rb +2 -2
- data/lib/sparql/algebra/operator/coalesce.rb +1 -11
- data/lib/sparql/algebra/operator/compare.rb +15 -8
- data/lib/sparql/algebra/operator/concat.rb +2 -2
- data/lib/sparql/algebra/operator/construct.rb +4 -13
- data/lib/sparql/algebra/operator/contains.rb +2 -2
- data/lib/sparql/algebra/operator/copy.rb +2 -2
- data/lib/sparql/algebra/operator/count.rb +1 -1
- data/lib/sparql/algebra/operator/create.rb +2 -2
- data/lib/sparql/algebra/operator/dataset.rb +3 -14
- 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 +6 -6
- data/lib/sparql/algebra/operator/delete_data.rb +2 -2
- data/lib/sparql/algebra/operator/delete_where.rb +3 -3
- data/lib/sparql/algebra/operator/desc.rb +1 -1
- data/lib/sparql/algebra/operator/describe.rb +2 -12
- data/lib/sparql/algebra/operator/distinct.rb +2 -12
- data/lib/sparql/algebra/operator/divide.rb +1 -1
- data/lib/sparql/algebra/operator/drop.rb +2 -2
- data/lib/sparql/algebra/operator/encode_for_uri.rb +2 -2
- data/lib/sparql/algebra/operator/equal.rb +2 -2
- data/lib/sparql/algebra/operator/exists.rb +1 -1
- data/lib/sparql/algebra/operator/exprlist.rb +1 -11
- data/lib/sparql/algebra/operator/extend.rb +3 -12
- data/lib/sparql/algebra/operator/filter.rb +3 -13
- data/lib/sparql/algebra/operator/floor.rb +2 -2
- data/lib/sparql/algebra/operator/graph.rb +4 -15
- 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 +14 -14
- data/lib/sparql/algebra/operator/group_concat.rb +1 -1
- data/lib/sparql/algebra/operator/hours.rb +1 -1
- data/lib/sparql/algebra/operator/if.rb +1 -11
- data/lib/sparql/algebra/operator/in.rb +1 -11
- data/lib/sparql/algebra/operator/insert.rb +4 -4
- data/lib/sparql/algebra/operator/insert_data.rb +2 -2
- 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/is_triple.rb +30 -0
- data/lib/sparql/algebra/operator/join.rb +9 -7
- 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 +16 -9
- 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 +2 -2
- data/lib/sparql/algebra/operator/max.rb +8 -1
- data/lib/sparql/algebra/operator/md5.rb +1 -1
- data/lib/sparql/algebra/operator/min.rb +8 -1
- data/lib/sparql/algebra/operator/minus.rb +9 -8
- data/lib/sparql/algebra/operator/minutes.rb +1 -1
- data/lib/sparql/algebra/operator/modify.rb +1 -1
- data/lib/sparql/algebra/operator/month.rb +1 -1
- data/lib/sparql/algebra/operator/move.rb +2 -2
- 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 +2 -2
- data/lib/sparql/algebra/operator/notin.rb +1 -11
- data/lib/sparql/algebra/operator/notoneof.rb +2 -2
- data/lib/sparql/algebra/operator/now.rb +1 -1
- data/lib/sparql/algebra/operator/object.rb +27 -0
- data/lib/sparql/algebra/operator/or.rb +3 -3
- data/lib/sparql/algebra/operator/order.rb +2 -12
- data/lib/sparql/algebra/operator/path.rb +2 -2
- data/lib/sparql/algebra/operator/path_opt.rb +2 -2
- data/lib/sparql/algebra/operator/path_plus.rb +2 -2
- data/lib/sparql/algebra/operator/path_star.rb +2 -2
- data/lib/sparql/algebra/operator/plus.rb +2 -2
- data/lib/sparql/algebra/operator/predicate.rb +27 -0
- data/lib/sparql/algebra/operator/prefix.rb +8 -8
- data/lib/sparql/algebra/operator/project.rb +2 -12
- data/lib/sparql/algebra/operator/rand.rb +1 -1
- data/lib/sparql/algebra/operator/reduced.rb +2 -12
- data/lib/sparql/algebra/operator/regex.rb +5 -5
- data/lib/sparql/algebra/operator/replace.rb +3 -3
- data/lib/sparql/algebra/operator/reverse.rb +2 -2
- 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 +9 -2
- data/lib/sparql/algebra/operator/seconds.rb +1 -1
- data/lib/sparql/algebra/operator/seq.rb +1 -1
- data/lib/sparql/algebra/operator/sequence.rb +1 -1
- 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 +2 -12
- 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/subject.rb +29 -0
- data/lib/sparql/algebra/operator/substr.rb +3 -3
- data/lib/sparql/algebra/operator/subtract.rb +1 -1
- data/lib/sparql/algebra/operator/sum.rb +1 -1
- data/lib/sparql/algebra/operator/table.rb +2 -2
- data/lib/sparql/algebra/operator/timezone.rb +1 -1
- data/lib/sparql/algebra/operator/triple.rb +27 -0
- 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 +7 -6
- data/lib/sparql/algebra/operator/update.rb +2 -2
- data/lib/sparql/algebra/operator/using.rb +2 -2
- data/lib/sparql/algebra/operator/uuid.rb +1 -1
- data/lib/sparql/algebra/operator/with.rb +3 -3
- data/lib/sparql/algebra/operator/year.rb +1 -1
- data/lib/sparql/algebra/query.rb +1 -1
- data/lib/sparql/algebra/update.rb +1 -1
- data/lib/sparql/algebra/version.rb +1 -1
- data/lib/sparql/extensions.rb +7 -13
- data/lib/sparql/grammar.rb +81 -6
- data/lib/sparql/grammar/meta.rb +5801 -1584
- data/lib/sparql/grammar/parser11.rb +116 -50
- data/lib/sparql/grammar/terminals11.rb +4 -0
- data/lib/sparql/results.rb +70 -43
- data/lib/sparql/version.rb +1 -1
- metadata +34 -39
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)
|
@@ -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,8 +25,8 @@ 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
|
28
|
+
# @see https://www.w3.org/TR/sparql11-service-description
|
29
|
+
# @see https://www.w3.org/TR/void/
|
30
30
|
def service_description(**options)
|
31
31
|
repository = options[:repository]
|
32
32
|
|
@@ -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
|
##
|
@@ -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]
|
@@ -69,13 +72,11 @@ module SPARQL
|
|
69
72
|
# @raise [SPARQL::MalformedQuery] on invalid input
|
70
73
|
def self.execute(query, queryable, **options, &block)
|
71
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:
|
@@ -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
|
@@ -4,6 +4,8 @@ module SPARQL; module Algebra
|
|
4
4
|
#
|
5
5
|
# @abstract
|
6
6
|
module Expression
|
7
|
+
include RDF::Util::Logger
|
8
|
+
|
7
9
|
##
|
8
10
|
# @example
|
9
11
|
# Expression.parse('(isLiteral 3.1415)')
|
@@ -20,13 +22,6 @@ module SPARQL; module Algebra
|
|
20
22
|
# @yieldreturn [void] ignored
|
21
23
|
# @return [Expression]
|
22
24
|
def self.parse(sse, **options, &block)
|
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
|
-
|
30
25
|
sse = sse.encode(Encoding::UTF_8)
|
31
26
|
sxp = SXP::Reader::SPARQL.new(sse) do |reader|
|
32
27
|
# Set base_uri if we have one
|
@@ -60,7 +55,7 @@ module SPARQL; module Algebra
|
|
60
55
|
def self.open(filename, **options, &block)
|
61
56
|
RDF::Util::File.open_file(filename, **options) do |file|
|
62
57
|
options[:base_uri] ||= filename
|
63
|
-
Expression.parse(file, options, &block)
|
58
|
+
Expression.parse(file, **options, &block)
|
64
59
|
end
|
65
60
|
end
|
66
61
|
|
@@ -120,9 +115,12 @@ module SPARQL; module Algebra
|
|
120
115
|
end
|
121
116
|
|
122
117
|
debug(options) {"#{operator.inspect}(#{operands.map(&:inspect).join(',')})"}
|
123
|
-
options.delete_if {|k, v| [:debug, :depth, :prefixes, :base_uri, :update, :validate].include?(k) }
|
124
|
-
|
125
|
-
|
118
|
+
options.delete_if {|k, v| [:debug, :logger, :depth, :prefixes, :base_uri, :update, :validate].include?(k) }
|
119
|
+
begin
|
120
|
+
operator.new(*operands, **options)
|
121
|
+
rescue ArgumentError => e
|
122
|
+
error(options) {"Operator=#{operator.inspect}: #{e}"}
|
123
|
+
end
|
126
124
|
end
|
127
125
|
|
128
126
|
##
|
@@ -175,8 +173,8 @@ module SPARQL; module Algebra
|
|
175
173
|
# @param [RDF::URI] function
|
176
174
|
# @param [Array<RDF::Term>] args splat of args to function
|
177
175
|
# @return [RDF::Term]
|
178
|
-
# @see
|
179
|
-
# @see
|
176
|
+
# @see https://www.w3.org/TR/sparql11-query/#extensionFunctions
|
177
|
+
# @see https://www.w3.org/TR/sparql11-query/#FunctionMapping
|
180
178
|
def self.extension(function, *args)
|
181
179
|
if function.to_s.start_with?(RDF::XSD.to_s)
|
182
180
|
self.cast(function, args.first)
|
@@ -197,7 +195,7 @@ module SPARQL; module Algebra
|
|
197
195
|
# Value, which should be a typed literal, where the type must be that specified
|
198
196
|
# @raise [TypeError] if datatype is not a URI or value cannot be cast to datatype
|
199
197
|
# @return [RDF::Term]
|
200
|
-
# @see
|
198
|
+
# @see https://www.w3.org/TR/sparql11-query/#FunctionMapping
|
201
199
|
def self.cast(datatype, value)
|
202
200
|
case datatype
|
203
201
|
when RDF::XSD.dateTime
|
@@ -229,21 +227,45 @@ module SPARQL; module Algebra
|
|
229
227
|
when RDF::Literal::DateTime, RDF::Literal::Date, RDF::Literal::Time, RDF::URI, RDF::Node
|
230
228
|
raise TypeError, "Value #{value.inspect} cannot be cast as #{datatype}"
|
231
229
|
else
|
232
|
-
RDF::Literal.new(
|
230
|
+
RDF::Literal::Boolean.new(value.value, datatype: datatype, validate: true)
|
233
231
|
end
|
234
232
|
when RDF::XSD.decimal, RDF::XSD.integer
|
235
233
|
case value
|
236
234
|
when RDF::Literal::Boolean
|
237
235
|
RDF::Literal.new(value.object ? 1 : 0, datatype: datatype)
|
238
|
-
when RDF::Literal::
|
239
|
-
RDF::Literal.new(value, datatype: datatype)
|
236
|
+
when RDF::Literal::Numeric
|
237
|
+
RDF::Literal.new(value.object, datatype: datatype)
|
240
238
|
when RDF::Literal::DateTime, RDF::Literal::Date, RDF::Literal::Time, RDF::URI, RDF::Node
|
241
239
|
raise TypeError, "Value #{value.inspect} cannot be cast as #{datatype}"
|
242
240
|
else
|
243
241
|
RDF::Literal.new(value.value, datatype: datatype, validate: true)
|
244
242
|
end
|
245
243
|
when RDF::XSD.string
|
246
|
-
|
244
|
+
# Cast to string rules based on https://www.w3.org/TR/xpath-functions/#casting-to-string
|
245
|
+
case value
|
246
|
+
when RDF::Literal::Integer
|
247
|
+
RDF::Literal.new(value.canonicalize.to_s, datatype: datatype)
|
248
|
+
when RDF::Literal::Decimal
|
249
|
+
if value == value.ceil
|
250
|
+
RDF::Literal.new(value.ceil, datatype: datatype)
|
251
|
+
else
|
252
|
+
RDF::Literal.new(value.canonicalize.to_s, datatype: datatype)
|
253
|
+
end
|
254
|
+
when RDF::Literal::Float, RDF::Literal::Double
|
255
|
+
if value.abs >= 0.000001 && value.abs < 1000000
|
256
|
+
# 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.
|
257
|
+
cast(datatype, RDF::Literal::Decimal.new(value.object))
|
258
|
+
elsif value.object.zero?
|
259
|
+
# If SV has the value positive or negative zero, TV is "0" or "-0" respectively.
|
260
|
+
RDF::Literal.new(value.to_s.start_with?('-') ? '-0' : '0', datatype: datatype)
|
261
|
+
else
|
262
|
+
# If SV is positive or negative infinity, TV is the string "INF" or "-INF" respectively.
|
263
|
+
# 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.
|
264
|
+
RDF::Literal.new(value.canonicalize.to_s, datatype: datatype)
|
265
|
+
end
|
266
|
+
else
|
267
|
+
RDF::Literal.new(value.canonicalize.to_s, datatype: datatype)
|
268
|
+
end
|
247
269
|
else
|
248
270
|
raise TypeError, "Expected datatype (#{datatype}) to be a recognized XPath function"
|
249
271
|
end
|
@@ -280,12 +302,26 @@ module SPARQL; module Algebra
|
|
280
302
|
##
|
281
303
|
# Returns an optimized version of this expression.
|
282
304
|
#
|
283
|
-
# This is the default implementation, which simply returns `self`.
|
305
|
+
# This is the default implementation, which simply returns a copy of `self`.
|
284
306
|
# Subclasses can override this method in order to implement something
|
285
307
|
# more useful.
|
286
308
|
#
|
287
|
-
# @
|
288
|
-
|
309
|
+
# @param [Hash{Symbol => Object}] options
|
310
|
+
# any additional options for optimization
|
311
|
+
# @return [Expression] a copy of `self`
|
312
|
+
# @see RDF::Query#optimize
|
313
|
+
def optimize(**options)
|
314
|
+
self.deep_dup.optimize!(**options)
|
315
|
+
end
|
316
|
+
|
317
|
+
##
|
318
|
+
# Optimizes this query.
|
319
|
+
#
|
320
|
+
# @param [Hash{Symbol => Object}] options
|
321
|
+
# any additional options for optimization
|
322
|
+
# @return [self]
|
323
|
+
# @see RDF::Query#optimize!
|
324
|
+
def optimize!(**options)
|
289
325
|
self
|
290
326
|
end
|
291
327
|
|
@@ -301,7 +337,7 @@ module SPARQL; module Algebra
|
|
301
337
|
# @param [Hash{Symbol => Object}] options ({})
|
302
338
|
# options passed from query
|
303
339
|
# @return [Expression] `self`
|
304
|
-
def evaluate(bindings, options
|
340
|
+
def evaluate(bindings, **options)
|
305
341
|
self
|
306
342
|
end
|
307
343
|
|
@@ -313,7 +349,7 @@ module SPARQL; module Algebra
|
|
313
349
|
# more useful.
|
314
350
|
#
|
315
351
|
# @return [Array] `self`
|
316
|
-
# @see
|
352
|
+
# @see https://openjena.org/wiki/SSE
|
317
353
|
def to_sxp_bin
|
318
354
|
self
|
319
355
|
end
|
@@ -349,7 +385,7 @@ module SPARQL; module Algebra
|
|
349
385
|
# @param [String] node processing node
|
350
386
|
# @param [String] message
|
351
387
|
# @param [Hash{Symbol => Object}] options
|
352
|
-
# @option options [
|
388
|
+
# @option options [Logger] :logger for logging progress
|
353
389
|
# @option options [Integer] :depth (@productions.length)
|
354
390
|
# Processing depth for indenting message output.
|
355
391
|
# @yieldreturn [String] appended to message, to allow for lazy-evaulation of message
|
@@ -357,7 +393,7 @@ module SPARQL; module Algebra
|
|
357
393
|
# @overload: May be called with node and an option hash
|
358
394
|
# @param [String] node processing node
|
359
395
|
# @param [Hash{Symbol => Object}] options
|
360
|
-
# @option options [
|
396
|
+
# @option options [Logger] :logger for logging progress
|
361
397
|
# @option options [Integer] :depth (@productions.length)
|
362
398
|
# Processing depth for indenting message output.
|
363
399
|
# @yieldreturn [String] appended to message, to allow for lazy-evaulation of message
|
@@ -365,26 +401,19 @@ module SPARQL; module Algebra
|
|
365
401
|
# @overload: May be called with only options, in which case the block is used to return the output message
|
366
402
|
# @param [String] node processing node
|
367
403
|
# @param [Hash{Symbol => Object}] options
|
368
|
-
# @option options [
|
404
|
+
# @option options [Logger] :logger for logging progress
|
369
405
|
# @option options [Integer] :depth (@productions.length)
|
370
406
|
# Processing depth for indenting message output.
|
371
407
|
# @yieldreturn [String] appended to message, to allow for lazy-evaulation of message
|
372
|
-
def self.debug(*args)
|
408
|
+
def self.debug(*args, &block)
|
373
409
|
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
|
410
|
+
return unless options[:logger]
|
411
|
+
options[:logger].debug(*args, **options, &block)
|
384
412
|
end
|
385
413
|
|
386
414
|
def debug(*args, &block)
|
387
|
-
|
415
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
416
|
+
log_debug(*args, **options, &block)
|
388
417
|
end
|
389
418
|
end # Expression
|
390
419
|
end; end # SPARQL::Algebra
|
@@ -27,6 +27,22 @@ class Object
|
|
27
27
|
def to_sse
|
28
28
|
SXP::Generator.string(self.to_sxp_bin)
|
29
29
|
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# A duplicate of this object.
|
33
|
+
#
|
34
|
+
# @return [Object] a copy of `self`
|
35
|
+
# @see SPARQL::Algebra::Expression#optimize
|
36
|
+
def optimize(**options)
|
37
|
+
self.deep_dup
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# Default for deep_dup is shallow dup
|
42
|
+
# @return [Object]
|
43
|
+
def deep_dup
|
44
|
+
dup
|
45
|
+
end
|
30
46
|
end
|
31
47
|
|
32
48
|
##
|
@@ -66,11 +82,34 @@ class Array
|
|
66
82
|
# @param [Hash{Symbol => Object}] options
|
67
83
|
# @raise [NotImplementedError]
|
68
84
|
# If an attempt is made to perform an unsupported operation
|
69
|
-
# @see
|
85
|
+
# @see https://www.w3.org/TR/sparql11-query/#sparqlAlgebra
|
70
86
|
def execute(queryable, **options)
|
71
87
|
raise NotImplementedError, "SPARQL::Algebra '#{first}' operator not implemented"
|
72
88
|
end
|
73
89
|
|
90
|
+
##
|
91
|
+
# Return an optimized version of this array.
|
92
|
+
#
|
93
|
+
# @return [Array] a copy of `self`
|
94
|
+
# @see SPARQL::Algebra::Expression#optimize
|
95
|
+
def optimize(**options)
|
96
|
+
self.map do |op|
|
97
|
+
op.optimize(**options) if op.respond_to?(:optimize)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
##
|
102
|
+
# Binds the pattern to a solution, making it no longer variable if all variables are resolved to bound variables
|
103
|
+
#
|
104
|
+
# @param [RDF::Query::Solution] solution
|
105
|
+
# @return [self]
|
106
|
+
def bind(solution)
|
107
|
+
map! do |op|
|
108
|
+
op.respond_to?(:bind) ? op.bind(solution) : op
|
109
|
+
end
|
110
|
+
self
|
111
|
+
end
|
112
|
+
|
74
113
|
##
|
75
114
|
# Returns `true` if any of the operands are variables, `false`
|
76
115
|
# otherwise.
|
@@ -78,7 +117,7 @@ class Array
|
|
78
117
|
# @return [Boolean] `true` or `false`
|
79
118
|
# @see #constant?
|
80
119
|
def variable?
|
81
|
-
any?(
|
120
|
+
any? {|op| op.respond_to?(:variable?) && op.variable?}
|
82
121
|
end
|
83
122
|
def constant?; !(variable?); end
|
84
123
|
|
@@ -166,6 +205,12 @@ class Array
|
|
166
205
|
each {|e| e.validate! if e.respond_to?(:validate!)}
|
167
206
|
self
|
168
207
|
end
|
208
|
+
|
209
|
+
##
|
210
|
+
# Deep duplicate
|
211
|
+
def deep_dup
|
212
|
+
map(&:deep_dup)
|
213
|
+
end
|
169
214
|
end
|
170
215
|
|
171
216
|
##
|
@@ -179,6 +224,21 @@ class Hash
|
|
179
224
|
to_a.to_sxp_bin
|
180
225
|
end
|
181
226
|
def to_sxp; to_sxp_bin; end
|
227
|
+
|
228
|
+
##
|
229
|
+
# A duplicate of this hash.
|
230
|
+
#
|
231
|
+
# @return [Hash] a copy of `self`
|
232
|
+
# @see SPARQL::Algebra::Expression#optimize
|
233
|
+
def optimize(**options)
|
234
|
+
self.deep_dup
|
235
|
+
end
|
236
|
+
|
237
|
+
##
|
238
|
+
# Deep duplicate
|
239
|
+
def deep_dup
|
240
|
+
inject({}) {|memo, (k, v)| memo.merge(k => v.deep_dup)}
|
241
|
+
end
|
182
242
|
end
|
183
243
|
|
184
244
|
##
|
@@ -210,8 +270,33 @@ module RDF::Term
|
|
210
270
|
def vars
|
211
271
|
variable? ? [self] : []
|
212
272
|
end
|
273
|
+
|
274
|
+
##
|
275
|
+
# A duplicate of this term.
|
276
|
+
#
|
277
|
+
# @return [RDF::Term] a copy of `self`
|
278
|
+
# @see SPARQL::Algebra::Expression#optimize
|
279
|
+
def optimize(**options)
|
280
|
+
optimized = self.deep_dup
|
281
|
+
optimized.lexical = nil if optimized.respond_to?(:lexical=)
|
282
|
+
optimized
|
283
|
+
end
|
213
284
|
end # RDF::Term
|
214
285
|
|
286
|
+
class RDF::Literal::Double
|
287
|
+
##
|
288
|
+
# Returns the SXP representation of this object.
|
289
|
+
#
|
290
|
+
# @return [String]
|
291
|
+
def to_sxp
|
292
|
+
case
|
293
|
+
when nan? then 'nan.0'
|
294
|
+
when infinite? then (infinite? > 0 ? '+inf.0' : '-inf.0')
|
295
|
+
else canonicalize.to_s.downcase
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
215
300
|
# Override RDF::Queryable to execute against SPARQL::Algebra::Query elements as well as RDF::Query and RDF::Pattern
|
216
301
|
module RDF::Queryable
|
217
302
|
alias_method :query_without_sparql, :query
|
@@ -223,7 +308,7 @@ module RDF::Queryable
|
|
223
308
|
#
|
224
309
|
# @example
|
225
310
|
# queryable.query([nil, RDF::DOAP.developer, nil])
|
226
|
-
# queryable.query(predicate: RDF::DOAP.developer)
|
311
|
+
# queryable.query({predicate: RDF::DOAP.developer})
|
227
312
|
#
|
228
313
|
# op = SPARQL::Algebra::Expression.parse(%q((bgp (triple ?a doap:developer ?b))))
|
229
314
|
# queryable.query(op)
|
@@ -235,7 +320,7 @@ module RDF::Queryable
|
|
235
320
|
# @yieldreturn [void] ignored
|
236
321
|
# @return [Enumerator]
|
237
322
|
# @see RDF::Queryable#query_pattern
|
238
|
-
def query(pattern, options
|
323
|
+
def query(pattern, **options, &block)
|
239
324
|
raise TypeError, "#{self} is not queryable" if respond_to?(:queryable?) && !queryable?
|
240
325
|
|
241
326
|
if pattern.is_a?(SPARQL::Algebra::Operator) && pattern.respond_to?(:execute)
|
@@ -258,19 +343,39 @@ module RDF::Queryable
|
|
258
343
|
query_without_sparql(pattern, **options, &block)
|
259
344
|
end
|
260
345
|
end
|
261
|
-
|
262
346
|
end
|
263
347
|
|
264
348
|
class RDF::Statement
|
265
349
|
# Transform Statement Pattern into an SXP
|
266
350
|
# @return [Array]
|
267
351
|
def to_sxp_bin
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
352
|
+
[ (has_graph? ? :quad : :triple),
|
353
|
+
(:inferred if inferred?),
|
354
|
+
subject,
|
355
|
+
predicate,
|
356
|
+
object,
|
357
|
+
graph_name
|
358
|
+
].compact.map(&:to_sxp_bin)
|
359
|
+
end
|
360
|
+
|
361
|
+
##
|
362
|
+
# Returns an S-Expression (SXP) representation
|
363
|
+
#
|
364
|
+
# @return [String]
|
365
|
+
def to_sxp
|
366
|
+
to_sxp_bin.to_sxp
|
273
367
|
end
|
368
|
+
|
369
|
+
##
|
370
|
+
# A duplicate of this Statement.
|
371
|
+
#
|
372
|
+
# @return [RDF::Statement] a copy of `self`
|
373
|
+
# @see SPARQL::Algebra::Expression#optimize
|
374
|
+
def optimize(**options)
|
375
|
+
self.dup
|
376
|
+
end
|
377
|
+
|
378
|
+
def executable?; false; end
|
274
379
|
end
|
275
380
|
|
276
381
|
class RDF::Query
|
@@ -306,6 +411,16 @@ class RDF::Query
|
|
306
411
|
end
|
307
412
|
end
|
308
413
|
|
414
|
+
##
|
415
|
+
# Binds the pattern to a solution, making it no longer variable if all variables are resolved to bound variables
|
416
|
+
#
|
417
|
+
# @param [RDF::Query::Solution] solution
|
418
|
+
# @return [self]
|
419
|
+
def bind(solution)
|
420
|
+
patterns.each {|p| p.bind(solution)}
|
421
|
+
self
|
422
|
+
end
|
423
|
+
|
309
424
|
# Query results in a boolean result (e.g., ASK)
|
310
425
|
# @return [Boolean]
|
311
426
|
def query_yields_boolean?
|
@@ -338,25 +453,34 @@ class RDF::Query
|
|
338
453
|
variables.values
|
339
454
|
end
|
340
455
|
|
456
|
+
alias_method :optimize_without_expression!, :optimize!
|
341
457
|
##
|
342
|
-
#
|
343
|
-
# otherwise.
|
458
|
+
# Optimize the query, removing lexical shortcuts in URIs
|
344
459
|
#
|
345
|
-
# @return [
|
460
|
+
# @return [self]
|
461
|
+
# @see SPARQL::Algebra::Expression#optimize!
|
462
|
+
def optimize!(**options)
|
463
|
+
@patterns = @patterns.map do |pattern|
|
464
|
+
components = pattern.to_quad.map do |term|
|
465
|
+
if term.respond_to?(:lexical=)
|
466
|
+
term.dup.instance_eval {@lexical = nil; self}
|
467
|
+
else
|
468
|
+
term
|
469
|
+
end
|
470
|
+
end
|
471
|
+
RDF::Query::Pattern.from(components, **pattern.options)
|
472
|
+
end
|
473
|
+
self.optimize_without_expression!(**options)
|
474
|
+
end
|
475
|
+
|
476
|
+
##
|
477
|
+
# Returns `true` as this is executable.
|
478
|
+
#
|
479
|
+
# @return [Boolean] `true`
|
346
480
|
def executable?; true; end
|
347
481
|
end
|
348
482
|
|
349
483
|
class RDF::Query::Pattern
|
350
|
-
# Transform Query Pattern into an SXP
|
351
|
-
# @return [Array]
|
352
|
-
def to_sxp_bin
|
353
|
-
if has_graph?
|
354
|
-
[:quad, subject, predicate, object, graph_name]
|
355
|
-
else
|
356
|
-
[:triple, subject, predicate, object]
|
357
|
-
end
|
358
|
-
end
|
359
|
-
|
360
484
|
##
|
361
485
|
# Return the non-destinguished variables contained within this pattern
|
362
486
|
# @return [Array<RDF::Query::Variable>]
|
@@ -370,6 +494,12 @@ class RDF::Query::Pattern
|
|
370
494
|
def vars
|
371
495
|
variables.values
|
372
496
|
end
|
497
|
+
|
498
|
+
##
|
499
|
+
# Returns `true` as this is executable.
|
500
|
+
#
|
501
|
+
# @return [Boolean] `true`
|
502
|
+
def executable?; true; end
|
373
503
|
end
|
374
504
|
|
375
505
|
##
|
@@ -390,6 +520,22 @@ class RDF::Query::Variable
|
|
390
520
|
raise TypeError if bindings.respond_to?(:bound?) && !bindings.bound?(self)
|
391
521
|
bindings[name.to_sym]
|
392
522
|
end
|
523
|
+
|
524
|
+
##
|
525
|
+
# Return self
|
526
|
+
#
|
527
|
+
# @return [RDF::Query::Variable] a copy of `self`
|
528
|
+
# @see SPARQL::Algebra::Expression#optimize
|
529
|
+
def optimize(**options)
|
530
|
+
self
|
531
|
+
end
|
532
|
+
|
533
|
+
# Display variable as SXP
|
534
|
+
# @return [Array]
|
535
|
+
def to_sxp
|
536
|
+
prefix = distinguished? ? (existential? ? '$' : '?') : (existential? ? '$$' : '??')
|
537
|
+
unbound? ? "#{prefix}#{name}".to_sym.to_sxp : ["#{prefix}#{name}".to_sym, value].to_sxp
|
538
|
+
end
|
393
539
|
end # RDF::Query::Variable
|
394
540
|
|
395
541
|
##
|
@@ -419,3 +565,16 @@ class RDF::Query::Solutions
|
|
419
565
|
end
|
420
566
|
alias_method :filter!, :filter
|
421
567
|
end # RDF::Query::Solutions
|
568
|
+
|
569
|
+
##
|
570
|
+
# Extensions for `RDF::Query::Solution`.
|
571
|
+
class RDF::Query::Solution
|
572
|
+
##
|
573
|
+
# Returns the SXP representation of this object, defaults to `self`.
|
574
|
+
#
|
575
|
+
# @return [String]
|
576
|
+
def to_sxp_bin
|
577
|
+
to_a.to_sxp_bin
|
578
|
+
end
|
579
|
+
def to_sxp; to_sxp_bin; end
|
580
|
+
end # RDF::Query::Solution
|