Class: RDF::N3::Algebra::Formula

Inherits:
SPARQL::Algebra::Operator show all
Includes:
Enumerable, Util::Logger, SPARQL::Algebra::Query, SPARQL::Algebra::Update
Defined in:
vendor/bundler/ruby/2.6.0/bundler/gems/rdf-n3-015ce184efe3/lib/rdf/n3/algebra/formula.rb

Overview

A Notation3 Formula combines a graph with a BGP query.

Constant Summary collapse

NAME =
[:formula]

Constants included from Util::Logger

Util::Logger::IOWrapper

Constants inherited from SPARQL::Algebra::Operator

SPARQL::Algebra::Operator::ARITY, SPARQL::Algebra::Operator::IsURI, SPARQL::Algebra::Operator::URI

Instance Attribute Summary collapse

Attributes included from Enumerable

#existentials, #universals

Attributes included from SPARQL::Algebra::Query

#solutions

Attributes inherited from SPARQL::Algebra::Operator

#operands

Instance Method Summary collapse

Methods included from Util::Logger

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

Methods included from Enumerable

add_entailment, #contain?, #dump, #each_graph, #each_object, #each_predicate, #each_quad, #each_statement, #each_subject, #each_term, #each_triple, #entail, #enum_graph, #enum_object, #enum_predicate, #enum_quad, #enum_statement, #enum_subject, #enum_term, #enum_triple, #graph_names, #has_graph?, #has_object?, #has_predicate?, #has_quad?, #has_statement?, #has_subject?, #has_term?, #has_triple?, #invalid?, #method_missing, #objects, #predicates, #project_graph, #quads, #respond_to_missing?, #subjects, #supports?, #terms, #to_a, #to_h, #to_set, #triples, #valid?, #validate!

Methods included from Util::Aliasing::LateBound

#alias_method

Methods included from Countable

#count, #empty?

Methods included from Isomorphic

#bijection_to, #isomorphic_with?

Methods included from SPARQL::Algebra::Update

#graph_name=, #unshift, #variables

Methods included from SPARQL::Algebra::Query

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

Methods inherited from SPARQL::Algebra::Operator

#aggregate?, arity, #base_uri, base_uri, base_uri=, #boolean, #constant?, #each_descendant, #eql?, #evaluatable?, evaluate, #executable?, #first_ancestor, for, #initialize, #inspect, #ndvars, #node?, #operand, #optimize, #parent, #parent=, #prefixes, prefixes, prefixes=, #rewrite, #to_binary, #to_sxp, #validate!, #variable?, #vars

Methods included from SPARQL::Algebra::Expression

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

Constructor Details

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

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class RDF::Enumerable

Instance Attribute Details

#queryObject

Returns the value of attribute query



12
13
14
# File 'vendor/bundler/ruby/2.6.0/bundler/gems/rdf-n3-015ce184efe3/lib/rdf/n3/algebra/formula.rb', line 12

def query
  @query
end

Instance Method Details

#constantsObject

Constants memoizer



155
156
157
158
# File 'vendor/bundler/ruby/2.6.0/bundler/gems/rdf-n3-015ce184efe3/lib/rdf/n3/algebra/formula.rb', line 155

def constants
  # BNodes in statements are existential variables
  @constants ||= statements.select(&:constant?)
end

#each {|statement| ... } ⇒ Object

Yields each statement from this formula bound to previously determined solutions.

Yields:

  • (statement)

    each matching statement

Yield Parameters:

Yield Returns:

  • (void)

    ignored



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'vendor/bundler/ruby/2.6.0/bundler/gems/rdf-n3-015ce184efe3/lib/rdf/n3/algebra/formula.rb', line 66

def each(&block)
  @solutions ||= begin
    # If there are no solutions, create a single solution
    RDF::Query::Solutions(RDF::Query::Solution.new)
  end
  log_debug {"formula #{graph_name} each #{@solutions.to_sxp}"}

  # Yield constant statements/patterns
  constants.each do |pattern|
    log_debug {"(formula constant) #{pattern.to_sxp}"}
    block.call(RDF::Statement.from(pattern, graph_name: graph_name))
  end

  # Yield patterns by binding variables
  # FIXME: do we need to do something with non-bound non-distinguished extistential variables?
  @solutions.each do |solution|
    # Bind blank nodes to the solution when it doesn't contain a solution for an existential variable
    existential_vars.each do |var|
      solution[var.name] ||= RDF::Node.intern(var.name.to_s.sub(/^\$+/, ''))
    end

    log_debug {"(formula apply) #{solution.to_sxp} to BGP"}
    # Yield each variable statement which is constant after applying solution
    patterns.each do |pattern|
      terms = {}
      [:subject, :predicate, :object].each do |r|
        terms[r] = case o = pattern.send(r)
        when RDF::Query::Variable then solution[o]
        else                           o
        end
      end

      statement = RDF::Statement.from(terms)

      # Sanity checking on statement
      if statement.variable? ||
         statement.predicate.literal? ||
         statement.subject.is_a?(SPARQL::Algebra::Operator) ||
         statement.object.is_a?(SPARQL::Algebra::Operator)
        log_debug {"(formula skip) #{statement.to_sxp}"}
        next
      end

      log_debug {"(formula add) #{statement.to_sxp}"}
      block.call(statement)
    end
  end

  # statements from sub-operands
  log_depth {sub_ops.each {|op| op.each(&block)}}
end

#execute(queryable, solutions: RDF::Query::Solutions(RDF::Query::Solution.new), **options) ⇒ RDF::Solutions

Yields solutions from patterns and other operands. Solutions are created by evaluating each pattern and other sub-operand against queryable.

When executing, blank nodes are turned into non-distinguished existential variables, noted with $$. These variables are removed from the returned solutions, as they can't be bound outside of the formula.

Parameters:

Options Hash (**options):

Returns:

  • (RDF::Solutions)

    distinct solutions



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'vendor/bundler/ruby/2.6.0/bundler/gems/rdf-n3-015ce184efe3/lib/rdf/n3/algebra/formula.rb', line 28

def execute(queryable, solutions: RDF::Query::Solutions(RDF::Query::Solution.new), **options)
  log_debug {"formula #{graph_name} #{operands.to_sxp}"}

  # If we were passed solutions in options, extract bindings to use for query
  bindings = solutions.bindings
  log_debug {"(formula bindings) #{bindings.map {|k,v| RDF::Query::Variable.new(k,v)}.to_sxp}"}

  # Only query as patterns if this is an embedded formula
  @query ||= RDF::Query.new(patterns).optimize!
  @solutions = @query.patterns.empty? ? solutions : queryable.query(@query, solutions: solutions, bindings: bindings, **options)

  # Merge solution sets
  # Reject solutions which include variables as values
  @solutions = @solutions
    .merge(options[:solutions])
    .filter {|s| s.enum_value.none?(&:variable?)}

  # Use our solutions for sub-ops
  # Join solutions from other operands
  log_depth do
    sub_ops.each do |op|
      @solutions = op.execute(queryable, solutions: @solutions)
    end
  end
  log_debug {"(formula solutions) #{@solutions.to_sxp}"}

  # Only return solutions with distinguished variables
  variable_names = @solutions.variable_names.reject {|v| v.to_s.start_with?('$$')}
  variable_names.empty? ? @solutions : @solutions.dup.project(*variable_names)
end

#existential_varsObject

Existential vars in this formula



176
177
178
# File 'vendor/bundler/ruby/2.6.0/bundler/gems/rdf-n3-015ce184efe3/lib/rdf/n3/algebra/formula.rb', line 176

def existential_vars
  @existentials ||= patterns.vars.select(&:existential?)
end

#graph_nameRDF::Resource

Graph name associated with this formula

Returns:



126
# File 'vendor/bundler/ruby/2.6.0/bundler/gems/rdf-n3-015ce184efe3/lib/rdf/n3/algebra/formula.rb', line 126

def graph_name; @options[:graph_name]; end

#patternsObject

Patterns memoizer



162
163
164
165
# File 'vendor/bundler/ruby/2.6.0/bundler/gems/rdf-n3-015ce184efe3/lib/rdf/n3/algebra/formula.rb', line 162

def patterns
  # BNodes in statements are existential variables
  @patterns ||= statements.reject(&:constant?)
end

#solutions=(solutions) ⇒ Object

Set solutions

Parameters:



120
121
122
# File 'vendor/bundler/ruby/2.6.0/bundler/gems/rdf-n3-015ce184efe3/lib/rdf/n3/algebra/formula.rb', line 120

def solutions=(solutions)
  @solutions = solutions
end

#statementsObject

Statements memoizer



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'vendor/bundler/ruby/2.6.0/bundler/gems/rdf-n3-015ce184efe3/lib/rdf/n3/algebra/formula.rb', line 130

def statements
  # BNodes in statements are non-distinguished existential variables
  @statements ||= operands.
    select {|op| op.is_a?(RDF::Statement)}.
    map do |pattern|

    # Map nodes to non-distinguished existential variables (except when in top-level formula)
    if graph_name
      terms = {}
      [:subject, :predicate, :object].each do |r|
        terms[r] = case o = pattern.send(r)
        when RDF::Node then RDF::Query::Variable.new(o.id, existential: true, distinguished: false)
        else                o
        end
      end

      RDF::Query::Pattern.from(terms)
    else
      RDF::Query::Pattern.from(pattern)
    end
  end
end

#sub_opsObject

Non-statement operands memoizer



169
170
171
172
# File 'vendor/bundler/ruby/2.6.0/bundler/gems/rdf-n3-015ce184efe3/lib/rdf/n3/algebra/formula.rb', line 169

def sub_ops
  # operands that aren't statements, ordered by their graph_name
  @sub_ops ||= operands.reject {|op| op.is_a?(RDF::Statement)}
end

#to_sxp_binObject



180
181
182
183
# File 'vendor/bundler/ruby/2.6.0/bundler/gems/rdf-n3-015ce184efe3/lib/rdf/n3/algebra/formula.rb', line 180

def to_sxp_bin
  [:formula, graph_name].compact +
  operands.map(&:to_sxp_bin)
end