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 }