github.com/go-graphite/carbonapi@v0.17.0/expr/functions/transformNull/function.go (about)

     1  package transformNull
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"math"
     7  	"strconv"
     8  
     9  	pbv3 "github.com/go-graphite/protocol/carbonapi_v3_pb"
    10  
    11  	"github.com/go-graphite/carbonapi/expr/helper"
    12  	"github.com/go-graphite/carbonapi/expr/interfaces"
    13  	"github.com/go-graphite/carbonapi/expr/tags"
    14  	"github.com/go-graphite/carbonapi/expr/types"
    15  	"github.com/go-graphite/carbonapi/pkg/parser"
    16  )
    17  
    18  type transformNull struct{}
    19  
    20  func GetOrder() interfaces.Order {
    21  	return interfaces.Any
    22  }
    23  
    24  func New(configFile string) []interfaces.FunctionMetadata {
    25  	res := make([]interfaces.FunctionMetadata, 0)
    26  	f := &transformNull{}
    27  	functions := []string{"transformNull"}
    28  	for _, n := range functions {
    29  		res = append(res, interfaces.FunctionMetadata{Name: n, F: f})
    30  	}
    31  	return res
    32  }
    33  
    34  // transformNull(seriesList, default=0)
    35  func (f *transformNull) Do(ctx context.Context, eval interfaces.Evaluator, e parser.Expr, from, until int64, values map[parser.MetricRequest][]*types.MetricData) ([]*types.MetricData, error) {
    36  	arg, err := helper.GetSeriesArg(ctx, eval, e.Arg(0), from, until, values)
    37  	if err != nil {
    38  		return nil, err
    39  	}
    40  	defv, err := e.GetFloatNamedOrPosArgDefault("default", 1, 0)
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  	defaultOnAbsent, err := e.GetBoolNamedOrPosArgDefault("defaultOnAbsent", 3, false)
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  
    49  	_, ok := e.NamedArg("default")
    50  	if !ok {
    51  		ok = e.ArgsLen() > 1
    52  	}
    53  	defvStr := strconv.FormatFloat(defv, 'g', -1, 64)
    54  
    55  	var valMap []bool
    56  	referenceSeriesExpr := e.GetNamedArg("referenceSeries")
    57  	if !referenceSeriesExpr.IsInterfaceNil() {
    58  		referenceSeries, err := helper.GetSeriesArg(ctx, eval, referenceSeriesExpr, from, until, values)
    59  		if err != nil {
    60  			return nil, err
    61  		}
    62  
    63  		if len(referenceSeries) == 0 {
    64  			return nil, fmt.Errorf("reference series is not a valid metric")
    65  		}
    66  		length := len(referenceSeries[0].Values)
    67  		if length != len(arg[0].Values) {
    68  			return nil, fmt.Errorf("length of series and reference series must be the same")
    69  		}
    70  		valMap = make([]bool, length)
    71  
    72  		for _, a := range referenceSeries {
    73  			for i, v := range a.Values {
    74  				if !math.IsNaN(v) {
    75  					valMap[i] = true
    76  				}
    77  			}
    78  		}
    79  	}
    80  
    81  	results := make([]*types.MetricData, 0, len(arg)+1)
    82  	for _, a := range arg {
    83  		var name string
    84  		if ok {
    85  			name = "transformNull(" + a.Name + "," + defvStr + ")"
    86  		} else {
    87  			name = "transformNull(" + a.Name + ")"
    88  		}
    89  
    90  		r := a.CopyLink()
    91  		r.Name = name
    92  		r.Values = make([]float64, len(a.Values))
    93  		r.Tags["transformNull"] = defvStr
    94  
    95  		for i, v := range a.Values {
    96  			if math.IsNaN(v) {
    97  				if len(valMap) == 0 {
    98  					v = defv
    99  				} else if valMap[i] {
   100  					v = defv
   101  				}
   102  			}
   103  
   104  			r.Values[i] = v
   105  		}
   106  
   107  		results = append(results, r)
   108  	}
   109  	if len(arg) == 0 && defaultOnAbsent {
   110  		values := []float64{defv, defv}
   111  		step := until - from
   112  		name := e.ToString()
   113  		tags := tags.ExtractTags(types.ExtractName(name))
   114  		tags["transformNull"] = defvStr
   115  		results = append(results, &types.MetricData{
   116  			FetchResponse: pbv3.FetchResponse{
   117  				Name:      name,
   118  				StartTime: from,
   119  				StopTime:  from + step*int64(len(values)),
   120  				StepTime:  step,
   121  				Values:    values,
   122  			},
   123  			Tags: tags,
   124  		})
   125  	}
   126  	return results, nil
   127  }
   128  
   129  // Description is auto-generated description, based on output of https://github.com/graphite-project/graphite-web
   130  func (f *transformNull) Description() map[string]types.FunctionDescription {
   131  	return map[string]types.FunctionDescription{
   132  		"transformNull": {
   133  			Description: `Takes a metric or wildcard seriesList and replaces null values with the value
   134    specified by 'default'.  The value 0 used if not specified.  The optional
   135    referenceSeries, if specified, is a metric or wildcard series list that governs
   136    which time intervals nulls should be replaced.  If specified, nulls are replaced
   137    only in intervals where a non-null is found for the same interval in any of
   138    referenceSeries.  This method compliments the drawNullAsZero function in
   139    graphical mode, but also works in text-only mode.
   140    defaultOnAbsent if specified, would produce a constant line if no metrics will be returned by backends.
   141    Example:
   142    .. code-block:: none
   143      &target=transformNull(webapp.pages.*.views,-1)
   144    This would take any page that didn't have values and supply negative 1 as a default.
   145    Any other numeric value may be used as well.
   146  `,
   147  			Function: "transformNull(seriesList, default=0, referenceSeries=None)",
   148  			Group:    "Transform",
   149  			Module:   "graphite.render.functions",
   150  			Name:     "transformNull",
   151  			Params: []types.FunctionParam{
   152  				{
   153  					Name:     "seriesList",
   154  					Required: true,
   155  					Type:     types.SeriesList,
   156  				},
   157  				{
   158  					Default: types.NewSuggestion(0),
   159  					Name:    "default",
   160  					Type:    types.Float,
   161  				},
   162  				{
   163  					Name: "referenceSeries",
   164  					Type: types.SeriesList,
   165  				},
   166  				{
   167  					Name: "defaultOnAbsent",
   168  					Type: types.Boolean,
   169  				},
   170  			},
   171  			NameChange:   true, // name changed
   172  			ValuesChange: true, // values changed
   173  		},
   174  	}
   175  }