class RDF::Query::Solutions

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

@example 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))

@example 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 }

@example 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})

@example Selecting/Projecting particular variables only

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

@example Eliminating duplicate solutions

solutions.distinct

@example Limiting the number of solutions

solutions.offset(20).limit(10)

@example Counting the number of matching solutions

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

@example Iterating over all found solutions

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

@since 0.3.0

Public Instance Methods

==(other) click to toggle source

Equals of solution

Calls superclass method
# File lib/rdf/query/solutions.rb, line 315
def ==(other)
  super && (!other.respond_to?(:variable_names) || variable_names.eql?(other.variable_names))
end
bindings() click to toggle source

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 @return [Hash{Symbol => Array<RDF::Term>}]

# File lib/rdf/query/solutions.rb, line 119
def bindings
  bindings = {}
  each do |solution|
    solution.each do |key, value|
      bindings[key] ||= []
      bindings[key] << value
    end
  end
  bindings
end
count(&block) click to toggle source

Returns the number of matching query solutions.

@overload count

@return [Integer]

@overload count { |solution| … }

@yield  [solution]
@yieldparam  [RDF::Query::Solution] solution
@yieldreturn [Boolean]
@return [Integer]

@return [Integer]

Calls superclass method
# File lib/rdf/query/solutions.rb, line 59
def count(&block)
  super
end
distinct() click to toggle source

Ensures that the solutions in this solution sequence are unique.

@return [self]

# File lib/rdf/query/solutions.rb, line 264
def distinct
  self.uniq!
  self
end
Also aliased as: distinct!, reduced, reduced!
distinct!()
Alias for: distinct
dup() click to toggle source

Duplicates each solution. @return [RDF::Query::Solutions]

# File lib/rdf/query/solutions.rb, line 133
def dup
  RDF::Query::Solutions.new(self.compact.map(&:dup))
end
eql?(other) click to toggle source

Equivalence of solution

Calls superclass method
# File lib/rdf/query/solutions.rb, line 309
def eql?(other)
  super && (!other.respond_to?(:variable_names) || variable_names.eql?(other.variable_names))
end
filter(criteria = {}) { |is_a?(Solution) ? solution : solution| ... } click to toggle source

Filters this solution sequence by the given ‘criteria`.

@param [Hash{Symbol => Object}] criteria @yield [solution] @yieldparam [RDF::Query::Solution] solution @yieldreturn [Boolean] @return [self]

# File lib/rdf/query/solutions.rb, line 160
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
Also aliased as: filter!
filter!(criteria = {})
Alias for: filter
has_variables?(*args)
Alias for: have_variables?
have_variables?(*args)
Also aliased as: has_variables?
Alias for: variable?
limit(length) click to toggle source

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

@param [Integer, to_i] length

zero or a positive integer

@return [self] @raise [ArgumentError] if ‘length` is negative

# File lib/rdf/query/solutions.rb, line 296
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
Also aliased as: limit!
limit!(length)
Alias for: limit
merge(other) click to toggle source

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

@param [RDF::Query::Solutions] other @return [RDF::Query::Solutions]

# File lib/rdf/query/solutions.rb, line 142
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) click to toggle source

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.

@param [RDF::Query::Solutions] other @return [RDF::Query::Solutions] a new solution set @see www.w3.org/TR/2013/REC-sparql11-query-20130321/#defn_algMinus

# File lib/rdf/query/solutions.rb, line 191
def minus(other)
  self.dup.filter! do |soln|
    !other.any? {|soln2| soln.compatible?(soln2) && !soln.disjoint?(soln2)}
  end
end
offset(start) click to toggle source

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

@param [Integer, to_i] start

zero or a positive or negative integer

@return [self]

# File lib/rdf/query/solutions.rb, line 279
def offset(start)
  case start = start.to_i
    when 0 then nil
    else self.slice!(0...start)
  end
  self
end
Also aliased as: offset!
offset!(start)
Alias for: offset
order(*variables) { |is_a?(Solution ? a : solution), (is_a?(Solution) ? b : solution)| ... } click to toggle source

Reorders this solution sequence by the given ‘variables`.

Variables may be symbols or {Query::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 <=>.

@param [Array<Proc, Query::Variable, Symbol, to_sym>] variables @yield [solution] @yieldparam [RDF::Query::Solution] q @yieldparam [RDF::Query::Solution] b @yieldreturn [Integer] -1, 0, or 1 depending on value of comparator @return [self]

# File lib/rdf/query/solutions.rb, line 213
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
Also aliased as: order_by
order_by(*variables)
Alias for: order
project(*variables) click to toggle source

Restricts this solution sequence to the given ‘variables` only.

@param [Array<Symbol, to_sym>] variables @return [self]

# File lib/rdf/query/solutions.rb, line 240
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
Also aliased as: select
reduced()
Alias for: distinct
reduced!()
Alias for: distinct
select(*variables)
Alias for: project
variable?(*args) click to toggle source

@overload variable?

Returns `false`.

@return [Boolean]

@overload variable?(variables)

Returns `true` if this solution sequence contains bindings for any of

the given ‘variables`.

@param  [Array<Symbol, #to_sym>] variables
@return [Boolean]

@see RDF::Query::Solution#variable? @see RDF::Query#execute

# File lib/rdf/query/solutions.rb, line 102
def variable?(*args)
  case args.length
  when 0 then false
  when 1
    self.any? { |solution| solution.variables?(args.first) }
  else raise ArgumentError("wrong number of arguments (given #{args.length}, expected 0 or 1)")
  end
end
Also aliased as: variables?, have_variables?
variable_names() click to toggle source

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

@return [Array<Symbol>]

# 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
variable_names=(vars) click to toggle source

Sets variable names used in these solutions. If not set, the default is determined by the variables used in each solution.

@param [Array<Symbol, RDF::Query::Variable>] vars @return [Array<Symbol>]

# File lib/rdf/query/solutions.rb, line 85
def variable_names=(vars)
  @variable_names = vars.map(&:to_sym)
end
variables?(*args)
Alias for: variable?