class RDF::Query::Pattern
An RDF
query pattern.
Attributes
The estimated cost of this pattern (for query optimization).
@return [Numeric]
Any additional options for this pattern.
@return [Hash]
Public Class Methods
Source
# File lib/rdf/query/pattern.rb, line 8 def self.from(pattern, graph_name: nil, **options) case pattern when Pattern then pattern when Array, Statement graph_name ||= pattern[3] self.new(pattern[0], pattern[1], pattern[2], graph_name: graph_name, **options) when Hash then self.new(options.merge(pattern)) else raise ArgumentError, "expected RDF::Query::Pattern, RDF::Statement, Hash, or Array, but got #{pattern.inspect}" end end
@private @since 0.2.2
Source
# File lib/rdf/query/pattern.rb, line 39 def initialize(subject = nil, predicate = nil, object = nil, options = {}) super end
@overload initialize(options = {})
@param [Hash{Symbol => Object}] options @option options [Variable, Resource, Symbol, nil] :subject (nil) @option options [Variable, URI, Symbol, nil] :predicate (nil) @option options [Variable, Term, Symbol, nil] :object (nil) @option options [Variable, Resource, Symbol, nil, false] :graph_name (nil) A graph_name of nil matches any graph, a graph_name of false, matches only the default graph. (See {RDF::Query#initialize}) @option options [Boolean] :optional (false)
@overload initialize(subject, predicate, object, options = {})
@param [Variable, Resource, Symbol, nil] subject @param [Variable, URI, Symbol, nil] predicate @param [Variable, Termm, Symbol, nil] object @param [Hash{Symbol => Object}] options @option options [Variable, Resource, Symbol, nil, false] :graph_name (nil) A graph_name of nil matches any graph, a graph_name of false, matches only the default graph. (See {RDF::Query#initialize}) @option options [Boolean] :optional (false)
@note {Statement} treats symbols as interned {Node} instances, in a {Pattern}, they are treated as {Variable}.
RDF::Statement::new
Public Instance Methods
Source
# File lib/rdf/query/pattern.rb, line 298 def bind(solution) self.to_quad.each_with_index do |term, index| if term.is_a?(Variable) && solution[term] self[index] = solution[term] elsif term.is_a?(Pattern) term.bind(solution) end end self end
Binds the pattern to a solution, making it no longer variable if all variables are resolved to bound variables
@param [RDF::Query::Solution] solution @return [self]
Source
# File lib/rdf/query/pattern.rb, line 321 def binding_count bindings.size end
Returns the number of bindings in this pattern.
@return [Integer] (0..3)
Source
# File lib/rdf/query/pattern.rb, line 329 def bindings bindings = {} bindings.merge!(subject.bindings) if subject && subject.variable? bindings.merge!(predicate.bindings) if predicate && predicate.variable? bindings.merge!(object.bindings) if object && object.variable? bindings.merge!(graph_name.bindings) if graph_name && graph_name.variable? bindings end
Returns all bindings in this pattern.
@return [Hash{Symbol => RDF::Term
}]
Source
# File lib/rdf/query/pattern.rb, line 313 def bindings? !bindings.empty? end
Returns ‘true` if this pattern contains bindings.
@return [Boolean] ‘true` or `false`
Source
# File lib/rdf/query/pattern.rb, line 85 def blank? subject.nil? && predicate.nil? && object.nil? && graph_name.nil? end
Returns ‘true` if this is a blank pattern, with all terms being `nil`.
@return [Boolean] ‘true` or `false` @since 0.3.0
Source
# File lib/rdf/query/pattern.rb, line 342 def bound? !variables.empty? && variables.values.all?(&:bound?) end
Returns ‘true` if all variables in this pattern are bound.
@return [Boolean] ‘true` or `false`
Source
# File lib/rdf/query/pattern.rb, line 350 def bound_variables variables.reject { |name, variable| variable.unbound? } end
Returns all bound variables in this pattern.
@return [Hash{Symbol => Variable}]
Source
# File lib/rdf/query/pattern.rb, line 64 def dup self.class.from(self.to_quad.map {|t| t.is_a?(RDF::Query::Pattern) ? t.dup : t}) end
Create a new pattern from the quads, recursivly dupping sub-patterns.
Source
# File lib/rdf/query/pattern.rb, line 141 def eql?(other) return false unless other.is_a?(Statement) && (self.graph_name || false) == (other.graph_name || false) predicate == other.predicate && (subject.is_a?(Pattern) ? subject.eql?(other.subject) : subject == other.subject) && (object.is_a?(Pattern) ? object.eql?(other.object) : object == other.object) end
Checks pattern equality against a statement, considering nesting.
-
A pattern which has a pattern as a subject or an object, matches a statement having a statement as a subject or an object using {#eql?}.
@param [Statement] other @return [Boolean]
@see RDF::URI#==
@see RDF::Node#==
@see RDF::Literal#==
@see RDF::Query::Variable#==
Source
# File lib/rdf/query/pattern.rb, line 173 def execute(queryable, bindings = {}, &block) bindings = bindings.to_h if bindings.is_a?(Solution) query = { subject: subject.is_a?(Variable) && bindings[subject.to_sym] ? bindings[subject.to_sym] : subject, predicate: predicate.is_a?(Variable) && bindings[predicate.to_sym] ? bindings[predicate.to_sym] : predicate, object: object.is_a?(Variable) && bindings[object.to_sym] ? bindings[object.to_sym] : object, graph_name: graph_name.is_a?(Variable) && bindings[graph_name.to_sym] ? bindings[graph_name.to_sym] : graph_name, }.delete_if{|k,v| v.nil?} # Do all the variable terms refer to distinct variables? variables = self.variables if variable_count == variables.size # If so, we can just let the repository implementation handle # everything and yield matching statements directly: queryable.query(query, &block) # No, some terms actually refer to the same variable... else # Considering embedding, figure out if variables that may appear more than once resolve to the same value. vars = variables.keys queryable.query(query).select do |statement| if vars.all? {|var| self.var_values(var, statement).uniq.size == 1} yield statement if block_given? true end end end end
Executes this query pattern on the given ‘queryable` object.
Values are matched using using Queryable#query_pattern.
If the optional ‘bindings` are given, variables will be substituted with their values when executing the query.
To match triples only in the default graph, set graph_name to ‘false`.
@example
Pattern.new(:s, :p, :o).execute(RDF::Repository.load('etc/doap.nt'))
@param [RDF::Queryable] queryable
the graph or repository to query
@param [Hash{Symbol => RDF::Term
}, RDF::Query::Solution
] bindings
optional variable bindings to use
@yield [statement]
each matching statement
@yieldparam [RDF::Statement] statement
an RDF statement matching this pattern
@return [Enumerable<RDF::Query::Pattern>]
an enumerator yielding matching statements
@see RDF::Queryable#query
@since 0.3.0
Source
# File lib/rdf/query/pattern.rb, line 45 def initialize! @graph_name = Variable.new(@graph_name) if @graph_name.is_a?(Symbol) @subject = Variable.new(@subject) if @subject.is_a?(Symbol) @predicate = Variable.new(@predicate) if @predicate.is_a?(Symbol) @object = Variable.new(@object) if @object.is_a?(Symbol) # Estmate cost positionally, with variables being least expensive as objects, then predicates, then subjects, then graph_names. # XXX does not consider bound variables, which would need to be dynamically calculated. @cost = (@object.nil? || @object.is_a?(Variable) ? 8 : 0) + (@predicate.nil? || @predicate.is_a?(Variable) ? 4 : 0) + (@subject.nil? || @subject.is_a?(Variable) ? 2 : 0) + (@graph_name.is_a?(Variable) ? 1 : 0) + (@object.is_a?(Pattern) ? (@object.cost * 4) : 0) + (@subject.is_a?(Pattern) ? (@subject.cost * 2) : 0) super end
@private
RDF::Statement#initialize!
Source
# File lib/rdf/query/pattern.rb, line 111 def optional? !!options[:optional] end
Returns ‘true` if this is an optional pattern.
@example
Pattern.new(:s, :p, :o).optional? #=> false Pattern.new(:s, :p, :o, optional: true).optional? #=> true
@return [Boolean] ‘true` or `false` @since 0.3.0
Source
# File lib/rdf/query/pattern.rb, line 218 def solution(statement) RDF::Query::Solution.new do |solution| solution[subject.to_sym] = statement.subject if subject.is_a?(Variable) solution[predicate.to_sym] = statement.predicate if predicate.is_a?(Variable) solution[object.to_sym] = statement.object if object.is_a?(Variable) solution[graph_name.to_sym] = statement.graph_name if graph_name.is_a?(Variable) solution.merge!(subject.solution(statement.subject)) if subject.respond_to?(:solution) solution.merge!(object.solution(statement.object)) if object.respond_to?(:solution) end end
Returns a query solution constructed by binding any variables in this pattern with the corresponding terms in the given ‘statement`.
@example
pattern = Pattern.new(:s, :p, :o) solution = pattern.solution(statement) pattern[:s] #=> statement.subject pattern[:p] #=> statement.predicate pattern[:o] #=> statement.object
@param [RDF::Statement] statement
an RDF statement to bind terms from
@return [RDF::Query::Solution] @since 0.3.0
Source
# File lib/rdf/query/pattern.rb, line 374 def to_s (optional? ? 'OPTIONAL ' : '') + super end
Returns a string representation of this pattern.
@return [String]
RDF::Statement#to_s
Source
# File lib/rdf/query/pattern.rb, line 358 def unbound? !variables.empty? && variables.values.all?(&:unbound?) end
Returns ‘true` if all variables in this pattern are unbound.
@return [Boolean] ‘true` or `false`
Source
# File lib/rdf/query/pattern.rb, line 366 def unbound_variables variables.reject { |name, variable| variable.bound? } end
Returns all unbound variables in this pattern.
@return [Hash{Symbol => Variable}]
Source
# File lib/rdf/query/pattern.rb, line 119 def valid? (subject? ? (subject.resource? || subject.variable?) && subject.valid? : true) && (predicate? ? (predicate.uri? || predicate.variable?) && predicate.valid? : true) && (object? ? (object.term? || object.variable?) && object.valid? : true) && (graph? ? (graph_name.resource? || graph_name.variable?) && graph_name.valid? : true) rescue NoMethodError false end
Is this pattern composed only of valid components?
@return [Boolean] ‘true` or `false`
Source
# File lib/rdf/query/pattern.rb, line 257 def var_values(var, statement) %i(subject predicate object graph_name).map do |position| po = self.send(position) so = statement.send(position) po.var_values(var, so) if po.respond_to?(:var_values) end.flatten.compact end
Returns all values the statement in the same pattern position
@param [Symbol] var @param [RDF::Statement] statement @return [Array<RDF::Term>]
Source
# File lib/rdf/query/pattern.rb, line 272 def variable_count [subject, predicate, object, graph_name].inject(0) do |memo, term| memo += (term.is_a?(Variable) ? 1 : (term.respond_to?(:variable_count) ? term.variable_count : 0)) end end
Returns the number of variables in this pattern.
Note: this does not count distinct variables, and will therefore e.g. return 3 even if two terms are actually the same variable.
@return [Integer] (0..3)
Source
# File lib/rdf/query/pattern.rb, line 240 def variable_terms(name = nil) warn "[DEPRECATION] RDF::Query::Pattern#variable_terms is deprecated and will be removed in a future version.\n" + "Called from #{Gem.location_of_caller.join(':')}" terms = [] terms << :subject if subject.is_a?(Variable) && (!name || name.eql?(subject.name)) terms << :predicate if predicate.is_a?(Variable) && (!name || name.eql?(predicate.name)) terms << :object if object.is_a?(Variable) && (!name || name.eql?(object.name)) terms << :graph_name if graph_name.is_a?(Variable) && (!name || name.eql?(graph_name.name)) terms end
Returns the variable terms in this pattern.
@example
Pattern.new(RDF::Node.new, :p, 123).variable_terms #=> [:predicate]
@param [Symbol, to_sym] name
an optional variable name
@return [Array<Symbol>] @deprecated use {#var_values} instead @since 0.3.0
Source
# File lib/rdf/query/pattern.rb, line 287 def variables [subject, predicate, object, graph_name].inject({}) do |memo, term| term && term.variable? ? memo.merge(term.variables) : memo end end
Returns all variables in this pattern.
Note: this returns a hash containing distinct variables only.
@return [Hash{Symbol => Variable}]
Source
# File lib/rdf/query/pattern.rb, line 94 def variables? subject && subject.variable? || predicate && predicate.variable? || object && object.variable? || graph_name && graph_name.variable? end
Returns ‘true` if this pattern contains any variables.
@return [Boolean] ‘true` or `false` @since 0.3.0