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