Class: SPARQL::Algebra::Operator::Join

Inherits:
Binary show all
Includes:
Query
Defined in:
vendor/bundler/ruby/3.3.0/bundler/gems/sparql-796d3be4aa08/lib/sparql/algebra/operator/join.rb

Overview

The SPARQL GraphPattern join operator.

[54] GroupGraphPatternSub ::= TriplesBlock? (GraphPatternNotTriples "."? TriplesBlock? )*

Examples:

SPARQL Grammar

PREFIX : <http://example/> 
SELECT * { 
   ?s ?p ?o
   GRAPH ?g { ?s ?q ?v }
}

SSE

(prefix ((: <http://example/>))
  (join
    (bgp (triple ?s ?p ?o))
    (graph ?g
      (bgp (triple ?s ?q ?v)))))

SPARQL Grammar (inline filter)

PREFIX : <http://xmlns.com/foaf/0.1/>
ASK {
  :who :homepage ?homepage 
  FILTER REGEX(?homepage, "^http://example.org/") 
  :who :schoolHomepage ?schoolPage
}

SSE (inline filter)

(prefix ((: <http://xmlns.com/foaf/0.1/>))
 (ask
  (filter (regex ?homepage "^http://example.org/")
   (join
    (bgp (triple :who :homepage ?homepage))
    (bgp (triple :who :schoolHomepage ?schoolPage))))))

See Also:

Constant Summary collapse

NAME =
[:join]

Constants inherited from Binary

Binary::ARITY

Constants inherited from SPARQL::Algebra::Operator

ARITY, IsURI, URI

Constants included from Expression

Expression::PATTERN_PARENTS

Constants included from RDF::Util::Logger

RDF::Util::Logger::IOWrapper

Instance Attribute Summary

Attributes included from Query

#solutions

Attributes inherited from SPARQL::Algebra::Operator

#operands

Instance Method Summary collapse

Methods included from Query

#each_solution, #empty?, #failed?, #graph_name=, #matched?, #query_yields_boolean?, #query_yields_solutions?, #query_yields_statements?, #unshift, #variables

Methods inherited from Binary

#initialize

Methods inherited from SPARQL::Algebra::Operator

#aggregate?, arity, base_uri, #base_uri, base_uri=, #bind, #boolean, #constant?, #deep_dup, #each_descendant, #eql?, #evaluatable?, evaluate, #executable?, #first_ancestor, for, #formulae, #initialize, #inspect, #mergable?, #ndvars, #node?, #operand, #optimize, #parent, #parent=, #prefixes, prefixes, prefixes=, #rewrite, #to_binary, to_sparql, #to_sxp, #to_sxp_bin, #variable?, #variables, #vars

Methods included from Expression

cast, #constant?, #evaluate, extension, extension?, extensions, for, #invalid?, new, #node?, open, #optimize, parse, register_extension, #to_sxp_bin, #valid?, #variable?

Methods included from RDF::Util::Logger

#log_debug, #log_depth, #log_error, #log_fatal, #log_info, #log_recover, #log_recovering?, #log_statistics, #log_warn, #logger

Constructor Details

This class inherits a constructor from SPARQL::Algebra::Operator::Binary

Instance Method Details

#execute(queryable, **options) {|solution| ... } ⇒ RDF::Query::Solutions

Executes each operand with queryable and performs the join operation by creating a new solution set containing the merge of all solutions from each set that are compatible with each other.

Parameters:

Yields:

  • (solution)

    each matching solution

Yield Parameters:

Yield Returns:

  • (void)

    ignored

Returns:

See Also:



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'vendor/bundler/ruby/3.3.0/bundler/gems/sparql-796d3be4aa08/lib/sparql/algebra/operator/join.rb', line 62

def execute(queryable, **options, &block)
  # Join(Ω1, Ω2) = { merge(μ1, μ2) | μ1 in Ω1 and μ2 in Ω2, and μ1 and μ2 are compatible }
  # eval(D(G), Join(P1, P2)) = Join(eval(D(G), P1), eval(D(G), P2))
  #
  # Generate solutions independently, merge based on solution compatibility
  debug(options) {"Join #{operands.to_sse}"}
 
  left = queryable.query(operand(0), **options.merge(depth: options[:depth].to_i + 1))
  debug(options) {"(join)=>(left) #{left.map(&:to_h).to_sse}"}

  right = queryable.query(operand(1), **options.merge(depth: options[:depth].to_i + 1))
  debug(options) {"(join)=>(right) #{right.map(&:to_h).to_sse}"}

  @solutions = RDF::Query::Solutions(left.map do |s1|
    right.map { |s2| s2.merge(s1) if s2.compatible?(s1) }
  end.flatten.compact)
  debug(options) {"(join)=> #{@solutions.map(&:to_h).to_sse}"}
  @solutions.each(&block) if block_given?
  @solutions
end

#optimize!(**options) ⇒ Join, ...

Optimizes this query.

Groups of one graph pattern (not a filter) become join(Z, A) and can be replaced by A. The empty graph pattern Z is the identity for join: Replace join(Z, A) by A Replace join(A, Z) by A

Returns:

See Also:



105
106
107
108
109
# File 'vendor/bundler/ruby/3.3.0/bundler/gems/sparql-796d3be4aa08/lib/sparql/algebra/operator/join.rb', line 105

def optimize!(**options)
  ops = operands.map {|o| o.optimize(**options) }.select {|o| o.respond_to?(:empty?) && !o.empty?}
  @operands = ops
  self
end

#to_sparql(top_level: true, filter_ops: [], extensions: {}, **options) ⇒ String

Returns a partial SPARQL grammar for this operator.

Parameters:

  • top_level (Boolean) (defaults to: true)

    (true) Treat this as a top-level, generating SELECT ... WHERE {}

  • extensions (Hash{String => Operator}) (defaults to: {})

    Variable bindings

  • filter_ops (Array<Operator>) (defaults to: [])

    ([]) Filter Operations

Returns:



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'vendor/bundler/ruby/3.3.0/bundler/gems/sparql-796d3be4aa08/lib/sparql/algebra/operator/join.rb', line 122

def to_sparql(top_level: true, filter_ops: [], extensions: {}, **options)
  # If this is top-level, and the last operand is a Table (values), put the values at the outer-level
  str = "{\n" + operands.first.to_sparql(top_level: false, extensions: {}, **options)

  # Any accrued filters go here.
  filter_ops.each do |op|
    str << "\nFILTER (#{op.to_sparql(**options)}) ."
  end

  if top_level && operands.last.is_a?(Table)
    str << "\n}"
    options = options.merge(values_clause: operands.last)
  else
    str << "\n{\n" + operands.last.to_sparql(top_level: false, extensions: {}, **options) + "\n}\n}"
  end

  top_level ? Operator.to_sparql(str, extensions: extensions, **options) : str
end

#validate!Object

The same blank node label cannot be used in two different basic graph patterns in the same query



84
85
86
87
88
89
90
91
92
# File 'vendor/bundler/ruby/3.3.0/bundler/gems/sparql-796d3be4aa08/lib/sparql/algebra/operator/join.rb', line 84

def validate!
  left_nodes, right_nodes = operand(0).ndvars, operand(1).ndvars

  unless (left_nodes.compact & right_nodes.compact).empty?
    raise ArgumentError,
         "sub-operands share non-distinguished variables: #{(left_nodes.compact & right_nodes.compact).to_sse}"
  end
  super
end