Class: RDF::Query::Solutions

Inherits:
Array
  • Object
show all
Defined in:
lib/rdf/query/solutions.rb

Overview

An RDF basic graph pattern (BGP) query solution sequence.

Examples:

Filtering solutions using a hash

solutions.filter(author:  RDF::URI("http://ar.to/#self"))
solutions.filter(author:  "Gregg Kellogg")
solutions.filter(author:  [RDF::URI("http://ar.to/#self"), "Gregg Kellogg"])
solutions.filter(updated: RDF::Literal(Date.today))

Filtering solutions using a block

solutions.filter { |solution| solution.author.literal? }
solutions.filter { |solution| solution.title.to_s =~ /^SPARQL/ }
solutions.filter { |solution| solution.price < 30.5 }
solutions.filter { |solution| solution.bound?(:date) }
solutions.filter { |solution| solution.age.datatype == RDF::XSD.integer }
solutions.filter { |solution| solution.name.language == :es }

Reordering solutions based on a variable or proc

solutions.order_by(:updated)
solutions.order_by(:updated, :created)
solutions.order_by(:updated, lambda {|a, b| b <=> a})

Selecting/Projecting particular variables only

solutions.select(:title)
solutions.select(:title, :description)
solutions.project(:title)

Eliminating duplicate solutions

solutions.distinct

Limiting the number of solutions

solutions.offset(20).limit(10)

Counting the number of matching solutions

solutions.count
solutions.count { |solution| solution.price < 30.5 }

Iterating over all found solutions

solutions.each { |solution| puts solution.inspect }

Since:

  • 0.3.0

Instance Method Summary collapse

Instance Method Details

#bindingsHash{Symbol => Array<RDF::Term>}

Returns hash of bindings from each solution. Each bound variable will have an array of bound values representing those from each solution, where a given solution will have just a single value for each bound variable

Returns:

Since:

  • 0.3.0



99
100
101
102
103
104
105
106
107
108
# File 'lib/rdf/query/solutions.rb', line 99

def bindings
  bindings = {}
  each do |solution|
    solution.each do |key, value|
      bindings[key] ||= []
      bindings[key] << value
    end
  end
  bindings
end

#countInteger #count({ |solution| ... }) {|solution| ... } ⇒ Integer

Returns the number of matching query solutions.

Overloads:

  • #countInteger

    Returns:

    • (Integer)
  • #count({ |solution| ... }) {|solution| ... } ⇒ Integer

    Yields:

    • (solution)

    Yield Parameters:

    Yield Returns:

    • (Boolean)

    Returns:

    • (Integer)

Returns:

  • (Integer)

Since:

  • 0.3.0



59
60
61
# File 'lib/rdf/query/solutions.rb', line 59

def count(&block)
  super
end

#distinctself Also known as: distinct!, reduced, reduced!

Ensures that the solutions in this solution sequence are unique.

Returns:

  • (self)

Since:

  • 0.3.0



244
245
246
247
# File 'lib/rdf/query/solutions.rb', line 244

def distinct
  self.uniq!
  self
end

#dupRDF::Query::Solutions

Duplicates each solution.

Returns:

Since:

  • 0.3.0



113
114
115
# File 'lib/rdf/query/solutions.rb', line 113

def dup
  RDF::Query::Solutions.new(self.compact.map(&:dup))
end

#filter(criteria = {}) {|solution| ... } ⇒ self Also known as: filter!

Filters this solution sequence by the given criteria.

Parameters:

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

Yields:

  • (solution)

Yield Parameters:

Yield Returns:

  • (Boolean)

Returns:

  • (self)

Since:

  • 0.3.0



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/rdf/query/solutions.rb', line 140

def filter(criteria = {})
  @variable_names = nil
  if block_given?
    self.reject! do |solution|
      !yield(solution.is_a?(Solution) ? solution : Solution.new(solution))
    end
  else
    self.reject! do |solution|
      solution = solution.is_a?(Solution) ? solution : Solution.new(solution)
      results = criteria.map do |name, value|
        case value
        when Array then value.any? {|v| solution[name] == v}
        when Regexp then solution[name].to_s.match(value)
        else solution[name] == value
        end
      end
      !results.all?
    end
  end
  self
end

#have_variables?(variables) ⇒ Boolean Also known as: has_variables?

Returns true if this solution sequence contains bindings for any of the given variables.

Parameters:

  • variables (Array<Symbol, #to_sym>)

    an array of variables to check

Returns:

  • (Boolean)

    true or false

See Also:

Since:

  • 0.3.0



89
90
91
# File 'lib/rdf/query/solutions.rb', line 89

def have_variables?(variables)
  self.any? { |solution| solution.has_variables?(variables) }
end

#limit(length) ⇒ self Also known as: limit!

Limits the number of solutions in this solution sequence to a maximum of length.

Parameters:

  • length (Integer, #to_i)

    zero or a positive integer

Returns:

  • (self)

Raises:

  • (ArgumentError)

    if length is negative

Since:

  • 0.3.0



276
277
278
279
280
281
282
283
284
# File 'lib/rdf/query/solutions.rb', line 276

def limit(length)
  length = length.to_i
  raise ArgumentError, "expected zero or a positive integer, got #{length}" if length < 0
  case length
    when 0 then self.clear
    else self.slice!(length..-1) if length < self.size
  end
  self
end

#merge(other) ⇒ RDF::Query::Solutions

Merge solutions in other into a new solutions instance. Each solution in other is merged into those solutions in self that are compatible.

Parameters:

Returns:

Since:

  • 0.3.0



122
123
124
125
126
127
128
129
130
# File 'lib/rdf/query/solutions.rb', line 122

def merge(other)
  other ||= RDF::Query::Solutions()
  return other if self.empty?
  return self if other.empty?

  RDF::Query::Solutions(self.map do |s1|
    other.map { |s2| s2.merge(s1) if s2.compatible?(s1) }
  end.flatten.compact)
end

#minus(other) ⇒ RDF::Query::Solutions

Difference between solution sets, from SPARQL 1.1.

The minus operation on solutions returns those solutions which either have no compatible solution in other, or the solution domains are disjoint.

Parameters:

Returns:

See Also:

Since:

  • 0.3.0



171
172
173
174
175
# File 'lib/rdf/query/solutions.rb', line 171

def minus(other)
  self.dup.filter! do |soln|
    !other.any? {|soln2| soln.compatible?(soln2) && !soln.disjoint?(soln2)}
  end
end

#offset(start) ⇒ self Also known as: offset!

Limits this solution sequence to bindings starting from the start offset in the overall solution sequence.

Parameters:

  • start (Integer, #to_i)

    zero or a positive or negative integer

Returns:

  • (self)

Since:

  • 0.3.0



259
260
261
262
263
264
265
# File 'lib/rdf/query/solutions.rb', line 259

def offset(start)
  case start = start.to_i
    when 0 then nil
    else self.slice!(0...start)
  end
  self
end

#order(*variables) {|solution| ... } ⇒ self Also known as: order_by

Reorders this solution sequence by the given variables.

Variables may be symbols or Variable instances. A variable may also be a Procedure/Lambda, compatible with ‘::Enumerable#sort`. This takes two arguments (solutions) and returns -1, 0, or 1 equivalently to <=>.

If called with a block, variables are ignored, and the block is invoked with pairs of solutions. The block is expected to return -1, 0, or 1 equivalently to <=>.

Parameters:

Yields:

  • (solution)

Yield Parameters:

Yield Returns:

  • (Integer)

    -1, 0, or 1 depending on value of comparator

Returns:

  • (self)

Since:

  • 0.3.0



193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/rdf/query/solutions.rb', line 193

def order(*variables)
  if variables.empty? && !block_given?
    raise ArgumentError, "wrong number of arguments (0 for 1)"
  else
    self.sort! do |a, b|
      if block_given?
        yield((a.is_a?(Solution) ? a : Solution.new(a)), (b.is_a?(Solution) ? b : Solution.new(b)))
      else
        # Try each variable until a difference is found.
        variables.inject(nil) do |memo, v|
          memo || begin
            comp = v.is_a?(Proc) ? v.call(a, b) : (v = v.to_sym; a[v] <=> b[v])
            comp == 0 ? false : comp
          end
        end || 0
      end
    end
  end
  self
end

#project(*variables) ⇒ self Also known as: select

Restricts this solution sequence to the given variables only.

Parameters:

  • variables (Array<Symbol, #to_sym>)

Returns:

  • (self)

Since:

  • 0.3.0



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/rdf/query/solutions.rb', line 220

def project(*variables)
  if variables.empty?
    raise ArgumentError, "wrong number of arguments (0 for 1)"
  else
    variables.map!(&:to_sym)
    self.each do |solution|
      solution.bindings.delete_if { |k, v| !variables.include?(k.to_sym) }
    end
  end

  # Make sure variable_names are ordered by projected variables
  projected_vars, vars = variables.map(&:to_sym), variable_names
  vars = variable_names

  # Maintain projected order, and add any non-projected variables
  @variable_names = (projected_vars & vars) + (vars - projected_vars)
  self
end

#variable_namesArray<Symbol>

Returns an array of the distinct variable names used in this solution sequence.

Returns:

  • (Array<Symbol>)

Since:

  • 0.3.0



68
69
70
71
72
73
74
75
76
77
78
# File 'lib/rdf/query/solutions.rb', line 68

def variable_names
  @variable_names ||= begin
    variables = self.inject({}) do |result, solution|
      solution.each_name do |name|
        result[name] ||= true
      end
      result
    end
    variables.keys
  end
end