Class: RDF::Query::Pattern
- Defined in:
- lib/rdf/query/pattern.rb
Overview
An RDF query pattern.
Instance Attribute Summary collapse
-
#cost ⇒ Numeric
The estimated cost of this pattern (for query optimization).
-
#options ⇒ Hash
readonly
Any additional options for this pattern.
Attributes inherited from Statement
#graph_name, #id, #object, #predicate, #subject
Class Method Summary collapse
Instance Method Summary collapse
-
#bind(solution) ⇒ self
Binds the pattern to a solution, making it no longer variable if all variables are resolved to bound variables.
-
#binding_count ⇒ Integer
Returns the number of bindings in this pattern.
-
#bindings ⇒ Hash{Symbol => RDF::Term}
Returns all bindings in this pattern.
-
#bindings? ⇒ Boolean
Returns
trueif this pattern contains bindings. -
#blank? ⇒ Boolean
Returns
trueif this is a blank pattern, with all terms beingnil. -
#bound? ⇒ Boolean
Returns
trueif all variables in this pattern are bound. -
#bound_variables ⇒ Hash{Symbol => Variable}
Returns all bound variables in this pattern.
-
#eql?(other) ⇒ Boolean
Checks pattern equality against a statement, considering nesting.
-
#execute(queryable, bindings = {}) {|statement| ... } ⇒ Enumerable<RDF::Query::Pattern>
Executes this query pattern on the given
queryableobject. -
#has_variables? ⇒ Boolean
(also: #variables?)
Returns
trueif this pattern contains any variables. -
#initialize(subject = nil, predicate = nil, object = nil, options = {}) ⇒ Pattern
constructor
A new instance of Pattern.
- #initialize! ⇒ Object
-
#optional? ⇒ Boolean
Returns
trueif this is an optional pattern. -
#solution(statement) ⇒ RDF::Query::Solution
Returns a query solution constructed by binding any variables in this pattern with the corresponding terms in the given
statement. -
#to_s ⇒ String
Returns a string representation of this pattern.
-
#unbound? ⇒ Boolean
Returns
trueif all variables in this pattern are unbound. -
#unbound_variables ⇒ Hash{Symbol => Variable}
Returns all unbound variables in this pattern.
-
#valid? ⇒ Boolean
Is this pattern composed only of valid components?.
-
#variable_count ⇒ Integer
(also: #cardinality, #arity)
Returns the number of variables in this pattern.
-
#variable_terms(name = nil) ⇒ Array<Symbol>
Returns the variable terms in this pattern.
-
#variables ⇒ Hash{Symbol => Variable}
Returns all variables in this pattern.
Methods inherited from Statement
#==, #===, #[], #[]=, #asserted?, #canonicalize, #canonicalize!, #complete?, #embedded?, #has_graph?, #has_object?, #has_predicate?, #has_subject?, #hash, #incomplete?, #inferred?, #invalid?, #node?, #quoted?, #reified, #statement?, #to_h, #to_quad, #to_triple, #variable?
Methods included from Resource
Methods included from Term
#<=>, #==, #compatible?, #term?, #to_base, #to_term
Methods included from Value
#anonymous?, #canonicalize, #canonicalize!, #constant?, #graph?, #inspect, #inspect!, #invalid?, #iri?, #list?, #literal?, #node?, #resource?, #start_with?, #statement?, #term?, #to_nquads, #to_ntriples, #to_rdf, #to_term, #type_error, #uri?, #validate!, #variable?
Constructor Details
#initialize(options = {}) ⇒ Pattern #initialize(subject, predicate, object, options = {}) ⇒ Pattern
Statement treats symbols as interned Node instances, in a RDF::Query::Pattern, they are treated as Variable.
Returns a new instance of Pattern.
39 40 41 |
# File 'lib/rdf/query/pattern.rb', line 39 def initialize(subject = nil, predicate = nil, object = nil, = {}) super end |
Instance Attribute Details
#cost ⇒ Numeric
The estimated cost of this pattern (for query optimization).
72 73 74 |
# File 'lib/rdf/query/pattern.rb', line 72 def cost @cost end |
#options ⇒ Hash (readonly)
Any additional options for this pattern.
66 67 68 |
# File 'lib/rdf/query/pattern.rb', line 66 def end |
Class Method Details
.from(pattern, graph_name: nil, **options) ⇒ Object
8 9 10 11 12 13 14 15 16 17 |
# File 'lib/rdf/query/pattern.rb', line 8 def self.from(pattern, graph_name: nil, **) 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, **) when Hash then self.new(.merge(pattern)) else raise ArgumentError, "expected RDF::Query::Pattern, RDF::Statement, Hash, or Array, but got #{pattern.inspect}" end end |
Instance Method Details
#bind(solution) ⇒ self
Binds the pattern to a solution, making it no longer variable if all variables are resolved to bound variables
280 281 282 283 284 285 286 287 288 289 |
# File 'lib/rdf/query/pattern.rb', line 280 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 |
#binding_count ⇒ Integer
Returns the number of bindings in this pattern.
303 304 305 |
# File 'lib/rdf/query/pattern.rb', line 303 def binding_count bindings.size end |
#bindings ⇒ Hash{Symbol => RDF::Term}
Returns all bindings in this pattern.
311 312 313 314 315 316 317 318 |
# File 'lib/rdf/query/pattern.rb', line 311 def bindings bindings = {} bindings.merge!(subject.bindings) if subject && subject.variable? bindings.merge!(predicate.bindings) if predicate.is_a?(Variable) bindings.merge!(object.bindings) if object && object.variable? bindings.merge!(graph_name.bindings) if graph_name.is_a?(Variable) bindings end |
#bindings? ⇒ Boolean
Returns true if this pattern contains bindings.
295 296 297 |
# File 'lib/rdf/query/pattern.rb', line 295 def bindings? !bindings.empty? end |
#blank? ⇒ Boolean
Returns true if this is a blank pattern, with all terms being nil.
79 80 81 |
# File 'lib/rdf/query/pattern.rb', line 79 def blank? subject.nil? && predicate.nil? && object.nil? && graph_name.nil? end |
#bound? ⇒ Boolean
Returns true if all variables in this pattern are bound.
324 325 326 |
# File 'lib/rdf/query/pattern.rb', line 324 def bound? !variables.empty? && variables.values.all?(&:bound?) end |
#bound_variables ⇒ Hash{Symbol => Variable}
Returns all bound variables in this pattern.
332 333 334 |
# File 'lib/rdf/query/pattern.rb', line 332 def bound_variables variables.reject { |name, variable| variable.unbound? } end |
#eql?(other) ⇒ Boolean
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?.
135 136 137 138 139 140 141 |
# File 'lib/rdf/query/pattern.rb', line 135 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 |
#execute(queryable, bindings = {}) {|statement| ... } ⇒ Enumerable<RDF::Query::Pattern>
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.
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/rdf/query/pattern.rb', line 167 def execute(queryable, bindings = {}, &block) 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 # Figure out which terms refer to the same variable: terms = variables.each_key.find do |name| terms = variable_terms(name) break terms if terms.size > 1 end queryable.query(query).select do |statement| # Only yield those matching statements where the variable # constraint is also satisfied: # FIXME: `Array#uniq` uses `#eql?` and `#hash`, not `#==` if terms.map { |term| statement.send(term) }.uniq.size.equal?(1) yield statement if block_given? true end end end end |
#has_variables? ⇒ Boolean Also known as: variables?
Returns true if this pattern contains any variables.
88 89 90 91 92 93 |
# File 'lib/rdf/query/pattern.rb', line 88 def has_variables? subject && subject.variable? || predicate && predicate.variable? || object && object.variable? || graph_name && graph_name.variable? end |
#initialize! ⇒ Object
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
# 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 |
#optional? ⇒ Boolean
Returns true if this is an optional pattern.
105 106 107 |
# File 'lib/rdf/query/pattern.rb', line 105 def optional? !![:optional] end |
#solution(statement) ⇒ RDF::Query::Solution
Returns a query solution constructed by binding any variables in this pattern with the corresponding terms in the given statement.
217 218 219 220 221 222 223 224 225 226 |
# File 'lib/rdf/query/pattern.rb', line 217 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.is_a?(Pattern) solution.merge!(object.solution(statement.object)) if object.is_a?(Pattern) end end |
#to_s ⇒ String
Returns a string representation of this pattern.
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 |
# File 'lib/rdf/query/pattern.rb', line 356 def to_s StringIO.open do |buffer| # FIXME in RDF::Statement buffer << 'OPTIONAL ' if optional? buffer << [subject, predicate, object].map do |r| if r.is_a?(RDF::Query::Variable) r.to_s elsif r.is_a?(RDF::Query::Pattern) "<<#{r.to_s[0..-3]}>>" else RDF::NTriples.serialize(r) end end.join(" ") buffer << case graph_name when nil, false then " ." when Variable then " #{graph_name.to_s} ." else " #{RDF::NTriples.serialize(graph_name)} ." end buffer.string end end |
#unbound? ⇒ Boolean
Returns true if all variables in this pattern are unbound.
340 341 342 |
# File 'lib/rdf/query/pattern.rb', line 340 def unbound? !variables.empty? && variables.values.all?(&:unbound?) end |
#unbound_variables ⇒ Hash{Symbol => Variable}
Returns all unbound variables in this pattern.
348 349 350 |
# File 'lib/rdf/query/pattern.rb', line 348 def unbound_variables variables.reject { |name, variable| variable.bound? } end |
#valid? ⇒ Boolean
Is this pattern composed only of valid components?
113 114 115 116 117 118 119 120 |
# File 'lib/rdf/query/pattern.rb', line 113 def valid? (has_subject? ? (subject.resource? || subject.variable?) && subject.valid? : true) && (has_predicate? ? (predicate.uri? || predicate.variable?) && predicate.valid? : true) && (has_object? ? (object.term? || object.variable?) && object.valid? : true) && (has_graph? ? (graph_name.resource? || graph_name.variable?) && graph_name.valid? : true) rescue NoMethodError false end |
#variable_count ⇒ Integer Also known as: cardinality, arity
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.
254 255 256 257 258 259 |
# File 'lib/rdf/query/pattern.rb', line 254 def variable_count [subject, predicate, object, graph_name].inject(0) do |memo, term| memo += (term.is_a?(Variable) ? 1 : (term.is_a?(Pattern) ? term.variable_count : 0)) end end |
#variable_terms(name = nil) ⇒ Array<Symbol>
Returns the variable terms in this pattern.
238 239 240 241 242 243 244 245 |
# File 'lib/rdf/query/pattern.rb', line 238 def variable_terms(name = nil) 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 |
#variables ⇒ Hash{Symbol => Variable}
Returns all variables in this pattern.
Note: this returns a hash containing distinct variables only.
269 270 271 272 273 |
# File 'lib/rdf/query/pattern.rb', line 269 def variables [subject, predicate, object, graph_name].inject({}) do |memo, term| term && term.variable? ? memo.merge(term.variables) : memo end end |