github.com/go-graphite/carbonapi@v0.17.0/expr/functions/substr/function.go (about) 1 package substr 2 3 import ( 4 "context" 5 "errors" 6 "strings" 7 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 substr 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 := &substr{} 23 functions := []string{"substr"} 24 for _, n := range functions { 25 res = append(res, interfaces.FunctionMetadata{Name: n, F: f}) 26 } 27 return res 28 } 29 30 // aliasSub(seriesList, start, stop) 31 func (f *substr) Do(ctx context.Context, eval interfaces.Evaluator, e parser.Expr, from, until int64, values map[parser.MetricRequest][]*types.MetricData) ([]*types.MetricData, error) { 32 // BUG: affected by the same positional arg issue as 'threshold'. 33 args, err := helper.GetSeriesArg(ctx, eval, e.Arg(0), from, until, values) 34 if err != nil { 35 return nil, err 36 } 37 38 startField, err := e.GetIntNamedOrPosArgDefault("start", 1, 0) 39 if err != nil { 40 return nil, err 41 } 42 43 stopField, err := e.GetIntNamedOrPosArgDefault("stop", 2, 0) 44 if err != nil { 45 return nil, err 46 } 47 48 results := make([]*types.MetricData, len(args)) 49 50 for n, a := range args { 51 metric := a.Tags["name"] 52 nodes := strings.Split(metric, ".") 53 realStartField := startField 54 if startField != 0 { 55 if startField < 0 { 56 realStartField = len(nodes) + startField 57 if realStartField < 0 { 58 realStartField = 0 59 } 60 } 61 if realStartField > len(nodes)-1 { 62 return nil, errors.New("start out of range") 63 } 64 nodes = nodes[realStartField:] 65 } 66 if stopField != 0 && stopField < len(nodes)+realStartField { 67 realStopField := stopField 68 if stopField < 0 { 69 realStopField = len(nodes) + stopField 70 } else { 71 realStopField = realStopField - realStartField 72 } 73 if realStopField < 0 { 74 return nil, errors.New("stop out of range") 75 } 76 nodes = nodes[:realStopField] 77 } 78 79 r := a.CopyLinkTags() 80 r.Name = strings.Join(nodes, ".") 81 results[n] = r 82 } 83 84 return results, nil 85 } 86 87 // Description is auto-generated description, based on output of https://github.com/graphite-project/graphite-web 88 func (f *substr) Description() map[string]types.FunctionDescription { 89 return map[string]types.FunctionDescription{ 90 "substr": { 91 Description: "Takes one metric or a wildcard seriesList followed by 1 or 2 integers. Assume that the\nmetric name is a list or array, with each element separated by dots. Prints\nn - length elements of the array (if only one integer n is passed) or n - m\nelements of the array (if two integers n and m are passed). The list starts\nwith element 0 and ends with element (length - 1).\n\nExample:\n\n.. code-block:: none\n\n &target=substr(carbon.agents.hostname.avgUpdateTime,2,4)\n\nThe label would be printed as \"hostname.avgUpdateTime\".", 92 Function: "substr(seriesList, start=0, stop=0)", 93 Group: "Special", 94 Module: "graphite.render.functions", 95 Name: "substr", 96 Params: []types.FunctionParam{ 97 { 98 Name: "seriesList", 99 Required: true, 100 Type: types.SeriesList, 101 }, 102 { 103 Default: types.NewSuggestion(0), 104 Name: "start", 105 Type: types.Node, 106 }, 107 { 108 Default: types.NewSuggestion(0), 109 Name: "stop", 110 Type: types.Node, 111 }, 112 }, 113 }, 114 } 115 }