sparql 3.1.0 → 3.1.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|