| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | package expr | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2022-09-22 03:14:11 +08:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-21 19:08:52 +08:00
										 |  |  | 	"github.com/grafana/grafana-plugin-sdk-go/backend/gtime" | 
					
						
							| 
									
										
										
										
											2022-05-23 22:08:14 +08:00
										 |  |  | 	"github.com/grafana/grafana-plugin-sdk-go/data" | 
					
						
							| 
									
										
										
										
											2023-04-18 20:04:51 +08:00
										 |  |  | 	"go.opentelemetry.io/otel/attribute" | 
					
						
							| 
									
										
										
										
											2022-02-02 21:50:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/expr/mathexp" | 
					
						
							| 
									
										
										
										
											2023-04-18 20:04:51 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/infra/tracing" | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Command is an interface for all expression commands.
 | 
					
						
							|  |  |  | type Command interface { | 
					
						
							|  |  |  | 	NeedsVars() []string | 
					
						
							| 
									
										
										
										
											2023-04-18 20:04:51 +08:00
										 |  |  | 	Execute(ctx context.Context, now time.Time, vars mathexp.Vars, tracer tracing.Tracer) (mathexp.Results, error) | 
					
						
							| 
									
										
										
										
											2024-03-19 22:00:03 +08:00
										 |  |  | 	Type() string | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // MathCommand is a command for a math expression such as "1 + $GA / 2"
 | 
					
						
							|  |  |  | type MathCommand struct { | 
					
						
							|  |  |  | 	RawExpression string | 
					
						
							|  |  |  | 	Expression    *mathexp.Expr | 
					
						
							| 
									
										
										
										
											2020-12-11 19:59:12 +08:00
										 |  |  | 	refID         string | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewMathCommand creates a new MathCommand. It will return an error
 | 
					
						
							|  |  |  | // if there is an error parsing expr.
 | 
					
						
							| 
									
										
										
										
											2020-12-11 19:59:12 +08:00
										 |  |  | func NewMathCommand(refID, expr string) (*MathCommand, error) { | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 	parsedExpr, err := mathexp.New(expr) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return &MathCommand{ | 
					
						
							|  |  |  | 		RawExpression: expr, | 
					
						
							|  |  |  | 		Expression:    parsedExpr, | 
					
						
							| 
									
										
										
										
											2020-12-11 19:59:12 +08:00
										 |  |  | 		refID:         refID, | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 	}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // UnmarshalMathCommand creates a MathCommand from Grafana's frontend query.
 | 
					
						
							|  |  |  | func UnmarshalMathCommand(rn *rawNode) (*MathCommand, error) { | 
					
						
							|  |  |  | 	rawExpr, ok := rn.Query["expression"] | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2022-09-22 03:14:11 +08:00
										 |  |  | 		return nil, errors.New("command is missing an expression") | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	exprString, ok := rawExpr.(string) | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2022-09-22 03:14:11 +08:00
										 |  |  | 		return nil, fmt.Errorf("math expression is expected to be a string, got %T", rawExpr) | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-11 19:59:12 +08:00
										 |  |  | 	gm, err := NewMathCommand(rn.RefID, exprString) | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2022-09-22 03:14:11 +08:00
										 |  |  | 		return nil, fmt.Errorf("invalid math command type: %w", err) | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return gm, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NeedsVars returns the variable names (refIds) that are dependencies
 | 
					
						
							|  |  |  | // to execute the command and allows the command to fulfill the Command interface.
 | 
					
						
							|  |  |  | func (gm *MathCommand) NeedsVars() []string { | 
					
						
							|  |  |  | 	return gm.Expression.VarNames | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Execute runs the command and returns the results or an error if the command
 | 
					
						
							|  |  |  | // failed to execute.
 | 
					
						
							| 
									
										
										
										
											2023-04-18 20:04:51 +08:00
										 |  |  | func (gm *MathCommand) Execute(ctx context.Context, _ time.Time, vars mathexp.Vars, tracer tracing.Tracer) (mathexp.Results, error) { | 
					
						
							|  |  |  | 	_, span := tracer.Start(ctx, "SSE.ExecuteMath") | 
					
						
							| 
									
										
										
										
											2023-10-03 20:54:20 +08:00
										 |  |  | 	span.SetAttributes(attribute.String("expression", gm.RawExpression)) | 
					
						
							| 
									
										
										
										
											2023-04-18 20:04:51 +08:00
										 |  |  | 	defer span.End() | 
					
						
							|  |  |  | 	return gm.Expression.Execute(gm.refID, vars, tracer) | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-19 22:00:03 +08:00
										 |  |  | func (gm *MathCommand) Type() string { | 
					
						
							|  |  |  | 	return TypeMath.String() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | // ReduceCommand is an expression command for reduction of a timeseries such as a min, mean, or max.
 | 
					
						
							|  |  |  | type ReduceCommand struct { | 
					
						
							| 
									
										
										
										
											2024-02-17 08:59:11 +08:00
										 |  |  | 	Reducer      mathexp.ReducerID | 
					
						
							| 
									
										
										
										
											2022-02-02 21:50:44 +08:00
										 |  |  | 	VarToReduce  string | 
					
						
							|  |  |  | 	refID        string | 
					
						
							|  |  |  | 	seriesMapper mathexp.ReduceMapper | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewReduceCommand creates a new ReduceCMD.
 | 
					
						
							| 
									
										
										
										
											2024-02-17 08:59:11 +08:00
										 |  |  | func NewReduceCommand(refID string, reducer mathexp.ReducerID, varToReduce string, mapper mathexp.ReduceMapper) (*ReduceCommand, error) { | 
					
						
							| 
									
										
										
										
											2022-02-02 21:50:44 +08:00
										 |  |  | 	_, err := mathexp.GetReduceFunc(reducer) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-02-02 21:50:44 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return &ReduceCommand{ | 
					
						
							|  |  |  | 		Reducer:      reducer, | 
					
						
							|  |  |  | 		VarToReduce:  varToReduce, | 
					
						
							|  |  |  | 		refID:        refID, | 
					
						
							|  |  |  | 		seriesMapper: mapper, | 
					
						
							|  |  |  | 	}, nil | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // UnmarshalReduceCommand creates a MathCMD from Grafana's frontend query.
 | 
					
						
							|  |  |  | func UnmarshalReduceCommand(rn *rawNode) (*ReduceCommand, error) { | 
					
						
							|  |  |  | 	rawVar, ok := rn.Query["expression"] | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2022-09-22 03:14:11 +08:00
										 |  |  | 		return nil, errors.New("no expression ID is specified to reduce. Must be a reference to an existing query or expression") | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	varToReduce, ok := rawVar.(string) | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2022-09-22 03:14:11 +08:00
										 |  |  | 		return nil, fmt.Errorf("expression ID is expected to be a string, got %T", rawVar) | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	varToReduce = strings.TrimPrefix(varToReduce, "$") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rawReducer, ok := rn.Query["reducer"] | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2022-09-22 03:14:11 +08:00
										 |  |  | 		return nil, errors.New("no reducer specified") | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-02-17 08:59:11 +08:00
										 |  |  | 	redString, ok := rawReducer.(string) | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2022-09-22 03:14:11 +08:00
										 |  |  | 		return nil, fmt.Errorf("expected reducer to be a string, got %T", rawReducer) | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-02-17 08:59:11 +08:00
										 |  |  | 	redFunc := mathexp.ReducerID(strings.ToLower(redString)) | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-02 21:50:44 +08:00
										 |  |  | 	var mapper mathexp.ReduceMapper = nil | 
					
						
							|  |  |  | 	settings, ok := rn.Query["settings"] | 
					
						
							|  |  |  | 	if ok { | 
					
						
							|  |  |  | 		switch s := settings.(type) { | 
					
						
							| 
									
										
										
										
											2023-08-30 23:46:47 +08:00
										 |  |  | 		case map[string]any: | 
					
						
							| 
									
										
										
										
											2022-02-02 21:50:44 +08:00
										 |  |  | 			mode, ok := s["mode"] | 
					
						
							|  |  |  | 			if ok && mode != "" { | 
					
						
							|  |  |  | 				switch mode { | 
					
						
							|  |  |  | 				case "dropNN": | 
					
						
							|  |  |  | 					mapper = mathexp.DropNonNumber{} | 
					
						
							|  |  |  | 				case "replaceNN": | 
					
						
							|  |  |  | 					valueStr, ok := s["replaceWithValue"] | 
					
						
							|  |  |  | 					if !ok { | 
					
						
							| 
									
										
										
										
											2022-09-22 03:14:11 +08:00
										 |  |  | 						return nil, errors.New("setting replaceWithValue must be specified when mode is 'replaceNN'") | 
					
						
							| 
									
										
										
										
											2022-02-02 21:50:44 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 					switch value := valueStr.(type) { | 
					
						
							|  |  |  | 					case float64: | 
					
						
							|  |  |  | 						mapper = mathexp.ReplaceNonNumberWithValue{Value: value} | 
					
						
							|  |  |  | 					default: | 
					
						
							| 
									
										
										
										
											2022-09-22 03:14:11 +08:00
										 |  |  | 						return nil, fmt.Errorf("setting replaceWithValue must be a number, got %T", value) | 
					
						
							| 
									
										
										
										
											2022-02-02 21:50:44 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				default: | 
					
						
							| 
									
										
										
										
											2022-09-22 03:14:11 +08:00
										 |  |  | 					return nil, fmt.Errorf("reducer mode '%s' is not supported. Supported only: [dropNN,replaceNN]", mode) | 
					
						
							| 
									
										
										
										
											2022-02-02 21:50:44 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		default: | 
					
						
							| 
									
										
										
										
											2022-09-22 03:14:11 +08:00
										 |  |  | 			return nil, fmt.Errorf("field settings must be an object, got %T for refId %v", s, rn.RefID) | 
					
						
							| 
									
										
										
										
											2022-02-02 21:50:44 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return NewReduceCommand(rn.RefID, redFunc, varToReduce, mapper) | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NeedsVars returns the variable names (refIds) that are dependencies
 | 
					
						
							|  |  |  | // to execute the command and allows the command to fulfill the Command interface.
 | 
					
						
							|  |  |  | func (gr *ReduceCommand) NeedsVars() []string { | 
					
						
							|  |  |  | 	return []string{gr.VarToReduce} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Execute runs the command and returns the results or an error if the command
 | 
					
						
							|  |  |  | // failed to execute.
 | 
					
						
							| 
									
										
										
										
											2023-04-18 20:04:51 +08:00
										 |  |  | func (gr *ReduceCommand) Execute(ctx context.Context, _ time.Time, vars mathexp.Vars, tracer tracing.Tracer) (mathexp.Results, error) { | 
					
						
							|  |  |  | 	_, span := tracer.Start(ctx, "SSE.ExecuteReduce") | 
					
						
							|  |  |  | 	defer span.End() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-17 08:59:11 +08:00
										 |  |  | 	span.SetAttributes(attribute.String("reducer", string(gr.Reducer))) | 
					
						
							| 
									
										
										
										
											2023-04-18 20:04:51 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 	newRes := mathexp.Results{} | 
					
						
							| 
									
										
										
										
											2023-09-15 23:07:17 +08:00
										 |  |  | 	for i, val := range vars[gr.VarToReduce].Values { | 
					
						
							| 
									
										
										
										
											2022-05-23 22:08:14 +08:00
										 |  |  | 		switch v := val.(type) { | 
					
						
							|  |  |  | 		case mathexp.Series: | 
					
						
							|  |  |  | 			num, err := v.Reduce(gr.refID, gr.Reducer, gr.seriesMapper) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return newRes, err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			newRes.Values = append(newRes.Values, num) | 
					
						
							|  |  |  | 		case mathexp.Number: // if incoming vars is just a number, any reduce op is just a noop, add it as it is
 | 
					
						
							| 
									
										
										
										
											2023-09-15 00:26:00 +08:00
										 |  |  | 			value := v.GetFloat64Value() | 
					
						
							|  |  |  | 			if gr.seriesMapper != nil { | 
					
						
							|  |  |  | 				value = gr.seriesMapper.MapInput(value) | 
					
						
							|  |  |  | 				if value == nil { // same logic as in mapSeries
 | 
					
						
							|  |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-05-23 22:08:14 +08:00
										 |  |  | 			copyV := mathexp.NewNumber(gr.refID, v.GetLabels()) | 
					
						
							| 
									
										
										
										
											2023-09-15 00:26:00 +08:00
										 |  |  | 			copyV.SetValue(value) | 
					
						
							| 
									
										
										
										
											2023-09-15 23:07:17 +08:00
										 |  |  | 			if gr.seriesMapper == nil && i == 0 { // Add notice to only the first result to not multiple them in presentation
 | 
					
						
							| 
									
										
										
										
											2023-09-15 00:26:00 +08:00
										 |  |  | 				copyV.AddNotice(data.Notice{ | 
					
						
							|  |  |  | 					Severity: data.NoticeSeverityWarning, | 
					
						
							|  |  |  | 					Text:     fmt.Sprintf("Reduce operation is not needed. Input query or expression %s is already reduced data.", gr.VarToReduce), | 
					
						
							|  |  |  | 				}) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-05-23 22:08:14 +08:00
										 |  |  | 			newRes.Values = append(newRes.Values, copyV) | 
					
						
							| 
									
										
										
										
											2022-09-21 17:01:51 +08:00
										 |  |  | 		case mathexp.NoData: | 
					
						
							|  |  |  | 			newRes.Values = append(newRes.Values, v.New()) | 
					
						
							| 
									
										
										
										
											2022-05-23 22:08:14 +08:00
										 |  |  | 		default: | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 			return newRes, fmt.Errorf("can only reduce type series, got type %v", val.Type()) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return newRes, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-19 22:00:03 +08:00
										 |  |  | func (gr *ReduceCommand) Type() string { | 
					
						
							|  |  |  | 	return TypeReduce.String() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | // ResampleCommand is an expression command for resampling of a timeseries.
 | 
					
						
							|  |  |  | type ResampleCommand struct { | 
					
						
							|  |  |  | 	Window        time.Duration | 
					
						
							|  |  |  | 	VarToResample string | 
					
						
							| 
									
										
										
										
											2024-03-02 01:38:32 +08:00
										 |  |  | 	Downsampler   mathexp.ReducerID | 
					
						
							|  |  |  | 	Upsampler     mathexp.Upsampler | 
					
						
							| 
									
										
										
										
											2021-04-23 22:52:32 +08:00
										 |  |  | 	TimeRange     TimeRange | 
					
						
							| 
									
										
										
										
											2020-12-11 19:59:12 +08:00
										 |  |  | 	refID         string | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewResampleCommand creates a new ResampleCMD.
 | 
					
						
							| 
									
										
										
										
											2024-03-02 01:38:32 +08:00
										 |  |  | func NewResampleCommand(refID, rawWindow, varToResample string, downsampler mathexp.ReducerID, upsampler mathexp.Upsampler, tr TimeRange) (*ResampleCommand, error) { | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 	// TODO: validate reducer here, before execution
 | 
					
						
							|  |  |  | 	window, err := gtime.ParseDuration(rawWindow) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf(`failed to parse resample "window" duration field %q: %w`, window, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return &ResampleCommand{ | 
					
						
							|  |  |  | 		Window:        window, | 
					
						
							|  |  |  | 		VarToResample: varToResample, | 
					
						
							|  |  |  | 		Downsampler:   downsampler, | 
					
						
							|  |  |  | 		Upsampler:     upsampler, | 
					
						
							|  |  |  | 		TimeRange:     tr, | 
					
						
							| 
									
										
										
										
											2020-12-11 19:59:12 +08:00
										 |  |  | 		refID:         refID, | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 	}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // UnmarshalResampleCommand creates a ResampleCMD from Grafana's frontend query.
 | 
					
						
							|  |  |  | func UnmarshalResampleCommand(rn *rawNode) (*ResampleCommand, error) { | 
					
						
							| 
									
										
										
										
											2022-10-27 04:13:58 +08:00
										 |  |  | 	if rn.TimeRange == nil { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("time range must be specified for refID %s", rn.RefID) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 	rawVar, ok := rn.Query["expression"] | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2022-09-22 03:14:11 +08:00
										 |  |  | 		return nil, errors.New("no expression ID to resample. must be a reference to an existing query or expression") | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	varToReduce, ok := rawVar.(string) | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2022-09-22 03:14:11 +08:00
										 |  |  | 		return nil, fmt.Errorf("expected resample input variable to be type string, but got type %T", rawVar) | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	varToReduce = strings.TrimPrefix(varToReduce, "$") | 
					
						
							|  |  |  | 	varToResample := varToReduce | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rawWindow, ok := rn.Query["window"] | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2022-09-22 03:14:11 +08:00
										 |  |  | 		return nil, errors.New("no time duration specified for the window in resample command") | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	window, ok := rawWindow.(string) | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2022-09-22 03:14:11 +08:00
										 |  |  | 		return nil, fmt.Errorf("resample window is expected to be a string, got %T", rawWindow) | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rawDownsampler, ok := rn.Query["downsampler"] | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2022-09-22 03:14:11 +08:00
										 |  |  | 		return nil, errors.New("no downsampler function specified in resample command") | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	downsampler, ok := rawDownsampler.(string) | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2022-09-22 03:14:11 +08:00
										 |  |  | 		return nil, fmt.Errorf("expected resample downsampler to be a string, got type %T", downsampler) | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rawUpsampler, ok := rn.Query["upsampler"] | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2022-09-22 03:14:11 +08:00
										 |  |  | 		return nil, errors.New("no upsampler specified in resample command") | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	upsampler, ok := rawUpsampler.(string) | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2022-09-22 03:14:11 +08:00
										 |  |  | 		return nil, fmt.Errorf("expected resample downsampler to be a string, got type %T", upsampler) | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-02 01:38:32 +08:00
										 |  |  | 	return NewResampleCommand(rn.RefID, window, | 
					
						
							|  |  |  | 		varToResample, | 
					
						
							|  |  |  | 		mathexp.ReducerID(downsampler), | 
					
						
							|  |  |  | 		mathexp.Upsampler(upsampler), | 
					
						
							|  |  |  | 		rn.TimeRange) | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NeedsVars returns the variable names (refIds) that are dependencies
 | 
					
						
							|  |  |  | // to execute the command and allows the command to fulfill the Command interface.
 | 
					
						
							|  |  |  | func (gr *ResampleCommand) NeedsVars() []string { | 
					
						
							|  |  |  | 	return []string{gr.VarToResample} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Execute runs the command and returns the results or an error if the command
 | 
					
						
							|  |  |  | // failed to execute.
 | 
					
						
							| 
									
										
										
										
											2023-04-18 20:04:51 +08:00
										 |  |  | func (gr *ResampleCommand) Execute(ctx context.Context, now time.Time, vars mathexp.Vars, tracer tracing.Tracer) (mathexp.Results, error) { | 
					
						
							|  |  |  | 	_, span := tracer.Start(ctx, "SSE.ExecuteResample") | 
					
						
							|  |  |  | 	defer span.End() | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 	newRes := mathexp.Results{} | 
					
						
							| 
									
										
										
										
											2022-10-27 04:13:58 +08:00
										 |  |  | 	timeRange := gr.TimeRange.AbsoluteTime(now) | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 	for _, val := range vars[gr.VarToResample].Values { | 
					
						
							| 
									
										
										
										
											2023-01-19 01:39:38 +08:00
										 |  |  | 		if val == nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-01-19 01:39:38 +08:00
										 |  |  | 		switch v := val.(type) { | 
					
						
							|  |  |  | 		case mathexp.Series: | 
					
						
							|  |  |  | 			num, err := v.Resample(gr.refID, gr.Window, gr.Downsampler, gr.Upsampler, timeRange.From, timeRange.To) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return newRes, err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			newRes.Values = append(newRes.Values, num) | 
					
						
							|  |  |  | 		case mathexp.NoData: | 
					
						
							|  |  |  | 			newRes.Values = append(newRes.Values, v.New()) | 
					
						
							|  |  |  | 			return newRes, nil | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			return newRes, fmt.Errorf("can only resample type series, got type %v", val.Type()) | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return newRes, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-19 22:00:03 +08:00
										 |  |  | func (gr *ResampleCommand) Type() string { | 
					
						
							|  |  |  | 	return TypeResample.String() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | // CommandType is the type of the expression command.
 | 
					
						
							|  |  |  | type CommandType int | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	// TypeUnknown is the CMDType for an unrecognized expression type.
 | 
					
						
							|  |  |  | 	TypeUnknown CommandType = iota | 
					
						
							|  |  |  | 	// TypeMath is the CMDType for a math expression.
 | 
					
						
							|  |  |  | 	TypeMath | 
					
						
							|  |  |  | 	// TypeReduce is the CMDType for a reduction expression.
 | 
					
						
							|  |  |  | 	TypeReduce | 
					
						
							|  |  |  | 	// TypeResample is the CMDType for a resampling expression.
 | 
					
						
							|  |  |  | 	TypeResample | 
					
						
							| 
									
										
										
										
											2021-03-03 02:51:33 +08:00
										 |  |  | 	// TypeClassicConditions is the CMDType for the classic condition operation.
 | 
					
						
							|  |  |  | 	TypeClassicConditions | 
					
						
							| 
									
										
										
										
											2022-09-26 22:05:44 +08:00
										 |  |  | 	// TypeThreshold is the CMDType for checking if a threshold has been crossed
 | 
					
						
							|  |  |  | 	TypeThreshold | 
					
						
							| 
									
										
										
										
											2024-02-28 05:16:00 +08:00
										 |  |  | 	// TypeSQL is the CMDType for running SQL expressions
 | 
					
						
							|  |  |  | 	TypeSQL | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (gt CommandType) String() string { | 
					
						
							|  |  |  | 	switch gt { | 
					
						
							|  |  |  | 	case TypeMath: | 
					
						
							|  |  |  | 		return "math" | 
					
						
							|  |  |  | 	case TypeReduce: | 
					
						
							|  |  |  | 		return "reduce" | 
					
						
							|  |  |  | 	case TypeResample: | 
					
						
							|  |  |  | 		return "resample" | 
					
						
							| 
									
										
										
										
											2021-03-03 02:51:33 +08:00
										 |  |  | 	case TypeClassicConditions: | 
					
						
							|  |  |  | 		return "classic_conditions" | 
					
						
							| 
									
										
										
										
											2024-03-19 22:00:03 +08:00
										 |  |  | 	case TypeThreshold: | 
					
						
							|  |  |  | 		return "threshold" | 
					
						
							| 
									
										
										
										
											2024-02-28 05:16:00 +08:00
										 |  |  | 	case TypeSQL: | 
					
						
							|  |  |  | 		return "sql" | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 	default: | 
					
						
							|  |  |  | 		return "unknown" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ParseCommandType returns a CommandType from its string representation.
 | 
					
						
							|  |  |  | func ParseCommandType(s string) (CommandType, error) { | 
					
						
							|  |  |  | 	switch s { | 
					
						
							|  |  |  | 	case "math": | 
					
						
							|  |  |  | 		return TypeMath, nil | 
					
						
							|  |  |  | 	case "reduce": | 
					
						
							|  |  |  | 		return TypeReduce, nil | 
					
						
							|  |  |  | 	case "resample": | 
					
						
							|  |  |  | 		return TypeResample, nil | 
					
						
							| 
									
										
										
										
											2021-03-03 02:51:33 +08:00
										 |  |  | 	case "classic_conditions": | 
					
						
							|  |  |  | 		return TypeClassicConditions, nil | 
					
						
							| 
									
										
										
										
											2022-09-26 22:05:44 +08:00
										 |  |  | 	case "threshold": | 
					
						
							|  |  |  | 		return TypeThreshold, nil | 
					
						
							| 
									
										
										
										
											2024-02-28 05:16:00 +08:00
										 |  |  | 	case "sql": | 
					
						
							|  |  |  | 		return TypeSQL, nil | 
					
						
							| 
									
										
										
										
											2020-11-19 20:17:00 +08:00
										 |  |  | 	default: | 
					
						
							|  |  |  | 		return TypeUnknown, fmt.Errorf("'%v' is not a recognized expression type", s) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |