signoz/pkg/querier/postprocess_formula.go
2025-06-12 16:50:10 +05:30

126 lines
3.6 KiB
Go

package querier
import (
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
)
// applyFormulas processes formula queries in the composite query
func (q *querier) applyFormulas(results map[string]*qbtypes.Result, req *qbtypes.QueryRangeRequest) map[string]*qbtypes.Result {
// Collect formula queries
formulaQueries := make(map[string]qbtypes.QueryBuilderFormula)
for _, query := range req.CompositeQuery.Queries {
if query.Type == qbtypes.QueryTypeFormula {
if formula, ok := query.Spec.(qbtypes.QueryBuilderFormula); ok {
formulaQueries[formula.Name] = formula
}
}
}
// Process each formula
for name, formula := range formulaQueries {
// Prepare time series data for formula evaluation
timeSeriesData := make(map[string]*qbtypes.TimeSeriesData)
// Extract time series data from results
for queryName, result := range results {
if tsData, ok := result.Value.(*qbtypes.TimeSeriesData); ok {
timeSeriesData[queryName] = tsData
}
}
// Create formula evaluator
canDefaultZero := make(map[string]bool)
for _, query := range req.CompositeQuery.Queries {
switch spec := query.Spec.(type) {
case qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]:
// Metrics can default to zero for rate/increase operations
canDefaultZero[spec.Name] = true
case qbtypes.QueryBuilderQuery[qbtypes.TraceAggregation]:
canDefaultZero[spec.Name] = false
case qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]:
canDefaultZero[spec.Name] = false
}
}
evaluator, err := qbtypes.NewFormulaEvaluator(formula.Expression, canDefaultZero)
if err != nil {
q.logger.Error("failed to create formula evaluator", "error", err, "formula", name)
continue
}
// Evaluate the formula
formulaSeries, err := evaluator.EvaluateFormula(timeSeriesData)
if err != nil {
q.logger.Error("failed to evaluate formula", "error", err, "formula", name)
continue
}
// Create result for formula
formulaResult := &qbtypes.TimeSeriesData{
QueryName: name,
Aggregations: []*qbtypes.AggregationBucket{
{
Index: 0,
Series: formulaSeries,
},
},
}
// Apply functions if any
if len(formula.Functions) > 0 {
for _, agg := range formulaResult.Aggregations {
for i, series := range agg.Series {
agg.Series[i] = qbtypes.ApplyFunctions(formula.Functions, series)
}
}
}
results[name] = &qbtypes.Result{
Value: formulaResult,
}
}
return results
}
// filterDisabledQueries removes results for disabled queries
func (q *querier) filterDisabledQueries(results map[string]*qbtypes.Result, req *qbtypes.QueryRangeRequest) map[string]*qbtypes.Result {
filtered := make(map[string]*qbtypes.Result)
for _, query := range req.CompositeQuery.Queries {
var queryName string
var disabled bool
switch spec := query.Spec.(type) {
case qbtypes.QueryBuilderQuery[qbtypes.TraceAggregation]:
queryName = spec.Name
disabled = spec.Disabled
case qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]:
queryName = spec.Name
disabled = spec.Disabled
case qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]:
queryName = spec.Name
disabled = spec.Disabled
case qbtypes.QueryBuilderFormula:
queryName = spec.Name
// Formulas don't have a disabled flag, include them
disabled = false
case qbtypes.PromQuery:
queryName = spec.Name
disabled = spec.Disabled
case qbtypes.ClickHouseQuery:
queryName = spec.Name
disabled = spec.Disabled
}
if !disabled {
if result, ok := results[queryName]; ok {
filtered[queryName] = result
}
}
}
return filtered
}