github.com/go-graphite/carbonapi@v0.17.0/expr/functions/divideSeries/function.go (about) 1 package divideSeries 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "math" 8 9 "github.com/go-graphite/carbonapi/expr/helper" 10 "github.com/go-graphite/carbonapi/expr/interfaces" 11 "github.com/go-graphite/carbonapi/expr/types" 12 "github.com/go-graphite/carbonapi/pkg/parser" 13 ) 14 15 type divideSeries struct{} 16 17 func GetOrder() interfaces.Order { 18 return interfaces.Any 19 } 20 21 func New(configFile string) []interfaces.FunctionMetadata { 22 res := make([]interfaces.FunctionMetadata, 0) 23 f := ÷Series{} 24 functions := []string{"divideSeries"} 25 for _, n := range functions { 26 res = append(res, interfaces.FunctionMetadata{Name: n, F: f}) 27 } 28 return res 29 } 30 31 // divideSeries(dividendSeriesList, divisorSeriesList) 32 func (f *divideSeries) Do(ctx context.Context, eval interfaces.Evaluator, e parser.Expr, from, until int64, values map[parser.MetricRequest][]*types.MetricData) ([]*types.MetricData, error) { 33 if e.ArgsLen() < 1 { 34 return nil, parser.ErrMissingTimeseries 35 } 36 37 firstArg, err := helper.GetSeriesArg(ctx, eval, e.Arg(0), from, until, values) 38 if err != nil { 39 return nil, err 40 } 41 42 var useMetricNames bool 43 44 var numerators []*types.MetricData 45 var denominator *types.MetricData 46 var results []*types.MetricData 47 48 if e.ArgsLen() == 2 { 49 useMetricNames = true 50 numerators = firstArg 51 denominators, err := helper.GetSeriesArg(ctx, eval, e.Arg(1), from, until, values) 52 if err != nil { 53 return nil, err 54 } 55 if len(denominators) == 0 { 56 results := make([]*types.MetricData, 0, len(numerators)) 57 for _, numerator := range numerators { 58 r := numerator.CopyLink() 59 r.Values = make([]float64, len(numerator.Values)) 60 r.Name = fmt.Sprintf("divideSeries(%s,MISSING)", numerator.Name) 61 for i := range numerator.Values { 62 r.Values[i] = math.NaN() 63 } 64 results = append(results, r) 65 } 66 return results, nil 67 } 68 69 if len(denominators) > 1 { 70 return nil, types.ErrWildcardNotAllowed 71 } 72 73 denominator = denominators[0] 74 } else if len(firstArg) == 2 && e.ArgsLen() == 1 { 75 numerators = append(numerators, firstArg[0]) 76 denominator = firstArg[1] 77 } else { 78 return nil, errors.New("must be called with 2 series or a wildcard that matches exactly 2 series") 79 } 80 81 for _, numerator := range numerators { 82 var name string 83 if useMetricNames { 84 name = "divideSeries(" + numerator.Name + "," + denominator.Name + ")" 85 } else { 86 name = "divideSeries(" + e.RawArgs() + ")" 87 } 88 89 numerator, denominator = helper.ConsolidateSeriesByStep(numerator, denominator) 90 91 r := numerator.CopyTag(name, numerator.Tags) 92 r.Values = make([]float64, len(numerator.Values)) 93 94 for i, v := range numerator.Values { 95 // math.IsNaN(v) || math.IsNaN(denominator.Values[i]) covered by nature of math.NaN 96 if denominator.Values[i] == 0 { 97 r.Values[i] = math.NaN() 98 } else { 99 r.Values[i] = v / denominator.Values[i] 100 } 101 } 102 results = append(results, r) 103 } 104 105 return results, nil 106 107 } 108 109 // Description is auto-generated description, based on output of https://github.com/graphite-project/graphite-web 110 func (f *divideSeries) Description() map[string]types.FunctionDescription { 111 return map[string]types.FunctionDescription{ 112 "divideSeries": { 113 Description: "Takes a dividend metric and a divisor metric and draws the division result.\nA constant may *not* be passed. To divide by a constant, use the scale()\nfunction (which is essentially a multiplication operation) and use the inverse\nof the dividend. (Division by 8 = multiplication by 1/8 or 0.125)\n\nExample:\n\n.. code-block:: none\n\n &target=divideSeries(Series.dividends,Series.divisors)", 114 Function: "divideSeries(dividendSeriesList, divisorSeries)", 115 Group: "Combine", 116 Module: "graphite.render.functions", 117 Name: "divideSeries", 118 Params: []types.FunctionParam{ 119 { 120 Name: "dividendSeriesList", 121 Required: true, 122 Type: types.SeriesList, 123 }, 124 { 125 Name: "divisorSeries", 126 Required: true, 127 Type: types.SeriesList, 128 }, 129 }, 130 SeriesChange: true, // function aggregate metrics or change series items count 131 NameChange: true, // name changed 132 TagsChange: true, // name tag changed 133 ValuesChange: true, // values changed 134 }, 135 } 136 }