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,28 @@
|
|
1
|
+
module SPARQL; module Algebra
|
2
|
+
class Operator
|
3
|
+
##
|
4
|
+
# The SPARQL GraphPattern `graph` operator.
|
5
|
+
#
|
6
|
+
# This is a wrapper to add a `context` to the query.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# (prefix ((: <http://example/>))
|
10
|
+
# (graph ?g
|
11
|
+
# (bgp (triple ?s ?p ?o))))
|
12
|
+
#
|
13
|
+
# @see http://www.w3.org/TR/rdf-sparql-query/#sparqlAlgebra
|
14
|
+
class Graph < Operator::Binary
|
15
|
+
NAME = [:graph]
|
16
|
+
##
|
17
|
+
# A `graph` is an RDF::Query with a context.
|
18
|
+
#
|
19
|
+
# @param [RDF::URI, RDF::Query::Variable] context
|
20
|
+
# @param [RDF::Query] bgp
|
21
|
+
# @return [RDF::Query]
|
22
|
+
def self.new(context, bgp)
|
23
|
+
bgp.context = context
|
24
|
+
bgp
|
25
|
+
end
|
26
|
+
end # Graph
|
27
|
+
end # Operator
|
28
|
+
end; end # SPARQL::Algebra
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module SPARQL; module Algebra
|
2
|
+
class Operator
|
3
|
+
##
|
4
|
+
# The SPARQL relational `>` (greater than) 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
|
+
# @see http://www.w3.org/TR/xpath-functions/#func-numeric-greater-than
|
12
|
+
# @see http://www.w3.org/TR/xpath-functions/#func-boolean-greater-than
|
13
|
+
# @see http://www.w3.org/TR/xpath-functions/#func-dateTime-greater-than
|
14
|
+
class GreaterThan < Compare
|
15
|
+
NAME = :>
|
16
|
+
|
17
|
+
##
|
18
|
+
# Returns `true` if the first operand is greater than the second
|
19
|
+
# operand; returns `false` otherwise.
|
20
|
+
#
|
21
|
+
# @param [RDF::Literal] left
|
22
|
+
# a literal
|
23
|
+
# @param [RDF::Literal] right
|
24
|
+
# a literal
|
25
|
+
# @return [RDF::Literal::Boolean] `true` or `false`
|
26
|
+
# @raise [TypeError] if either operand is not a literal
|
27
|
+
def apply(left, right)
|
28
|
+
super
|
29
|
+
end
|
30
|
+
end # GreaterThan
|
31
|
+
end # Operator
|
32
|
+
end; end # SPARQL::Algebra
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module SPARQL; module Algebra
|
2
|
+
class Operator
|
3
|
+
##
|
4
|
+
# The SPARQL relational `>=` (greater than or equal) comparison
|
5
|
+
# operator.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# (>= ?x ?y)
|
9
|
+
#
|
10
|
+
# @see http://www.w3.org/TR/rdf-sparql-query/#OperatorMapping
|
11
|
+
# @see http://www.w3.org/TR/xpath-functions/#func-compare
|
12
|
+
# @see http://www.w3.org/TR/xpath-functions/#func-numeric-greater-than
|
13
|
+
# @see http://www.w3.org/TR/xpath-functions/#func-boolean-greater-than
|
14
|
+
# @see http://www.w3.org/TR/xpath-functions/#func-dateTime-greater-than
|
15
|
+
class GreaterThanOrEqual < Compare
|
16
|
+
NAME = :>=
|
17
|
+
|
18
|
+
##
|
19
|
+
# Returns `true` if the first operand is greater than or equal to the
|
20
|
+
# second operand; returns `false` otherwise.
|
21
|
+
#
|
22
|
+
# @param [RDF::Literal] left
|
23
|
+
# a literal
|
24
|
+
# @param [RDF::Literal] right
|
25
|
+
# a literal
|
26
|
+
# @return [RDF::Literal::Boolean] `true` or `false`
|
27
|
+
# @raise [TypeError] if either operand is not a literal
|
28
|
+
def apply(left, right)
|
29
|
+
super
|
30
|
+
end
|
31
|
+
end # GreaterThanOrEqual
|
32
|
+
end # Operator
|
33
|
+
end; end # SPARQL::Algebra
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module SPARQL; module Algebra
|
2
|
+
class Operator
|
3
|
+
##
|
4
|
+
# The SPARQL `isBlank` operator.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# (prefix ((xsd: <http://www.w3.org/2001/XMLSchema#>)
|
8
|
+
# (: <http://example.org/things#>))
|
9
|
+
# (project (?x ?v)
|
10
|
+
# (filter (isBlank ?v)
|
11
|
+
# (bgp (triple ?x :p ?v)))))
|
12
|
+
#
|
13
|
+
# @see http://www.w3.org/TR/rdf-sparql-query/#func-isBlank
|
14
|
+
class IsBlank < Operator::Unary
|
15
|
+
include Evaluatable
|
16
|
+
|
17
|
+
NAME = :isBlank
|
18
|
+
|
19
|
+
##
|
20
|
+
# Returns `true` if the operand is an `RDF::Node`, `false` otherwise.
|
21
|
+
#
|
22
|
+
# @param [RDF::Term] term
|
23
|
+
# an RDF term
|
24
|
+
# @return [RDF::Literal::Boolean] `true` or `false`
|
25
|
+
# @raise [TypeError] if the operand is not an RDF term
|
26
|
+
def apply(term)
|
27
|
+
case term
|
28
|
+
when RDF::Node then RDF::Literal::TRUE
|
29
|
+
when RDF::Term then RDF::Literal::FALSE
|
30
|
+
else raise TypeError, "expected an RDF::Term, but got #{term.inspect}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end # IsBlank
|
34
|
+
end # Operator
|
35
|
+
end; end # SPARQL::Algebra
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module SPARQL; module Algebra
|
2
|
+
class Operator
|
3
|
+
##
|
4
|
+
# The SPARQL `isIRI`/`isURI` operator.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# (prefix ((xsd: <http://www.w3.org/2001/XMLSchema#>)
|
8
|
+
# (: <http://example.org/things#>))
|
9
|
+
# (project (?x ?v)
|
10
|
+
# (filter (isIRI ?v)
|
11
|
+
# (bgp (triple ?x :p ?v)))))
|
12
|
+
#
|
13
|
+
# @see http://www.w3.org/TR/rdf-sparql-query/#func-isIRI
|
14
|
+
class IsIRI < Operator::Unary
|
15
|
+
include Evaluatable
|
16
|
+
|
17
|
+
NAME = [:isIRI, :isURI]
|
18
|
+
|
19
|
+
##
|
20
|
+
# Returns `true` if the operand is an `RDF::URI`, `false` otherwise.
|
21
|
+
#
|
22
|
+
# @param [RDF::Term] term
|
23
|
+
# an RDF term
|
24
|
+
# @return [RDF::Literal::Boolean] `true` or `false`
|
25
|
+
# @raise [TypeError] if the operand is not an RDF term
|
26
|
+
def apply(term)
|
27
|
+
case term
|
28
|
+
when RDF::URI then RDF::Literal::TRUE
|
29
|
+
when RDF::Term then RDF::Literal::FALSE
|
30
|
+
else raise TypeError, "expected an RDF::Term, but got #{term.inspect}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
Operator::IsURI = IsIRI
|
35
|
+
end # IsIRI
|
36
|
+
end # Operator
|
37
|
+
end; end # SPARQL::Algebra
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module SPARQL; module Algebra
|
2
|
+
class Operator
|
3
|
+
##
|
4
|
+
# The SPARQL `isLiteral` operator.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# (prefix ((xsd: <http://www.w3.org/2001/XMLSchema#>)
|
8
|
+
# (: <http://example.org/things#>))
|
9
|
+
# (project (?x ?v)
|
10
|
+
# (filter (isLiteral ?v)
|
11
|
+
# (bgp (triple ?x :p ?v)))))
|
12
|
+
#
|
13
|
+
# @see http://www.w3.org/TR/rdf-sparql-query/#func-isLiteral
|
14
|
+
class IsLiteral < Operator::Unary
|
15
|
+
include Evaluatable
|
16
|
+
|
17
|
+
NAME = :isLiteral
|
18
|
+
|
19
|
+
##
|
20
|
+
# Returns `true` if the operand is an `RDF::Literal`, `false`
|
21
|
+
# otherwise.
|
22
|
+
#
|
23
|
+
# @param [RDF::Term] term
|
24
|
+
# an RDF term
|
25
|
+
# @return [RDF::Literal::Boolean] `true` or `false`
|
26
|
+
# @raise [TypeError] if the operand is not an RDF term
|
27
|
+
def apply(term)
|
28
|
+
case term
|
29
|
+
when RDF::Literal then RDF::Literal::TRUE
|
30
|
+
when RDF::Term then RDF::Literal::FALSE
|
31
|
+
else raise TypeError, "expected an RDF::Term, but got #{term.inspect}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end # IsLiteral
|
35
|
+
end # Operator
|
36
|
+
end; end # SPARQL::Algebra
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module SPARQL; module Algebra
|
2
|
+
class Operator
|
3
|
+
##
|
4
|
+
# The SPARQL GraphPattern `join` operator.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# (prefix ((: <http://example/>))
|
8
|
+
# (join
|
9
|
+
# (bgp (triple ?s ?p ?o))
|
10
|
+
# (graph ?g
|
11
|
+
# (bgp (triple ?s ?q ?v)))))
|
12
|
+
#
|
13
|
+
# @see http://www.w3.org/TR/rdf-sparql-query/#sparqlAlgebra
|
14
|
+
class Join < Operator::Binary
|
15
|
+
include Query
|
16
|
+
|
17
|
+
NAME = [:join]
|
18
|
+
|
19
|
+
##
|
20
|
+
# Executes each operand with `queryable` and performs the `join` operation
|
21
|
+
# by creating a new solution set containing the `merge` of all solutions
|
22
|
+
# from each set that are `compatible` with each other.
|
23
|
+
#
|
24
|
+
# @param [RDF::Queryable] queryable
|
25
|
+
# the graph or repository to query
|
26
|
+
# @param [Hash{Symbol => Object}] options
|
27
|
+
# any additional keyword options
|
28
|
+
# @return [RDF::Query::Solutions]
|
29
|
+
# the resulting solution sequence
|
30
|
+
# @see http://www.w3.org/TR/rdf-sparql-query/#sparqlAlgebra
|
31
|
+
# @see http://rdf.rubyforge.org/RDF/Query/Solution.html#merge-instance_method
|
32
|
+
# @see http://rdf.rubyforge.org/RDF/Query/Solution.html#compatible%3F-instance_method
|
33
|
+
def execute(queryable, options = {})
|
34
|
+
# Join(Ω1, Ω2) = { merge(μ1, μ2) | μ1 in Ω1 and μ2 in Ω2, and μ1 and μ2 are compatible }
|
35
|
+
# eval(D(G), Join(P1, P2)) = Join(eval(D(G), P1), eval(D(G), P2))
|
36
|
+
#
|
37
|
+
# Generate solutions independently, merge based on solution compatibility
|
38
|
+
debug(options) {"Join"}
|
39
|
+
solutions1 = operand(0).execute(queryable, options.merge(:depth => options[:depth].to_i + 1)) || {}
|
40
|
+
debug(options) {"=>(left) #{solutions1.inspect}"}
|
41
|
+
solutions2 = operand(1).execute(queryable, options.merge(:depth => options[:depth].to_i + 1)) || {}
|
42
|
+
debug(options) {"=>(right) #{solutions2.inspect}"}
|
43
|
+
@solutions = solutions1.map do |s1|
|
44
|
+
solutions2.map { |s2| s2.merge(s1) if s2.compatible?(s1) }
|
45
|
+
end.flatten.compact
|
46
|
+
@solutions = RDF::Query::Solutions.new(@solutions)
|
47
|
+
debug(options) {"=> #{@solutions.inspect}"}
|
48
|
+
@solutions
|
49
|
+
end
|
50
|
+
|
51
|
+
##
|
52
|
+
# Returns an optimized version of this query.
|
53
|
+
#
|
54
|
+
# Groups of one graph pattern (not a filter) become join(Z, A) and can be replaced by A.
|
55
|
+
# The empty graph pattern Z is the identity for join:
|
56
|
+
# Replace join(Z, A) by A
|
57
|
+
# Replace join(A, Z) by A
|
58
|
+
#
|
59
|
+
# @return [Join, RDF::Query] `self`
|
60
|
+
def optimize
|
61
|
+
ops = operands.map {|o| o.optimize }.select {|o| o.respond_to?(:empty?) && !o.empty?}
|
62
|
+
@operands = ops
|
63
|
+
self
|
64
|
+
end
|
65
|
+
end # Join
|
66
|
+
end # Operator
|
67
|
+
end; end # SPARQL::Algebra
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module SPARQL; module Algebra
|
2
|
+
class Operator
|
3
|
+
##
|
4
|
+
# The SPARQL `lang` operator.
|
5
|
+
#
|
6
|
+
# @see http://www.w3.org/TR/rdf-sparql-query/#func-lang
|
7
|
+
class Lang < Operator::Unary
|
8
|
+
include Evaluatable
|
9
|
+
|
10
|
+
NAME = :lang
|
11
|
+
|
12
|
+
##
|
13
|
+
# Returns the language tag of the operand, if it has one.
|
14
|
+
#
|
15
|
+
# If the operand has no language tag, returns `""`.
|
16
|
+
#
|
17
|
+
# @param [RDF::Literal] literal
|
18
|
+
# a literal
|
19
|
+
# @return [RDF::Literal] a simple literal
|
20
|
+
# @raise [TypeError] if the operand is not a literal
|
21
|
+
def apply(literal)
|
22
|
+
case literal
|
23
|
+
when RDF::Literal then RDF::Literal(literal.language.to_s)
|
24
|
+
else raise TypeError, "expected an RDF::Literal, but got #{literal.inspect}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end # Lang
|
28
|
+
end # Operator
|
29
|
+
end; end # SPARQL::Algebra
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module SPARQL; module Algebra
|
2
|
+
class Operator
|
3
|
+
##
|
4
|
+
# The SPARQL `langMatches` operator.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# (prefix ((: <http://example.org/#>))
|
8
|
+
# (filter (langMatches (lang ?v) "en-GB")
|
9
|
+
# (bgp (triple :x ?p ?v))))
|
10
|
+
#
|
11
|
+
# @see http://www.w3.org/TR/rdf-sparql-query/#func-langMatches
|
12
|
+
# @see http://tools.ietf.org/html/rfc4647#section-3.3.1
|
13
|
+
class LangMatches < Operator::Binary
|
14
|
+
include Evaluatable
|
15
|
+
|
16
|
+
NAME = :langMatches
|
17
|
+
|
18
|
+
##
|
19
|
+
# Returns `true` if the language tag (the first operand) matches the
|
20
|
+
# language range (the second operand).
|
21
|
+
#
|
22
|
+
# @param [RDF::Literal] language_tag
|
23
|
+
# a simple literal containing a language tag
|
24
|
+
# @param [RDF::Literal] language_range
|
25
|
+
# a simple literal containing a language range, per
|
26
|
+
# [RFC 4647 section 2.1](http://tools.ietf.org/html/rfc4647#section-2.1)
|
27
|
+
# @return [RDF::Literal::Boolean] `true` or `false`
|
28
|
+
# @raise [TypeError] if either operand is unbound
|
29
|
+
# @raise [TypeError] if either operand is not a simple literal
|
30
|
+
def apply(language_tag, language_range)
|
31
|
+
raise TypeError, "expected a plain RDF::Literal for language_tag, but got #{language_tag.inspect}" unless language_tag.is_a?(RDF::Literal) && language_tag.plain?
|
32
|
+
language_tag = language_tag.to_s.downcase
|
33
|
+
|
34
|
+
raise TypeError, "expected a plain RDF::Literal for language_range, but got #{language_range.inspect}" unless language_range.is_a?(RDF::Literal) && language_range.plain?
|
35
|
+
language_range = language_range.to_s.downcase
|
36
|
+
|
37
|
+
case
|
38
|
+
# A language range of "*" matches any non-empty language tag.
|
39
|
+
when language_range.eql?('*')
|
40
|
+
RDF::Literal(!(language_tag.empty?))
|
41
|
+
# A language range matches a particular language tag if, in a
|
42
|
+
# case-insensitive comparison, it exactly equals the tag, ...
|
43
|
+
when language_tag.eql?(language_range)
|
44
|
+
RDF::Literal::TRUE
|
45
|
+
# ... or if it exactly equals a prefix of the tag such that the
|
46
|
+
# first character following the prefix is "-".
|
47
|
+
else
|
48
|
+
RDF::Literal(language_tag.start_with?(language_range + '-'))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end # LangMatches
|
52
|
+
end # Operator
|
53
|
+
end; end # SPARQL::Algebra
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module SPARQL; module Algebra
|
2
|
+
class Operator
|
3
|
+
##
|
4
|
+
# The SPARQL GraphPattern `leftjoin` operator.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# (prefix ((: <http://example/>))
|
8
|
+
# (leftjoin
|
9
|
+
# (bgp (triple ?x :p ?v))
|
10
|
+
# (bgp (triple ?y :q ?w))
|
11
|
+
# (= ?v 2)))
|
12
|
+
#
|
13
|
+
# @see http://www.w3.org/TR/rdf-sparql-query/#sparqlAlgebra
|
14
|
+
class LeftJoin < Operator
|
15
|
+
include Query
|
16
|
+
|
17
|
+
NAME = [:leftjoin]
|
18
|
+
|
19
|
+
##
|
20
|
+
# Executes each operand with `queryable` and performs the `leftjoin` operation
|
21
|
+
# by adding every solution from the left, merging compatible solutions from the right
|
22
|
+
# that match an optional filter.
|
23
|
+
#
|
24
|
+
# @param [RDF::Queryable] queryable
|
25
|
+
# the graph or repository to query
|
26
|
+
# @param [Hash{Symbol => Object}] options
|
27
|
+
# any additional keyword options
|
28
|
+
# @return [RDF::Query::Solutions]
|
29
|
+
# the resulting solution sequence
|
30
|
+
# @see http://www.w3.org/TR/rdf-sparql-query/#sparqlAlgebra
|
31
|
+
# @see http://rdf.rubyforge.org/RDF/Query/Solution.html#merge-instance_method
|
32
|
+
# @see http://rdf.rubyforge.org/RDF/Query/Solution.html#compatible%3F-instance_method
|
33
|
+
def execute(queryable, options = {})
|
34
|
+
filter = operand(2)
|
35
|
+
|
36
|
+
|
37
|
+
debug(options) {"LeftJoin"}
|
38
|
+
left = operand(0).execute(queryable, options.merge(:depth => options[:depth].to_i + 1)) || {}
|
39
|
+
debug(options) {"=>(left) #{left.inspect}"}
|
40
|
+
right = operand(1).execute(queryable, options.merge(:depth => options[:depth].to_i + 1)) || {}
|
41
|
+
debug(options) {"=>(right) #{right.inspect}"}
|
42
|
+
|
43
|
+
# LeftJoin(Ω1, Ω2, expr) =
|
44
|
+
solutions = []
|
45
|
+
left.each do |s1|
|
46
|
+
load_left = true
|
47
|
+
right.each do |s2|
|
48
|
+
s = s2.merge(s1)
|
49
|
+
expr = filter ? boolean(filter.evaluate(s)).true? : true rescue false
|
50
|
+
debug(options) {"===>(evaluate) #{s.inspect}"} if filter
|
51
|
+
|
52
|
+
if expr && s1.compatible?(s2)
|
53
|
+
# { merge(μ1, μ2) | μ1 in Ω1 and μ2 in Ω2, and μ1 and μ2 are compatible and expr(merge(μ1, μ2)) is true }
|
54
|
+
debug(options) {"=>(merge s1 s2) #{s.inspect}"}
|
55
|
+
solutions << s
|
56
|
+
load_left = false # Left solution added one or more times due to merge
|
57
|
+
end
|
58
|
+
end
|
59
|
+
if load_left
|
60
|
+
debug(options) {"=>(add) #{s1.inspect}"}
|
61
|
+
solutions << s1
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
@solutions = RDF::Query::Solutions.new(solutions)
|
66
|
+
debug(options) {"=> #{@solutions.inspect}"}
|
67
|
+
@solutions
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# Returns an optimized version of this query.
|
72
|
+
#
|
73
|
+
# If optimize operands, and if the first two operands are both Queries, replace
|
74
|
+
# with the unique sum of the query elements
|
75
|
+
#
|
76
|
+
# @return [Union, RDF::Query] `self`
|
77
|
+
def optimize
|
78
|
+
ops = operands.map {|o| o.optimize }.select {|o| o.respond_to?(:empty?) && !o.empty?}
|
79
|
+
expr = ops.pop unless ops.last.executable?
|
80
|
+
expr = nil if expr.respond_to?(:true?) && expr.true?
|
81
|
+
|
82
|
+
# ops now is one or two executable operators
|
83
|
+
# expr is a filter expression, which may have been optimized to 'true'
|
84
|
+
case ops.length
|
85
|
+
when 0
|
86
|
+
RDF::Query.new # Empty query, expr doesn't matter
|
87
|
+
when 1
|
88
|
+
expr ? Filter.new(expr, ops.first) : ops.first
|
89
|
+
else
|
90
|
+
expr ? LeftJoin(ops[0], ops[1], expr) : LeftJoin(ops[0], ops[1])
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end # LeftJoin
|
94
|
+
end # Operator
|
95
|
+
end; end # SPARQL::Algebra
|