Class: Alf::Aggregator

Inherits:
Object
  • Object
show all
Extended by:
Support::Registry
Defined in:
lib/alf-aggregator/alf/aggregator.rb,
lib/alf-aggregator/alf/aggregator/avg.rb,
lib/alf-aggregator/alf/aggregator/max.rb,
lib/alf-aggregator/alf/aggregator/min.rb,
lib/alf-aggregator/alf/aggregator/sum.rb,
lib/alf-aggregator/alf/aggregator/count.rb,
lib/alf-aggregator/alf/aggregator/concat.rb,
lib/alf-aggregator/alf/aggregator/stddev.rb,
lib/alf-aggregator/alf/aggregator/collect.rb,
lib/alf-aggregator/alf/aggregator/variance.rb

Overview

Aggregation operator.

This class provides a basis for implementing aggregation operators. It should always be used as a superclass for such implementations.

Aggregation operators are made available through factory methods on the Aggregator class itself:

Aggregator.count
Aggregator.sum{ qty }

The coercion method should always be used for building aggregators from lispy source code:

Aggregator.coerce("count")
Aggregator.coerce("sum{ qty }")

Once built, aggregators can be used either in black-box or white-box modes.

relation = ...
agg = Aggregator.sum{ qty }

# Black box mode:
result = agg.aggregate(relation)

# White box mode:
memo = agg.least
relation.each do |tuple|
  memo = agg.happens(memo, tuple)
end
result = agg.finalize(memo)

Direct Known Subclasses

Avg, Collect, Concat, Count, Max, Min, Sum, Variance

Defined Under Namespace

Classes: Avg, Collect, Concat, Count, Max, Min, Stddev, Sum, Variance

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Support::Registry

each, listen, listeners, register, registered

Constructor Details

#initialize(options = {}, &block) ⇒ Aggregator

Creates an Aggregator instance.

Example:

Aggregator.new{ size * price }


105
106
107
108
109
# File 'lib/alf-aggregator/alf/aggregator.rb', line 105

def initialize(options = {}, &block)
  options, block = {}, options if options.is_a?(Symbol) && block.nil?
  @options = default_options.merge(options)
  @functor = Support.coerce(block, TupleExpression)
end

Instance Attribute Details

#functorTupleExpression (readonly)

Returns the underlying functor.

Returns:

  • (TupleExpression)

    the underlying functor



94
95
96
# File 'lib/alf-aggregator/alf/aggregator.rb', line 94

def functor
  @functor
end

#optionsHash (readonly)

Returns Aggregation options.

Returns:

  • (Hash)

    Aggregation options



91
92
93
# File 'lib/alf-aggregator/alf/aggregator.rb', line 91

def options
  @options
end

#sourceString

Returns source code of the aggregator, if any.

Returns:

  • (String)

    source code of the aggregator, if any



97
98
99
# File 'lib/alf-aggregator/alf/aggregator.rb', line 97

def source
  @source
end

Class Method Details

.coerce(arg) ⇒ Aggregator

Coerces ‘arg` to an Aggregator

Implemented coercions are:

  • Aggregator -> self

  • String -> through factory methods on self

Parameters:

  • arg (Object)

    a value to coerce to an aggregator

Returns:

Raises:

  • (ArgumentError)

    if the coercion fails



76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/alf-aggregator/alf/aggregator.rb', line 76

def coerce(arg)
  case arg
  when Aggregator
    arg
  when String
    agg = instance_eval(arg)
    agg.source = arg
    agg
  else
    raise ArgumentError, "Invalid arg `arg` for Aggregator()"
  end
end

.inherited(clazz) ⇒ Object

Automatically installs factory methods for inherited classes.

Parameters:

  • clazz (Class)

    a class that extends Aggregator



63
64
65
# File 'lib/alf-aggregator/alf/aggregator.rb', line 63

def inherited(clazz)
  register(clazz, Aggregator)
end

Instance Method Details

#==(other) ⇒ Boolean

Checks equality with another aggregator

Parameters:

Returns:

  • (Boolean)

    true is self and other are equal, false otherwise



196
197
198
199
200
201
# File 'lib/alf-aggregator/alf/aggregator.rb', line 196

def ==(other)
  return false unless other.is_a?(Aggregator)
  has_source_code! == other.has_source_code!
rescue NotImplementedError
  super
end

#aggregate(enum) ⇒ Object

Aggregates over an enumeration of tuples.

Parameters:

  • an (Enumerable<Tuple>)

    enumerable of tuples

Returns:

  • (Object)

    the computed aggregation value



161
162
163
164
# File 'lib/alf-aggregator/alf/aggregator.rb', line 161

def aggregate(enum)
  scope = Support::TupleScope.new
  finalize(enum.inject(least){|m,t| happens(m, scope.__set_tuple(t))})
end

#default_optionsHash

Returns the default options to use

Returns:

  • (Hash)

    the default aggregation options



114
115
116
# File 'lib/alf-aggregator/alf/aggregator.rb', line 114

def default_options
  {}
end

#finalize(memo) ⇒ Object

This method finalizes an aggregation.

Argument memo is either least or the result of aggregating through happens. The default implementation simply returns memo. The method is intended to be overriden for complex aggregations that need statefull information such as ‘avg`.

Parameters:

  • memo (Object)

    the current aggregation value

Returns:

  • (Object)

    the aggregation value, as finalized



153
154
155
# File 'lib/alf-aggregator/alf/aggregator.rb', line 153

def finalize(memo)
  memo
end

#happens(memo, scope) ⇒ Object

This method is called on each aggregated tuple and must return an updated memo value. It can be seen as the block typically given to Enumerable.inject.

The default implementation collects the pre-value on the tuple and delegates to _happens.

Parameters:

  • memo (Object)

    the current aggregation value

  • a (Support::TupleScope)

    tuple scope bound to the current tuple

Returns:

  • (Object)

    updated memo value



139
140
141
142
# File 'lib/alf-aggregator/alf/aggregator.rb', line 139

def happens(memo, scope)
  raise unless Support::TupleScope===scope
  _happens(memo, @functor.evaluate(scope))
end

#has_source_code!String

Asserts that this aggregator knows its source code or raises a NotImplementedError.

Returns:

  • (String)

    the source code when known



175
176
177
178
179
180
181
# File 'lib/alf-aggregator/alf/aggregator.rb', line 175

def has_source_code!
  if source.nil?
    raise NotImplementedError, "No known source code for this aggregator"
  else
    source
  end
end

#infer_typeObject

Infers the resulting type from expression source code



167
168
169
# File 'lib/alf-aggregator/alf/aggregator.rb', line 167

def infer_type
  Object
end

#leastObject

Returns the least value, which is the one to use on an empty set.

This method is intended to be overriden by subclasses; default implementation returns nil.

Returns:

  • (Object)

    the least value for this aggregator



125
126
127
# File 'lib/alf-aggregator/alf/aggregator.rb', line 125

def least
  nil
end

#to_lispyString

Returns a lispy expression

Returns:

  • (String)

    a lispy expression for this aggregator



186
187
188
189
190
# File 'lib/alf-aggregator/alf/aggregator.rb', line 186

def to_lispy
  has_source_code!
rescue NotImplementedError
  "[lispy code unavailable]"
end