influxdb/engine/aggregator_operators.go

1533 lines
40 KiB
Go

package engine
import (
"fmt"
"math"
"sort"
"strconv"
"strings"
"time"
"code.google.com/p/goprotobuf/proto"
"github.com/influxdb/influxdb/common"
"github.com/influxdb/influxdb/parser"
"github.com/influxdb/influxdb/protocol"
)
type PointSlice []protocol.Point
type Aggregator interface {
AggregatePoint(state interface{}, p *protocol.Point) (interface{}, error)
InitializeFieldsMetadata(series *protocol.Series) error
GetValues(state interface{}) [][]*protocol.FieldValue
CalculateSummaries(state interface{})
ColumnNames() []string
}
// Initialize a new aggregator given the query, the function call of
// the aggregator and the default value that should be returned if
// the bucket doesn't have any points
type AggregatorInitializer func(*parser.SelectQuery, *parser.Value, *parser.Value) (Aggregator, error)
var registeredAggregators = make(map[string]AggregatorInitializer)
func init() {
registeredAggregators["max"] = NewMaxAggregator
registeredAggregators["count"] = NewCountAggregator
registeredAggregators["histogram"] = NewHistogramAggregator
registeredAggregators["derivative"] = NewDerivativeAggregator
registeredAggregators["difference"] = NewDifferenceAggregator
registeredAggregators["stddev"] = NewStandardDeviationAggregator
registeredAggregators["min"] = NewMinAggregator
registeredAggregators["sum"] = NewSumAggregator
registeredAggregators["percentile"] = NewPercentileAggregator
registeredAggregators["median"] = NewMedianAggregator
registeredAggregators["mean"] = NewMeanAggregator
registeredAggregators["mode"] = NewModeAggregator
registeredAggregators["distinct"] = NewDistinctAggregator
registeredAggregators["first"] = NewFirstAggregator
registeredAggregators["last"] = NewLastAggregator
registeredAggregators["top"] = NewTopAggregator
registeredAggregators["bottom"] = NewBottomAggregator
}
// used in testing to get a list of all aggregators
func GetRegisteredAggregators() (names []string) {
for n := range registeredAggregators {
names = append(names, n)
}
return
}
type AbstractAggregator struct {
Aggregator
value *parser.Value
columns []string
}
func (self *AbstractAggregator) InitializeFieldsMetadata(series *protocol.Series) error {
self.columns = series.Fields
return nil
}
func (self *AbstractAggregator) CalculateSummaries(state interface{}) {
}
func wrapDefaultValue(defaultValue *parser.Value) (*protocol.FieldValue, error) {
if defaultValue == nil {
return nil, nil
}
switch defaultValue.Type {
case parser.ValueInt:
v, _ := strconv.Atoi(defaultValue.Name)
value := int64(v)
return &protocol.FieldValue{Int64Value: &value}, nil
case parser.ValueSimpleName:
if defaultValue.Name != "null" {
return nil, fmt.Errorf("Unsupported fill value %s", defaultValue.Name)
}
return nil, nil
default:
return nil, fmt.Errorf("Unknown type %s", defaultValue.Type)
}
}
type Operation func(currentValue float64, newValue *protocol.FieldValue) float64
type CumulativeArithmeticAggregatorState float64
type CumulativeArithmeticAggregator struct {
AbstractAggregator
name string
operation Operation
initialValue float64
defaultValue *protocol.FieldValue
}
func (self *CumulativeArithmeticAggregator) AggregatePoint(state interface{}, p *protocol.Point) (interface{}, error) {
if state == nil {
state = self.initialValue
}
value, err := GetValue(self.value, self.columns, p)
if err != nil {
return nil, err
}
return self.operation(state.(float64), value), nil
}
func (self *CumulativeArithmeticAggregator) ColumnNames() []string {
return []string{self.name}
}
func (self *CumulativeArithmeticAggregator) GetValues(state interface{}) [][]*protocol.FieldValue {
if state == nil {
return [][]*protocol.FieldValue{{self.defaultValue}}
}
return [][]*protocol.FieldValue{
{
{
DoubleValue: protocol.Float64(state.(float64)),
},
},
}
}
func NewCumulativeArithmeticAggregator(name string, value *parser.Value, initialValue float64, defaultValue *parser.Value, operation Operation) (Aggregator, error) {
if len(value.Elems) != 1 {
return nil, common.NewQueryError(common.WrongNumberOfArguments, "function max() requires only one argument")
}
wrappedDefaultValue, err := wrapDefaultValue(defaultValue)
if err != nil {
return nil, err
}
if value.Alias != "" {
name = value.Alias
}
return &CumulativeArithmeticAggregator{
AbstractAggregator: AbstractAggregator{
value: value.Elems[0],
},
name: name,
operation: operation,
initialValue: initialValue,
defaultValue: wrappedDefaultValue,
}, nil
}
func NewMaxAggregator(_ *parser.SelectQuery, value *parser.Value, defaultValue *parser.Value) (Aggregator, error) {
return NewCumulativeArithmeticAggregator("max", value, -math.MaxFloat64, defaultValue, func(currentValue float64, p *protocol.FieldValue) float64 {
if p.Int64Value != nil {
if fv := float64(*p.Int64Value); fv > currentValue {
return fv
}
} else if p.DoubleValue != nil {
if fv := *p.DoubleValue; fv > currentValue {
return fv
}
}
return currentValue
})
}
func NewMinAggregator(_ *parser.SelectQuery, value *parser.Value, defaultValue *parser.Value) (Aggregator, error) {
return NewCumulativeArithmeticAggregator("min", value, math.MaxFloat64, defaultValue, func(currentValue float64, p *protocol.FieldValue) float64 {
if p.Int64Value != nil {
if fv := float64(*p.Int64Value); fv < currentValue {
return fv
}
} else if p.DoubleValue != nil {
if fv := *p.DoubleValue; fv < currentValue {
return fv
}
}
return currentValue
})
}
func NewSumAggregator(_ *parser.SelectQuery, value *parser.Value, defaultValue *parser.Value) (Aggregator, error) {
return NewCumulativeArithmeticAggregator("sum", value, 0, defaultValue, func(currentValue float64, p *protocol.FieldValue) float64 {
var fv float64
if p.Int64Value != nil {
fv = float64(*p.Int64Value)
} else if p.DoubleValue != nil {
fv = *p.DoubleValue
}
return currentValue + fv
})
}
//
// Composite Aggregator
//
type CompositeAggregatorState struct {
rightState interface{}
leftState interface{}
}
type CompositeAggregator struct {
left Aggregator
right Aggregator
}
func (self *CompositeAggregator) AggregatePoint(state interface{}, p *protocol.Point) (interface{}, error) {
s, ok := state.(*CompositeAggregatorState)
if !ok {
s = &CompositeAggregatorState{}
}
var err error
s.rightState, err = self.right.AggregatePoint(s.rightState, p)
return s, err
}
func (self *CompositeAggregator) ColumnNames() []string {
return self.left.ColumnNames()
}
func (self *CompositeAggregator) CalculateSummaries(state interface{}) {
s := state.(*CompositeAggregatorState)
self.right.CalculateSummaries(s.rightState)
values := self.right.GetValues(s.rightState)
for _, v := range values {
point := &protocol.Point{Values: v}
var err error
s.leftState, err = self.left.AggregatePoint(s.leftState, point)
if err != nil {
panic(fmt.Errorf("Error returned from aggregator: %s", err))
}
}
s.rightState = nil
self.left.CalculateSummaries(s.leftState)
}
func (self *CompositeAggregator) GetValues(state interface{}) [][]*protocol.FieldValue {
s, ok := state.(*CompositeAggregatorState)
if !ok {
return self.left.GetValues(nil)
}
return self.left.GetValues(s.leftState)
}
func (self *CompositeAggregator) InitializeFieldsMetadata(series *protocol.Series) error {
return self.right.InitializeFieldsMetadata(series)
}
func NewCompositeAggregator(left, right Aggregator) (Aggregator, error) {
return &CompositeAggregator{left, right}, nil
}
// StandardDeviation Aggregator
type StandardDeviationRunning struct {
count int
totalX2 float64
totalX float64
}
type StandardDeviationAggregator struct {
AbstractAggregator
defaultValue *protocol.FieldValue
alias string
}
func (self *StandardDeviationAggregator) AggregatePoint(state interface{}, p *protocol.Point) (interface{}, error) {
fieldValue, err := GetValue(self.value, self.columns, p)
if err != nil {
return nil, err
}
var value float64
if ptr := fieldValue.Int64Value; ptr != nil {
value = float64(*ptr)
} else if ptr := fieldValue.DoubleValue; ptr != nil {
value = *ptr
} else {
// else ignore this point
return state, nil
}
running, ok := state.(*StandardDeviationRunning)
if !ok {
running = &StandardDeviationRunning{}
}
running.count++
running.totalX += value
running.totalX2 += value * value
return running, nil
}
func (self *StandardDeviationAggregator) ColumnNames() []string {
if self.alias != "" {
return []string{self.alias}
}
return []string{"stddev"}
}
func (self *StandardDeviationAggregator) GetValues(state interface{}) [][]*protocol.FieldValue {
r, ok := state.(*StandardDeviationRunning)
if !ok {
return [][]*protocol.FieldValue{{self.defaultValue}}
}
eX := r.totalX / float64(r.count)
eX *= eX
eX2 := r.totalX2 / float64(r.count)
standardDeviation := math.Sqrt(eX2 - eX)
return [][]*protocol.FieldValue{
{
{DoubleValue: &standardDeviation},
},
}
}
func NewStandardDeviationAggregator(q *parser.SelectQuery, v *parser.Value, defaultValue *parser.Value) (Aggregator, error) {
if len(v.Elems) != 1 {
return nil, common.NewQueryError(common.WrongNumberOfArguments, "function stddev() requires exactly one argument")
}
if v.Elems[0].Type == parser.ValueWildcard {
return nil, common.NewQueryError(common.InvalidArgument, "function stddev() doesn't work with wildcards")
}
value, err := wrapDefaultValue(defaultValue)
if err != nil {
return nil, err
}
return &StandardDeviationAggregator{
AbstractAggregator: AbstractAggregator{
value: v.Elems[0],
},
defaultValue: value,
alias: v.Alias,
}, nil
}
//
// Derivative Aggregator
//
type DerivativeAggregatorState struct {
firstValue *protocol.Point
lastValue *protocol.Point
}
type DerivativeAggregator struct {
AbstractAggregator
duration *time.Duration // if it's group by time()
lastState *DerivativeAggregatorState
defaultValue *protocol.FieldValue
alias string
}
func (self *DerivativeAggregator) AggregatePoint(state interface{}, p *protocol.Point) (interface{}, error) {
fieldValue, err := GetValue(self.value, self.columns, p)
if err != nil {
return nil, err
}
var value float64
if ptr := fieldValue.Int64Value; ptr != nil {
value = float64(*ptr)
} else if ptr := fieldValue.DoubleValue; ptr != nil {
value = *ptr
} else {
// else ignore this point
return state, nil
}
newValue := &protocol.Point{
Timestamp: p.Timestamp,
Values: []*protocol.FieldValue{{DoubleValue: &value}},
}
s, ok := state.(*DerivativeAggregatorState)
if !ok {
s = &DerivativeAggregatorState{}
}
// starting a new bucket? (only for group by time())
if s != self.lastState && self.duration != nil {
// if there was a previous bucket, update its lastValue
if self.lastState != nil {
self.lastState.lastValue = newValue
}
// save the current state as the last
self.lastState = s
}
if s.firstValue == nil {
s.firstValue = newValue
return s, nil
}
if self.duration == nil {
s.lastValue = newValue
} else {
s.lastValue = s.firstValue
}
return s, nil
}
func (self *DerivativeAggregator) ColumnNames() []string {
if self.alias != "" {
return []string{self.alias}
}
return []string{"derivative"}
}
func (self *DerivativeAggregator) GetValues(state interface{}) [][]*protocol.FieldValue {
s, ok := state.(*DerivativeAggregatorState)
if !ok {
return [][]*protocol.FieldValue{{self.defaultValue}}
}
if s.firstValue == nil || s.lastValue == nil {
return nil
}
// if an old value exist, then compute the derivative and insert it in the points slice
deltaT := float64(*s.lastValue.Timestamp-*s.firstValue.Timestamp) / float64(time.Second/time.Microsecond)
deltaV := *s.lastValue.Values[0].DoubleValue - *s.firstValue.Values[0].DoubleValue
derivative := deltaV / deltaT
return [][]*protocol.FieldValue{
{
{DoubleValue: &derivative},
},
}
}
func NewDerivativeAggregator(q *parser.SelectQuery, v *parser.Value, defaultValue *parser.Value) (Aggregator, error) {
if len(v.Elems) != 1 {
return nil, common.NewQueryError(common.WrongNumberOfArguments, "function derivative() requires exactly one argument")
}
if v.Elems[0].Type == parser.ValueWildcard {
return nil, common.NewQueryError(common.InvalidArgument, "function derivative() doesn't work with wildcards")
}
wrappedDefaultValue, err := wrapDefaultValue(defaultValue)
if err != nil {
return nil, err
}
da := &DerivativeAggregator{
AbstractAggregator: AbstractAggregator{
value: v.Elems[0],
},
defaultValue: wrappedDefaultValue,
alias: v.Alias,
}
da.duration, _, err = q.GetGroupByClause().GetGroupByTime()
if err != nil {
return nil, err
}
return da, nil
}
//
// Difference Aggregator
//
type DifferenceAggregatorState struct {
firstValue *protocol.Point
lastValue *protocol.Point
}
type DifferenceAggregator struct {
AbstractAggregator
defaultValue *protocol.FieldValue
alias string
}
func (self *DifferenceAggregator) AggregatePoint(state interface{}, p *protocol.Point) (interface{}, error) {
fieldValue, err := GetValue(self.value, self.columns, p)
if err != nil {
return nil, err
}
var value float64
if ptr := fieldValue.Int64Value; ptr != nil {
value = float64(*ptr)
} else if ptr := fieldValue.DoubleValue; ptr != nil {
value = *ptr
} else {
// else ignore this point
return state, nil
}
newValue := &protocol.Point{
Timestamp: p.Timestamp,
Values: []*protocol.FieldValue{{DoubleValue: &value}},
}
s, ok := state.(*DifferenceAggregatorState)
if !ok {
s = &DifferenceAggregatorState{}
}
if s.firstValue == nil {
s.firstValue = newValue
return s, nil
}
s.lastValue = newValue
return s, nil
}
func (self *DifferenceAggregator) ColumnNames() []string {
if self.alias != "" {
return []string{self.alias}
}
return []string{"difference"}
}
func (self *DifferenceAggregator) GetValues(state interface{}) [][]*protocol.FieldValue {
s, ok := state.(*DifferenceAggregatorState)
if !(ok && s.firstValue != nil && s.lastValue != nil) {
return [][]*protocol.FieldValue{{self.defaultValue}}
}
difference := *s.lastValue.Values[0].DoubleValue - *s.firstValue.Values[0].DoubleValue
return [][]*protocol.FieldValue{
{
{DoubleValue: &difference},
},
}
}
func NewDifferenceAggregator(q *parser.SelectQuery, v *parser.Value, defaultValue *parser.Value) (Aggregator, error) {
if len(v.Elems) != 1 {
return nil, common.NewQueryError(common.WrongNumberOfArguments, "function difference() requires exactly one argument")
}
if v.Elems[0].Type == parser.ValueWildcard {
return nil, common.NewQueryError(common.InvalidArgument, "function difference() doesn't work with wildcards")
}
wrappedDefaultValue, err := wrapDefaultValue(defaultValue)
if err != nil {
return nil, err
}
return &DifferenceAggregator{
AbstractAggregator: AbstractAggregator{
value: v.Elems[0],
},
defaultValue: wrappedDefaultValue,
alias: v.Alias,
}, nil
}
//
// Histogram Aggregator
//
type HistogramAggregatorState map[int]int
type HistogramAggregator struct {
AbstractAggregator
bucketSize float64
bucketStart float64
explicitBucketStart bool
bucketStopIdx int
columnNames []string
defaultValue *protocol.FieldValue
}
func (self *HistogramAggregator) AggregatePoint(state interface{}, p *protocol.Point) (interface{}, error) {
buckets, ok := state.(HistogramAggregatorState)
if !ok {
buckets = make(map[int]int)
}
fieldValue, err := GetValue(self.value, self.columns, p)
if err != nil {
return nil, err
}
var value float64
if ptr := fieldValue.Int64Value; ptr != nil {
value = float64(*ptr)
} else if ptr := fieldValue.DoubleValue; ptr != nil {
value = *ptr
}
bucket := int(math.Floor((value - self.bucketStart) / self.bucketSize))
if self.bucketStopIdx >= 0 {
if bucket <= self.bucketStopIdx {
buckets[bucket] += 1
}
} else {
buckets[bucket] += 1
}
return buckets, nil
}
func (self *HistogramAggregator) ColumnNames() []string {
return self.columnNames
}
func (self *HistogramAggregator) GetValues(state interface{}) [][]*protocol.FieldValue {
returnValues := [][]*protocol.FieldValue{}
if state == nil {
_size := int64(0)
returnValues = append(returnValues, []*protocol.FieldValue{
self.defaultValue,
{Int64Value: &_size},
})
return returnValues
}
buckets := state.(HistogramAggregatorState)
for bucket, size := range buckets {
_bucket := float64(bucket)*self.bucketSize + self.bucketStart
_size := int64(size)
if self.explicitBucketStart && _bucket < self.bucketStart {
continue
}
returnValues = append(returnValues, []*protocol.FieldValue{
{DoubleValue: &_bucket},
{Int64Value: &_size},
})
}
if self.bucketStopIdx >= 0 {
for i := 0; i <= self.bucketStopIdx; i++ {
if _, ok := buckets[i]; !ok {
_bucket := float64(i)*self.bucketSize + self.bucketStart
_size := int64(0)
returnValues = append(returnValues, []*protocol.FieldValue{
{DoubleValue: &_bucket},
{Int64Value: &_size},
})
}
}
}
return returnValues
}
func NewHistogramAggregator(q *parser.SelectQuery, v *parser.Value, defaultValue *parser.Value) (Aggregator, error) {
if len(v.Elems) < 1 {
return nil, common.NewQueryError(common.WrongNumberOfArguments, "function histogram() requires at least one arguments")
}
if len(v.Elems) > 4 {
return nil, common.NewQueryError(common.WrongNumberOfArguments, "function histogram() takes at most four arguments")
}
if v.Elems[0].Type == parser.ValueWildcard {
return nil, common.NewQueryError(common.InvalidArgument, "function histogram() doesn't work with wildcards")
}
bucketSize := 1.0
bucketStart := 0.0
explicitBucketStart := false
bucketStop := 0.0
bucketStopIdx := -1
if len(v.Elems) > 1 {
switch v.Elems[1].Type {
case parser.ValueInt, parser.ValueFloat:
var err error
bucketSize, err = strconv.ParseFloat(v.Elems[1].Name, 64)
if err != nil {
return nil, common.NewQueryError(common.InvalidArgument, "Cannot parse %s into a float", v.Elems[1].Name)
}
default:
return nil, common.NewQueryError(common.InvalidArgument, "Cannot parse %s into a float", v.Elems[1].Name)
}
if len(v.Elems) > 2 {
switch v.Elems[2].Type {
case parser.ValueInt, parser.ValueFloat:
var err error
bucketStart, err = strconv.ParseFloat(v.Elems[2].Name, 64)
explicitBucketStart = true
if err != nil {
return nil, common.NewQueryError(common.InvalidArgument, "Cannot parse %s into a float", v.Elems[2].Name)
}
default:
return nil, common.NewQueryError(common.InvalidArgument, "Cannot parse %s into a float", v.Elems[2].Name)
}
if len(v.Elems) == 4 {
switch v.Elems[3].Type {
case parser.ValueInt, parser.ValueFloat:
var err error
bucketStop, err = strconv.ParseFloat(v.Elems[3].Name, 64)
bucketStopIdx = int(math.Floor((bucketStop - bucketStart) / bucketSize))
if err != nil {
return nil, common.NewQueryError(common.InvalidArgument, "Cannot parse %s into a float", v.Elems[3].Name)
}
default:
return nil, common.NewQueryError(common.InvalidArgument, "Cannot parse %s into a float", v.Elems[3].Name)
}
}
}
}
columnNames := []string{"bucket_start", "count"}
if v.Alias != "" {
columnNames[0] = fmt.Sprintf("%s_bucket_start", v.Alias)
columnNames[1] = fmt.Sprintf("%s_count", v.Alias)
}
return &HistogramAggregator{
AbstractAggregator: AbstractAggregator{
value: v.Elems[0],
},
bucketSize: bucketSize,
bucketStart: bucketStart,
explicitBucketStart: explicitBucketStart,
bucketStopIdx: bucketStopIdx,
columnNames: columnNames,
}, nil
}
//
// Count Aggregator
//
type CountAggregator struct {
AbstractAggregator
defaultValue *protocol.FieldValue
alias string
}
type CountAggregatorState int64
func (self *CountAggregator) AggregatePoint(state interface{}, p *protocol.Point) (interface{}, error) {
if state == nil {
return int64(1), nil
}
return state.(int64) + 1, nil
}
func (self *CountAggregator) ColumnNames() []string {
if self.alias != "" {
return []string{self.alias}
}
return []string{"count"}
}
func (self *CountAggregator) GetValues(state interface{}) [][]*protocol.FieldValue {
returnValues := [][]*protocol.FieldValue{}
if state == nil {
returnValues = append(returnValues, []*protocol.FieldValue{self.defaultValue})
} else {
value := state.(int64)
returnValues = append(returnValues, []*protocol.FieldValue{
{Int64Value: &value},
})
}
return returnValues
}
func (self *CountAggregator) InitializeFieldsMetadata(series *protocol.Series) error { return nil }
func NewCountAggregator(q *parser.SelectQuery, v *parser.Value, defaultValue *parser.Value) (Aggregator, error) {
if len(v.Elems) != 1 {
return nil, common.NewQueryError(common.WrongNumberOfArguments, "function count() requires exactly one argument")
}
if v.Elems[0].Type == parser.ValueWildcard {
return nil, common.NewQueryError(common.InvalidArgument, "function count() doesn't work with wildcards")
}
wrappedDefaultValue, err := wrapDefaultValue(defaultValue)
if err != nil {
return nil, err
}
if v.Elems[0].Type != parser.ValueSimpleName {
innerName := strings.ToLower(v.Elems[0].Name)
init := registeredAggregators[innerName]
if init == nil {
return nil, common.NewQueryError(common.InvalidArgument, fmt.Sprintf("Unknown function %s", innerName))
}
inner, err := init(q, v.Elems[0], defaultValue)
if err != nil {
return nil, err
}
return NewCompositeAggregator(&CountAggregator{AbstractAggregator{}, wrappedDefaultValue, v.Alias}, inner)
}
return &CountAggregator{AbstractAggregator{}, wrappedDefaultValue, v.Alias}, nil
}
//
// Mean Aggregator
//
type MeanAggregatorState struct {
mean float64
count float64
}
type MeanAggregator struct {
AbstractAggregator
defaultValue *protocol.FieldValue
alias string
}
func (self *MeanAggregator) AggregatePoint(state interface{}, p *protocol.Point) (interface{}, error) {
s, ok := state.(*MeanAggregatorState)
if !ok {
s = &MeanAggregatorState{}
}
fieldValue, err := GetValue(self.value, self.columns, p)
if err != nil {
return nil, err
}
var value float64
if ptr := fieldValue.Int64Value; ptr != nil {
value = float64(*ptr)
} else if ptr := fieldValue.DoubleValue; ptr != nil {
value = *ptr
}
s.count++
s.mean = s.mean*(s.count-1)/s.count + value/s.count
return s, nil
}
func (self *MeanAggregator) ColumnNames() []string {
if self.alias != "" {
return []string{self.alias}
}
return []string{"mean"}
}
func (self *MeanAggregator) GetValues(state interface{}) [][]*protocol.FieldValue {
returnValues := [][]*protocol.FieldValue{}
s, ok := state.(*MeanAggregatorState)
if !ok {
returnValues = append(returnValues, []*protocol.FieldValue{self.defaultValue})
} else {
returnValues = append(returnValues, []*protocol.FieldValue{
{DoubleValue: &s.mean},
})
}
return returnValues
}
func NewMeanAggregator(_ *parser.SelectQuery, value *parser.Value, defaultValue *parser.Value) (Aggregator, error) {
if len(value.Elems) != 1 {
return nil, common.NewQueryError(common.WrongNumberOfArguments, "function mean() requires exactly one argument")
}
wrappedDefaultValue, err := wrapDefaultValue(defaultValue)
if err != nil {
return nil, err
}
return &MeanAggregator{
AbstractAggregator: AbstractAggregator{
value: value.Elems[0],
},
defaultValue: wrappedDefaultValue,
alias: value.Alias,
}, nil
}
func NewMedianAggregator(_ *parser.SelectQuery, value *parser.Value, defaultValue *parser.Value) (Aggregator, error) {
if len(value.Elems) != 1 {
return nil, common.NewQueryError(common.WrongNumberOfArguments, "function median() requires exactly one argument")
}
wrappedDefaultValue, err := wrapDefaultValue(defaultValue)
if err != nil {
return nil, err
}
functionName := "median"
if value.Alias != "" {
functionName = value.Alias
}
aggregator := &PercentileAggregator{
AbstractAggregator: AbstractAggregator{
value: value.Elems[0],
},
functionName: functionName,
percentile: 50.0,
defaultValue: wrappedDefaultValue,
alias: value.Alias,
}
return aggregator, nil
}
//
// Percentile Aggregator
//
type PercentileAggregatorState struct {
values []float64
percentileValue float64
}
type PercentileAggregator struct {
AbstractAggregator
functionName string
percentile float64
defaultValue *protocol.FieldValue
alias string
}
func (self *PercentileAggregator) AggregatePoint(state interface{}, p *protocol.Point) (interface{}, error) {
v, err := GetValue(self.value, self.columns, p)
if err != nil {
return nil, err
}
value := 0.0
if v.Int64Value != nil {
value = float64(*v.Int64Value)
} else if v.DoubleValue != nil {
value = *v.DoubleValue
} else {
return state, nil
}
s, ok := state.(*PercentileAggregatorState)
if !ok {
s = &PercentileAggregatorState{}
}
s.values = append(s.values, value)
return s, nil
}
func (self *PercentileAggregator) ColumnNames() []string {
if self.alias != "" {
return []string{self.alias}
}
return []string{self.functionName}
}
func (self *PercentileAggregator) GetValues(state interface{}) [][]*protocol.FieldValue {
s, ok := state.(*PercentileAggregatorState)
if !ok {
return [][]*protocol.FieldValue{{self.defaultValue}}
}
return [][]*protocol.FieldValue{
{{DoubleValue: &s.percentileValue}},
}
}
func (self *PercentileAggregator) CalculateSummaries(state interface{}) {
s, ok := state.(*PercentileAggregatorState)
if !ok {
return
}
sort.Float64s(s.values)
length := len(s.values)
index := int(math.Floor(float64(length)*self.percentile/100.0+0.5)) - 1
if index < 0 || index >= len(s.values) {
return
}
s.percentileValue = s.values[index]
s.values = nil
}
func NewPercentileAggregator(_ *parser.SelectQuery, value *parser.Value, defaultValue *parser.Value) (Aggregator, error) {
if len(value.Elems) != 2 {
return nil, common.NewQueryError(common.WrongNumberOfArguments, "function percentile() requires exactly two arguments")
}
if value.Elems[0].Type == parser.ValueWildcard {
return nil, common.NewQueryError(common.WrongNumberOfArguments, "wildcard cannot be used with percentile")
}
percentile, err := strconv.ParseFloat(value.Elems[1].Name, 64)
if err != nil || percentile <= 0 || percentile >= 100 {
return nil, common.NewQueryError(common.InvalidArgument, "function percentile() requires a numeric second argument between 0 and 100")
}
wrappedDefaultValue, err := wrapDefaultValue(defaultValue)
if err != nil {
return nil, err
}
functionName := "percentile"
if value.Alias != "" {
functionName = value.Alias
}
return &PercentileAggregator{
AbstractAggregator: AbstractAggregator{
value: value.Elems[0],
},
functionName: functionName,
percentile: percentile,
defaultValue: wrappedDefaultValue,
}, nil
}
//
// Mode Aggregator
//
type ModeAggregatorState struct {
counts map[interface{}]int
}
type ModeAggregator struct {
AbstractAggregator
defaultValue *protocol.FieldValue
alias string
size int
}
func (self *ModeAggregator) AggregatePoint(state interface{}, p *protocol.Point) (interface{}, error) {
s, ok := state.(*ModeAggregatorState)
if !ok {
s = &ModeAggregatorState{make(map[interface{}]int)}
}
point, err := GetValue(self.value, self.columns, p)
if err != nil {
return nil, err
}
var value interface{}
if point.Int64Value != nil {
value = float64(*point.Int64Value)
} else if point.DoubleValue != nil {
value = *point.DoubleValue
} else if point.BoolValue != nil {
value = *point.BoolValue
} else if point.StringValue != nil {
value = *point.StringValue
} else {
value = nil
}
s.counts[value]++
return s, nil
}
func (self *ModeAggregator) ColumnNames() []string {
if self.alias != "" {
return []string{self.alias}
}
return []string{"mode"}
}
func (self *ModeAggregator) GetValues(state interface{}) [][]*protocol.FieldValue {
s, ok := state.(*ModeAggregatorState)
if !ok {
return [][]*protocol.FieldValue{{self.defaultValue}}
}
counts := make([]int, len(s.counts))
countMap := make(map[int][]interface{}, len(s.counts))
for value, count := range s.counts {
counts = append(counts, count)
countMap[count] = append(countMap[count], value)
}
// descending sort
sort.Sort(sort.Reverse(sort.IntSlice(counts)))
returnValues := [][]*protocol.FieldValue{}
last := 0
for _, count := range counts {
// counts can contain duplicates, but we only want to append each count-set once
if count == last {
continue
}
last = count
for _, value := range countMap[count] {
switch v := value.(type) {
case int:
n := int64(v)
returnValues = append(returnValues, []*protocol.FieldValue{{Int64Value: &n}})
case string:
returnValues = append(returnValues, []*protocol.FieldValue{{StringValue: &v}})
case bool:
returnValues = append(returnValues, []*protocol.FieldValue{{BoolValue: &v}})
case float64:
returnValues = append(returnValues, []*protocol.FieldValue{{DoubleValue: &v}})
case nil:
returnValues = append(returnValues, []*protocol.FieldValue{{IsNull: proto.Bool(true)}})
}
}
// size is really "minimum size"
if len(returnValues) >= self.size {
break
}
}
return returnValues
}
func NewModeAggregator(_ *parser.SelectQuery, value *parser.Value, defaultValue *parser.Value) (Aggregator, error) {
if len(value.Elems) < 1 {
return nil, common.NewQueryError(common.WrongNumberOfArguments, "function mode() requires at least one argument")
}
// TODO: Mode can in fact take two argument, the second specifies
// the "size", but it's not clear if size is set to 2 whether to
// return at least 2 elements, or return the most common values and
// the second most common values. The difference will be apparent if
// the data set is multimodel and there are two most common
// values. In the first case, the two most common values will be
// returned, but in the second case the two most common values and
// the second most common values will be returned
if len(value.Elems) > 1 {
return nil, common.NewQueryError(common.WrongNumberOfArguments, "function mode() takes at most one arguments")
}
size := 1
if len(value.Elems) == 2 {
switch value.Elems[1].Type {
case parser.ValueInt:
var err error
size, err = strconv.Atoi(value.Elems[1].Name)
if err != nil {
return nil, common.NewQueryError(common.InvalidArgument, "Cannot parse %s into an int", value.Elems[1].Name)
}
default:
return nil, common.NewQueryError(common.InvalidArgument, "Cannot parse %s into a int", value.Elems[1].Name)
}
}
wrappedDefaultValue, err := wrapDefaultValue(defaultValue)
if err != nil {
return nil, err
}
return &ModeAggregator{
AbstractAggregator: AbstractAggregator{
value: value.Elems[0],
},
defaultValue: wrappedDefaultValue,
alias: value.Alias,
size: size,
}, nil
}
//
// Distinct Aggregator
//
type DistinctAggregatorState struct {
counts map[interface{}]struct{}
}
type DistinctAggregator struct {
AbstractAggregator
defaultValue *protocol.FieldValue
alias string
}
func (self *DistinctAggregator) AggregatePoint(state interface{}, p *protocol.Point) (interface{}, error) {
s, ok := state.(*DistinctAggregatorState)
if !ok {
s = &DistinctAggregatorState{make(map[interface{}]struct{})}
}
point, err := GetValue(self.value, self.columns, p)
if err != nil {
return nil, err
}
var value interface{}
if point.Int64Value != nil {
value = float64(*point.Int64Value)
} else if point.DoubleValue != nil {
value = *point.DoubleValue
} else if point.BoolValue != nil {
value = *point.BoolValue
} else if point.StringValue != nil {
value = *point.StringValue
} else {
value = nil
}
s.counts[value] = struct{}{}
return s, nil
}
func (self *DistinctAggregator) ColumnNames() []string {
if self.alias != "" {
return []string{self.alias}
}
return []string{"distinct"}
}
func (self *DistinctAggregator) GetValues(state interface{}) [][]*protocol.FieldValue {
returnValues := [][]*protocol.FieldValue{}
s, ok := state.(*DistinctAggregatorState)
if !ok || len(s.counts) == 0 {
returnValues = append(returnValues, []*protocol.FieldValue{self.defaultValue})
return returnValues
}
for value := range s.counts {
switch v := value.(type) {
case int:
i := int64(v)
returnValues = append(returnValues, []*protocol.FieldValue{{Int64Value: &i}})
case string:
returnValues = append(returnValues, []*protocol.FieldValue{{StringValue: &v}})
case bool:
returnValues = append(returnValues, []*protocol.FieldValue{{BoolValue: &v}})
case float64:
returnValues = append(returnValues, []*protocol.FieldValue{{DoubleValue: &v}})
}
}
return returnValues
}
func NewDistinctAggregator(_ *parser.SelectQuery, value *parser.Value, defaultValue *parser.Value) (Aggregator, error) {
wrappedDefaultValue, err := wrapDefaultValue(defaultValue)
if err != nil {
return nil, err
}
return &DistinctAggregator{
AbstractAggregator: AbstractAggregator{
value: value.Elems[0],
},
defaultValue: wrappedDefaultValue,
alias: value.Alias,
}, nil
}
//
// Max, Min and Sum Aggregators
//
type FirstOrLastAggregatorState *protocol.FieldValue
type FirstOrLastAggregator struct {
AbstractAggregator
name string
isFirst bool
defaultValue *protocol.FieldValue
}
func (self *FirstOrLastAggregator) AggregatePoint(state interface{}, p *protocol.Point) (interface{}, error) {
value, err := GetValue(self.value, self.columns, p)
if err != nil {
return nil, err
}
if state == nil || !self.isFirst {
state = FirstOrLastAggregatorState(value)
}
return state, nil
}
func (self *FirstOrLastAggregator) ColumnNames() []string {
return []string{self.name}
}
func (self *FirstOrLastAggregator) GetValues(state interface{}) [][]*protocol.FieldValue {
s, ok := state.(FirstOrLastAggregatorState)
if !ok {
return [][]*protocol.FieldValue{{self.defaultValue}}
}
return [][]*protocol.FieldValue{
{
s,
},
}
}
func NewFirstOrLastAggregator(name string, v *parser.Value, isFirst bool, defaultValue *parser.Value) (Aggregator, error) {
if len(v.Elems) != 1 {
return nil, common.NewQueryError(common.WrongNumberOfArguments, "function max() requires only one argument")
}
wrappedDefaultValue, err := wrapDefaultValue(defaultValue)
if err != nil {
return nil, err
}
if v.Alias != "" {
name = v.Alias
}
return &FirstOrLastAggregator{
AbstractAggregator: AbstractAggregator{
value: v.Elems[0],
},
name: name,
isFirst: isFirst,
defaultValue: wrappedDefaultValue,
}, nil
}
func NewFirstAggregator(_ *parser.SelectQuery, value *parser.Value, defaultValue *parser.Value) (Aggregator, error) {
return NewFirstOrLastAggregator("first", value, true, defaultValue)
}
func NewLastAggregator(_ *parser.SelectQuery, value *parser.Value, defaultValue *parser.Value) (Aggregator, error) {
return NewFirstOrLastAggregator("last", value, false, defaultValue)
}
//
// Top, Bottom aggregators
//
type ByPointColumnDesc struct {
protocol.PointsCollection
}
type ByPointColumnAsc struct {
protocol.PointsCollection
}
func (s ByPointColumnDesc) Less(i, j int) bool {
if s.PointsCollection[i] == nil || s.PointsCollection[j] == nil {
return false
}
if s.PointsCollection[i].Values[0].Int64Value != nil && s.PointsCollection[j].Values[0].Int64Value != nil {
return *s.PointsCollection[i].Values[0].Int64Value > *s.PointsCollection[j].Values[0].Int64Value
} else if s.PointsCollection[i].Values[0].DoubleValue != nil && s.PointsCollection[j].Values[0].DoubleValue != nil {
return *s.PointsCollection[i].Values[0].DoubleValue > *s.PointsCollection[j].Values[0].DoubleValue
} else if s.PointsCollection[i].Values[0].StringValue != nil && s.PointsCollection[j].Values[0].StringValue != nil {
return *s.PointsCollection[i].Values[0].StringValue > *s.PointsCollection[j].Values[0].StringValue
}
return false
}
func (s ByPointColumnAsc) Less(i, j int) bool {
if s.PointsCollection[i] == nil || s.PointsCollection[j] == nil {
return false
}
return comparePointValue(s.PointsCollection[i], s.PointsCollection[j])
}
type TopOrBottomAggregatorState struct {
values protocol.PointsCollection
counter int64
}
type TopOrBottomAggregator struct {
AbstractAggregator
name string
isTop bool
defaultValue *protocol.FieldValue
alias string
limit int64
target string
}
func comparePointValue(a, b *protocol.Point) bool {
if a.Values[0].Int64Value != nil && b.Values[0].Int64Value != nil {
return *a.Values[0].Int64Value < *b.Values[0].Int64Value
} else if a.Values[0].DoubleValue != nil && b.Values[0].DoubleValue != nil {
return *a.Values[0].DoubleValue < *b.Values[0].DoubleValue
} else if a.Values[0].StringValue != nil && b.Values[0].StringValue != nil {
return *a.Values[0].StringValue < *b.Values[0].StringValue
}
return false
}
func (self *TopOrBottomAggregator) comparePoint(a, b *protocol.Point, greater bool) bool {
result := comparePointValue(a, b)
if !greater {
return !result
} else {
return result
}
}
func (self *TopOrBottomAggregator) AggregatePoint(state interface{}, p *protocol.Point) (interface{}, error) {
var s *TopOrBottomAggregatorState
fieldValue, err := GetValue(self.value, self.columns, p)
if err != nil {
return nil, err
}
if state == nil {
s = &TopOrBottomAggregatorState{values: protocol.PointsCollection{}}
} else {
s = state.(*TopOrBottomAggregatorState)
}
sorter := func(values protocol.PointsCollection, isTop bool) {
if isTop {
sort.Sort(ByPointColumnDesc{values})
} else {
sort.Sort(ByPointColumnAsc{values})
}
}
asFieldValue := func(p *protocol.Point) *protocol.FieldValue {
pp := &protocol.FieldValue{}
if fieldValue.Int64Value != nil {
pp.Int64Value = fieldValue.Int64Value
} else if fieldValue.DoubleValue != nil {
pp.DoubleValue = fieldValue.DoubleValue
} else if fieldValue.StringValue != nil {
pp.StringValue = fieldValue.StringValue
}
return pp
}
if s.counter < self.limit {
newvalue := &protocol.Point{
Values: []*protocol.FieldValue{asFieldValue(p)},
}
s.values = append(s.values, newvalue)
sorter(s.values, self.isTop)
s.counter++
} else {
newvalue := &protocol.Point{
Values: []*protocol.FieldValue{asFieldValue(p)},
}
if self.comparePoint(s.values[s.counter-1], newvalue, self.isTop) {
s.values = append(s.values, p)
sorter(s.values, self.isTop)
s.values = s.values[0:self.limit]
}
}
return s, nil
}
func (self *TopOrBottomAggregator) ColumnNames() []string {
if self.alias != "" {
return []string{self.alias}
} else {
return []string{self.name}
}
}
func (self *TopOrBottomAggregator) GetValues(state interface{}) [][]*protocol.FieldValue {
returnValues := [][]*protocol.FieldValue{}
if state == nil {
returnValues = append(returnValues, []*protocol.FieldValue{self.defaultValue})
} else {
s := state.(*TopOrBottomAggregatorState)
for _, values := range s.values {
returnValues = append(returnValues, []*protocol.FieldValue{values.Values[0]})
}
}
return returnValues
}
func (self *TopOrBottomAggregator) InitializeFieldsMetadata(series *protocol.Series) error {
self.columns = series.Fields
return nil
}
func NewTopOrBottomAggregator(name string, v *parser.Value, isTop bool, defaultValue *parser.Value) (Aggregator, error) {
if len(v.Elems) != 2 {
return nil, common.NewQueryError(common.WrongNumberOfArguments, fmt.Sprintf("function %s() requires at exactly 2 arguments", name))
}
if v.Elems[1].Type != parser.ValueInt {
return nil, common.NewQueryError(common.InvalidArgument, fmt.Sprintf("function %s() second parameter expect int", name))
}
wrappedDefaultValue, err := wrapDefaultValue(defaultValue)
if err != nil {
return nil, err
}
limit, err := strconv.ParseInt(v.Elems[1].Name, 10, 64)
if err != nil {
return nil, err
}
if limit < 1 {
return nil, common.NewQueryError(common.InvalidArgument, fmt.Sprintf("function %s() second parameter must be > 0", name))
}
return &TopOrBottomAggregator{
AbstractAggregator: AbstractAggregator{
value: v.Elems[0],
},
name: name,
isTop: isTop,
defaultValue: wrappedDefaultValue,
alias: v.Alias,
limit: limit}, nil
}
func NewTopAggregator(_ *parser.SelectQuery, value *parser.Value, defaultValue *parser.Value) (Aggregator, error) {
return NewTopOrBottomAggregator("top", value, true, defaultValue)
}
func NewBottomAggregator(_ *parser.SelectQuery, value *parser.Value, defaultValue *parser.Value) (Aggregator, error) {
return NewTopOrBottomAggregator("bottom", value, false, defaultValue)
}