github.com/m3db/m3@v1.5.0/src/query/functions/aggregation/base.go (about)

     1  // Copyright (c) 2018 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package aggregation
    22  
    23  import (
    24  	"fmt"
    25  
    26  	"github.com/m3db/m3/src/query/block"
    27  	"github.com/m3db/m3/src/query/executor/transform"
    28  	"github.com/m3db/m3/src/query/functions/utils"
    29  	"github.com/m3db/m3/src/query/models"
    30  	"github.com/m3db/m3/src/query/parser"
    31  )
    32  
    33  type aggregationFn func(values []float64, bucket []int) float64
    34  
    35  var aggregationFunctions = map[string]aggregationFn{
    36  	AbsentType:            absentFn,
    37  	SumType:               sumFn,
    38  	MinType:               minFn,
    39  	MaxType:               maxFn,
    40  	AverageType:           averageFn,
    41  	StandardDeviationType: stddevFn,
    42  	StandardVarianceType:  varianceFn,
    43  	CountType:             countFn,
    44  }
    45  
    46  // NodeParams contains additional parameters required for aggregation ops.
    47  type NodeParams struct {
    48  	// MatchingTags is the set of tags by which the aggregation groups
    49  	// output series.
    50  	MatchingTags [][]byte
    51  	// Without indicates if series should use only the MatchingTags or if
    52  	// MatchingTags should be excluded from grouping.
    53  	Without bool
    54  	// Parameter is the param value for the aggregation op when appropriate.
    55  	Parameter float64
    56  	// StringParameter is the string representation of the param value.
    57  	StringParameter string
    58  }
    59  
    60  // NewAggregationOp creates a new aggregation operation.
    61  func NewAggregationOp(
    62  	opType string,
    63  	params NodeParams,
    64  ) (parser.Params, error) {
    65  	if fn, ok := aggregationFunctions[opType]; ok {
    66  		return newBaseOp(params, opType, fn), nil
    67  	}
    68  
    69  	if fn, ok := makeQuantileFn(opType, params.Parameter); ok {
    70  		return newBaseOp(params, opType, fn), nil
    71  	}
    72  
    73  	return baseOp{}, fmt.Errorf("operator not supported: %s", opType)
    74  }
    75  
    76  // baseOp stores required properties for the baseOp.
    77  type baseOp struct {
    78  	params NodeParams
    79  	opType string
    80  	aggFn  aggregationFn
    81  }
    82  
    83  // OpType for the operator.
    84  func (o baseOp) OpType() string {
    85  	return o.opType
    86  }
    87  
    88  // String representation.
    89  func (o baseOp) String() string {
    90  	return fmt.Sprintf("type: %s", o.OpType())
    91  }
    92  
    93  // Node creates an execution node.
    94  func (o baseOp) Node(
    95  	controller *transform.Controller,
    96  	_ transform.Options,
    97  ) transform.OpNode {
    98  	return &baseNode{
    99  		op:         o,
   100  		controller: controller,
   101  	}
   102  }
   103  
   104  func newBaseOp(params NodeParams, opType string, aggFn aggregationFn) baseOp {
   105  	return baseOp{
   106  		params: params,
   107  		opType: opType,
   108  		aggFn:  aggFn,
   109  	}
   110  }
   111  
   112  type baseNode struct {
   113  	op         baseOp
   114  	controller *transform.Controller
   115  }
   116  
   117  func (n *baseNode) Params() parser.Params {
   118  	return n.op
   119  }
   120  
   121  // Process the block
   122  func (n *baseNode) Process(queryCtx *models.QueryContext, ID parser.NodeID, b block.Block) error {
   123  	return transform.ProcessSimpleBlock(n, n.controller, queryCtx, ID, b)
   124  }
   125  
   126  // ProcessBlock performs the aggregation on the input block, and returns the
   127  // aggregated result.
   128  func (n *baseNode) ProcessBlock(
   129  	queryCtx *models.QueryContext,
   130  	ID parser.NodeID,
   131  	b block.Block,
   132  ) (block.Block, error) {
   133  	stepIter, err := b.StepIter()
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  
   138  	params := n.op.params
   139  	meta := b.Meta()
   140  	seriesMetas := utils.FlattenMetadata(meta, stepIter.SeriesMeta())
   141  	buckets, metas := utils.GroupSeries(
   142  		params.MatchingTags,
   143  		params.Without,
   144  		[]byte(n.op.opType),
   145  		seriesMetas,
   146  	)
   147  
   148  	meta.Tags, metas = utils.DedupeMetadata(metas, meta.Tags.Opts)
   149  	builder, err := n.controller.BlockBuilder(queryCtx, meta, metas)
   150  	if err != nil {
   151  		return nil, err
   152  	}
   153  
   154  	if err := builder.AddCols(stepIter.StepCount()); err != nil {
   155  		return nil, err
   156  	}
   157  
   158  	aggregatedValues := make([]float64, len(buckets))
   159  	for index := 0; stepIter.Next(); index++ {
   160  		step := stepIter.Current()
   161  		values := step.Values()
   162  		for i, bucket := range buckets {
   163  			aggregatedValues[i] = n.op.aggFn(values, bucket)
   164  		}
   165  
   166  		if err := builder.AppendValues(index, aggregatedValues); err != nil {
   167  			return nil, err
   168  		}
   169  	}
   170  
   171  	if err = stepIter.Err(); err != nil {
   172  		return nil, err
   173  	}
   174  
   175  	return builder.Build(), nil
   176  }