class NoSE::Backend::PreparedUpdate

An update prepared with a backend which is ready to execute

Attributes

statement[R]
steps[R]

Public Class Methods

new(statement, support_plans, steps) click to toggle source
# File lib/nose/backend.rb, line 402
def initialize(statement, support_plans, steps)
  @statement = statement
  @support_plans = support_plans
  @delete_step = steps.find do |step|
    step.is_a? Backend::DeleteStatementStep
  end
  @insert_step = steps.find do |step|
    step.is_a? Backend::InsertStatementStep
  end
end

Public Instance Methods

execute(update_settings, update_conditions) click to toggle source

Execute the statement for the given set of conditions @return [void]

# File lib/nose/backend.rb, line 415
def execute(update_settings, update_conditions)
  # Execute all the support queries
  settings = initial_update_settings update_settings, update_conditions

  # Execute the support queries for this update
  support = support_results update_conditions

  # Perform the deletion
  @delete_step.process support unless support.empty? || @delete_step.nil?
  return if @insert_step.nil?

  # Get the fields which should be used from the original statement
  # If we didn't delete old entries, then we just need the primary key
  # attributes of the index, otherwise we need everything
  index = @insert_step.index
  include_fields = if @delete_step.nil?
                     index.hash_fields + index.order_fields
                   else
                     index.all_fields
                   end

  # Add fields from the original statement
  update_conditions.each_value do |condition|
    next unless include_fields.include? condition.field
    settings.merge! condition.field.id => condition.value
  end

  if support.empty?
    support = [settings]
  else
    support.each do |row|
      row.merge!(settings) { |_, value, _| value }
    end
  end

  # Stop if we have nothing to insert, otherwise insert
  return if support.empty?
  @insert_step.process support
end

Private Instance Methods

initial_update_settings(update_settings, update_conditions) click to toggle source

Get the initial values which will be used in the first plan step @return [Hash]

# File lib/nose/backend.rb, line 459
def initial_update_settings(update_settings, update_conditions)
  if !@insert_step.nil? && @delete_step.nil?
    # Populate the data to insert for Insert statements
    settings = Hash[update_settings.map do |setting|
      [setting.field.id, setting.value]
    end]
  else
    # Get values for updates and deletes
    settings = Hash[update_conditions.map do |field_id, condition|
      [field_id, condition.value]
    end]
  end

  settings
end
support_results(settings) click to toggle source

Execute all the support queries @return [Array<Hash>]

# File lib/nose/backend.rb, line 477
def support_results(settings)
  return [] if @support_plans.empty?

  # Get a hash of values used in settings, first
  # resolving any settings which specify foreign keys
  settings = Hash[settings.map do |k, v|
    new_condition = v.resolve_foreign_key
    [new_condition.field.id, new_condition]
  end]
  setting_values = Hash[settings.map { |k, v| [k, v.value] }]

  # If we have no query for IDs on the first entity, we must
  # have the fields we need to execute the other support queries
  if !@statement.nil? &&
     @support_plans.first.query.entity != @statement.entity
    support = @support_plans.map do |plan|
      plan.execute settings
    end

    # Combine the results from multiple support queries
    unless support.empty?
      support = support.first.product(*support[1..-1])
      support.map! do |results|
        results.reduce(&:merge!).merge!(setting_values)
      end
    end
  else
    # Execute the first support query to get a list of IDs
    first_query = @support_plans.first.query

    # We may not have a statement if this is manually defined
    if @statement.nil?
      select_key = false
      entity_fields = nil
    else
      id = @statement.entity.id_field
      select_key = first_query.select.include? id

      # Select any fields from the entity being modified if required
      entity_fields = @support_plans.first.execute settings \
        if first_query.graph.size == 1 && \
           first_query.graph.entities.first == @statement.entity
    end

    if select_key
      # Pull the IDs from the first support query
      conditions = entity_fields.map do |row|
        { id.id => Condition.new(id, :'=', row[id.id]) }
      end
    else
      # Use the ID specified in the statement conditions
      conditions = [settings]
    end

    # Execute the support queries for each ID
    support = conditions.each_with_index.flat_map do |condition, i|
      results = @support_plans[(select_key ? 1 : 0)..-1].map do |plan|
        plan.execute condition
      end

      # Combine the results of the different support queries
      results[0].product(*results[1..-1]).map do |result|
        row = result.reduce(&:merge!)
        row.merge!(entity_fields[i]) unless entity_fields.nil?
        row.merge!(setting_values)

        row
      end
    end
  end

  support
end