From f327667ff4633dd24dd3eb4911a51877eadeb437 Mon Sep 17 00:00:00 2001 From: Benjamin Raskin Date: Thu, 18 Oct 2018 12:36:33 -0400 Subject: [PATCH] Add sort and sort_desc (#1104) --- src/query/functions/linear/sort.go | 32 +++++++++++++++++ src/query/parser/promql/parse.go | 6 +++- src/query/parser/promql/parse_test.go | 24 +++++++++++++ src/query/parser/promql/types.go | 49 +++++++++++++++++++-------- 4 files changed, 95 insertions(+), 16 deletions(-) create mode 100644 src/query/functions/linear/sort.go diff --git a/src/query/functions/linear/sort.go b/src/query/functions/linear/sort.go new file mode 100644 index 0000000000..b0f7b88665 --- /dev/null +++ b/src/query/functions/linear/sort.go @@ -0,0 +1,32 @@ +// Copyright (c) 2018 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package linear + +const ( + // NB: Because Prometheus's sort and sort_desc only look at the last value, + // these functions are essentially noops in M3 as we don't support instant queries. + + // SortType returns timeseries elements sorted by their values, in ascending order. + SortType = "sort" + + // SortDescType is the same as sort, but sorts in descending order. + SortDescType = "sort_desc" +) diff --git a/src/query/parser/promql/parse.go b/src/query/parser/promql/parse.go index 6cd7cea826..4dc71dd87c 100644 --- a/src/query/parser/promql/parse.go +++ b/src/query/parser/promql/parse.go @@ -160,11 +160,15 @@ func (p *parseState) walk(node pql.Node) error { } } - op, err := NewFunctionExpr(n.Func.Name, argValues, stringValues) + op, ok, err := NewFunctionExpr(n.Func.Name, argValues, stringValues) if err != nil { return err } + if !ok { + return nil + } + opTransform := parser.NewTransformFromOperation(op, p.transformLen()) if op.OpType() != scalar.TimeType { p.edges = append(p.edges, parser.Edge{ diff --git a/src/query/parser/promql/parse_test.go b/src/query/parser/promql/parse_test.go index ea1e91c46e..3be0dd64c4 100644 --- a/src/query/parser/promql/parse_test.go +++ b/src/query/parser/promql/parse_test.go @@ -151,6 +151,30 @@ func TestLinearParses(t *testing.T) { } } +var sortTests = []struct { + q string + expectedType string +}{ + {"sort(up)", linear.SortType}, + {"sort_desc(up)", linear.SortDescType}, +} + +func TestSort(t *testing.T) { + for _, tt := range sortTests { + t.Run(tt.q, func(t *testing.T) { + q := tt.q + p, err := Parse(q, models.NewTagOptions()) + require.NoError(t, err) + transforms, edges, err := p.DAG() + require.NoError(t, err) + assert.Len(t, transforms, 1) + assert.Equal(t, transforms[0].Op.OpType(), functions.FetchType) + assert.Equal(t, transforms[0].ID, parser.NodeID("0")) + assert.Len(t, edges, 0) + }) + } +} + func TestTimeTypeParse(t *testing.T) { q := "time()" p, err := Parse(q, models.NewTagOptions()) diff --git a/src/query/parser/promql/types.go b/src/query/parser/promql/types.go index 0b9eb5980a..1b24a8b49b 100644 --- a/src/query/parser/promql/types.go +++ b/src/query/parser/promql/types.go @@ -171,55 +171,74 @@ func NewFunctionExpr( name string, argValues []interface{}, stringValues []string, -) (parser.Params, error) { +) (parser.Params, bool, error) { + var p parser.Params + var err error + switch name { case linear.AbsType, linear.CeilType, linear.ExpType, linear.FloorType, linear.LnType, linear.Log10Type, linear.Log2Type, linear.SqrtType: - return linear.NewMathOp(name) + p, err = linear.NewMathOp(name) + return p, true, err case linear.AbsentType: - return linear.NewAbsentOp(), nil + p = linear.NewAbsentOp() + return p, true, err case linear.ClampMinType, linear.ClampMaxType: - return linear.NewClampOp(argValues, name) + p, err = linear.NewClampOp(argValues, name) + return p, true, err case linear.RoundType: - return linear.NewRoundOp(argValues) + p, err = linear.NewRoundOp(argValues) + return p, true, err case linear.DayOfMonthType, linear.DayOfWeekType, linear.DaysInMonthType, linear.HourType, linear.MinuteType, linear.MonthType, linear.YearType: - return linear.NewDateOp(name) + p, err = linear.NewDateOp(name) + return p, true, err case tag.TagJoinType, tag.TagReplaceType: - return tag.NewTagOp(name, stringValues) + p, err = tag.NewTagOp(name, stringValues) + return p, true, err case temporal.AvgType, temporal.CountType, temporal.MinType, temporal.MaxType, temporal.SumType, temporal.StdDevType, temporal.StdVarType: - return temporal.NewAggOp(argValues, name) + p, err = temporal.NewAggOp(argValues, name) + return p, true, err case temporal.HoltWintersType: - return temporal.NewHoltWintersOp(argValues) + p, err = temporal.NewHoltWintersOp(argValues) + return p, true, err case temporal.IRateType, temporal.IDeltaType, temporal.RateType, temporal.IncreaseType, temporal.DeltaType: - return temporal.NewRateOp(argValues, name) + p, err = temporal.NewRateOp(argValues, name) + return p, true, err case temporal.PredictLinearType, temporal.DerivType: - return temporal.NewLinearRegressionOp(argValues, name) + p, err = temporal.NewLinearRegressionOp(argValues, name) + return p, true, err case temporal.ResetsType, temporal.ChangesType: - return temporal.NewFunctionOp(argValues, name) + p, err = temporal.NewFunctionOp(argValues, name) + return p, true, err + + case linear.SortType, linear.SortDescType: + return nil, false, err case unconsolidated.TimestampType: - return unconsolidated.NewTimestampOp(name) + p, err = unconsolidated.NewTimestampOp(name) + return p, true, err case scalar.TimeType: - return scalar.NewScalarOp(func(t time.Time) float64 { return float64(t.Unix()) }, scalar.TimeType) + p, err = scalar.NewScalarOp(func(t time.Time) float64 { return float64(t.Unix()) }, scalar.TimeType) + return p, true, err default: // TODO: handle other types - return nil, fmt.Errorf("function not supported: %s", name) + return nil, false, fmt.Errorf("function not supported: %s", name) } }