mirror of https://github.com/grafana/grafana.git
				
				
				
			
		
			
				
	
	
		
			204 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			204 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Go
		
	
	
	
package classic
 | 
						|
 | 
						|
import (
 | 
						|
	"math"
 | 
						|
	"sort"
 | 
						|
 | 
						|
	"github.com/grafana/grafana/pkg/expr/mathexp"
 | 
						|
)
 | 
						|
 | 
						|
type reducer string
 | 
						|
 | 
						|
func (cr reducer) ValidReduceFunc() bool {
 | 
						|
	switch cr {
 | 
						|
	case "avg", "sum", "min", "max", "count", "last", "median":
 | 
						|
		return true
 | 
						|
	case "diff", "diff_abs", "percent_diff", "percent_diff_abs", "count_non_null":
 | 
						|
		return true
 | 
						|
	}
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
//nolint:gocyclo
 | 
						|
func (cr reducer) Reduce(series mathexp.Series) mathexp.Number {
 | 
						|
	num := mathexp.NewNumber("", nil)
 | 
						|
 | 
						|
	if series.GetLabels() != nil {
 | 
						|
		num.SetLabels(series.GetLabels().Copy())
 | 
						|
	}
 | 
						|
 | 
						|
	num.SetValue(nil)
 | 
						|
 | 
						|
	if series.Len() == 0 {
 | 
						|
		return num
 | 
						|
	}
 | 
						|
 | 
						|
	value := float64(0)
 | 
						|
	allNull := true
 | 
						|
 | 
						|
	vF := series.Frame.Fields[1]
 | 
						|
	ff := mathexp.Float64Field(*vF)
 | 
						|
 | 
						|
	switch cr {
 | 
						|
	case "avg":
 | 
						|
		validPointsCount := 0
 | 
						|
		for i := 0; i < ff.Len(); i++ {
 | 
						|
			f := ff.GetValue(i)
 | 
						|
			if nilOrNaN(f) {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			value += *f
 | 
						|
			validPointsCount++
 | 
						|
			allNull = false
 | 
						|
		}
 | 
						|
		if validPointsCount > 0 {
 | 
						|
			value /= float64(validPointsCount)
 | 
						|
		}
 | 
						|
	case "sum":
 | 
						|
		for i := 0; i < ff.Len(); i++ {
 | 
						|
			f := ff.GetValue(i)
 | 
						|
			if nilOrNaN(f) {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			value += *f
 | 
						|
			allNull = false
 | 
						|
		}
 | 
						|
	case "min":
 | 
						|
		value = math.MaxFloat64
 | 
						|
		for i := 0; i < ff.Len(); i++ {
 | 
						|
			f := ff.GetValue(i)
 | 
						|
			if nilOrNaN(f) {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			allNull = false
 | 
						|
			if value > *f {
 | 
						|
				value = *f
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if allNull {
 | 
						|
			value = 0
 | 
						|
		}
 | 
						|
	case "max":
 | 
						|
		value = -math.MaxFloat64
 | 
						|
		for i := 0; i < ff.Len(); i++ {
 | 
						|
			f := ff.GetValue(i)
 | 
						|
			if nilOrNaN(f) {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			allNull = false
 | 
						|
			if value < *f {
 | 
						|
				value = *f
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if allNull {
 | 
						|
			value = 0
 | 
						|
		}
 | 
						|
	case "count":
 | 
						|
		value = float64(ff.Len())
 | 
						|
		allNull = false
 | 
						|
	case "last":
 | 
						|
		for i := ff.Len() - 1; i >= 0; i-- {
 | 
						|
			f := ff.GetValue(i)
 | 
						|
			if !nilOrNaN(f) {
 | 
						|
				value = *f
 | 
						|
				allNull = false
 | 
						|
				break
 | 
						|
			}
 | 
						|
		}
 | 
						|
	case "median":
 | 
						|
		var values []float64
 | 
						|
		for i := 0; i < ff.Len(); i++ {
 | 
						|
			f := ff.GetValue(i)
 | 
						|
			if nilOrNaN(f) {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			allNull = false
 | 
						|
			values = append(values, *f)
 | 
						|
		}
 | 
						|
		if len(values) >= 1 {
 | 
						|
			sort.Float64s(values)
 | 
						|
			length := len(values)
 | 
						|
			if length%2 == 1 {
 | 
						|
				value = values[(length-1)/2]
 | 
						|
			} else {
 | 
						|
				value = (values[(length/2)-1] + values[length/2]) / 2
 | 
						|
			}
 | 
						|
		}
 | 
						|
	case "diff":
 | 
						|
		allNull, value = calculateDiff(ff, allNull, value, diff)
 | 
						|
	case "diff_abs":
 | 
						|
		allNull, value = calculateDiff(ff, allNull, value, diffAbs)
 | 
						|
	case "percent_diff":
 | 
						|
		allNull, value = calculateDiff(ff, allNull, value, percentDiff)
 | 
						|
	case "percent_diff_abs":
 | 
						|
		allNull, value = calculateDiff(ff, allNull, value, percentDiffAbs)
 | 
						|
	case "count_non_null":
 | 
						|
		for i := 0; i < ff.Len(); i++ {
 | 
						|
			f := ff.GetValue(i)
 | 
						|
			if nilOrNaN(f) {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			value++
 | 
						|
		}
 | 
						|
 | 
						|
		if value > 0 {
 | 
						|
			allNull = false
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if allNull {
 | 
						|
		return num
 | 
						|
	}
 | 
						|
 | 
						|
	num.SetValue(&value)
 | 
						|
	return num
 | 
						|
}
 | 
						|
 | 
						|
func calculateDiff(ff mathexp.Float64Field, allNull bool, value float64, fn func(float64, float64) float64) (bool, float64) {
 | 
						|
	var (
 | 
						|
		first float64
 | 
						|
		i     int
 | 
						|
	)
 | 
						|
	// get the newest point
 | 
						|
	for i = ff.Len() - 1; i >= 0; i-- {
 | 
						|
		f := ff.GetValue(i)
 | 
						|
		if !nilOrNaN(f) {
 | 
						|
			first = *f
 | 
						|
			allNull = false
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if i >= 1 {
 | 
						|
		// get the oldest point
 | 
						|
		for i := 0; i < ff.Len(); i++ {
 | 
						|
			f := ff.GetValue(i)
 | 
						|
			if !nilOrNaN(f) {
 | 
						|
				value = fn(first, *f)
 | 
						|
				allNull = false
 | 
						|
				break
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return allNull, value
 | 
						|
}
 | 
						|
 | 
						|
func nilOrNaN(f *float64) bool {
 | 
						|
	return f == nil || math.IsNaN(*f)
 | 
						|
}
 | 
						|
 | 
						|
var diff = func(newest, oldest float64) float64 {
 | 
						|
	return newest - oldest
 | 
						|
}
 | 
						|
 | 
						|
var diffAbs = func(newest, oldest float64) float64 {
 | 
						|
	return math.Abs(newest - oldest)
 | 
						|
}
 | 
						|
 | 
						|
var percentDiff = func(newest, oldest float64) float64 {
 | 
						|
	return (newest - oldest) / math.Abs(oldest) * 100
 | 
						|
}
 | 
						|
 | 
						|
var percentDiffAbs = func(newest, oldest float64) float64 {
 | 
						|
	return math.Abs((newest - oldest) / oldest * 100)
 | 
						|
}
 |