Class: RDF::Query::Pattern

Inherits:
Statement show all
Defined in:
lib/rdf/query/pattern.rb

Overview

An RDF query pattern.

Since:

  • 0.3.0

Instance Attribute Summary collapse

Attributes inherited from Statement

#graph_name, #id, #object, #predicate, #subject

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Statement

#==, #===, #[], #[]=, #asserted?, #canonicalize, #canonicalize!, #complete?, #eql?, #has_graph?, #has_object?, #has_predicate?, #has_subject?, #incomplete?, #inferred?, #invalid?, #node?, #quoted?, #reified, #statement?, #to_hash, #to_quad, #to_triple, #variable?

Methods included from Value

#anonymous?, #canonicalize, #canonicalize!, #constant?, #graph?, #inspect, #inspect!, #invalid?, #iri?, #list?, #literal?, #node?, #resource?, #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

Note:

Statement treats symbols as interned Node instances, in a RDF::Query::Pattern, they are treated as Variable.

Returns a new instance of Pattern.

Overloads:

  • #initialize(options = {}) ⇒ Pattern

    Parameters:

    • options (Hash{Symbol => Object}) (defaults to: {})

    Options Hash (options):

    • :subject (Variable, Resource, Symbol, nil) — default: nil
    • :predicate (Variable, URI, Symbol, nil) — default: nil
    • :object (Variable, Term, Symbol, nil) — default: nil
    • :graph_name (Variable, Resource, Symbol, nil, false) — default: nil

      A graph_name of nil matches any graph, a graph_name of false, matches only the default graph.

    • :optional (Boolean) — default: false
  • #initialize(subject, predicate, object, options = {}) ⇒ Pattern

    Parameters:

    Options Hash (options):

    • :graph_name (Variable, Resource, Symbol, nil, false) — default: nil

      A graph_name of nil matches any graph, a graph_name of false, matches only the default graph.

    • :optional (Boolean) — default: false

Since:

  • 0.3.0



38
39
40
# File 'lib/rdf/query/pattern.rb', line 38

def initialize(subject = nil, predicate = nil, object = nil, options = {})
  super
end

Instance Attribute Details

#costNumeric

The estimated cost of this pattern (for query optimization).

Returns:

  • (Numeric)

Since:

  • 0.3.0



62
63
64
# File 'lib/rdf/query/pattern.rb', line 62

def cost
  @cost
end

#optionsHash (readonly)

Any additional options for this pattern.

Returns:

  • (Hash)

Since:

  • 0.3.0



56
57
58
# File 'lib/rdf/query/pattern.rb', line 56

def options
  @options
end

Class Method Details

.from(pattern, options = {}) ⇒ Object

Since:

  • 0.2.2



8
9
10
11
12
13
14
15
16
# File 'lib/rdf/query/pattern.rb', line 8

def self.from(pattern, options = {})
  case pattern
    when Pattern then pattern
    when Array, Statement
      self.new(pattern[0], pattern[1], pattern[2], options.merge(graph_name: pattern[3]))
    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

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

Parameters:

Returns:

  • (self)

Since:

  • 0.3.0



253
254
255
256
257
258
259
260
# File 'lib/rdf/query/pattern.rb', line 253

def bind(solution)
  self.to_quad.each_with_index do |term, index|
    if term && term.variable? && solution[term]
      self[index] = solution[term] 
    end
  end
  self
end

#binding_countInteger

Returns the number of bindings in this pattern.

Returns:

  • (Integer)

    (0..3)

Since:

  • 0.3.0



274
275
276
# File 'lib/rdf/query/pattern.rb', line 274

def binding_count
  bindings.size
end

#bindingsHash{Symbol => RDF::Term}

Returns all bindings in this pattern.

Returns:

Since:

  • 0.3.0



282
283
284
285
286
287
288
289
# File 'lib/rdf/query/pattern.rb', line 282

def bindings
  bindings = {}
  bindings.merge!(subject.bindings)    if subject.is_a?(Variable)
  bindings.merge!(predicate.bindings)  if predicate.is_a?(Variable)
  bindings.merge!(object.bindings)     if object.is_a?(Variable)
  bindings.merge!(graph_name.bindings) if graph_name.is_a?(Variable)
  bindings
end

#bindings?Boolean

Returns ‘true` if this pattern contains bindings.

Returns:

  • (Boolean)

    ‘true` or `false`

Since:

  • 0.3.0



266
267
268
# File 'lib/rdf/query/pattern.rb', line 266

def bindings?
  !bindings.empty?
end

#blank?Boolean

Returns ‘true` if this is a blank pattern, with all terms being `nil`.

Returns:

  • (Boolean)

    ‘true` or `false`

Since:

  • 0.3.0



69
70
71
# File 'lib/rdf/query/pattern.rb', line 69

def blank?
  subject.nil? && predicate.nil? && object.nil? && graph_name.nil?
end

#bound?Boolean

Returns ‘true` if all variables in this pattern are bound.

Returns:

  • (Boolean)

    ‘true` or `false`

Since:

  • 0.3.0



295
296
297
# File 'lib/rdf/query/pattern.rb', line 295

def bound?
  !variables.empty? && variables.values.all?(&:bound?)
end

#bound_variablesHash{Symbol => Variable}

Returns all bound variables in this pattern.

Returns:

Since:

  • 0.3.0



303
304
305
# File 'lib/rdf/query/pattern.rb', line 303

def bound_variables
  variables.reject { |name, variable| variable.unbound? }
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`.

Examples:

Pattern.new(:s, :p, :o).execute(RDF::Repository.load('etc/doap.nt'))

Parameters:

  • queryable (RDF::Queryable)

    the graph or repository to query

  • bindings (Hash{Symbol => RDF::Term}) (defaults to: {})

    optional variable bindings to use

Yields:

  • (statement)

    each matching statement

Yield Parameters:

  • statement (RDF::Statement)

    an RDF statement matching this pattern

Returns:

See Also:

Since:

  • 0.3.0



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/rdf/query/pattern.rb', line 137

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.

Returns:

  • (Boolean)

    ‘true` or `false`

Since:

  • 0.3.0



78
79
80
81
82
83
# File 'lib/rdf/query/pattern.rb', line 78

def has_variables?
  subject.is_a?(Variable) ||
    predicate.is_a?(Variable) ||
    object.is_a?(Variable) ||
    graph_name.is_a?(Variable)
end

#initialize!Object

Since:

  • 0.3.0



44
45
46
47
48
49
50
# File 'lib/rdf/query/pattern.rb', line 44

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)
  super
end

#optional?Boolean

Returns ‘true` if this is an optional pattern.

Examples:

Pattern.new(:s, :p, :o).optional?                     #=> false
Pattern.new(:s, :p, :o, optional: true).optional?  #=> true

Returns:

  • (Boolean)

    ‘true` or `false`

Since:

  • 0.3.0



95
96
97
# File 'lib/rdf/query/pattern.rb', line 95

def optional?
  !!options[: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`.

Examples:

pattern = Pattern.new(:s, :p, :o)
solution = pattern.solution(statement)

pattern[:s] #=> statement.subject
pattern[:p] #=> statement.predicate
pattern[:o] #=> statement.object

Parameters:

Returns:

Since:

  • 0.3.0



187
188
189
190
191
192
193
194
# File 'lib/rdf/query/pattern.rb', line 187

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)
  end
end

#to_sString

Returns a string representation of this pattern.

Returns:

  • (String)

Since:

  • 0.3.0



327
328
329
330
331
332
333
334
335
336
337
338
339
340
# File 'lib/rdf/query/pattern.rb', line 327

def to_s
  StringIO.open do |buffer| # FIXME in RDF::Statement
    buffer << 'OPTIONAL ' if optional?
    buffer << [subject, predicate, object].map do |r|
      r.is_a?(RDF::Query::Variable) ? r.to_s : RDF::NTriples.serialize(r)
    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.

Returns:

  • (Boolean)

    ‘true` or `false`

Since:

  • 0.3.0



311
312
313
# File 'lib/rdf/query/pattern.rb', line 311

def unbound?
  !variables.empty? && variables.values.all?(&:unbound?)
end

#unbound_variablesHash{Symbol => Variable}

Returns all unbound variables in this pattern.

Returns:

Since:

  • 0.3.0



319
320
321
# File 'lib/rdf/query/pattern.rb', line 319

def unbound_variables
  variables.reject { |name, variable| variable.bound? }
end

#valid?Boolean

Is this pattern composed only of valid components?

Returns:

  • (Boolean)

    ‘true` or `false`

Since:

  • 0.3.0



103
104
105
106
107
108
109
110
# File 'lib/rdf/query/pattern.rb', line 103

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_countInteger 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.

Returns:

  • (Integer)

    (0..3)

Since:

  • 0.3.0



222
223
224
225
226
227
228
229
# File 'lib/rdf/query/pattern.rb', line 222

def variable_count
  count = 0
  count += 1 if subject.is_a?(Variable)
  count += 1 if predicate.is_a?(Variable)
  count += 1 if object.is_a?(Variable)
  count += 1 if graph_name.is_a?(Variable)
  count
end

#variable_terms(name = nil) ⇒ Array<Symbol>

Returns the variable terms in this pattern.

Examples:

Pattern.new(RDF::Node.new, :p, 123).variable_terms    #=> [:predicate]

Parameters:

  • name (Symbol, #to_sym) (defaults to: nil)

    an optional variable name

Returns:

  • (Array<Symbol>)

Since:

  • 0.3.0



206
207
208
209
210
211
212
213
# File 'lib/rdf/query/pattern.rb', line 206

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

#variablesHash{Symbol => Variable}

Returns all variables in this pattern.

Note: this returns a hash containing distinct variables only.

Returns:

Since:

  • 0.3.0



239
240
241
242
243
244
245
246
# File 'lib/rdf/query/pattern.rb', line 239

def variables
  variables = {}
  variables.merge!(subject.variables)    if subject.is_a?(Variable)
  variables.merge!(predicate.variables)  if predicate.is_a?(Variable)
  variables.merge!(object.variables)     if object.is_a?(Variable)
  variables.merge!(graph_name.variables) if graph_name.is_a?(Variable)
  variables
end