github.com/go-graphite/carbonapi@v0.17.0/expr/functions/weightedAverage/function.go (about) 1 package weightedAverage 2 3 import ( 4 "context" 5 "sort" 6 7 "github.com/go-graphite/carbonapi/expr/consolidations" 8 "github.com/go-graphite/carbonapi/expr/helper" 9 "github.com/go-graphite/carbonapi/expr/interfaces" 10 "github.com/go-graphite/carbonapi/expr/types" 11 "github.com/go-graphite/carbonapi/pkg/parser" 12 ) 13 14 type weightedAverage struct{} 15 16 func GetOrder() interfaces.Order { 17 return interfaces.Any 18 } 19 20 func New(configFile string) []interfaces.FunctionMetadata { 21 res := make([]interfaces.FunctionMetadata, 0) 22 f := &weightedAverage{} 23 functions := []string{"weightedAverage"} 24 for _, n := range functions { 25 res = append(res, interfaces.FunctionMetadata{Name: n, F: f}) 26 } 27 return res 28 } 29 30 // weightedAverage(seriesListAvg, seriesListWeight, *nodes) 31 func (f *weightedAverage) Do(ctx context.Context, eval interfaces.Evaluator, e parser.Expr, from, until int64, values map[parser.MetricRequest][]*types.MetricData) ([]*types.MetricData, error) { 32 if e.ArgsLen() < 2 { 33 return nil, parser.ErrMissingArgument 34 } 35 36 aggKeyPairs := make(map[string]map[string]*types.MetricData) 37 var productList []*types.MetricData 38 39 avgs, err := helper.GetSeriesArg(ctx, eval, e.Arg(0), from, until, values) 40 if err != nil { 41 return nil, err 42 } 43 44 weights, err := helper.GetSeriesArg(ctx, eval, e.Arg(1), from, until, values) 45 if err != nil { 46 return nil, err 47 } 48 49 // TODO: should fail if len(avgs) != len(weights) 50 if len(avgs)+len(weights) == 0 { 51 return []*types.MetricData{}, nil 52 } 53 54 alignedMetrics := helper.AlignSeries(append(avgs, weights...)) 55 avgs = alignedMetrics[0:len(avgs)] 56 weights = alignedMetrics[len(avgs):] 57 xFilesFactor := float64(alignedMetrics[0].XFilesFactor) 58 59 nodes, err := e.GetNodeOrTagArgs(2, false) 60 if err != nil { 61 return nil, err 62 } 63 64 avgNames := make([]string, 0, len(avgs)) 65 weightNames := make([]string, 0, len(weights)) 66 67 for _, metric := range avgs { 68 key := helper.AggKey(metric, nodes) 69 if val, ok := aggKeyPairs[key]; !ok { 70 // Normally, key shouldn't exist 71 aggKeyPairs[key] = map[string]*types.MetricData{"avg": metric} 72 } else { 73 // According to graphite-web, this is overriden, so only the latest `key` is used 74 val["avg"] = metric 75 } 76 avgNames = append(avgNames, metric.Name) 77 } 78 sort.Strings(avgNames) 79 80 for _, metric := range weights { 81 key := helper.AggKey(metric, nodes) 82 if val, ok := aggKeyPairs[key]; !ok { 83 // Normally, key shouldn't exist 84 aggKeyPairs[key] = map[string]*types.MetricData{"weight": metric} 85 } else { 86 // According to graphite-web, this is overriden, so only the latest `key` is used 87 val["weight"] = metric 88 } 89 weightNames = append(weightNames, metric.Name) 90 } 91 sort.Strings(weightNames) 92 93 for _, pair := range aggKeyPairs { 94 if _, ok := pair["avg"]; !ok { 95 continue 96 } 97 if _, ok := pair["weight"]; !ok { 98 continue 99 } 100 product, err := helper.AggregateSeries(e, []*types.MetricData{pair["avg"], pair["weight"]}, consolidations.ConsolidationToFunc["multiply"], xFilesFactor, false) 101 if err != nil { 102 return nil, err 103 } 104 productList = append(productList, product...) 105 } 106 if len(productList) == 0 { 107 return []*types.MetricData{}, nil 108 } 109 110 sumProducts, err := helper.AggregateSeries(e, productList, consolidations.AggSum, xFilesFactor, false) 111 if err != nil { 112 return nil, err 113 } 114 sumWeights, err := helper.AggregateSeries(e, weights, consolidations.AggSum, xFilesFactor, false) 115 if err != nil { 116 return nil, err 117 } 118 weightedAverageSeries, err := helper.AggregateSeries(e, append(sumProducts, sumWeights...), func(v []float64) float64 { return v[0] / v[1] }, xFilesFactor, false) 119 if err != nil { 120 return nil, err 121 } 122 123 return weightedAverageSeries, nil 124 } 125 126 // Description is auto-generated description, based on output of https://github.com/graphite-project/graphite-web 127 func (f *weightedAverage) Description() map[string]types.FunctionDescription { 128 return map[string]types.FunctionDescription{ 129 "weightedAverage": { 130 Function: "weightedAverage(seriesListAvg, seriesListWeight, *nodes)", 131 Module: "graphite.render.functions", 132 Params: []types.FunctionParam{ 133 { 134 Type: types.SeriesList, 135 Name: "seriesListAvg", 136 Required: true, 137 }, 138 { 139 Type: types.SeriesList, 140 Name: "seriesListWeight", 141 Required: true, 142 }, 143 { 144 Type: types.NodeOrTag, 145 Name: "nodes", 146 Multiple: true, 147 }, 148 }, 149 Group: "Combine", 150 Description: "Takes a series of average values and a series of weights and\nproduces a weighted average for all values.\nThe corresponding values should share one or more zero-indexed nodes and/or tags.\n\nExample:\n\n.. code-block:: none\n\n &target=weightedAverage(*.transactions.mean,*.transactions.count,0)\n\nEach node may be an integer referencing a node in the series name or a string identifying a tag.", 151 Name: "weightedAverage", 152 }, 153 } 154 }