mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
SSE: Refactor to simplify Series type (#35063)
Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
This commit is contained in:
parent
134dba5101
commit
4093fae99a
@ -38,7 +38,7 @@ func (cr classicReducer) Reduce(series mathexp.Series) mathexp.Number {
|
||||
value := float64(0)
|
||||
allNull := true
|
||||
|
||||
vF := series.Frame.Fields[series.ValueIdx]
|
||||
vF := series.Frame.Fields[1]
|
||||
ff := mathexp.Float64Field(*vF)
|
||||
|
||||
switch cr {
|
||||
|
@ -399,9 +399,9 @@ func TestPercentDiffAbsReducer(t *testing.T) {
|
||||
}
|
||||
|
||||
func valBasedSeries(vals ...*float64) mathexp.Series {
|
||||
newSeries := mathexp.NewSeries("", nil, 0, false, 1, true, len(vals))
|
||||
newSeries := mathexp.NewSeries("", nil, len(vals))
|
||||
for idx, f := range vals {
|
||||
err := newSeries.SetPoint(idx, unixTimePointer(int64(idx)), f)
|
||||
err := newSeries.SetPoint(idx, time.Unix(int64(idx), 0), f)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -410,9 +410,9 @@ func valBasedSeries(vals ...*float64) mathexp.Series {
|
||||
}
|
||||
|
||||
func valBasedSeriesWithLabels(l data.Labels, vals ...*float64) mathexp.Series {
|
||||
newSeries := mathexp.NewSeries("", l, 0, false, 1, true, len(vals))
|
||||
newSeries := mathexp.NewSeries("", l, len(vals))
|
||||
for idx, f := range vals {
|
||||
err := newSeries.SetPoint(idx, unixTimePointer(int64(idx)), f)
|
||||
err := newSeries.SetPoint(idx, time.Unix(int64(idx), 0), f)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -420,11 +420,6 @@ func valBasedSeriesWithLabels(l data.Labels, vals ...*float64) mathexp.Series {
|
||||
return newSeries
|
||||
}
|
||||
|
||||
func unixTimePointer(sec int64) *time.Time {
|
||||
t := time.Unix(sec, 0)
|
||||
return &t
|
||||
}
|
||||
|
||||
func valBasedNumber(f *float64) mathexp.Number {
|
||||
newNumber := mathexp.NewNumber("", nil)
|
||||
newNumber.SetValue(f)
|
||||
|
@ -129,7 +129,7 @@ func (e *State) walkUnary(node *parse.UnaryNode) (Results, error) {
|
||||
}
|
||||
|
||||
func (e *State) unarySeries(s Series, op string) (Series, error) {
|
||||
newSeries := NewSeries(e.RefID, s.GetLabels(), s.TimeIdx, s.TimeIsNullable, s.ValueIdx, s.ValueIsNullable, s.Len())
|
||||
newSeries := NewSeries(e.RefID, s.GetLabels(), s.Len())
|
||||
for i := 0; i < s.Len(); i++ {
|
||||
t, f := s.GetPoint(i)
|
||||
if f == nil {
|
||||
@ -431,7 +431,7 @@ func (e *State) biScalarNumber(labels data.Labels, op string, number Number, sca
|
||||
}
|
||||
|
||||
func (e *State) biSeriesNumber(labels data.Labels, op string, s Series, scalarVal *float64, seriesFirst bool) (Series, error) {
|
||||
newSeries := NewSeries(e.RefID, labels, s.TimeIdx, s.TimeIsNullable, s.ValueIdx, s.ValueIsNullable, s.Len())
|
||||
newSeries := NewSeries(e.RefID, labels, s.Len())
|
||||
var err error
|
||||
for i := 0; i < s.Len(); i++ {
|
||||
nF := math.NaN()
|
||||
@ -464,15 +464,10 @@ func (e *State) biSeriesSeries(labels data.Labels, op string, aSeries, bSeries S
|
||||
bPoints := make(map[string]*float64)
|
||||
for i := 0; i < bSeries.Len(); i++ {
|
||||
t, f := bSeries.GetPoint(i)
|
||||
if t != nil {
|
||||
bPoints[t.UTC().String()] = f
|
||||
}
|
||||
bPoints[t.UTC().String()] = f
|
||||
}
|
||||
|
||||
newSeries := NewSeries(
|
||||
e.RefID, labels, aSeries.TimeIdx, aSeries.TimeIsNullable || bSeries.TimeIsNullable, aSeries.ValueIdx,
|
||||
aSeries.ValueIsNullable || bSeries.ValueIsNullable, 0,
|
||||
)
|
||||
newSeries := NewSeries(e.RefID, labels, 0)
|
||||
for aIdx := 0; aIdx < aSeries.Len(); aIdx++ {
|
||||
aTime, aF := aSeries.GetPoint(aIdx)
|
||||
bF, ok := bPoints[aTime.UTC().String()]
|
||||
|
@ -3,6 +3,7 @@ package mathexp
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
@ -29,7 +30,7 @@ func TestNaN(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "unary -: Op Number(NaN) is NaN",
|
||||
expr: "! $A",
|
||||
expr: "-$A",
|
||||
vars: Vars{"A": Results{[]Value{makeNumber("", nil, NaN)}}},
|
||||
newErrIs: assert.NoError,
|
||||
execErrIs: assert.NoError,
|
||||
@ -57,10 +58,10 @@ func TestNaN(t *testing.T) {
|
||||
vars: Vars{
|
||||
"A": Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("temp", nil, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(2),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), NaN,
|
||||
makeSeries("temp", nil, tp{
|
||||
time.Unix(5, 0), float64Pointer(2),
|
||||
}, tp{
|
||||
time.Unix(10, 0), NaN,
|
||||
}),
|
||||
},
|
||||
},
|
||||
@ -69,10 +70,10 @@ func TestNaN(t *testing.T) {
|
||||
execErrIs: assert.NoError,
|
||||
results: Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(-1),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), NaN,
|
||||
makeSeries("", nil, tp{
|
||||
time.Unix(5, 0), float64Pointer(-1),
|
||||
}, tp{
|
||||
time.Unix(10, 0), NaN,
|
||||
}),
|
||||
},
|
||||
},
|
||||
@ -83,10 +84,10 @@ func TestNaN(t *testing.T) {
|
||||
vars: Vars{
|
||||
"A": Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("temp", nil, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(2),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), NaN,
|
||||
makeSeries("temp", nil, tp{
|
||||
time.Unix(5, 0), float64Pointer(2),
|
||||
}, tp{
|
||||
time.Unix(10, 0), NaN,
|
||||
}),
|
||||
},
|
||||
},
|
||||
@ -96,10 +97,10 @@ func TestNaN(t *testing.T) {
|
||||
execErrIs: assert.NoError,
|
||||
results: Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(0),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), NaN,
|
||||
makeSeries("", nil, tp{
|
||||
time.Unix(5, 0), float64Pointer(0),
|
||||
}, tp{
|
||||
time.Unix(10, 0), NaN,
|
||||
}),
|
||||
},
|
||||
},
|
||||
@ -110,10 +111,10 @@ func TestNaN(t *testing.T) {
|
||||
vars: Vars{
|
||||
"A": Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("temp", nil, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(2),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), NaN,
|
||||
makeSeries("temp", nil, tp{
|
||||
time.Unix(5, 0), float64Pointer(2),
|
||||
}, tp{
|
||||
time.Unix(10, 0), NaN,
|
||||
}),
|
||||
},
|
||||
},
|
||||
@ -123,10 +124,10 @@ func TestNaN(t *testing.T) {
|
||||
execErrIs: assert.NoError,
|
||||
results: Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(5, 0), NaN,
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), NaN,
|
||||
makeSeries("", nil, tp{
|
||||
time.Unix(5, 0), NaN,
|
||||
}, tp{
|
||||
time.Unix(10, 0), NaN,
|
||||
}),
|
||||
},
|
||||
},
|
||||
@ -197,10 +198,10 @@ func TestNullValues(t *testing.T) {
|
||||
vars: Vars{
|
||||
"A": Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(1),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), nil,
|
||||
makeSeries("", nil, tp{
|
||||
time.Unix(5, 0), float64Pointer(1),
|
||||
}, tp{
|
||||
time.Unix(10, 0), nil,
|
||||
}),
|
||||
},
|
||||
},
|
||||
@ -209,10 +210,10 @@ func TestNullValues(t *testing.T) {
|
||||
execErrIs: assert.NoError,
|
||||
results: Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(-1),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), nil,
|
||||
makeSeries("", nil, tp{
|
||||
time.Unix(5, 0), float64Pointer(-1),
|
||||
}, tp{
|
||||
time.Unix(10, 0), nil,
|
||||
}),
|
||||
},
|
||||
},
|
||||
@ -223,10 +224,10 @@ func TestNullValues(t *testing.T) {
|
||||
vars: Vars{
|
||||
"A": Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(1),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), nil,
|
||||
makeSeries("", nil, tp{
|
||||
time.Unix(5, 0), float64Pointer(1),
|
||||
}, tp{
|
||||
time.Unix(10, 0), nil,
|
||||
}),
|
||||
},
|
||||
},
|
||||
@ -235,10 +236,10 @@ func TestNullValues(t *testing.T) {
|
||||
execErrIs: assert.NoError,
|
||||
results: Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(0),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), nil,
|
||||
makeSeries("", nil, tp{
|
||||
time.Unix(5, 0), float64Pointer(0),
|
||||
}, tp{
|
||||
time.Unix(10, 0), nil,
|
||||
}),
|
||||
},
|
||||
},
|
||||
@ -249,10 +250,10 @@ func TestNullValues(t *testing.T) {
|
||||
vars: Vars{
|
||||
"A": Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(1),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), nil,
|
||||
makeSeries("", nil, tp{
|
||||
time.Unix(5, 0), float64Pointer(1),
|
||||
}, tp{
|
||||
time.Unix(10, 0), nil,
|
||||
}),
|
||||
},
|
||||
},
|
||||
@ -261,10 +262,10 @@ func TestNullValues(t *testing.T) {
|
||||
execErrIs: assert.NoError,
|
||||
results: Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(0),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), nil,
|
||||
makeSeries("", nil, tp{
|
||||
time.Unix(5, 0), float64Pointer(0),
|
||||
}, tp{
|
||||
time.Unix(10, 0), nil,
|
||||
}),
|
||||
},
|
||||
},
|
||||
@ -339,10 +340,10 @@ func TestNullValues(t *testing.T) {
|
||||
},
|
||||
"B": Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(1),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), nil,
|
||||
makeSeries("", nil, tp{
|
||||
time.Unix(5, 0), float64Pointer(1),
|
||||
}, tp{
|
||||
time.Unix(10, 0), nil,
|
||||
}),
|
||||
},
|
||||
},
|
||||
@ -351,10 +352,10 @@ func TestNullValues(t *testing.T) {
|
||||
execErrIs: assert.NoError,
|
||||
results: Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(1),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), nil,
|
||||
makeSeries("", nil, tp{
|
||||
time.Unix(5, 0), float64Pointer(1),
|
||||
}, tp{
|
||||
time.Unix(10, 0), nil,
|
||||
}),
|
||||
},
|
||||
},
|
||||
@ -370,10 +371,10 @@ func TestNullValues(t *testing.T) {
|
||||
},
|
||||
"B": Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(1),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), nil,
|
||||
makeSeries("", nil, tp{
|
||||
time.Unix(5, 0), float64Pointer(1),
|
||||
}, tp{
|
||||
time.Unix(10, 0), nil,
|
||||
}),
|
||||
},
|
||||
},
|
||||
@ -382,10 +383,10 @@ func TestNullValues(t *testing.T) {
|
||||
execErrIs: assert.NoError,
|
||||
results: Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(5, 0), nil,
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), nil,
|
||||
makeSeries("", nil, tp{
|
||||
time.Unix(5, 0), nil,
|
||||
}, tp{
|
||||
time.Unix(10, 0), nil,
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
@ -21,185 +21,6 @@ func TestSeriesExpr(t *testing.T) {
|
||||
{
|
||||
name: "unary series",
|
||||
expr: "! ! $A",
|
||||
vars: aSeriesNullableTime,
|
||||
newErrIs: assert.NoError,
|
||||
execErrIs: assert.NoError,
|
||||
results: Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("", nil, nullTimeTP{ // Not sure about preservering names...
|
||||
unixTimePointer(5, 0), float64Pointer(1),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), float64Pointer(1),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "binary scalar Op series",
|
||||
expr: "98 + $A",
|
||||
vars: aSeriesNullableTime,
|
||||
newErrIs: assert.NoError,
|
||||
execErrIs: assert.NoError,
|
||||
results: Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("", nil, nullTimeTP{ // Not sure about preservering names...
|
||||
unixTimePointer(5, 0), float64Pointer(100),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), float64Pointer(99),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "binary series Op scalar",
|
||||
expr: "$A + 98",
|
||||
vars: aSeriesNullableTime,
|
||||
newErrIs: assert.NoError,
|
||||
execErrIs: assert.NoError,
|
||||
results: Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("", nil, nullTimeTP{ // Not sure about preservering names...
|
||||
unixTimePointer(5, 0), float64Pointer(100),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), float64Pointer(99),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "series Op series",
|
||||
expr: "$A + $A",
|
||||
vars: aSeriesNullableTime,
|
||||
newErrIs: assert.NoError,
|
||||
execErrIs: assert.NoError,
|
||||
results: Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("", nil, nullTimeTP{ // Not sure about preservering names...
|
||||
unixTimePointer(5, 0), float64Pointer(4),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), float64Pointer(2),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "series Op number",
|
||||
expr: "$A + $B",
|
||||
vars: aSeriesbNumber,
|
||||
newErrIs: assert.NoError,
|
||||
execErrIs: assert.NoError,
|
||||
results: Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("", data.Labels{"id": "1"}, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(9),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), float64Pointer(8),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "number Op series",
|
||||
expr: "$B + $A",
|
||||
vars: aSeriesbNumber,
|
||||
newErrIs: assert.NoError,
|
||||
execErrIs: assert.NoError,
|
||||
results: Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("", data.Labels{"id": "1"}, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(9),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), float64Pointer(8),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "series Op series with label union",
|
||||
expr: "$A * $B",
|
||||
vars: twoSeriesSets,
|
||||
newErrIs: assert.NoError,
|
||||
execErrIs: assert.NoError,
|
||||
results: Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("", data.Labels{"sensor": "a", "turbine": "1"}, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(6 * .5),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), float64Pointer(8 * .2),
|
||||
}),
|
||||
makeSeriesNullableTime("", data.Labels{"sensor": "b", "turbine": "1"}, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(10 * .5),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), float64Pointer(16 * .2),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
// Length of resulting series is A when A + B. However, only points where the time matches
|
||||
// for A and B are added to the result
|
||||
{
|
||||
name: "series Op series with sparse time join",
|
||||
expr: "$A + $B",
|
||||
vars: Vars{
|
||||
"A": Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("temp", data.Labels{}, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(1),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), float64Pointer(2),
|
||||
}),
|
||||
},
|
||||
},
|
||||
"B": Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("efficiency", data.Labels{}, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(3),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(9, 0), float64Pointer(4),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
newErrIs: assert.NoError,
|
||||
execErrIs: assert.NoError,
|
||||
results: Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("", nil, nullTimeTP{ // Not sure about preserving names...
|
||||
unixTimePointer(5, 0), float64Pointer(4),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
e, err := New(tt.expr)
|
||||
tt.newErrIs(t, err)
|
||||
if e != nil {
|
||||
res, err := e.Execute("", tt.vars)
|
||||
tt.execErrIs(t, err)
|
||||
if diff := cmp.Diff(tt.results, res, data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeriesAlternateFormsExpr(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
expr string
|
||||
vars Vars
|
||||
newErrIs assert.ErrorAssertionFunc
|
||||
execErrIs assert.ErrorAssertionFunc
|
||||
results Results
|
||||
}{
|
||||
{
|
||||
name: "unary series: non-nullable time",
|
||||
expr: "! ! $A",
|
||||
vars: aSeries,
|
||||
newErrIs: assert.NoError,
|
||||
execErrIs: assert.NoError,
|
||||
@ -214,159 +35,144 @@ func TestSeriesAlternateFormsExpr(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "unary series: non-nullable time, time second",
|
||||
expr: "! ! $A",
|
||||
vars: aSeriesTimeSecond,
|
||||
name: "binary scalar Op series",
|
||||
expr: "98 + $A",
|
||||
vars: aSeries,
|
||||
newErrIs: assert.NoError,
|
||||
execErrIs: assert.NoError,
|
||||
results: Results{
|
||||
[]Value{
|
||||
makeSeriesTimeSecond("", nil, timeSecondTP{ // Not sure about preservering names...
|
||||
float64Pointer(1), time.Unix(5, 0),
|
||||
}, timeSecondTP{
|
||||
float64Pointer(1), time.Unix(10, 0),
|
||||
makeSeries("", nil, tp{ // Not sure about preservering names...
|
||||
time.Unix(5, 0), float64Pointer(100),
|
||||
}, tp{
|
||||
time.Unix(10, 0), float64Pointer(99),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "unary series: non-nullable value",
|
||||
expr: "! ! $A",
|
||||
vars: aSeriesNoNull,
|
||||
name: "binary series Op scalar",
|
||||
expr: "$A + 98",
|
||||
vars: aSeries,
|
||||
newErrIs: assert.NoError,
|
||||
execErrIs: assert.NoError,
|
||||
results: Results{
|
||||
[]Value{
|
||||
makeNoNullSeries("", nil, noNullTP{ // Not sure about preservering names...
|
||||
time.Unix(5, 0), 1,
|
||||
}, noNullTP{
|
||||
time.Unix(10, 0), 1,
|
||||
makeSeries("", nil, tp{ // Not sure about preservering names...
|
||||
time.Unix(5, 0), float64Pointer(100),
|
||||
}, tp{
|
||||
time.Unix(10, 0), float64Pointer(99),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "series Op series: nullable and non-nullable time",
|
||||
expr: "$A + $B",
|
||||
vars: Vars{
|
||||
"A": Results{
|
||||
[]Value{
|
||||
makeSeries("temp", data.Labels{}, tp{
|
||||
time.Unix(5, 0), float64Pointer(1),
|
||||
}, tp{
|
||||
time.Unix(10, 0), float64Pointer(2),
|
||||
}),
|
||||
},
|
||||
},
|
||||
"B": Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("efficiency", data.Labels{}, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(3),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), float64Pointer(4),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
name: "series Op series",
|
||||
expr: "$A + $A",
|
||||
vars: aSeries,
|
||||
newErrIs: assert.NoError,
|
||||
execErrIs: assert.NoError,
|
||||
results: Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(4),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), float64Pointer(6),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "series Op series: nullable (time second) and non-nullable time (time first)",
|
||||
expr: "$B + $A", // takes order from first operator
|
||||
vars: Vars{
|
||||
"A": Results{
|
||||
[]Value{
|
||||
makeSeriesTimeSecond("temp", data.Labels{}, timeSecondTP{
|
||||
float64Pointer(1), time.Unix(5, 0),
|
||||
}, timeSecondTP{
|
||||
float64Pointer(2), time.Unix(10, 0),
|
||||
}),
|
||||
},
|
||||
},
|
||||
"B": Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("efficiency", data.Labels{}, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(3),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), float64Pointer(4),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
newErrIs: assert.NoError,
|
||||
execErrIs: assert.NoError,
|
||||
results: Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(4),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), float64Pointer(6),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "series Op series: nullable and non-nullable values",
|
||||
expr: "$A + $B",
|
||||
vars: Vars{
|
||||
"A": Results{
|
||||
[]Value{
|
||||
makeSeries("temp", data.Labels{}, tp{
|
||||
time.Unix(5, 0), float64Pointer(1),
|
||||
}, tp{
|
||||
time.Unix(10, 0), float64Pointer(2),
|
||||
}),
|
||||
},
|
||||
},
|
||||
"B": Results{
|
||||
[]Value{
|
||||
makeNoNullSeries("efficiency", data.Labels{}, noNullTP{
|
||||
time.Unix(5, 0), 3,
|
||||
}, noNullTP{
|
||||
time.Unix(10, 0), 4,
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
newErrIs: assert.NoError,
|
||||
execErrIs: assert.NoError,
|
||||
results: Results{
|
||||
[]Value{
|
||||
makeSeries("", nil, tp{
|
||||
makeSeries("", nil, tp{ // Not sure about preservering names...
|
||||
time.Unix(5, 0), float64Pointer(4),
|
||||
}, tp{
|
||||
time.Unix(10, 0), float64Pointer(6),
|
||||
time.Unix(10, 0), float64Pointer(2),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "binary scalar Op series: non-nullable time second",
|
||||
expr: "98 + $A",
|
||||
vars: aSeriesTimeSecond,
|
||||
name: "series Op number",
|
||||
expr: "$A + $B",
|
||||
vars: aSeriesbNumber,
|
||||
newErrIs: assert.NoError,
|
||||
execErrIs: assert.NoError,
|
||||
results: Results{
|
||||
[]Value{
|
||||
makeSeriesTimeSecond("", nil, timeSecondTP{ // Not sure about preservering names...
|
||||
float64Pointer(100), time.Unix(5, 0),
|
||||
}, timeSecondTP{
|
||||
float64Pointer(99), time.Unix(10, 0),
|
||||
makeSeries("", data.Labels{"id": "1"}, tp{
|
||||
time.Unix(5, 0), float64Pointer(9),
|
||||
}, tp{
|
||||
time.Unix(10, 0), float64Pointer(8),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "number Op series",
|
||||
expr: "$B + $A",
|
||||
vars: aSeriesbNumber,
|
||||
newErrIs: assert.NoError,
|
||||
execErrIs: assert.NoError,
|
||||
results: Results{
|
||||
[]Value{
|
||||
makeSeries("", data.Labels{"id": "1"}, tp{
|
||||
time.Unix(5, 0), float64Pointer(9),
|
||||
}, tp{
|
||||
time.Unix(10, 0), float64Pointer(8),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "series Op series with label union",
|
||||
expr: "$A * $B",
|
||||
vars: twoSeriesSets,
|
||||
newErrIs: assert.NoError,
|
||||
execErrIs: assert.NoError,
|
||||
results: Results{
|
||||
[]Value{
|
||||
makeSeries("", data.Labels{"sensor": "a", "turbine": "1"}, tp{
|
||||
time.Unix(5, 0), float64Pointer(6 * .5),
|
||||
}, tp{
|
||||
time.Unix(10, 0), float64Pointer(8 * .2),
|
||||
}),
|
||||
makeSeries("", data.Labels{"sensor": "b", "turbine": "1"}, tp{
|
||||
time.Unix(5, 0), float64Pointer(10 * .5),
|
||||
}, tp{
|
||||
time.Unix(10, 0), float64Pointer(16 * .2),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
// Length of resulting series is A when A + B. However, only points where the time matches
|
||||
// for A and B are added to the result
|
||||
{
|
||||
name: "series Op series with sparse time join",
|
||||
expr: "$A + $B",
|
||||
vars: Vars{
|
||||
"A": Results{
|
||||
[]Value{
|
||||
makeSeries("temp", data.Labels{}, tp{
|
||||
time.Unix(5, 0), float64Pointer(1),
|
||||
}, tp{
|
||||
time.Unix(10, 0), float64Pointer(2),
|
||||
}),
|
||||
},
|
||||
},
|
||||
"B": Results{
|
||||
[]Value{
|
||||
makeSeries("efficiency", data.Labels{}, tp{
|
||||
time.Unix(5, 0), float64Pointer(3),
|
||||
}, tp{
|
||||
time.Unix(9, 0), float64Pointer(4),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
newErrIs: assert.NoError,
|
||||
execErrIs: assert.NoError,
|
||||
results: Results{
|
||||
[]Value{
|
||||
makeSeries("", nil, tp{ // Not sure about preserving names...
|
||||
time.Unix(5, 0), float64Pointer(4),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
e, err := New(tt.expr)
|
||||
|
@ -8,60 +8,15 @@ import (
|
||||
)
|
||||
|
||||
// Common Test Constructor Utils and Types
|
||||
type nullTimeTP struct {
|
||||
t *time.Time
|
||||
f *float64
|
||||
}
|
||||
|
||||
type tp struct {
|
||||
t time.Time
|
||||
f *float64
|
||||
}
|
||||
|
||||
type timeSecondTP struct {
|
||||
f *float64
|
||||
t time.Time
|
||||
}
|
||||
|
||||
type noNullTP struct {
|
||||
t time.Time
|
||||
f float64
|
||||
}
|
||||
|
||||
func makeSeriesNullableTime(name string, labels data.Labels, points ...nullTimeTP) Series {
|
||||
newSeries := NewSeries(name, labels, 0, true, 1, true, len(points))
|
||||
for idx, p := range points {
|
||||
_ = newSeries.SetPoint(idx, p.t, p.f)
|
||||
}
|
||||
return newSeries
|
||||
}
|
||||
|
||||
func makeSeries(name string, labels data.Labels, points ...tp) Series {
|
||||
newSeries := NewSeries(name, labels, 0, false, 1, true, len(points))
|
||||
newSeries := NewSeries(name, labels, len(points))
|
||||
for idx, p := range points {
|
||||
err := newSeries.SetPoint(idx, &p.t, p.f)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
return newSeries
|
||||
}
|
||||
|
||||
func makeNoNullSeries(name string, labels data.Labels, points ...noNullTP) Series {
|
||||
newSeries := NewSeries(name, labels, 0, false, 1, false, len(points))
|
||||
for idx, p := range points {
|
||||
err := newSeries.SetPoint(idx, &p.t, &p.f)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
return newSeries
|
||||
}
|
||||
|
||||
func makeSeriesTimeSecond(name string, labels data.Labels, points ...timeSecondTP) Series {
|
||||
newSeries := NewSeries(name, labels, 1, false, 0, true, len(points))
|
||||
for idx, p := range points {
|
||||
err := newSeries.SetPoint(idx, &p.t, p.f)
|
||||
err := newSeries.SetPoint(idx, p.t, p.f)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -75,27 +30,10 @@ func makeNumber(name string, labels data.Labels, f *float64) Number {
|
||||
return newNumber
|
||||
}
|
||||
|
||||
func unixTimePointer(sec, nsec int64) *time.Time {
|
||||
t := time.Unix(sec, nsec)
|
||||
return &t
|
||||
}
|
||||
|
||||
func float64Pointer(f float64) *float64 {
|
||||
return &f
|
||||
}
|
||||
|
||||
var aSeriesNullableTime = Vars{
|
||||
"A": Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("temp", nil, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(2),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), float64Pointer(1),
|
||||
}),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var aSeries = Vars{
|
||||
"A": Results{
|
||||
[]Value{
|
||||
@ -108,37 +46,13 @@ var aSeries = Vars{
|
||||
},
|
||||
}
|
||||
|
||||
var aSeriesTimeSecond = Vars{
|
||||
"A": Results{
|
||||
[]Value{
|
||||
makeSeriesTimeSecond("temp", nil, timeSecondTP{
|
||||
float64Pointer(2), time.Unix(5, 0),
|
||||
}, timeSecondTP{
|
||||
float64Pointer(1), time.Unix(10, 0),
|
||||
}),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var aSeriesNoNull = Vars{
|
||||
"A": Results{
|
||||
[]Value{
|
||||
makeNoNullSeries("temp", nil, noNullTP{
|
||||
time.Unix(5, 0), 2,
|
||||
}, noNullTP{
|
||||
time.Unix(10, 0), 1,
|
||||
}),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var aSeriesbNumber = Vars{
|
||||
"A": Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("temp", nil, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(2),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), float64Pointer(1),
|
||||
makeSeries("temp", nil, tp{
|
||||
time.Unix(5, 0), float64Pointer(2),
|
||||
}, tp{
|
||||
time.Unix(10, 0), float64Pointer(1),
|
||||
}),
|
||||
},
|
||||
},
|
||||
@ -152,24 +66,24 @@ var aSeriesbNumber = Vars{
|
||||
var twoSeriesSets = Vars{
|
||||
"A": Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("temp", data.Labels{"sensor": "a", "turbine": "1"}, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(6),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), float64Pointer(8),
|
||||
makeSeries("temp", data.Labels{"sensor": "a", "turbine": "1"}, tp{
|
||||
time.Unix(5, 0), float64Pointer(6),
|
||||
}, tp{
|
||||
time.Unix(10, 0), float64Pointer(8),
|
||||
}),
|
||||
makeSeriesNullableTime("temp", data.Labels{"sensor": "b", "turbine": "1"}, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(10),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), float64Pointer(16),
|
||||
makeSeries("temp", data.Labels{"sensor": "b", "turbine": "1"}, tp{
|
||||
time.Unix(5, 0), float64Pointer(10),
|
||||
}, tp{
|
||||
time.Unix(10, 0), float64Pointer(16),
|
||||
}),
|
||||
},
|
||||
},
|
||||
"B": Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("efficiency", data.Labels{"turbine": "1"}, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(.5),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), float64Pointer(.2),
|
||||
makeSeries("efficiency", data.Labels{"turbine": "1"}, tp{
|
||||
time.Unix(5, 0), float64Pointer(.5),
|
||||
}, tp{
|
||||
time.Unix(10, 0), float64Pointer(.2),
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
@ -95,10 +95,7 @@ func perFloat(e *State, val Value, floatF func(x float64) float64) (Value, error
|
||||
newVal = NewScalar(e.RefID, &nF)
|
||||
case parse.TypeSeriesSet:
|
||||
resSeries := val.(Series)
|
||||
newSeries := NewSeries(
|
||||
e.RefID, resSeries.GetLabels(), resSeries.TimeIdx, resSeries.TimeIsNullable, resSeries.ValueIdx,
|
||||
resSeries.ValueIsNullable, resSeries.Len(),
|
||||
)
|
||||
newSeries := NewSeries(e.RefID, resSeries.GetLabels(), resSeries.Len())
|
||||
for i := 0; i < resSeries.Len(); i++ {
|
||||
t, f := resSeries.GetPoint(i)
|
||||
nF := math.NaN()
|
||||
|
@ -2,6 +2,7 @@ package mathexp
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@ -46,10 +47,10 @@ func TestFunc(t *testing.T) {
|
||||
vars: Vars{
|
||||
"A": Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(-2),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), float64Pointer(-1),
|
||||
makeSeries("", nil, tp{
|
||||
time.Unix(5, 0), float64Pointer(-2),
|
||||
}, tp{
|
||||
time.Unix(10, 0), float64Pointer(-1),
|
||||
}),
|
||||
},
|
||||
},
|
||||
@ -59,10 +60,10 @@ func TestFunc(t *testing.T) {
|
||||
resultIs: assert.Equal,
|
||||
results: Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(2),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), float64Pointer(1),
|
||||
makeSeries("", nil, tp{
|
||||
time.Unix(5, 0), float64Pointer(2),
|
||||
}, tp{
|
||||
time.Unix(10, 0), float64Pointer(1),
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
@ -77,7 +77,7 @@ func (s Series) Reduce(refID, rFunc string) (Number, error) {
|
||||
}
|
||||
number := NewNumber(refID, l)
|
||||
var f *float64
|
||||
fVec := s.Frame.Fields[s.ValueIdx]
|
||||
fVec := s.Frame.Fields[seriesTypeValIdx]
|
||||
floatField := Float64Field(*fVec)
|
||||
switch rFunc {
|
||||
case "sum":
|
||||
|
@ -44,7 +44,7 @@ func TestSeriesReduce(t *testing.T) {
|
||||
name: "foo reduction will error",
|
||||
red: "foo",
|
||||
varToReduce: "A",
|
||||
vars: aSeriesNullableTime,
|
||||
vars: aSeries,
|
||||
errIs: require.Error,
|
||||
resultsIs: require.Equal,
|
||||
},
|
||||
@ -52,7 +52,7 @@ func TestSeriesReduce(t *testing.T) {
|
||||
name: "sum series",
|
||||
red: "sum",
|
||||
varToReduce: "A",
|
||||
vars: aSeriesNullableTime,
|
||||
vars: aSeries,
|
||||
errIs: require.NoError,
|
||||
resultsIs: require.Equal,
|
||||
results: Results{
|
||||
@ -169,20 +169,7 @@ func TestSeriesReduce(t *testing.T) {
|
||||
name: "mean series",
|
||||
red: "mean",
|
||||
varToReduce: "A",
|
||||
vars: aSeriesNullableTime,
|
||||
errIs: require.NoError,
|
||||
resultsIs: require.Equal,
|
||||
results: Results{
|
||||
[]Value{
|
||||
makeNumber("", nil, float64Pointer(1.5)),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "mean series (non-null-value)",
|
||||
red: "mean",
|
||||
varToReduce: "A",
|
||||
vars: aSeriesNoNull,
|
||||
vars: aSeries,
|
||||
errIs: require.NoError,
|
||||
resultsIs: require.Equal,
|
||||
results: Results{
|
||||
@ -211,10 +198,10 @@ func TestSeriesReduce(t *testing.T) {
|
||||
vars: Vars{
|
||||
"A": Results{
|
||||
[]Value{
|
||||
makeSeriesNullableTime("temp", data.Labels{"host": "a"}, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(2),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), float64Pointer(1),
|
||||
makeSeries("temp", data.Labels{"host": "a"}, tp{
|
||||
time.Unix(5, 0), float64Pointer(2),
|
||||
}, tp{
|
||||
time.Unix(10, 0), float64Pointer(1),
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
@ -13,7 +13,7 @@ func (s Series) Resample(refID string, interval time.Duration, downsampler strin
|
||||
if newSeriesLength <= 0 {
|
||||
return s, fmt.Errorf("the series cannot be sampled further; the time range is shorter than the interval")
|
||||
}
|
||||
resampled := NewSeries(refID, s.GetLabels(), s.TimeIdx, true, s.ValueIdx, true, newSeriesLength+1)
|
||||
resampled := NewSeries(refID, s.GetLabels(), newSeriesLength+1)
|
||||
bookmark := 0
|
||||
var lastSeen *float64
|
||||
idx := 0
|
||||
@ -72,8 +72,7 @@ func (s Series) Resample(refID string, interval time.Duration, downsampler strin
|
||||
}
|
||||
value = tmp
|
||||
}
|
||||
tv := t // his is required otherwise all points keep the latest timestamp; anything better?
|
||||
if err := resampled.SetPoint(idx, &tv, value); err != nil {
|
||||
if err := resampled.SetPoint(idx, t, value); err != nil {
|
||||
return resampled, err
|
||||
}
|
||||
t = t.Add(interval)
|
||||
|
@ -28,10 +28,10 @@ func TestResampleSeries(t *testing.T) {
|
||||
From: time.Unix(0, 0),
|
||||
To: time.Unix(4, 0),
|
||||
},
|
||||
seriesToResample: makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(2, 0), float64Pointer(2),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(7, 0), float64Pointer(1),
|
||||
seriesToResample: makeSeries("", nil, tp{
|
||||
time.Unix(2, 0), float64Pointer(2),
|
||||
}, tp{
|
||||
time.Unix(7, 0), float64Pointer(1),
|
||||
}),
|
||||
},
|
||||
{
|
||||
@ -43,10 +43,10 @@ func TestResampleSeries(t *testing.T) {
|
||||
From: time.Unix(11, 0),
|
||||
To: time.Unix(0, 0),
|
||||
},
|
||||
seriesToResample: makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(2, 0), float64Pointer(2),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(7, 0), float64Pointer(1),
|
||||
seriesToResample: makeSeries("", nil, tp{
|
||||
time.Unix(2, 0), float64Pointer(2),
|
||||
}, tp{
|
||||
time.Unix(7, 0), float64Pointer(1),
|
||||
}),
|
||||
},
|
||||
{
|
||||
@ -58,51 +58,23 @@ func TestResampleSeries(t *testing.T) {
|
||||
From: time.Unix(0, 0),
|
||||
To: time.Unix(16, 0),
|
||||
},
|
||||
seriesToResample: makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(2, 0), float64Pointer(2),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(4, 0), float64Pointer(3),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(7, 0), float64Pointer(1),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(9, 0), float64Pointer(2),
|
||||
seriesToResample: makeSeries("", nil, tp{
|
||||
time.Unix(2, 0), float64Pointer(2),
|
||||
}, tp{
|
||||
time.Unix(4, 0), float64Pointer(3),
|
||||
}, tp{
|
||||
time.Unix(7, 0), float64Pointer(1),
|
||||
}, tp{
|
||||
time.Unix(9, 0), float64Pointer(2),
|
||||
}),
|
||||
series: makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(0, 0), nil,
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(2.5),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), float64Pointer(1.5),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(15, 0), float64Pointer(2),
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "resample series: downsampling (mean / pad) (no-nullable)",
|
||||
interval: time.Second * 5,
|
||||
downsampler: "mean",
|
||||
upsampler: "pad",
|
||||
timeRange: backend.TimeRange{
|
||||
From: time.Unix(0, 0),
|
||||
To: time.Unix(16, 0),
|
||||
},
|
||||
seriesToResample: makeNoNullSeries("", nil, noNullTP{
|
||||
time.Unix(2, 0), 2,
|
||||
}, noNullTP{
|
||||
time.Unix(4, 0), 3,
|
||||
}, noNullTP{
|
||||
time.Unix(7, 0), 1,
|
||||
}, noNullTP{
|
||||
time.Unix(9, 0), 2,
|
||||
}),
|
||||
series: makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(0, 0), nil,
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(2.5),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), float64Pointer(1.5),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(15, 0), float64Pointer(2),
|
||||
series: makeSeries("", nil, tp{
|
||||
time.Unix(0, 0), nil,
|
||||
}, tp{
|
||||
time.Unix(5, 0), float64Pointer(2.5),
|
||||
}, tp{
|
||||
time.Unix(10, 0), float64Pointer(1.5),
|
||||
}, tp{
|
||||
time.Unix(15, 0), float64Pointer(2),
|
||||
}),
|
||||
},
|
||||
{
|
||||
@ -114,23 +86,23 @@ func TestResampleSeries(t *testing.T) {
|
||||
From: time.Unix(0, 0),
|
||||
To: time.Unix(16, 0),
|
||||
},
|
||||
seriesToResample: makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(2, 0), float64Pointer(2),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(4, 0), float64Pointer(3),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(7, 0), float64Pointer(1),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(9, 0), float64Pointer(2),
|
||||
seriesToResample: makeSeries("", nil, tp{
|
||||
time.Unix(2, 0), float64Pointer(2),
|
||||
}, tp{
|
||||
time.Unix(4, 0), float64Pointer(3),
|
||||
}, tp{
|
||||
time.Unix(7, 0), float64Pointer(1),
|
||||
}, tp{
|
||||
time.Unix(9, 0), float64Pointer(2),
|
||||
}),
|
||||
series: makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(0, 0), nil,
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(3),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), float64Pointer(2),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(15, 0), nil,
|
||||
series: makeSeries("", nil, tp{
|
||||
time.Unix(0, 0), nil,
|
||||
}, tp{
|
||||
time.Unix(5, 0), float64Pointer(3),
|
||||
}, tp{
|
||||
time.Unix(10, 0), float64Pointer(2),
|
||||
}, tp{
|
||||
time.Unix(15, 0), nil,
|
||||
}),
|
||||
},
|
||||
{
|
||||
@ -142,23 +114,23 @@ func TestResampleSeries(t *testing.T) {
|
||||
From: time.Unix(0, 0),
|
||||
To: time.Unix(16, 0),
|
||||
},
|
||||
seriesToResample: makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(2, 0), float64Pointer(2),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(4, 0), float64Pointer(3),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(7, 0), float64Pointer(1),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(9, 0), float64Pointer(2),
|
||||
seriesToResample: makeSeries("", nil, tp{
|
||||
time.Unix(2, 0), float64Pointer(2),
|
||||
}, tp{
|
||||
time.Unix(4, 0), float64Pointer(3),
|
||||
}, tp{
|
||||
time.Unix(7, 0), float64Pointer(1),
|
||||
}, tp{
|
||||
time.Unix(9, 0), float64Pointer(2),
|
||||
}),
|
||||
series: makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(0, 0), nil,
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(2),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), float64Pointer(1),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(15, 0), nil,
|
||||
series: makeSeries("", nil, tp{
|
||||
time.Unix(0, 0), nil,
|
||||
}, tp{
|
||||
time.Unix(5, 0), float64Pointer(2),
|
||||
}, tp{
|
||||
time.Unix(10, 0), float64Pointer(1),
|
||||
}, tp{
|
||||
time.Unix(15, 0), nil,
|
||||
}),
|
||||
},
|
||||
{
|
||||
@ -170,23 +142,23 @@ func TestResampleSeries(t *testing.T) {
|
||||
From: time.Unix(0, 0),
|
||||
To: time.Unix(16, 0),
|
||||
},
|
||||
seriesToResample: makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(2, 0), float64Pointer(2),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(4, 0), float64Pointer(3),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(7, 0), float64Pointer(1),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(9, 0), float64Pointer(2),
|
||||
seriesToResample: makeSeries("", nil, tp{
|
||||
time.Unix(2, 0), float64Pointer(2),
|
||||
}, tp{
|
||||
time.Unix(4, 0), float64Pointer(3),
|
||||
}, tp{
|
||||
time.Unix(7, 0), float64Pointer(1),
|
||||
}, tp{
|
||||
time.Unix(9, 0), float64Pointer(2),
|
||||
}),
|
||||
series: makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(0, 0), nil,
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(5),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), float64Pointer(3),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(15, 0), nil,
|
||||
series: makeSeries("", nil, tp{
|
||||
time.Unix(0, 0), nil,
|
||||
}, tp{
|
||||
time.Unix(5, 0), float64Pointer(5),
|
||||
}, tp{
|
||||
time.Unix(10, 0), float64Pointer(3),
|
||||
}, tp{
|
||||
time.Unix(15, 0), nil,
|
||||
}),
|
||||
},
|
||||
{
|
||||
@ -198,23 +170,23 @@ func TestResampleSeries(t *testing.T) {
|
||||
From: time.Unix(0, 0),
|
||||
To: time.Unix(16, 0),
|
||||
},
|
||||
seriesToResample: makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(2, 0), float64Pointer(2),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(4, 0), float64Pointer(3),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(7, 0), float64Pointer(1),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(9, 0), float64Pointer(2),
|
||||
seriesToResample: makeSeries("", nil, tp{
|
||||
time.Unix(2, 0), float64Pointer(2),
|
||||
}, tp{
|
||||
time.Unix(4, 0), float64Pointer(3),
|
||||
}, tp{
|
||||
time.Unix(7, 0), float64Pointer(1),
|
||||
}, tp{
|
||||
time.Unix(9, 0), float64Pointer(2),
|
||||
}),
|
||||
series: makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(0, 0), nil,
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(5, 0), float64Pointer(2.5),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), float64Pointer(1.5),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(15, 0), nil,
|
||||
series: makeSeries("", nil, tp{
|
||||
time.Unix(0, 0), nil,
|
||||
}, tp{
|
||||
time.Unix(5, 0), float64Pointer(2.5),
|
||||
}, tp{
|
||||
time.Unix(10, 0), float64Pointer(1.5),
|
||||
}, tp{
|
||||
time.Unix(15, 0), nil,
|
||||
}),
|
||||
},
|
||||
{
|
||||
@ -226,23 +198,23 @@ func TestResampleSeries(t *testing.T) {
|
||||
From: time.Unix(0, 0),
|
||||
To: time.Unix(11, 0),
|
||||
},
|
||||
seriesToResample: makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(2, 0), float64Pointer(2),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(7, 0), float64Pointer(1),
|
||||
seriesToResample: makeSeries("", nil, tp{
|
||||
time.Unix(2, 0), float64Pointer(2),
|
||||
}, tp{
|
||||
time.Unix(7, 0), float64Pointer(1),
|
||||
}),
|
||||
series: makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(0, 0), nil,
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(2, 0), float64Pointer(2),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(4, 0), float64Pointer(2),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(6, 0), float64Pointer(2),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(8, 0), float64Pointer(1),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), float64Pointer(1),
|
||||
series: makeSeries("", nil, tp{
|
||||
time.Unix(0, 0), nil,
|
||||
}, tp{
|
||||
time.Unix(2, 0), float64Pointer(2),
|
||||
}, tp{
|
||||
time.Unix(4, 0), float64Pointer(2),
|
||||
}, tp{
|
||||
time.Unix(6, 0), float64Pointer(2),
|
||||
}, tp{
|
||||
time.Unix(8, 0), float64Pointer(1),
|
||||
}, tp{
|
||||
time.Unix(10, 0), float64Pointer(1),
|
||||
}),
|
||||
},
|
||||
{
|
||||
@ -254,23 +226,23 @@ func TestResampleSeries(t *testing.T) {
|
||||
From: time.Unix(0, 0),
|
||||
To: time.Unix(11, 0),
|
||||
},
|
||||
seriesToResample: makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(2, 0), float64Pointer(2),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(7, 0), float64Pointer(1),
|
||||
seriesToResample: makeSeries("", nil, tp{
|
||||
time.Unix(2, 0), float64Pointer(2),
|
||||
}, tp{
|
||||
time.Unix(7, 0), float64Pointer(1),
|
||||
}),
|
||||
series: makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(0, 0), float64Pointer(2),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(2, 0), float64Pointer(2),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(4, 0), float64Pointer(1),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(6, 0), float64Pointer(1),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(8, 0), float64Pointer(1),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(10, 0), nil,
|
||||
series: makeSeries("", nil, tp{
|
||||
time.Unix(0, 0), float64Pointer(2),
|
||||
}, tp{
|
||||
time.Unix(2, 0), float64Pointer(2),
|
||||
}, tp{
|
||||
time.Unix(4, 0), float64Pointer(1),
|
||||
}, tp{
|
||||
time.Unix(6, 0), float64Pointer(1),
|
||||
}, tp{
|
||||
time.Unix(8, 0), float64Pointer(1),
|
||||
}, tp{
|
||||
time.Unix(10, 0), nil,
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
@ -9,79 +9,108 @@ import (
|
||||
"github.com/grafana/grafana/pkg/expr/mathexp/parse"
|
||||
)
|
||||
|
||||
// Series has time.Time and ...? *float64 fields.
|
||||
// seriesTypeTimeIdx is the data frame field index for the Series type's Time column.
|
||||
const seriesTypeTimeIdx = 0
|
||||
|
||||
// seriesTypeValIdx is the data frame field index for the Series type's Value column.
|
||||
const seriesTypeValIdx = 1
|
||||
|
||||
// Series has a time.Time and a *float64 fields.
|
||||
type Series struct {
|
||||
Frame *data.Frame
|
||||
TimeIsNullable bool
|
||||
TimeIdx int
|
||||
ValueIsNullable bool
|
||||
ValueIdx int
|
||||
Frame *data.Frame
|
||||
// TODO:
|
||||
// - Multiple Value Fields
|
||||
// - Value can be different number types
|
||||
}
|
||||
|
||||
// SeriesFromFrame validates that the dataframe can be considered a Series type
|
||||
// and populate meta information on Series about the frame.
|
||||
// and mutates the frame to be in the format that additional SSE operations expect.
|
||||
func SeriesFromFrame(frame *data.Frame) (s Series, err error) {
|
||||
if len(frame.Fields) != 2 {
|
||||
return s, fmt.Errorf("frame must have exactly two fields to be a series, has %v", len(frame.Fields))
|
||||
}
|
||||
|
||||
foundTime := false
|
||||
foundValue := false
|
||||
valueIdx := -1
|
||||
timeIdx := -1
|
||||
|
||||
timeNullable := false
|
||||
valueNullable := false
|
||||
|
||||
FIELDS:
|
||||
for i, field := range frame.Fields {
|
||||
switch field.Type() {
|
||||
case data.FieldTypeTime:
|
||||
s.TimeIdx = i
|
||||
foundTime = true
|
||||
timeIdx = i
|
||||
case data.FieldTypeNullableTime:
|
||||
s.TimeIsNullable = true
|
||||
foundTime = true
|
||||
s.TimeIdx = i
|
||||
timeNullable = true
|
||||
timeIdx = i
|
||||
case data.FieldTypeFloat64:
|
||||
foundValue = true
|
||||
s.ValueIdx = i
|
||||
valueIdx = i
|
||||
case data.FieldTypeNullableFloat64:
|
||||
s.ValueIsNullable = true
|
||||
foundValue = true
|
||||
s.ValueIdx = i
|
||||
valueNullable = true
|
||||
valueIdx = i
|
||||
default:
|
||||
// Handle default case
|
||||
if valueIdx != -1 && timeIdx != -1 {
|
||||
break FIELDS
|
||||
}
|
||||
}
|
||||
}
|
||||
if !foundTime {
|
||||
|
||||
if timeIdx == -1 {
|
||||
return s, fmt.Errorf("no time column found in frame %v", frame.Name)
|
||||
}
|
||||
if !foundValue {
|
||||
if valueIdx == -1 {
|
||||
return s, fmt.Errorf("no float64 value column found in frame %v", frame.Name)
|
||||
}
|
||||
|
||||
if timeNullable { // make time not nullable if it is in the input
|
||||
timeSlice := make([]time.Time, 0, frame.Fields[timeIdx].Len())
|
||||
for rowIdx := 0; rowIdx < frame.Fields[timeIdx].Len(); rowIdx++ {
|
||||
val, ok := frame.At(0, rowIdx).(*time.Time)
|
||||
if !ok {
|
||||
return s, fmt.Errorf("unexpected time type, expected *time.Time but got %T", val)
|
||||
}
|
||||
if val == nil {
|
||||
return s, fmt.Errorf("time series with null time stamps are not supported")
|
||||
}
|
||||
timeSlice = append(timeSlice, *val)
|
||||
}
|
||||
nF := data.NewField(frame.Fields[timeIdx].Name, nil, timeSlice) // (labels are not used on time field)
|
||||
nF.Config = frame.Fields[timeIdx].Config
|
||||
frame.Fields[timeIdx] = nF
|
||||
}
|
||||
|
||||
if !valueNullable { // make value nullable if it is not in the input
|
||||
floatSlice := make([]*float64, 0, frame.Fields[valueIdx].Len())
|
||||
for rowIdx := 0; rowIdx < frame.Fields[valueIdx].Len(); rowIdx++ {
|
||||
val, ok := frame.At(valueIdx, rowIdx).(float64)
|
||||
if !ok {
|
||||
return s, fmt.Errorf("unexpected time type, expected float64 but got %T", val)
|
||||
}
|
||||
floatSlice = append(floatSlice, &val)
|
||||
}
|
||||
nF := data.NewField(frame.Fields[valueIdx].Name, frame.Fields[valueIdx].Labels, floatSlice)
|
||||
nF.Config = frame.Fields[valueIdx].Config
|
||||
frame.Fields[valueIdx] = nF
|
||||
}
|
||||
|
||||
fields := make([]*data.Field, 2)
|
||||
fields[seriesTypeTimeIdx] = frame.Fields[timeIdx]
|
||||
fields[seriesTypeValIdx] = frame.Fields[valueIdx]
|
||||
|
||||
frame.Fields = fields
|
||||
s.Frame = frame
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// NewSeries returns a dataframe of type Series.
|
||||
func NewSeries(refID string, labels data.Labels, timeIdx int, nullableTime bool, valueIdx int, nullableValue bool, size int) Series {
|
||||
func NewSeries(refID string, labels data.Labels, size int) Series {
|
||||
fields := make([]*data.Field, 2)
|
||||
|
||||
if nullableValue {
|
||||
fields[valueIdx] = data.NewField(refID, labels, make([]*float64, size))
|
||||
} else {
|
||||
fields[valueIdx] = data.NewField(refID, labels, make([]float64, size))
|
||||
}
|
||||
|
||||
if nullableTime {
|
||||
fields[timeIdx] = data.NewField("Time", nil, make([]*time.Time, size))
|
||||
} else {
|
||||
fields[timeIdx] = data.NewField("Time", nil, make([]time.Time, size))
|
||||
}
|
||||
fields[seriesTypeTimeIdx] = data.NewField("Time", nil, make([]time.Time, size))
|
||||
fields[seriesTypeValIdx] = data.NewField(refID, labels, make([]*float64, size))
|
||||
|
||||
return Series{
|
||||
Frame: data.NewFrame("", fields...),
|
||||
TimeIsNullable: nullableTime,
|
||||
TimeIdx: timeIdx,
|
||||
ValueIsNullable: nullableValue,
|
||||
ValueIdx: valueIdx,
|
||||
Frame: data.NewFrame("", fields...),
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,9 +120,9 @@ func (s Series) Type() parse.ReturnType { return parse.TypeSeriesSet }
|
||||
// Value returns the actual value allows it to fulfill the Value interface.
|
||||
func (s Series) Value() interface{} { return &s }
|
||||
|
||||
func (s Series) GetLabels() data.Labels { return s.Frame.Fields[s.ValueIdx].Labels }
|
||||
func (s Series) GetLabels() data.Labels { return s.Frame.Fields[seriesTypeValIdx].Labels }
|
||||
|
||||
func (s Series) SetLabels(ls data.Labels) { s.Frame.Fields[s.ValueIdx].Labels = ls }
|
||||
func (s Series) SetLabels(ls data.Labels) { s.Frame.Fields[seriesTypeValIdx].Labels = ls }
|
||||
|
||||
func (s Series) GetName() string { return s.Frame.Name }
|
||||
|
||||
@ -109,73 +138,37 @@ func (s Series) SetMeta(v interface{}) {
|
||||
func (s Series) AsDataFrame() *data.Frame { return s.Frame }
|
||||
|
||||
// GetPoint returns the time and value at the specified index.
|
||||
func (s Series) GetPoint(pointIdx int) (*time.Time, *float64) {
|
||||
func (s Series) GetPoint(pointIdx int) (time.Time, *float64) {
|
||||
return s.GetTime(pointIdx), s.GetValue(pointIdx)
|
||||
}
|
||||
|
||||
// SetPoint sets the time and value on the corresponding vectors at the specified index.
|
||||
func (s Series) SetPoint(pointIdx int, t *time.Time, f *float64) (err error) {
|
||||
if s.TimeIsNullable {
|
||||
s.Frame.Fields[s.TimeIdx].Set(pointIdx, t)
|
||||
} else {
|
||||
if t == nil {
|
||||
return fmt.Errorf("can not set null time value on non-nullable time field for series name %v", s.Frame.Name)
|
||||
}
|
||||
s.Frame.Fields[s.TimeIdx].Set(pointIdx, *t)
|
||||
}
|
||||
if s.ValueIsNullable {
|
||||
s.Frame.Fields[s.ValueIdx].Set(pointIdx, f)
|
||||
} else {
|
||||
if f == nil {
|
||||
return fmt.Errorf("can not set null float value on non-nullable float field for series name %v", s.Frame.Name)
|
||||
}
|
||||
s.Frame.Fields[s.ValueIdx].Set(pointIdx, *f)
|
||||
}
|
||||
func (s Series) SetPoint(pointIdx int, t time.Time, f *float64) (err error) {
|
||||
s.Frame.Fields[seriesTypeTimeIdx].Set(pointIdx, t)
|
||||
s.Frame.Fields[seriesTypeValIdx].Set(pointIdx, f)
|
||||
return
|
||||
}
|
||||
|
||||
// AppendPoint appends a point (time/value).
|
||||
func (s Series) AppendPoint(pointIdx int, t *time.Time, f *float64) (err error) {
|
||||
if s.TimeIsNullable {
|
||||
s.Frame.Fields[s.TimeIdx].Append(t)
|
||||
} else {
|
||||
if t == nil {
|
||||
return fmt.Errorf("can not append null time value on non-nullable time field for series name %v", s.Frame.Name)
|
||||
}
|
||||
s.Frame.Fields[s.TimeIdx].Append(*t)
|
||||
}
|
||||
if s.ValueIsNullable {
|
||||
s.Frame.Fields[s.ValueIdx].Append(f)
|
||||
} else {
|
||||
if f == nil {
|
||||
return fmt.Errorf("can not append null float value on non-nullable float field for series name %v", s.Frame.Name)
|
||||
}
|
||||
s.Frame.Fields[s.ValueIdx].Append(*f)
|
||||
}
|
||||
func (s Series) AppendPoint(pointIdx int, t time.Time, f *float64) (err error) {
|
||||
s.Frame.Fields[seriesTypeTimeIdx].Append(t)
|
||||
s.Frame.Fields[seriesTypeValIdx].Append(f)
|
||||
return
|
||||
}
|
||||
|
||||
// Len returns the length of the series.
|
||||
func (s Series) Len() int {
|
||||
return s.Frame.Fields[0].Len()
|
||||
return s.Frame.Fields[seriesTypeTimeIdx].Len()
|
||||
}
|
||||
|
||||
// GetTime returns the time at the specified index.
|
||||
func (s Series) GetTime(pointIdx int) *time.Time {
|
||||
if s.TimeIsNullable {
|
||||
return s.Frame.Fields[s.TimeIdx].At(pointIdx).(*time.Time)
|
||||
}
|
||||
t := s.Frame.Fields[s.TimeIdx].At(pointIdx).(time.Time)
|
||||
return &t
|
||||
func (s Series) GetTime(pointIdx int) time.Time {
|
||||
return s.Frame.Fields[seriesTypeTimeIdx].At(pointIdx).(time.Time)
|
||||
}
|
||||
|
||||
// GetValue returns the float value at the specified index.
|
||||
func (s Series) GetValue(pointIdx int) *float64 {
|
||||
if s.ValueIsNullable {
|
||||
return s.Frame.Fields[s.ValueIdx].At(pointIdx).(*float64)
|
||||
}
|
||||
f := s.Frame.Fields[s.ValueIdx].At(pointIdx).(float64)
|
||||
return &f
|
||||
return s.Frame.Fields[seriesTypeValIdx].At(pointIdx).(*float64)
|
||||
}
|
||||
|
||||
// SortByTime sorts the series by the time from oldest to newest.
|
||||
@ -205,5 +198,5 @@ func (ss SortSeriesByTime) Swap(i, j int) {
|
||||
func (ss SortSeriesByTime) Less(i, j int) bool {
|
||||
iTimeVal := Series(ss).GetTime(i)
|
||||
jTimeVal := Series(ss).GetTime(j)
|
||||
return iTimeVal.Before(*jTimeVal)
|
||||
return iTimeVal.Before(jTimeVal)
|
||||
}
|
||||
|
@ -20,39 +20,39 @@ func TestSeriesSort(t *testing.T) {
|
||||
{
|
||||
name: "unordered series should sort by time ascending",
|
||||
descending: false,
|
||||
series: makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(3, 0), float64Pointer(3),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(1, 0), float64Pointer(1),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(2, 0), float64Pointer(2),
|
||||
series: makeSeries("", nil, tp{
|
||||
time.Unix(3, 0), float64Pointer(3),
|
||||
}, tp{
|
||||
time.Unix(1, 0), float64Pointer(1),
|
||||
}, tp{
|
||||
time.Unix(2, 0), float64Pointer(2),
|
||||
}),
|
||||
sortedSeriesIs: assert.Equal,
|
||||
sortedSeries: makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(1, 0), float64Pointer(1),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(2, 0), float64Pointer(2),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(3, 0), float64Pointer(3),
|
||||
sortedSeries: makeSeries("", nil, tp{
|
||||
time.Unix(1, 0), float64Pointer(1),
|
||||
}, tp{
|
||||
time.Unix(2, 0), float64Pointer(2),
|
||||
}, tp{
|
||||
time.Unix(3, 0), float64Pointer(3),
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "unordered series should sort by time descending",
|
||||
descending: true,
|
||||
series: makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(3, 0), float64Pointer(3),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(1, 0), float64Pointer(1),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(2, 0), float64Pointer(2),
|
||||
series: makeSeries("", nil, tp{
|
||||
time.Unix(3, 0), float64Pointer(3),
|
||||
}, tp{
|
||||
time.Unix(1, 0), float64Pointer(1),
|
||||
}, tp{
|
||||
time.Unix(2, 0), float64Pointer(2),
|
||||
}),
|
||||
sortedSeriesIs: assert.Equal,
|
||||
sortedSeries: makeSeriesNullableTime("", nil, nullTimeTP{
|
||||
unixTimePointer(3, 0), float64Pointer(3),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(2, 0), float64Pointer(2),
|
||||
}, nullTimeTP{
|
||||
unixTimePointer(1, 0), float64Pointer(1),
|
||||
sortedSeries: makeSeries("", nil, tp{
|
||||
time.Unix(3, 0), float64Pointer(3),
|
||||
}, tp{
|
||||
time.Unix(2, 0), float64Pointer(2),
|
||||
}, tp{
|
||||
time.Unix(1, 0), float64Pointer(1),
|
||||
}),
|
||||
},
|
||||
}
|
||||
@ -89,22 +89,18 @@ func TestSeriesFromFrame(t *testing.T) {
|
||||
Name: "test",
|
||||
Fields: []*data.Field{
|
||||
data.NewField("time", nil, []time.Time{}),
|
||||
data.NewField("value", nil, []float64{}),
|
||||
data.NewField("value", nil, []*float64{}),
|
||||
},
|
||||
},
|
||||
TimeIdx: 0,
|
||||
TimeIsNullable: false,
|
||||
ValueIdx: 1,
|
||||
ValueIsNullable: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "[]*float, []*time frame should convert",
|
||||
name: "[]time, []*float frame should convert",
|
||||
frame: &data.Frame{
|
||||
Name: "test",
|
||||
Fields: []*data.Field{
|
||||
data.NewField("time", nil, []time.Time{time.Unix(5, 0)}),
|
||||
data.NewField("value", nil, []*float64{float64Pointer(5)}),
|
||||
data.NewField("time", nil, []*time.Time{unixTimePointer(5, 0)}),
|
||||
},
|
||||
},
|
||||
errIs: assert.NoError,
|
||||
@ -113,14 +109,31 @@ func TestSeriesFromFrame(t *testing.T) {
|
||||
Frame: &data.Frame{
|
||||
Name: "test",
|
||||
Fields: []*data.Field{
|
||||
data.NewField("time", nil, []time.Time{time.Unix(5, 0)}),
|
||||
data.NewField("value", nil, []*float64{float64Pointer(5)}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "[]*float, []time frame should convert",
|
||||
frame: &data.Frame{
|
||||
Name: "test",
|
||||
Fields: []*data.Field{
|
||||
data.NewField("value", nil, []*float64{float64Pointer(5)}),
|
||||
data.NewField("time", nil, []time.Time{time.Unix(5, 0)}),
|
||||
},
|
||||
},
|
||||
errIs: assert.NoError,
|
||||
Is: assert.Equal,
|
||||
Series: Series{
|
||||
Frame: &data.Frame{
|
||||
Name: "test",
|
||||
Fields: []*data.Field{
|
||||
data.NewField("time", nil, []time.Time{time.Unix(5, 0)}),
|
||||
data.NewField("value", nil, []*float64{float64Pointer(5)}),
|
||||
data.NewField("time", nil, []*time.Time{unixTimePointer(5, 0)}),
|
||||
},
|
||||
},
|
||||
TimeIdx: 1,
|
||||
TimeIsNullable: true,
|
||||
ValueIdx: 0,
|
||||
ValueIsNullable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -19,20 +19,20 @@ func Test_union(t *testing.T) {
|
||||
name: "equal tags single union",
|
||||
aResults: Results{
|
||||
Values: Values{
|
||||
makeSeriesNullableTime("a", data.Labels{"id": "1"}),
|
||||
makeSeries("a", data.Labels{"id": "1"}),
|
||||
},
|
||||
},
|
||||
bResults: Results{
|
||||
Values: Values{
|
||||
makeSeriesNullableTime("b", data.Labels{"id": "1"}),
|
||||
makeSeries("b", data.Labels{"id": "1"}),
|
||||
},
|
||||
},
|
||||
unionsAre: assert.EqualValues,
|
||||
unions: []*Union{
|
||||
{
|
||||
Labels: data.Labels{"id": "1"},
|
||||
A: makeSeriesNullableTime("a", data.Labels{"id": "1"}),
|
||||
B: makeSeriesNullableTime("b", data.Labels{"id": "1"}),
|
||||
A: makeSeries("a", data.Labels{"id": "1"}),
|
||||
B: makeSeries("b", data.Labels{"id": "1"}),
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -40,19 +40,19 @@ func Test_union(t *testing.T) {
|
||||
name: "equal tags keys with no matching values will result in a union when len(A) == 1 && len(B) == 1",
|
||||
aResults: Results{
|
||||
Values: Values{
|
||||
makeSeriesNullableTime("a", data.Labels{"id": "1"}),
|
||||
makeSeries("a", data.Labels{"id": "1"}),
|
||||
},
|
||||
},
|
||||
bResults: Results{
|
||||
Values: Values{
|
||||
makeSeriesNullableTime("b", data.Labels{"id": "2"}),
|
||||
makeSeries("b", data.Labels{"id": "2"}),
|
||||
},
|
||||
},
|
||||
unionsAre: assert.EqualValues,
|
||||
unions: []*Union{
|
||||
{
|
||||
A: makeSeriesNullableTime("a", data.Labels{"id": "1"}),
|
||||
B: makeSeriesNullableTime("b", data.Labels{"id": "2"}),
|
||||
A: makeSeries("a", data.Labels{"id": "1"}),
|
||||
B: makeSeries("b", data.Labels{"id": "2"}),
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -60,13 +60,13 @@ func Test_union(t *testing.T) {
|
||||
name: "equal tags keys with no matching values will result in no unions when len(A) != 1 && len(B) != 1",
|
||||
aResults: Results{
|
||||
Values: Values{
|
||||
makeSeriesNullableTime("a", data.Labels{"id": "1"}),
|
||||
makeSeriesNullableTime("q", data.Labels{"id": "3"}),
|
||||
makeSeries("a", data.Labels{"id": "1"}),
|
||||
makeSeries("q", data.Labels{"id": "3"}),
|
||||
},
|
||||
},
|
||||
bResults: Results{
|
||||
Values: Values{
|
||||
makeSeriesNullableTime("b", data.Labels{"id": "2"}),
|
||||
makeSeries("b", data.Labels{"id": "2"}),
|
||||
},
|
||||
},
|
||||
unionsAre: assert.EqualValues,
|
||||
@ -83,13 +83,13 @@ func Test_union(t *testing.T) {
|
||||
name: "incompatible tags of different length with will result in no unions when len(A) != 1 && len(B) != 1",
|
||||
aResults: Results{
|
||||
Values: Values{
|
||||
makeSeriesNullableTime("a", data.Labels{"ID": "1"}),
|
||||
makeSeriesNullableTime("q", data.Labels{"ID": "3"}),
|
||||
makeSeries("a", data.Labels{"ID": "1"}),
|
||||
makeSeries("q", data.Labels{"ID": "3"}),
|
||||
},
|
||||
},
|
||||
bResults: Results{
|
||||
Values: Values{
|
||||
makeSeriesNullableTime("b", data.Labels{"id": "1", "fish": "red snapper"}),
|
||||
makeSeries("b", data.Labels{"id": "1", "fish": "red snapper"}),
|
||||
},
|
||||
},
|
||||
unionsAre: assert.EqualValues,
|
||||
@ -99,20 +99,20 @@ func Test_union(t *testing.T) {
|
||||
name: "A is subset of B results in single union with Labels of B",
|
||||
aResults: Results{
|
||||
Values: Values{
|
||||
makeSeriesNullableTime("a", data.Labels{"id": "1"}),
|
||||
makeSeries("a", data.Labels{"id": "1"}),
|
||||
},
|
||||
},
|
||||
bResults: Results{
|
||||
Values: Values{
|
||||
makeSeriesNullableTime("b", data.Labels{"id": "1", "fish": "herring"}),
|
||||
makeSeries("b", data.Labels{"id": "1", "fish": "herring"}),
|
||||
},
|
||||
},
|
||||
unionsAre: assert.EqualValues,
|
||||
unions: []*Union{
|
||||
{
|
||||
Labels: data.Labels{"id": "1", "fish": "herring"}, // Union gets the labels that is not the subset
|
||||
A: makeSeriesNullableTime("a", data.Labels{"id": "1"}),
|
||||
B: makeSeriesNullableTime("b", data.Labels{"id": "1", "fish": "herring"}),
|
||||
A: makeSeries("a", data.Labels{"id": "1"}),
|
||||
B: makeSeries("b", data.Labels{"id": "1", "fish": "herring"}),
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -120,20 +120,20 @@ func Test_union(t *testing.T) {
|
||||
name: "B is subset of A results in single union with Labels of A",
|
||||
aResults: Results{
|
||||
Values: Values{
|
||||
makeSeriesNullableTime("a", data.Labels{"id": "1", "fish": "herring"}),
|
||||
makeSeries("a", data.Labels{"id": "1", "fish": "herring"}),
|
||||
},
|
||||
},
|
||||
bResults: Results{
|
||||
Values: Values{
|
||||
makeSeriesNullableTime("b", data.Labels{"id": "1"}),
|
||||
makeSeries("b", data.Labels{"id": "1"}),
|
||||
},
|
||||
},
|
||||
unionsAre: assert.EqualValues,
|
||||
unions: []*Union{
|
||||
{
|
||||
Labels: data.Labels{"id": "1", "fish": "herring"}, // Union gets the labels that is not the subset
|
||||
A: makeSeriesNullableTime("a", data.Labels{"id": "1", "fish": "herring"}),
|
||||
B: makeSeriesNullableTime("b", data.Labels{"id": "1"}),
|
||||
A: makeSeries("a", data.Labels{"id": "1", "fish": "herring"}),
|
||||
B: makeSeries("b", data.Labels{"id": "1"}),
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -141,26 +141,26 @@ func Test_union(t *testing.T) {
|
||||
name: "single valued A is subset of many valued B, results in many union with Labels of B",
|
||||
aResults: Results{
|
||||
Values: Values{
|
||||
makeSeriesNullableTime("a", data.Labels{"id": "1"}),
|
||||
makeSeries("a", data.Labels{"id": "1"}),
|
||||
},
|
||||
},
|
||||
bResults: Results{
|
||||
Values: Values{
|
||||
makeSeriesNullableTime("b", data.Labels{"id": "1", "fish": "herring"}),
|
||||
makeSeriesNullableTime("b", data.Labels{"id": "1", "fish": "red snapper"}),
|
||||
makeSeries("b", data.Labels{"id": "1", "fish": "herring"}),
|
||||
makeSeries("b", data.Labels{"id": "1", "fish": "red snapper"}),
|
||||
},
|
||||
},
|
||||
unionsAre: assert.EqualValues,
|
||||
unions: []*Union{
|
||||
{
|
||||
Labels: data.Labels{"id": "1", "fish": "herring"},
|
||||
A: makeSeriesNullableTime("a", data.Labels{"id": "1"}),
|
||||
B: makeSeriesNullableTime("b", data.Labels{"id": "1", "fish": "herring"}),
|
||||
A: makeSeries("a", data.Labels{"id": "1"}),
|
||||
B: makeSeries("b", data.Labels{"id": "1", "fish": "herring"}),
|
||||
},
|
||||
{
|
||||
Labels: data.Labels{"id": "1", "fish": "red snapper"},
|
||||
A: makeSeriesNullableTime("a", data.Labels{"id": "1"}),
|
||||
B: makeSeriesNullableTime("b", data.Labels{"id": "1", "fish": "red snapper"}),
|
||||
A: makeSeries("a", data.Labels{"id": "1"}),
|
||||
B: makeSeries("b", data.Labels{"id": "1", "fish": "red snapper"}),
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -170,32 +170,32 @@ func Test_union(t *testing.T) {
|
||||
// be uniquely identifiable.
|
||||
aResults: Results{
|
||||
Values: Values{
|
||||
makeSeriesNullableTime("a", data.Labels{"id": "1"}),
|
||||
makeSeriesNullableTime("aa", data.Labels{"id": "1", "fish": "herring"}),
|
||||
makeSeries("a", data.Labels{"id": "1"}),
|
||||
makeSeries("aa", data.Labels{"id": "1", "fish": "herring"}),
|
||||
},
|
||||
},
|
||||
bResults: Results{
|
||||
Values: Values{
|
||||
makeSeriesNullableTime("b", data.Labels{"id": "1", "fish": "herring"}),
|
||||
makeSeriesNullableTime("bb", data.Labels{"id": "1", "fish": "red snapper"}),
|
||||
makeSeries("b", data.Labels{"id": "1", "fish": "herring"}),
|
||||
makeSeries("bb", data.Labels{"id": "1", "fish": "red snapper"}),
|
||||
},
|
||||
},
|
||||
unionsAre: assert.EqualValues,
|
||||
unions: []*Union{
|
||||
{
|
||||
Labels: data.Labels{"id": "1", "fish": "herring"},
|
||||
A: makeSeriesNullableTime("a", data.Labels{"id": "1"}),
|
||||
B: makeSeriesNullableTime("b", data.Labels{"id": "1", "fish": "herring"}),
|
||||
A: makeSeries("a", data.Labels{"id": "1"}),
|
||||
B: makeSeries("b", data.Labels{"id": "1", "fish": "herring"}),
|
||||
},
|
||||
{
|
||||
Labels: data.Labels{"id": "1", "fish": "red snapper"},
|
||||
A: makeSeriesNullableTime("a", data.Labels{"id": "1"}),
|
||||
B: makeSeriesNullableTime("bb", data.Labels{"id": "1", "fish": "red snapper"}),
|
||||
A: makeSeries("a", data.Labels{"id": "1"}),
|
||||
B: makeSeries("bb", data.Labels{"id": "1", "fish": "red snapper"}),
|
||||
},
|
||||
{
|
||||
Labels: data.Labels{"id": "1", "fish": "herring"},
|
||||
A: makeSeriesNullableTime("aa", data.Labels{"id": "1", "fish": "herring"}),
|
||||
B: makeSeriesNullableTime("b", data.Labels{"id": "1", "fish": "herring"}),
|
||||
A: makeSeries("aa", data.Labels{"id": "1", "fish": "herring"}),
|
||||
B: makeSeries("b", data.Labels{"id": "1", "fish": "herring"}),
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -205,32 +205,32 @@ func Test_union(t *testing.T) {
|
||||
// be uniquely identifiable.
|
||||
aResults: Results{
|
||||
Values: Values{
|
||||
makeSeriesNullableTime("b", data.Labels{"id": "1", "fish": "herring"}),
|
||||
makeSeriesNullableTime("bb", data.Labels{"id": "1", "fish": "red snapper"}),
|
||||
makeSeries("b", data.Labels{"id": "1", "fish": "herring"}),
|
||||
makeSeries("bb", data.Labels{"id": "1", "fish": "red snapper"}),
|
||||
},
|
||||
},
|
||||
bResults: Results{
|
||||
Values: Values{
|
||||
makeSeriesNullableTime("a", data.Labels{"id": "1"}),
|
||||
makeSeriesNullableTime("aa", data.Labels{"id": "1", "fish": "herring"}),
|
||||
makeSeries("a", data.Labels{"id": "1"}),
|
||||
makeSeries("aa", data.Labels{"id": "1", "fish": "herring"}),
|
||||
},
|
||||
},
|
||||
unionsAre: assert.EqualValues,
|
||||
unions: []*Union{
|
||||
{
|
||||
Labels: data.Labels{"id": "1", "fish": "herring"},
|
||||
A: makeSeriesNullableTime("b", data.Labels{"id": "1", "fish": "herring"}),
|
||||
B: makeSeriesNullableTime("a", data.Labels{"id": "1"}),
|
||||
A: makeSeries("b", data.Labels{"id": "1", "fish": "herring"}),
|
||||
B: makeSeries("a", data.Labels{"id": "1"}),
|
||||
},
|
||||
{
|
||||
Labels: data.Labels{"id": "1", "fish": "herring"},
|
||||
A: makeSeriesNullableTime("b", data.Labels{"id": "1", "fish": "herring"}),
|
||||
B: makeSeriesNullableTime("aa", data.Labels{"id": "1", "fish": "herring"}),
|
||||
A: makeSeries("b", data.Labels{"id": "1", "fish": "herring"}),
|
||||
B: makeSeries("aa", data.Labels{"id": "1", "fish": "herring"}),
|
||||
},
|
||||
{
|
||||
Labels: data.Labels{"id": "1", "fish": "red snapper"},
|
||||
A: makeSeriesNullableTime("bb", data.Labels{"id": "1", "fish": "red snapper"}),
|
||||
B: makeSeriesNullableTime("a", data.Labels{"id": "1"}),
|
||||
A: makeSeries("bb", data.Labels{"id": "1", "fish": "red snapper"}),
|
||||
B: makeSeries("a", data.Labels{"id": "1"}),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -22,7 +22,7 @@ import (
|
||||
// nolint:staticcheck // plugins.DataPlugin deprecated
|
||||
func TestService(t *testing.T) {
|
||||
dsDF := data.NewFrame("test",
|
||||
data.NewField("time", nil, []*time.Time{utp(1)}),
|
||||
data.NewField("time", nil, []time.Time{time.Unix(1, 0)}),
|
||||
data.NewField("value", nil, []*float64{fp(2)}))
|
||||
|
||||
dataSvc := tsdb.NewService()
|
||||
@ -61,7 +61,7 @@ func TestService(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
bDF := data.NewFrame("",
|
||||
data.NewField("Time", nil, []*time.Time{utp(1)}),
|
||||
data.NewField("Time", nil, []time.Time{time.Unix(1, 0)}),
|
||||
data.NewField("B", nil, []*float64{fp(4)}))
|
||||
bDF.RefID = "B"
|
||||
|
||||
@ -90,11 +90,6 @@ func TestService(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func utp(sec int64) *time.Time {
|
||||
t := time.Unix(sec, 0)
|
||||
return &t
|
||||
}
|
||||
|
||||
func fp(f float64) *float64 {
|
||||
return &f
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user