sparql 3.2.0 → 3.2.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 +59 -38
- data/VERSION +1 -1
- data/bin/sparql +2 -31
- data/lib/rack/sparql/conneg.rb +22 -1
- data/lib/sinatra/sparql/extensions.rb +1 -1
- data/lib/sinatra/sparql.rb +57 -12
- data/lib/sparql/algebra/expression.rb +63 -10
- data/lib/sparql/algebra/extensions.rb +39 -35
- data/lib/sparql/algebra/operator/abs.rb +1 -1
- data/lib/sparql/algebra/operator/adjust.rb +69 -0
- data/lib/sparql/algebra/operator/alt.rb +1 -1
- data/lib/sparql/algebra/operator/avg.rb +3 -1
- data/lib/sparql/algebra/operator/bgp.rb +9 -1
- data/lib/sparql/algebra/operator/clear.rb +13 -3
- data/lib/sparql/algebra/operator/construct.rb +1 -1
- data/lib/sparql/algebra/operator/count.rb +36 -6
- data/lib/sparql/algebra/operator/create.rb +5 -4
- data/lib/sparql/algebra/operator/dataset.rb +29 -11
- data/lib/sparql/algebra/operator/day.rb +2 -2
- data/lib/sparql/algebra/operator/delete.rb +3 -1
- data/lib/sparql/algebra/operator/delete_data.rb +1 -1
- data/lib/sparql/algebra/operator/delete_where.rb +1 -1
- data/lib/sparql/algebra/operator/distinct.rb +2 -2
- data/lib/sparql/algebra/operator/divide.rb +1 -1
- data/lib/sparql/algebra/operator/drop.rb +15 -6
- data/lib/sparql/algebra/operator/encode_for_uri.rb +2 -4
- data/lib/sparql/algebra/operator/exprlist.rb +3 -1
- data/lib/sparql/algebra/operator/extend.rb +73 -5
- data/lib/sparql/algebra/operator/filter.rb +6 -1
- data/lib/sparql/algebra/operator/function_call.rb +64 -0
- data/lib/sparql/algebra/operator/graph.rb +57 -7
- data/lib/sparql/algebra/operator/group.rb +105 -6
- data/lib/sparql/algebra/operator/group_concat.rb +25 -1
- data/lib/sparql/algebra/operator/hours.rb +2 -2
- data/lib/sparql/algebra/operator/if.rb +10 -10
- data/lib/sparql/algebra/operator/insert.rb +3 -1
- data/lib/sparql/algebra/operator/insert_data.rb +1 -1
- data/lib/sparql/algebra/operator/is_blank.rb +1 -2
- data/lib/sparql/algebra/operator/is_iri.rb +1 -2
- data/lib/sparql/algebra/operator/is_literal.rb +1 -2
- data/lib/sparql/algebra/operator/is_numeric.rb +1 -2
- data/lib/sparql/algebra/operator/join.rb +39 -5
- data/lib/sparql/algebra/operator/lcase.rb +2 -3
- data/lib/sparql/algebra/operator/left_join.rb +27 -9
- data/lib/sparql/algebra/operator/max.rb +3 -1
- data/lib/sparql/algebra/operator/min.rb +4 -2
- data/lib/sparql/algebra/operator/minus.rb +46 -6
- data/lib/sparql/algebra/operator/minutes.rb +2 -2
- data/lib/sparql/algebra/operator/modify.rb +21 -0
- data/lib/sparql/algebra/operator/month.rb +2 -2
- data/lib/sparql/algebra/operator/multiply.rb +1 -1
- data/lib/sparql/algebra/operator/notoneof.rb +12 -3
- data/lib/sparql/algebra/operator/order.rb +44 -0
- data/lib/sparql/algebra/operator/path_opt.rb +9 -65
- data/lib/sparql/algebra/operator/path_plus.rb +18 -10
- data/lib/sparql/algebra/operator/path_range.rb +178 -0
- data/lib/sparql/algebra/operator/path_star.rb +7 -4
- data/lib/sparql/algebra/operator/path_zero.rb +110 -0
- data/lib/sparql/algebra/operator/plus.rb +8 -6
- data/lib/sparql/algebra/operator/project.rb +64 -5
- data/lib/sparql/algebra/operator/reduced.rb +3 -3
- data/lib/sparql/algebra/operator/regex.rb +1 -1
- data/lib/sparql/algebra/operator/reverse.rb +12 -1
- data/lib/sparql/algebra/operator/sample.rb +3 -1
- data/lib/sparql/algebra/operator/seconds.rb +2 -2
- data/lib/sparql/algebra/operator/seq.rb +4 -4
- data/lib/sparql/algebra/operator/sequence.rb +14 -1
- data/lib/sparql/algebra/operator/service.rb +86 -0
- data/lib/sparql/algebra/operator/strlang.rb +1 -2
- data/lib/sparql/algebra/operator/subtract.rb +10 -6
- data/lib/sparql/algebra/operator/sum.rb +9 -7
- data/lib/sparql/algebra/operator/table.rb +50 -7
- data/lib/sparql/algebra/operator/timezone.rb +2 -2
- data/lib/sparql/algebra/operator/triple.rb +51 -0
- data/lib/sparql/algebra/operator/tz.rb +2 -2
- data/lib/sparql/algebra/operator/ucase.rb +1 -1
- data/lib/sparql/algebra/operator/update.rb +22 -1
- data/lib/sparql/algebra/operator/using.rb +18 -1
- data/lib/sparql/algebra/operator/with.rb +1 -1
- data/lib/sparql/algebra/operator/year.rb +2 -2
- data/lib/sparql/algebra/operator.rb +69 -22
- data/lib/sparql/algebra/query.rb +5 -3
- data/lib/sparql/algebra.rb +42 -6
- data/lib/sparql/grammar/meta.rb +1367 -267
- data/lib/sparql/grammar/parser11.rb +842 -331
- data/lib/sparql/grammar/terminals11.rb +2 -2
- data/lib/sparql/grammar.rb +6 -4
- data/lib/sparql/results.rb +3 -2
- data/lib/sparql/server.rb +93 -0
- data/lib/sparql.rb +8 -5
- metadata +49 -13
@@ -44,8 +44,8 @@ module SPARQL; module Algebra
|
|
44
44
|
# @return [RDF::Query::Solutions]
|
45
45
|
# the resulting solution sequence
|
46
46
|
# @see https://www.w3.org/TR/sparql11-query/#sparqlAlgebra
|
47
|
-
# @see https://
|
48
|
-
# @see https://
|
47
|
+
# @see https://ruby-rdf.github.io/rdf/RDF/Query/Solution#merge-instance_method
|
48
|
+
# @see https://ruby-rdf.github.io/rdf/RDF/Query/Solution#compatible%3F-instance_method
|
49
49
|
def execute(queryable, **options, &block)
|
50
50
|
filter = operand(2)
|
51
51
|
|
@@ -66,6 +66,11 @@ module SPARQL; module Algebra
|
|
66
66
|
load_left = true
|
67
67
|
right.each do |s2|
|
68
68
|
s = s2.merge(s1)
|
69
|
+
# Re-bind to bindings, if defined, as they might not be found in solution
|
70
|
+
options[:bindings].each_binding do |name, value|
|
71
|
+
s[name] = value if filter.variables.include?(name)
|
72
|
+
end if options[:bindings] && filter.respond_to?(:variables)
|
73
|
+
|
69
74
|
expr = filter ? boolean(filter.evaluate(s)).true? : true rescue false
|
70
75
|
debug(options) {"===>(evaluate) #{s.inspect}"} if filter
|
71
76
|
|
@@ -131,14 +136,27 @@ module SPARQL; module Algebra
|
|
131
136
|
#
|
132
137
|
# @param [Boolean] top_level (true)
|
133
138
|
# Treat this as a top-level, generating SELECT ... WHERE {}
|
139
|
+
# @param [Hash{String => Operator}] extensions
|
140
|
+
# Variable bindings
|
141
|
+
# @param [Array<Operator>] filter_ops ([])
|
142
|
+
# Filter Operations
|
134
143
|
# @return [String]
|
135
|
-
def to_sparql(top_level: true, **options)
|
136
|
-
str = operands[0].to_sparql(top_level: false, **options)
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
144
|
+
def to_sparql(top_level: true, filter_ops: [], extensions: {}, **options)
|
145
|
+
str = "{\n" + operands[0].to_sparql(top_level: false, extensions: {}, **options)
|
146
|
+
str <<
|
147
|
+
"\nOPTIONAL {\n" +
|
148
|
+
operands[1].to_sparql(top_level: false, extensions: {}, **options)
|
149
|
+
case operands[2]
|
150
|
+
when SPARQL::Algebra::Operator::Exprlist
|
151
|
+
operands[2].operands.each do |op|
|
152
|
+
str << "\nFILTER (" + op.to_sparql(**options) + ")"
|
153
|
+
end
|
154
|
+
when nil
|
155
|
+
else
|
156
|
+
str << "\nFILTER (" + operands[2].to_sparql(**options) + ")"
|
157
|
+
end
|
158
|
+
str << "\n}}"
|
159
|
+
top_level ? Operator.to_sparql(str, filter_ops: filter_ops, extensions: extensions, **options) : str
|
142
160
|
end
|
143
161
|
end # LeftJoin
|
144
162
|
end # Operator
|
@@ -56,7 +56,9 @@ module SPARQL; module Algebra
|
|
56
56
|
#
|
57
57
|
# @return [String]
|
58
58
|
def to_sparql(**options)
|
59
|
-
|
59
|
+
distinct = operands.first == :distinct
|
60
|
+
args = distinct ? operands[1..-1] : operands
|
61
|
+
"MAX(#{'DISTINCT ' if distinct}#{args.to_sparql(**options)})"
|
60
62
|
end
|
61
63
|
end # Max
|
62
64
|
end # Operator
|
@@ -15,7 +15,7 @@ module SPARQL; module Algebra
|
|
15
15
|
# (project (?min)
|
16
16
|
# (extend ((?min ??.0))
|
17
17
|
# (group () ((??.0 (min ?o)))
|
18
|
-
# (bgp (triple ?s
|
18
|
+
# (bgp (triple ?s :dec ?o))))))
|
19
19
|
#
|
20
20
|
# @see https://www.w3.org/TR/sparql11-query/#defn_aggMin
|
21
21
|
class Min < Operator
|
@@ -56,7 +56,9 @@ module SPARQL; module Algebra
|
|
56
56
|
#
|
57
57
|
# @return [String]
|
58
58
|
def to_sparql(**options)
|
59
|
-
|
59
|
+
distinct = operands.first == :distinct
|
60
|
+
args = distinct ? operands[1..-1] : operands
|
61
|
+
"MIN(#{'DISTINCT ' if distinct}#{args.to_sparql(**options)})"
|
60
62
|
end
|
61
63
|
end # Min
|
62
64
|
end # Operator
|
@@ -14,6 +14,32 @@ module SPARQL; module Algebra
|
|
14
14
|
# (triple ?s ?p ?o))
|
15
15
|
# (bgp (triple ?s ?q ?v)))
|
16
16
|
#
|
17
|
+
# @example SPARQL Grammar (inline filter)
|
18
|
+
# PREFIX : <http://example/>
|
19
|
+
# SELECT (?s1 AS ?subset) (?s2 AS ?superset)
|
20
|
+
# WHERE {
|
21
|
+
# ?s2 a :Set .
|
22
|
+
# ?s1 a :Set .
|
23
|
+
# FILTER(?s1 != ?s2)
|
24
|
+
# MINUS {
|
25
|
+
# ?s1 a :Set .
|
26
|
+
# ?s2 a :Set .
|
27
|
+
# FILTER(?s1 != ?s2)
|
28
|
+
# }
|
29
|
+
# }
|
30
|
+
#
|
31
|
+
# @example SSE (inline filter)
|
32
|
+
# (prefix ((: <http://example/>))
|
33
|
+
# (project (?subset ?superset)
|
34
|
+
# (extend ((?subset ?s1) (?superset ?s2))
|
35
|
+
# (filter (!= ?s1 ?s2)
|
36
|
+
# (minus
|
37
|
+
# (bgp (triple ?s2 a :Set) (triple ?s1 a :Set))
|
38
|
+
# (filter (!= ?s1 ?s2)
|
39
|
+
# (bgp
|
40
|
+
# (triple ?s1 a :Set)
|
41
|
+
# (triple ?s2 a :Set))))))))
|
42
|
+
#
|
17
43
|
# @see https://www.w3.org/TR/xpath-functions/#func-numeric-unary-minus
|
18
44
|
# @see https://www.w3.org/TR/sparql11-query/#sparqlAlgebra
|
19
45
|
class Minus < Operator::Binary
|
@@ -74,15 +100,29 @@ module SPARQL; module Algebra
|
|
74
100
|
#
|
75
101
|
# Returns a partial SPARQL grammar for this operator.
|
76
102
|
#
|
103
|
+
# @param [Hash{String => Operator}] extensions
|
104
|
+
# Variable bindings
|
105
|
+
# @param [Array<Operator>] filter_ops ([])
|
106
|
+
# Filter Operations
|
77
107
|
# @param [Boolean] top_level (true)
|
78
108
|
# Treat this as a top-level, generating SELECT ... WHERE {}
|
79
109
|
# @return [String]
|
80
|
-
def to_sparql(top_level: true, **options)
|
81
|
-
|
82
|
-
str
|
83
|
-
|
84
|
-
|
85
|
-
|
110
|
+
def to_sparql(top_level: true, filter_ops: [], extensions: {}, **options)
|
111
|
+
lhs, *rhs = operands
|
112
|
+
str = "{\n" + lhs.to_sparql(top_level: false, extensions: {}, **options)
|
113
|
+
|
114
|
+
# Any accrued filters go here.
|
115
|
+
filter_ops.each do |op|
|
116
|
+
str << "\nFILTER (#{op.to_sparql(**options)}) ."
|
117
|
+
end
|
118
|
+
|
119
|
+
rhs.each do |minus|
|
120
|
+
str << "\nMINUS {\n"
|
121
|
+
str << minus.to_sparql(top_level: false, extensions: {}, **options)
|
122
|
+
str << "\n}"
|
123
|
+
end
|
124
|
+
str << "}"
|
125
|
+
top_level ? Operator.to_sparql(str, extensions: extensions, **options) : str
|
86
126
|
end
|
87
127
|
end # Minus
|
88
128
|
end # Operator
|
@@ -31,10 +31,10 @@ module SPARQL; module Algebra
|
|
31
31
|
#
|
32
32
|
# @param [RDF::Literal] operand
|
33
33
|
# the operand
|
34
|
-
# @return [RDF::Literal]
|
34
|
+
# @return [RDF::Literal::Temporal]
|
35
35
|
# @raise [TypeError] if the operand is not a simple literal
|
36
36
|
def apply(operand, **options)
|
37
|
-
raise TypeError, "expected an RDF::Literal::
|
37
|
+
raise TypeError, "expected an RDF::Literal::Temporal, but got #{operand.inspect}" unless operand.is_a?(RDF::Literal::Temporal)
|
38
38
|
RDF::Literal(operand.object.minute)
|
39
39
|
end
|
40
40
|
|
@@ -6,6 +6,11 @@ module SPARQL; module Algebra
|
|
6
6
|
#
|
7
7
|
# Wraps delete/insert
|
8
8
|
#
|
9
|
+
# If `options` contains any of the Protocol attributes, it is treated as if there is a USING or USING NAMED clause inserted.
|
10
|
+
#
|
11
|
+
# * `using-graph-uri`
|
12
|
+
# * `using-named-graph-uri`
|
13
|
+
#
|
9
14
|
# [41] Modify ::= ( 'WITH' iri )? ( DeleteClause InsertClause? | InsertClause ) UsingClause* 'WHERE' GroupGraphPattern
|
10
15
|
#
|
11
16
|
# @example SPARQL Grammar
|
@@ -35,6 +40,10 @@ module SPARQL; module Algebra
|
|
35
40
|
#
|
36
41
|
# Execute the first operand to get solutions, and apply those solutions to the subsequent operators.
|
37
42
|
#
|
43
|
+
# If `options` contains any of the Protocol attributes, any `using` clause is removed and a new `using` clause is added with entries taken from the `using-graph-uri` and `using-named-graph-uri`.
|
44
|
+
#
|
45
|
+
# It is an error to supply the using-graph-uri or using-named-graph-uri parameters when using this protocol to convey a SPARQL 1.1 Update request that contains an operation that uses the USING, USING NAMED, or WITH clause.
|
46
|
+
#
|
38
47
|
# @param [RDF::Queryable] queryable
|
39
48
|
# the graph or repository to write
|
40
49
|
# @param [Hash{Symbol => Object}] options
|
@@ -50,6 +59,18 @@ module SPARQL; module Algebra
|
|
50
59
|
debug(options) {"Modify"}
|
51
60
|
query = operands.shift
|
52
61
|
|
62
|
+
if %i(using-graph-uri using-named-graph-uri).any? {|k| options.key?(k)}
|
63
|
+
raise ArgumentError,
|
64
|
+
"query contains USING/WITH clause, which is incompatible with using-graph-uri or using-named-graph-uri query parameters" if
|
65
|
+
query.is_a?(Operator::Using) || query.is_a?(Operator::With)
|
66
|
+
|
67
|
+
debug("=> Insert USING clause", options)
|
68
|
+
defaults = Array(options.delete(:'using-graph-uri')).map {|uri| RDF::URI(uri)}
|
69
|
+
named = Array(options.delete(:'using-named-graph-uri')).map {|uri| [:named, RDF::URI(uri)]}
|
70
|
+
|
71
|
+
query = Operator::Using.new((defaults + named), query, **options)
|
72
|
+
end
|
73
|
+
|
53
74
|
queryable.query(query, depth: options[:depth].to_i + 1, **options) do |solution|
|
54
75
|
debug(options) {"(solution)=>#{solution.inspect}"}
|
55
76
|
|
@@ -31,10 +31,10 @@ module SPARQL; module Algebra
|
|
31
31
|
#
|
32
32
|
# @param [RDF::Literal] operand
|
33
33
|
# the operand
|
34
|
-
# @return [RDF::Literal]
|
34
|
+
# @return [RDF::Literal::Temporal]
|
35
35
|
# @raise [TypeError] if the operand is not a simple literal
|
36
36
|
def apply(operand, **options)
|
37
|
-
raise TypeError, "expected an RDF::Literal::
|
37
|
+
raise TypeError, "expected an RDF::Literal::Temporal, but got #{operand.inspect}" unless operand.is_a?(RDF::Literal::Temporal)
|
38
38
|
RDF::Literal(operand.object.month)
|
39
39
|
end
|
40
40
|
|
@@ -50,7 +50,7 @@ module SPARQL; module Algebra
|
|
50
50
|
#
|
51
51
|
# @return [String]
|
52
52
|
def to_sparql(**options)
|
53
|
-
"#{operands.first.to_sparql(**options)} * #{operands.last.to_sparql(**options)}"
|
53
|
+
"(#{operands.first.to_sparql(**options)} * #{operands.last.to_sparql(**options)})"
|
54
54
|
end
|
55
55
|
end # Multiply
|
56
56
|
end # Operator
|
@@ -8,13 +8,13 @@ module SPARQL; module Algebra
|
|
8
8
|
# @example SPARQL Grammar
|
9
9
|
# PREFIX ex: <http://www.example.org/schema#>
|
10
10
|
# PREFIX in: <http://www.example.org/instance#>
|
11
|
-
# ASK { in:
|
11
|
+
# ASK { in:a !(ex:p1|ex:p2) ?x }
|
12
12
|
#
|
13
13
|
# @example SSE
|
14
14
|
# (prefix ((ex: <http://www.example.org/schema#>)
|
15
|
-
#
|
15
|
+
# (in: <http://www.example.org/instance#>))
|
16
16
|
# (ask
|
17
|
-
# (path in:
|
17
|
+
# (path in:a (notoneof ex:p1 ex:p2) ?x)))
|
18
18
|
#
|
19
19
|
# @see https://www.w3.org/TR/sparql11-query/#eval_negatedPropertySet
|
20
20
|
class NotOneOf < Operator
|
@@ -56,6 +56,15 @@ module SPARQL; module Algebra
|
|
56
56
|
block.call(solution)
|
57
57
|
end
|
58
58
|
end
|
59
|
+
|
60
|
+
##
|
61
|
+
#
|
62
|
+
# Returns a partial SPARQL grammar for this operator.
|
63
|
+
#
|
64
|
+
# @return [String]
|
65
|
+
def to_sparql(**options)
|
66
|
+
"!(" + operands.to_sparql(delimiter: ' | ', **options) + ')'
|
67
|
+
end
|
59
68
|
end # NotOneOf
|
60
69
|
end # Operator
|
61
70
|
end; end # SPARQL::Algebra
|
@@ -17,6 +17,50 @@ module SPARQL; module Algebra
|
|
17
17
|
# (order ((asc ?name))
|
18
18
|
# (bgp (triple ?x foaf:name ?name)))))
|
19
19
|
#
|
20
|
+
# @example SPARQL Grammar (with builtin)
|
21
|
+
# PREFIX : <http://example.org/>
|
22
|
+
# SELECT ?s WHERE {
|
23
|
+
# ?s :p ?o .
|
24
|
+
# }
|
25
|
+
# ORDER BY str(?o)
|
26
|
+
#
|
27
|
+
# @example SSE (with builtin)
|
28
|
+
# (prefix ((: <http://example.org/>))
|
29
|
+
# (project (?s)
|
30
|
+
# (order ((str ?o))
|
31
|
+
# (bgp (triple ?s :p ?o)))))
|
32
|
+
#
|
33
|
+
# @example SPARQL Grammar (with bracketed expression)
|
34
|
+
# PREFIX : <http://example.org/>
|
35
|
+
# SELECT ?s WHERE {
|
36
|
+
# ?s :p ?o1 ; :q ?o2 .
|
37
|
+
# } ORDER BY (?o1 + ?o2)
|
38
|
+
#
|
39
|
+
# @example SSE (with bracketed expression)
|
40
|
+
# (prefix
|
41
|
+
# ((: <http://example.org/>))
|
42
|
+
# (project (?s)
|
43
|
+
# (order ((+ ?o1 ?o2))
|
44
|
+
# (bgp
|
45
|
+
# (triple ?s :p ?o1)
|
46
|
+
# (triple ?s :q ?o2)))))
|
47
|
+
#
|
48
|
+
# @example SPARQL Grammar (with function call)
|
49
|
+
# PREFIX : <http://example.org/ns#>
|
50
|
+
# PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
|
51
|
+
# SELECT *
|
52
|
+
# { ?s ?p ?o }
|
53
|
+
# ORDER BY
|
54
|
+
# DESC(?o+57) xsd:string(?o) ASC(?s)
|
55
|
+
#
|
56
|
+
# @example SSE (with function call)
|
57
|
+
# (prefix ((: <http://example.org/ns#>)
|
58
|
+
# (xsd: <http://www.w3.org/2001/XMLSchema#>))
|
59
|
+
# (order ((desc (+ ?o 57))
|
60
|
+
# (xsd:string ?o)
|
61
|
+
# (asc ?s))
|
62
|
+
# (bgp (triple ?s ?p ?o))))
|
63
|
+
#
|
20
64
|
# @see https://www.w3.org/TR/sparql11-query/#modOrderBy
|
21
65
|
class Order < Operator::Binary
|
22
66
|
include Query
|
@@ -3,8 +3,8 @@ module SPARQL; module Algebra
|
|
3
3
|
##
|
4
4
|
# The SPARQL Property Path `path?` (ZeroOrOnePath) operator.
|
5
5
|
#
|
6
|
-
# [91] PathElt
|
7
|
-
# [93] PathMod
|
6
|
+
# [91] PathElt ::= PathPrimary PathMod?
|
7
|
+
# [93] PathMod ::= '*' | '?' | '+' | '{' INTEGER? (',' INTEGER?)? '}'
|
8
8
|
|
9
9
|
# @example SPARQL Grammar
|
10
10
|
# PREFIX : <http://example/>
|
@@ -23,11 +23,10 @@ module SPARQL; module Algebra
|
|
23
23
|
NAME = :path?
|
24
24
|
|
25
25
|
##
|
26
|
-
#
|
26
|
+
# Optional path:
|
27
27
|
#
|
28
28
|
# (path x (path? :p) y)
|
29
|
-
# => (union (bgp ((x :p y))) (filter (x =
|
30
|
-
#
|
29
|
+
# => (union (bgp ((x :p y))) (filter (x = y) (solution x y)))
|
31
30
|
#
|
32
31
|
# @param [RDF::Queryable] queryable
|
33
32
|
# the graph or repository to query
|
@@ -44,76 +43,21 @@ module SPARQL; module Algebra
|
|
44
43
|
subject, object = options[:subject], options[:object]
|
45
44
|
debug(options) {"Path? #{[subject, operands, object].to_sse}"}
|
46
45
|
|
47
|
-
|
48
|
-
|
49
|
-
case
|
50
|
-
when subject.variable? && object.variable?
|
51
|
-
# Nodes is the set of all subjects and objects in queryable
|
52
|
-
# FIXME: should this be Queryable#enum_nodes?
|
53
|
-
# All subjects which are `object`
|
54
|
-
query = RDF::Query.new {|q| q.pattern({subject: subject})}
|
55
|
-
queryable.query(query, **options) do |solution|
|
56
|
-
solution.merge!(object.to_sym => solution[subject])
|
57
|
-
debug(options) {"(solution-s0)-> #{solution.to_h.to_sse}"}
|
58
|
-
solutions << solution
|
59
|
-
end if query.valid?
|
60
|
-
|
61
|
-
# All objects which are `object`
|
62
|
-
query = RDF::Query.new {|q| q.pattern({object: object})}
|
63
|
-
queryable.query(query, **options) do |solution|
|
64
|
-
solution.merge!(subject.to_sym => solution[object])
|
65
|
-
debug(options) {"(solution-o0)-> #{solution.to_h.to_sse}"}
|
66
|
-
solutions << solution
|
67
|
-
end if query.valid?
|
68
|
-
when subject.variable?
|
69
|
-
# All subjects which are `object`
|
70
|
-
query = RDF::Query.new {|q| q.pattern({subject: object})}
|
71
|
-
queryable.query(query, **options) do |solution|
|
72
|
-
solution.merge!(subject.to_sym => object)
|
73
|
-
debug(options) {"(solution-s0)-> #{solution.to_h.to_sse}"}
|
74
|
-
solutions << solution
|
75
|
-
end if query.valid?
|
76
|
-
|
77
|
-
# All objects which are `object`
|
78
|
-
query = RDF::Query.new {|q| q.pattern({object: object})}
|
79
|
-
queryable.query(query, **options) do |solution|
|
80
|
-
solution.merge!(subject.to_sym => object)
|
81
|
-
debug(options) {"(solution-o0)-> #{solution.to_h.to_sse}"}
|
82
|
-
solutions << solution
|
83
|
-
end if query.valid?
|
84
|
-
when object.variable?
|
85
|
-
# All subjects which are `subject`
|
86
|
-
query = RDF::Query.new {|q| q.pattern({subject: subject})}
|
87
|
-
queryable.query(query, **options) do |solution|
|
88
|
-
solution.merge!(object.to_sym => subject)
|
89
|
-
debug(options) {"(solution-s0)-> #{solution.to_h.to_sse}"}
|
90
|
-
solutions << solution
|
91
|
-
end if query.valid?
|
92
|
-
|
93
|
-
# All objects which are `subject
|
94
|
-
query = RDF::Query.new {|q| q.pattern({object: subject})}
|
95
|
-
queryable.query(query, **options) do |solution|
|
96
|
-
solution.merge!(object.to_sym => subject)
|
97
|
-
debug(options) {"(solution-o0)-> #{solution.to_h.to_sse}"}
|
98
|
-
solutions << solution
|
99
|
-
end if query.valid?
|
100
|
-
else
|
101
|
-
# Otherwise, if subject == object, an empty solution
|
102
|
-
solutions << RDF::Query::Solution.new if subject == object
|
103
|
-
end
|
46
|
+
query = PathZero.new(operand)
|
47
|
+
solutions = query.execute(queryable, **options.merge(depth: options[:depth].to_i + 1))
|
104
48
|
|
105
49
|
# Solutions where predicate exists
|
106
50
|
query = if operand.is_a?(RDF::Term)
|
107
51
|
RDF::Query.new do |q|
|
108
52
|
q.pattern [subject, operand, object]
|
109
53
|
end
|
110
|
-
else
|
54
|
+
else # path
|
111
55
|
operand
|
112
56
|
end
|
113
57
|
|
114
58
|
# Recurse into query
|
115
|
-
solutions +=
|
116
|
-
|
59
|
+
solutions += query.execute(queryable, **options.merge(depth: options[:depth].to_i + 1))
|
60
|
+
debug(options) {"(path?)=> #{solutions.to_sxp}"}
|
117
61
|
solutions.each(&block) if block_given?
|
118
62
|
solutions
|
119
63
|
end
|
@@ -3,8 +3,8 @@ module SPARQL; module Algebra
|
|
3
3
|
##
|
4
4
|
# The SPARQL Property Path `path+` (OneOrMorePath) operator.
|
5
5
|
#
|
6
|
-
# [91] PathElt
|
7
|
-
# [93] PathMod
|
6
|
+
# [91] PathElt ::= PathPrimary PathMod?
|
7
|
+
# [93] PathMod ::= '*' | '?' | '+' | '{' INTEGER? (',' INTEGER?)? '}'
|
8
8
|
|
9
9
|
# @example SPARQL Grammar
|
10
10
|
# PREFIX : <http://example/>
|
@@ -25,6 +25,16 @@ module SPARQL; module Algebra
|
|
25
25
|
##
|
26
26
|
# Match on simple relation of subject to object, and then recurse on solutions
|
27
27
|
#
|
28
|
+
# Path including at least one:
|
29
|
+
#
|
30
|
+
# (path :a (path+ :p) :b)
|
31
|
+
#
|
32
|
+
# into
|
33
|
+
#
|
34
|
+
# (union
|
35
|
+
# (bgp (triple :a :p :b))
|
36
|
+
# (path :a (path* :p) :b))
|
37
|
+
#
|
28
38
|
# @param [RDF::Queryable] queryable
|
29
39
|
# the graph or repository to query
|
30
40
|
# @param [Hash{Symbol => Object}] options
|
@@ -62,10 +72,8 @@ module SPARQL; module Algebra
|
|
62
72
|
|
63
73
|
# Keep track of solutions
|
64
74
|
# Recurse into query
|
65
|
-
immediate_solutions =
|
66
|
-
|
67
|
-
immediate_solutions << solution
|
68
|
-
end
|
75
|
+
immediate_solutions =
|
76
|
+
query.execute(queryable, depth: options[:depth].to_i + 1, **options)
|
69
77
|
|
70
78
|
# For all solutions, if they are not in the accumulator, add them and recurse, otherwise skip
|
71
79
|
recursive_solutions = RDF::Query::Solutions.new
|
@@ -76,23 +84,23 @@ module SPARQL; module Algebra
|
|
76
84
|
case
|
77
85
|
when subject.variable? && object.variable?
|
78
86
|
# Query starting with bound object as subject, but replace result with subject
|
79
|
-
rs =
|
87
|
+
rs = self.execute(queryable, **options.merge(
|
80
88
|
subject: solution[object],
|
81
89
|
accumulator: (cumulative_solutions + immediate_solutions),
|
82
90
|
depth: options[:depth].to_i + 1)).map {|s| s.merge(subject.to_sym => solution[subject])}
|
83
91
|
# Query starting with bound subject as object, but replace result with subject
|
84
|
-
ro =
|
92
|
+
ro = self.execute(queryable, **options.merge(
|
85
93
|
object: solution[subject],
|
86
94
|
accumulator: (cumulative_solutions + immediate_solutions),
|
87
95
|
depth: options[:depth].to_i + 1)).map {|s| s.merge(object.to_sym => solution[object])}
|
88
96
|
recursive_solutions += (rs + ro).uniq
|
89
97
|
when subject.variable?
|
90
|
-
recursive_solutions +=
|
98
|
+
recursive_solutions += self.execute(queryable, **options.merge(
|
91
99
|
object: solution[subject],
|
92
100
|
accumulator: (cumulative_solutions + immediate_solutions),
|
93
101
|
depth: options[:depth].to_i + 1)).uniq
|
94
102
|
when object.variable?
|
95
|
-
recursive_solutions +=
|
103
|
+
recursive_solutions += self.execute(queryable, **options.merge(
|
96
104
|
subject: solution[object],
|
97
105
|
accumulator: (cumulative_solutions + immediate_solutions),
|
98
106
|
depth: options[:depth].to_i + 1)).uniq
|