sparql 0.0.1 → 0.0.2
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.
- data/AUTHORS +3 -0
- data/CREDITS +0 -0
- data/README.markdown +103 -53
- data/UNLICENSE +24 -0
- data/VERSION +1 -0
- data/bin/sparql +87 -0
- data/lib/sparql.rb +105 -22
- data/lib/sparql/algebra.rb +369 -0
- data/lib/sparql/algebra/evaluatable.rb +37 -0
- data/lib/sparql/algebra/expression.rb +284 -0
- data/lib/sparql/algebra/extensions.rb +159 -0
- data/lib/sparql/algebra/operator.rb +492 -0
- data/lib/sparql/algebra/operator/add.rb +34 -0
- data/lib/sparql/algebra/operator/and.rb +65 -0
- data/lib/sparql/algebra/operator/asc.rb +29 -0
- data/lib/sparql/algebra/operator/ask.rb +46 -0
- data/lib/sparql/algebra/operator/base.rb +46 -0
- data/lib/sparql/algebra/operator/bgp.rb +26 -0
- data/lib/sparql/algebra/operator/bound.rb +48 -0
- data/lib/sparql/algebra/operator/compare.rb +84 -0
- data/lib/sparql/algebra/operator/construct.rb +85 -0
- data/lib/sparql/algebra/operator/dataset.rb +77 -0
- data/lib/sparql/algebra/operator/datatype.rb +42 -0
- data/lib/sparql/algebra/operator/desc.rb +17 -0
- data/lib/sparql/algebra/operator/describe.rb +71 -0
- data/lib/sparql/algebra/operator/distinct.rb +50 -0
- data/lib/sparql/algebra/operator/divide.rb +43 -0
- data/lib/sparql/algebra/operator/equal.rb +32 -0
- data/lib/sparql/algebra/operator/exprlist.rb +52 -0
- data/lib/sparql/algebra/operator/filter.rb +71 -0
- data/lib/sparql/algebra/operator/graph.rb +28 -0
- data/lib/sparql/algebra/operator/greater_than.rb +32 -0
- data/lib/sparql/algebra/operator/greater_than_or_equal.rb +33 -0
- data/lib/sparql/algebra/operator/is_blank.rb +35 -0
- data/lib/sparql/algebra/operator/is_iri.rb +37 -0
- data/lib/sparql/algebra/operator/is_literal.rb +36 -0
- data/lib/sparql/algebra/operator/join.rb +67 -0
- data/lib/sparql/algebra/operator/lang.rb +29 -0
- data/lib/sparql/algebra/operator/lang_matches.rb +53 -0
- data/lib/sparql/algebra/operator/left_join.rb +95 -0
- data/lib/sparql/algebra/operator/less_than.rb +32 -0
- data/lib/sparql/algebra/operator/less_than_or_equal.rb +32 -0
- data/lib/sparql/algebra/operator/minus.rb +31 -0
- data/lib/sparql/algebra/operator/multiply.rb +34 -0
- data/lib/sparql/algebra/operator/not.rb +35 -0
- data/lib/sparql/algebra/operator/not_equal.rb +26 -0
- data/lib/sparql/algebra/operator/or.rb +65 -0
- data/lib/sparql/algebra/operator/order.rb +69 -0
- data/lib/sparql/algebra/operator/plus.rb +31 -0
- data/lib/sparql/algebra/operator/prefix.rb +45 -0
- data/lib/sparql/algebra/operator/project.rb +46 -0
- data/lib/sparql/algebra/operator/reduced.rb +47 -0
- data/lib/sparql/algebra/operator/regex.rb +70 -0
- data/lib/sparql/algebra/operator/same_term.rb +46 -0
- data/lib/sparql/algebra/operator/slice.rb +60 -0
- data/lib/sparql/algebra/operator/str.rb +35 -0
- data/lib/sparql/algebra/operator/subtract.rb +32 -0
- data/lib/sparql/algebra/operator/union.rb +55 -0
- data/lib/sparql/algebra/query.rb +99 -0
- data/lib/sparql/algebra/sxp_extensions.rb +35 -0
- data/lib/sparql/algebra/version.rb +20 -0
- data/lib/sparql/extensions.rb +102 -0
- data/lib/sparql/grammar.rb +298 -0
- data/lib/sparql/grammar/lexer.rb +609 -0
- data/lib/sparql/grammar/parser.rb +1383 -0
- data/lib/sparql/grammar/parser/meta.rb +1801 -0
- data/lib/sparql/results.rb +220 -0
- data/lib/sparql/version.rb +20 -0
- metadata +232 -62
- data/Rakefile +0 -22
- data/coverage/index.html +0 -252
- data/coverage/lib-sparql-execute_sparql_rb.html +0 -621
- data/coverage/lib-sparql_rb.html +0 -622
- data/lib/sparql/execute_sparql.rb +0 -27
- data/lib/sparql/sparql.treetop +0 -159
- data/sparql.gemspec +0 -16
- data/spec/spec.opts +0 -2
- data/spec/spec_helper.rb +0 -24
- data/spec/unit/graph_parsing_spec.rb +0 -76
- data/spec/unit/iri_parsing_spec.rb +0 -46
- data/spec/unit/prefixed_names_parsing_spec.rb +0 -40
- data/spec/unit/primitives_parsing_spec.rb +0 -26
- data/spec/unit/sparql_parsing_spec.rb +0 -72
- data/spec/unit/variables_parsing_spec.rb +0 -36
@@ -0,0 +1,34 @@
|
|
1
|
+
module SPARQL; module Algebra
|
2
|
+
class Operator
|
3
|
+
include Evaluatable
|
4
|
+
|
5
|
+
##
|
6
|
+
# The SPARQL numeric `add` operator.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# (+ 1 ?x)
|
10
|
+
# (add 1 ?x)
|
11
|
+
#
|
12
|
+
# @see http://www.w3.org/TR/xpath-functions/#func-numeric-add
|
13
|
+
class Add < Operator::Binary
|
14
|
+
NAME = [:+, :add]
|
15
|
+
|
16
|
+
##
|
17
|
+
# Returns the arithmetic sum of the operands.
|
18
|
+
#
|
19
|
+
# @param [RDF::Literal::Numeric] left
|
20
|
+
# a numeric literal
|
21
|
+
# @param [RDF::Literal::Numeric] right
|
22
|
+
# a numeric literal
|
23
|
+
# @return [RDF::Literal::Numeric]
|
24
|
+
# @raise [TypeError] if either operand is not a numeric literal
|
25
|
+
def apply(left, right)
|
26
|
+
case
|
27
|
+
when left.is_a?(RDF::Literal::Numeric) && right.is_a?(RDF::Literal::Numeric)
|
28
|
+
left + right
|
29
|
+
else raise TypeError, "expected two RDF::Literal::Numeric operands, but got #{left.inspect} and #{right.inspect}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end # Add
|
33
|
+
end # Operator
|
34
|
+
end; end # SPARQL::Algebra
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module SPARQL; module Algebra
|
2
|
+
class Operator
|
3
|
+
##
|
4
|
+
# The SPARQL logical `and` operator.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# (&& ?x ?y)
|
8
|
+
# (and ?x ?y)
|
9
|
+
#
|
10
|
+
# @see http://www.w3.org/TR/rdf-sparql-query/#func-logical-and
|
11
|
+
# @see http://www.w3.org/TR/rdf-sparql-query/#evaluation
|
12
|
+
class And < Operator::Binary
|
13
|
+
include Evaluatable
|
14
|
+
|
15
|
+
NAME = [:and, :'&&']
|
16
|
+
|
17
|
+
##
|
18
|
+
# Initializes a new operator instance.
|
19
|
+
#
|
20
|
+
# @param [RDF::Literal::Boolean] left
|
21
|
+
# the left operand
|
22
|
+
# @param [RDF::Literal::Boolean] right
|
23
|
+
# the right operand
|
24
|
+
# @param [Hash{Symbol => Object}] options
|
25
|
+
# any additional options (see {Operator#initialize})
|
26
|
+
# @raise [TypeError] if any operand is invalid
|
27
|
+
def initialize(left, right, options = {})
|
28
|
+
super
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# Returns the logical `AND` of the left operand and the right operand.
|
33
|
+
#
|
34
|
+
# Note that this operator operates on the effective boolean value
|
35
|
+
# (EBV) of its operands.
|
36
|
+
#
|
37
|
+
# @param [RDF::Query::Solution, #[]] bindings
|
38
|
+
# @return [RDF::Literal::Boolean] `true` or `false`
|
39
|
+
# @raise [TypeError] if the operands could not be coerced to boolean literals
|
40
|
+
def evaluate(bindings = {})
|
41
|
+
begin
|
42
|
+
left = boolean(operand(0).evaluate(bindings)).true?
|
43
|
+
rescue TypeError
|
44
|
+
left = nil
|
45
|
+
end
|
46
|
+
|
47
|
+
begin
|
48
|
+
right = boolean(operand(1).evaluate(bindings)).true?
|
49
|
+
rescue TypeError
|
50
|
+
right = nil
|
51
|
+
end
|
52
|
+
|
53
|
+
# From http://www.w3.org/TR/rdf-sparql-query/#evaluation
|
54
|
+
# A logical-and that encounters an error on only one branch will return an error if the other branch is
|
55
|
+
# TRUE and FALSE if the other branch is FALSE.
|
56
|
+
case
|
57
|
+
when left.nil? && right.nil? then raise(TypeError)
|
58
|
+
when left.nil? then right ? raise(TypeError) : RDF::Literal::FALSE
|
59
|
+
when right.nil? then left ? raise(TypeError) : RDF::Literal::FALSE
|
60
|
+
else RDF::Literal(left && right)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end # And
|
64
|
+
end # Operator
|
65
|
+
end; end # SPARQL::Algebra
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module SPARQL; module Algebra
|
2
|
+
class Operator
|
3
|
+
##
|
4
|
+
# The SPARQL ascending sort operator.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# (prefix ((foaf: <http://xmlns.com/foaf/0.1/>))
|
8
|
+
# (project (?name)
|
9
|
+
# (order ((asc ?name))
|
10
|
+
# (bgp (triple ?x foaf:name ?name)))))
|
11
|
+
#
|
12
|
+
# @see http://www.w3.org/TR/rdf-sparql-query/#func-isLiteral
|
13
|
+
class Asc < Operator::Unary
|
14
|
+
include Evaluatable
|
15
|
+
|
16
|
+
NAME = :asc
|
17
|
+
|
18
|
+
##
|
19
|
+
# Returns the evaluation of it's operand. Default comparison is in
|
20
|
+
# ascending order. Ordering is applied in {Order}.
|
21
|
+
#
|
22
|
+
# @param [RDF::Query::Solution, #[]] bindings
|
23
|
+
# @return [RDF::Term]
|
24
|
+
def evaluate(bindings = {})
|
25
|
+
operand(0).evaluate(bindings)
|
26
|
+
end
|
27
|
+
end # Asc
|
28
|
+
end # Operator
|
29
|
+
end; end # SPARQL::Algebra
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module SPARQL; module Algebra
|
2
|
+
class Operator
|
3
|
+
##
|
4
|
+
# The SPARQL GraphPattern `ask` operator.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# (prefix ((: <http://example/>))
|
8
|
+
# (ask
|
9
|
+
# (bgp (triple :x :p ?x))))
|
10
|
+
#
|
11
|
+
# @see http://www.w3.org/TR/rdf-sparql-query/#ask
|
12
|
+
class Ask < Operator::Unary
|
13
|
+
include Query
|
14
|
+
|
15
|
+
NAME = [:ask]
|
16
|
+
|
17
|
+
##
|
18
|
+
# Executes this query on the given `queryable` graph or repository.
|
19
|
+
# Returns true if any solutions are found, false otherwise.
|
20
|
+
#
|
21
|
+
# @param [RDF::Queryable] queryable
|
22
|
+
# the graph or repository to query
|
23
|
+
# @param [Hash{Symbol => Object}] options
|
24
|
+
# any additional keyword options
|
25
|
+
# @return [RDF::Literal::Boolean]
|
26
|
+
# the resulting solution sequence
|
27
|
+
# @see http://www.w3.org/TR/rdf-sparql-query/#sparqlAlgebra
|
28
|
+
def execute(queryable, options = {})
|
29
|
+
debug(options) {"Ask #{operands.first}"}
|
30
|
+
boolean(!operands.last.
|
31
|
+
execute(queryable, options.merge(:depth => options[:depth].to_i + 1)).
|
32
|
+
empty?)
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Returns an optimized version of this query.
|
37
|
+
#
|
38
|
+
# Return optimized query
|
39
|
+
#
|
40
|
+
# @return [Union, RDF::Query] `self`
|
41
|
+
def optimize
|
42
|
+
operands = operands.map(&:optimize)
|
43
|
+
end
|
44
|
+
end # Ask
|
45
|
+
end # Operator
|
46
|
+
end; end # SPARQL::Algebra
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module SPARQL; module Algebra
|
2
|
+
class Operator
|
3
|
+
##
|
4
|
+
# The SPARQL GraphPattern `base` operator.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# (base <http://example.org/>
|
8
|
+
# (bgp (triple <a> <b> 123.0)))
|
9
|
+
#
|
10
|
+
# @see http://www.w3.org/TR/rdf-sparql-query/#QSynIRI
|
11
|
+
class Base < Binary
|
12
|
+
include Query
|
13
|
+
|
14
|
+
NAME = [:base]
|
15
|
+
|
16
|
+
##
|
17
|
+
# Executes this query on the given `queryable` graph or repository.
|
18
|
+
# Really a pass-through, as this is a syntactic object used for providing
|
19
|
+
# context for relative URIs.
|
20
|
+
#
|
21
|
+
# @param [RDF::Queryable] queryable
|
22
|
+
# the graph or repository to query
|
23
|
+
# @param [Hash{Symbol => Object}] options
|
24
|
+
# any additional keyword options
|
25
|
+
# @return [RDF::Query::Solutions]
|
26
|
+
# the resulting solution sequence
|
27
|
+
# @see http://www.w3.org/TR/rdf-sparql-query/#sparqlAlgebra
|
28
|
+
def execute(queryable, options = {})
|
29
|
+
debug(options) {"Base #{operands.first}"}
|
30
|
+
@solutions = operands.last.execute(queryable, options.merge(:depth => options[:depth].to_i + 1))
|
31
|
+
debug(options) {"=> #{@solutions.inspect}"}
|
32
|
+
@solutions
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Returns an optimized version of this query.
|
37
|
+
#
|
38
|
+
# Return optimized query
|
39
|
+
#
|
40
|
+
# @return [Union, RDF::Query] `self`
|
41
|
+
def optimize
|
42
|
+
operands.last.optimize
|
43
|
+
end
|
44
|
+
end # Base
|
45
|
+
end # Operator
|
46
|
+
end; end # SPARQL::Algebra
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module SPARQL; module Algebra
|
2
|
+
class Operator
|
3
|
+
##
|
4
|
+
# The SPARQL GraphPattern `bgp` operator.
|
5
|
+
#
|
6
|
+
# Query with `context` set to false.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# (prefix ((: <http://example/>))
|
10
|
+
# (bgp (triple ?s ?p ?o)))
|
11
|
+
#
|
12
|
+
# @see http://www.w3.org/TR/rdf-sparql-query/#sparqlAlgebra
|
13
|
+
class BGP < Operator
|
14
|
+
NAME = [:bgp]
|
15
|
+
##
|
16
|
+
# A `graph` is an RDF::Query with a context.
|
17
|
+
#
|
18
|
+
# @param [RDF::URI, RDF::Query::Variable] context
|
19
|
+
# @param [RDF::Query] bgp
|
20
|
+
# @return [RDF::Query]
|
21
|
+
def self.new(*patterns)
|
22
|
+
RDF::Query.new(*(patterns + [{:context => false}]))
|
23
|
+
end
|
24
|
+
end # BGP
|
25
|
+
end # Operator
|
26
|
+
end; end # SPARQL::Algebra
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module SPARQL; module Algebra
|
2
|
+
class Operator
|
3
|
+
##
|
4
|
+
# The SPARQL `bound` operator.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# (prefix ((: <http://example.org/ns#>))
|
8
|
+
# (project (?a ?c)
|
9
|
+
# (filter (! (bound ?e))
|
10
|
+
# (leftjoin
|
11
|
+
# (bgp (triple ?a :b ?c))
|
12
|
+
# (bgp (triple ?c :d ?e))))))
|
13
|
+
#
|
14
|
+
# @see http://www.w3.org/TR/rdf-sparql-query/#func-bound
|
15
|
+
class Bound < Operator::Unary
|
16
|
+
include Evaluatable
|
17
|
+
|
18
|
+
NAME = :bound
|
19
|
+
|
20
|
+
##
|
21
|
+
# Initializes a new operator instance.
|
22
|
+
#
|
23
|
+
# @param [RDF::Query::Variable] var
|
24
|
+
# a variable
|
25
|
+
# @param [Hash{Symbol => Object}] options
|
26
|
+
# any additional options (see {Operator#initialize})
|
27
|
+
# @raise [TypeError] if any operand is invalid
|
28
|
+
def initialize(var, options = {})
|
29
|
+
super
|
30
|
+
end
|
31
|
+
|
32
|
+
##
|
33
|
+
# Returns `true` if the operand is a variable that is bound to a
|
34
|
+
# value in the given `bindings`, `false` otherwise.
|
35
|
+
#
|
36
|
+
# @param [RDF::Query::Solution, #[]] bindings
|
37
|
+
# @return [RDF::Literal::Boolean] `true` or `false`
|
38
|
+
# @raise [TypeError] if the operand is not a variable
|
39
|
+
def evaluate(bindings = {})
|
40
|
+
case var = operand
|
41
|
+
when Variable
|
42
|
+
operand.evaluate(bindings) ? RDF::Literal::TRUE : RDF::Literal::FALSE
|
43
|
+
else raise TypeError, "expected an RDF::Query::Variable, but got #{var.inspect}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end # Bound
|
47
|
+
end # Operator
|
48
|
+
end; end # SPARQL::Algebra
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module SPARQL; module Algebra
|
2
|
+
class Operator
|
3
|
+
##
|
4
|
+
# A SPARQL relational `<=>` comparison operator.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# (<=> ?x ?y)
|
8
|
+
#
|
9
|
+
# @see http://www.w3.org/TR/rdf-sparql-query/#OperatorMapping
|
10
|
+
# @see http://www.w3.org/TR/xpath-functions/#func-compare
|
11
|
+
class Compare < Operator::Binary
|
12
|
+
include Evaluatable
|
13
|
+
|
14
|
+
NAME = :<=>
|
15
|
+
|
16
|
+
##
|
17
|
+
# Returns -1, 0, or 1, depending on whether the first operand is
|
18
|
+
# respectively less than, equal to, or greater than the second
|
19
|
+
# operand.
|
20
|
+
#
|
21
|
+
# SPARQL also fixes an order between some kinds of RDF terms that would not otherwise be ordered:
|
22
|
+
#
|
23
|
+
# (Lowest) no value assigned to the variable or expression in this solution.
|
24
|
+
# Blank nodes
|
25
|
+
# IRIs
|
26
|
+
# RDF literals
|
27
|
+
#
|
28
|
+
# @param [RDF::Literal] left
|
29
|
+
# a literal
|
30
|
+
# @param [RDF::Literal] right
|
31
|
+
# a literal
|
32
|
+
# @return [RDF::Literal::Integer] `-1`, `0`, or `1`
|
33
|
+
# @raise [TypeError] if either operand is not a literal
|
34
|
+
def apply(left, right)
|
35
|
+
case
|
36
|
+
# @see http://www.w3.org/TR/rdf-sparql-query/#OperatorMapping
|
37
|
+
# @see http://www.w3.org/TR/rdf-sparql-query/#modOrderBy
|
38
|
+
when left.is_a?(RDF::Literal) && right.is_a?(RDF::Literal)
|
39
|
+
case
|
40
|
+
# @see http://www.w3.org/TR/xpath-functions/#string-compare
|
41
|
+
# @see http://www.w3.org/TR/xpath-functions/#comp.numeric
|
42
|
+
# @see http://www.w3.org/TR/xpath-functions/#op.boolean
|
43
|
+
# @see http://www.w3.org/TR/xpath-functions/#comp.duration.datetime
|
44
|
+
when (left.simple? && right.simple?) ||
|
45
|
+
(left.is_a?(RDF::Literal::Numeric) && right.is_a?(RDF::Literal::Numeric)) ||
|
46
|
+
(left.datatype == right.datatype && left.language == right.language)
|
47
|
+
RDF::Literal(left.send(self.class.const_get(:NAME), right))
|
48
|
+
|
49
|
+
# A plain literal is lower than an RDF literal with type xsd:string of the same lexical form.
|
50
|
+
when left.simple? && right.datatype == RDF::XSD.string && left.value == right.value
|
51
|
+
RDF::Literal(-1)
|
52
|
+
when right.simple? && left.datatype == RDF::XSD.string && right.value == left.value
|
53
|
+
RDF::Literal(-1)
|
54
|
+
|
55
|
+
else raise TypeError, "unable to compare #{left.inspect} and #{right.inspect}"
|
56
|
+
end
|
57
|
+
|
58
|
+
when left.is_a?(RDF::URI) && right.is_a?(RDF::URI)
|
59
|
+
# Pairs of IRIs are ordered by comparing them as simple literals.
|
60
|
+
RDF::Literal(RDF::Literal(left.to_s).send(self.class.const_get(:NAME), RDF::Literal(right.to_s)))
|
61
|
+
when left.is_a?(RDF::Node) && right.is_a?(RDF::Node)
|
62
|
+
# BNode comparison is undefined.
|
63
|
+
RDF::Literal(0)
|
64
|
+
when left.nil? && right.nil?
|
65
|
+
RDF::Literal(0)
|
66
|
+
|
67
|
+
# SPARQL also fixes an order between some kinds of RDF terms that would not otherwise be ordered:
|
68
|
+
# 2. Blank nodes
|
69
|
+
when left.is_a?(RDF::Node) && right.is_a?(RDF::Term)
|
70
|
+
RDF::Literal(-1)
|
71
|
+
when right.is_a?(RDF::Node) && left.is_a?(RDF::Term)
|
72
|
+
RDF::Literal(1)
|
73
|
+
|
74
|
+
# 3. IRIs
|
75
|
+
when left.is_a?(RDF::URI) && right.is_a?(RDF::Term)
|
76
|
+
RDF::Literal(-1)
|
77
|
+
when right.is_a?(RDF::URI) && left.is_a?(RDF::Term)
|
78
|
+
RDF::Literal(1)
|
79
|
+
else raise TypeError, "expected two RDF::Term operands, but got #{left.inspect} and #{right.inspect}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end # Compare
|
83
|
+
end # Operator
|
84
|
+
end; end # SPARQL::Algebra
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module SPARQL; module Algebra
|
2
|
+
class Operator
|
3
|
+
##
|
4
|
+
# The SPARQL GraphPattern `construct` operator.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# (prefix ((rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>)
|
8
|
+
# (foaf: <http://xmlns.com/foaf/0.1/>))
|
9
|
+
# (construct ((triple ?s ?p ?o))
|
10
|
+
# (project (?s ?p ?o)
|
11
|
+
# (bgp (triple ?s ?p ?o)))))
|
12
|
+
#
|
13
|
+
# @see http://www.w3.org/TR/rdf-sparql-query/#ask
|
14
|
+
class Construct < Operator::Binary
|
15
|
+
include Query
|
16
|
+
|
17
|
+
NAME = [:construct]
|
18
|
+
|
19
|
+
##
|
20
|
+
# Executes this query on the given {RDF::Queryable} object.
|
21
|
+
# Binds variables to the array of patterns in the first operand and returns the resulting RDF::Graph object
|
22
|
+
#
|
23
|
+
# If any such instantiation produces a triple containing an unbound variable or an illegal RDF construct,
|
24
|
+
# such as an RDF::Literal in _subject_ or _predicate_ position, then that triple is not included in the output RDF
|
25
|
+
# graph. The graph template can contain triples with no variables (known as ground or explicit triples),
|
26
|
+
# and these also appear in the output RDF graph returned by the CONSTRUCT query form.
|
27
|
+
#
|
28
|
+
# @param [RDF::Queryable] queryable
|
29
|
+
# the graph or repository to query
|
30
|
+
# @param [Hash{Symbol => Object}] options
|
31
|
+
# any additional keyword options
|
32
|
+
# @return [RDF::Query::Solutions]
|
33
|
+
# the resulting solution sequence
|
34
|
+
# @see http://www.w3.org/TR/rdf-sparql-query/#construct
|
35
|
+
def execute(queryable, options = {})
|
36
|
+
debug(options) {"Construct #{operands.first}, #{options.inspect}"}
|
37
|
+
graph = RDF::Graph.new
|
38
|
+
patterns = operands.first
|
39
|
+
query = operands.last
|
40
|
+
|
41
|
+
query.execute(queryable, options.merge(:depth => options[:depth].to_i + 1)).each do |solution|
|
42
|
+
debug(options) {"=> Apply #{solution.inspect} to BGP"}
|
43
|
+
|
44
|
+
# Create a mapping from BNodes within the pattern list to newly constructed BNodes
|
45
|
+
nodes = {}
|
46
|
+
patterns.each do |pattern|
|
47
|
+
terms = {}
|
48
|
+
[:subject, :predicate, :object].each do |r|
|
49
|
+
terms[r] = case o = pattern.send(r)
|
50
|
+
when RDF::Node then nodes[o] ||= RDF::Node.new
|
51
|
+
when RDF::Query::Variable then solution[o]
|
52
|
+
else o
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
statement = RDF::Statement.from(terms)
|
57
|
+
|
58
|
+
# Sanity checking on statement
|
59
|
+
if statement.subject.nil? || statement.predicate.nil? || statement.object.nil? ||
|
60
|
+
statement.subject.literal? || statement.predicate.literal?
|
61
|
+
debug(options) {"==> skip #{statement.inspect}"}
|
62
|
+
next
|
63
|
+
end
|
64
|
+
|
65
|
+
debug(options) {"==> add #{statement.inspect}"}
|
66
|
+
graph << statement
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
debug(options) {"=>\n#{graph.dump(:ttl, :standard_prefixes => true)}"}
|
71
|
+
graph
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# Returns an optimized version of this query.
|
76
|
+
#
|
77
|
+
# Return optimized query
|
78
|
+
#
|
79
|
+
# @return [Union, RDF::Query] `self`
|
80
|
+
def optimize
|
81
|
+
operands = operands.map(&:optimize)
|
82
|
+
end
|
83
|
+
end # Construct
|
84
|
+
end # Operator
|
85
|
+
end; end # SPARQL::Algebra
|