class SPARQL::Algebra::Operator::PathOpt

The SPARQL Property Path `path?` (ZeroOrOnePath) operator.

@example

(path? :p)

@see www.w3.org/TR/sparql11-query/#defn_evalPP_ZeroOrOnePath

Constants

NAME

Public Instance Methods

execute(queryable, **options, &block) click to toggle source

Equivalent to:

(path x (path? :p) y)
 => (union (bgp ((x :p y))) (filter (x = x) (solution x y)))

@param [RDF::Queryable] queryable

the graph or repository to query

@param [Hash{Symbol => Object}] options

any additional keyword options

@option options [RDF::Term, RDF::Variable] :subject @option options [RDF::Term, RDF::Variable] :object @yield [solution]

each matching solution

@yieldparam [RDF::Query::Solution] solution @yieldreturn [void] ignored @see www.w3.org/TR/sparql11-query/#sparqlAlgebra

# File lib/sparql/algebra/operator/path_opt.rb, line 33
def execute(queryable, **options, &block)
  subject, object = options[:subject], options[:object]
  debug(options) {"Path? #{[subject, operands, object].to_sse}"}

  solutions = RDF::Query::Solutions.new
  # Solutions where subject == object with no predicate
  case
  when subject.variable? && object.variable?
    # Nodes is the set of all subjects and objects in queryable
    # FIXME: should this be Queryable#enum_nodes?
    # All subjects which are `object`
    query = RDF::Query.new {|q| q.pattern({subject: subject})}
    queryable.query(query, **options) do |solution|
      solution.merge!(object.to_sym => solution[subject])
      debug(options) {"(solution-s0)-> #{solution.to_h.to_sse}"}
      solutions << solution
    end if query.valid?

    # All objects which are `object`
    query = RDF::Query.new {|q| q.pattern({object: object})}
    queryable.query(query, **options) do |solution|
      solution.merge!(subject.to_sym => solution[object])
      debug(options) {"(solution-o0)-> #{solution.to_h.to_sse}"}
      solutions << solution
    end if query.valid?
  when subject.variable?
    # All subjects which are `object`
    query = RDF::Query.new {|q| q.pattern({subject: object})}
    queryable.query(query, **options) do |solution|
      solution.merge!(subject.to_sym => object)
      debug(options) {"(solution-s0)-> #{solution.to_h.to_sse}"}
      solutions << solution
    end if query.valid?

    # All objects which are `object`
    query = RDF::Query.new {|q| q.pattern({object: object})}
    queryable.query(query, **options) do |solution|
      solution.merge!(subject.to_sym => object)
      debug(options) {"(solution-o0)-> #{solution.to_h.to_sse}"}
      solutions << solution
    end if query.valid?
  when object.variable?
    # All subjects which are `subject`
    query = RDF::Query.new {|q| q.pattern({subject: subject})}
    queryable.query(query, **options) do |solution|
      solution.merge!(object.to_sym => subject)
      debug(options) {"(solution-s0)-> #{solution.to_h.to_sse}"}
      solutions << solution
    end if query.valid?

    # All objects which are `subject
    query = RDF::Query.new {|q| q.pattern({object: subject})}
    queryable.query(query, **options) do |solution|
      solution.merge!(object.to_sym => subject)
      debug(options) {"(solution-o0)-> #{solution.to_h.to_sse}"}
      solutions << solution
    end if query.valid?
  else
    # Otherwise, if subject == object, an empty solution
    solutions << RDF::Query::Solution.new if subject == object
  end

  # Solutions where predicate exists
  query = if operand.is_a?(RDF::Term)
    RDF::Query.new do |q|
      q.pattern [subject, operand, object]
    end
  else
    operand
  end

  # Recurse into query
  solutions += 
  queryable.query(query, depth: options[:depth].to_i + 1, **options)
  solutions.each(&block) if block_given?
  solutions
end