lib/axiom/optimizer/algebra/summarization.rb
# encoding: utf-8
module Axiom
class Optimizer
module Algebra
# Abstract base class representing Summarization optimizations
class Summarization < Relation::Operation::Unary
# The optimized summarize_per relation
#
# @return [Relation]
#
# @api private
attr_reader :summarize_per
# The optimized summarizers
#
# @return [Hash{Attribute => Function}]
#
# @api private
attr_reader :summarizers
# Initialize a Summarization optimizer
#
# @return [undefined]
#
# @api private
def initialize(*)
super
@summarize_per = optimize_summarize_per
@summarizers = optimize_summarizers
end
private
# Optimize the summarize per relation
#
# @return [Relation]
#
# @api private
def optimize_summarize_per
operation.summarize_per.optimize
end
# Optimize the summarizers
#
# @return [Hash{Attribute => Function}]
#
# @api private
def optimize_summarizers
Function.optimize_functions(operation.summarizers)
end
# Wrap the operand's operand in a Summarization
#
# @return [Summarization]
#
# @api private
def wrap_operand(operand = operand.operand)
operand.summarize(summarize_per, summarizers)
end
# Optimize when the operand is Empty
class EmptyOperand < self
# Return the default value for a function
#
# @param [Object] function
#
# @return [Object]
#
# @api private
def self.extension_default(function)
if function.respond_to?(:default)
function.finalize(function.default)
end
end
# Test if the operand is empty
#
# @return [Boolean]
#
# @api private
def optimizable?
operand.kind_of?(Axiom::Relation::Empty)
end
# Return an extended relation with the same headers
#
# @return [Extension]
#
# @api private
def optimize
summarize_per.extend(extensions)
end
private
# Return the extensions for the optimized relation
#
# @return [Hash{Attribute => #call}]
#
# @api private
def extensions
extensions = {}
operation.summarizers.each do |attribute, function|
extensions[attribute] = self.class.extension_default(function)
end
extensions
end
end # class EmptyOperand
# Optimize when the summarize_per is empty
class EmptySummarizePer < self
# Test if summarize_per is empty
#
# @return [Boolean]
#
# @api private
def optimizable?
summarize_per.kind_of?(Axiom::Relation::Empty)
end
# Return an empty relation if there is nothing to summarize over
#
# @return [Relation::Empty]
#
# @api private
def optimize
Axiom::Relation::Empty.new(operation.header)
end
end # class EmptySummarizePer
# Optimize when the operand is an Sorted
class SortedOperand < self
include Relation::Operation::Unary::SortedOperand
end # class SortedOperand
# Optimize when operand is optimizable
class UnoptimizedOperand < self
include Function::Unary::UnoptimizedOperand
# Test if the operand is unoptimized
#
# @return [Boolean]
#
# @api private
def optimizable?
super ||
summarize_per_optimizable? ||
summarizers_optimizable?
end
# Return a Summarization with an optimized operand
#
# @return [Rename]
#
# @api private
def optimize
wrap_operand(operand)
end
private
# Test if the summarize_per relation is optimizable
#
# @return [Boolean]
#
# @api private
def summarize_per_optimizable?
!summarize_per.equal?(operation.summarize_per)
end
# Test if the summarizers are optimizable
#
# @return [Boolean]
#
# @api private
def summarizers_optimizable?
!summarizers.eql?(operation.summarizers)
end
end # class UnoptimizedOperand
Axiom::Algebra::Summarization.optimizer = chain(
EmptyOperand,
EmptySummarizePer,
SortedOperand,
MaterializedOperand,
UnoptimizedOperand
)
end # class Summarization
end # module Algebra
end # class Optimizer
end # module Axiom