github.com/m3db/m3@v1.5.0/src/query/functions/linear/sort.go (about) 1 // Copyright (c) 2018 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package linear 22 23 import ( 24 "fmt" 25 "sort" 26 27 "github.com/m3db/m3/src/query/block" 28 "github.com/m3db/m3/src/query/executor/transform" 29 "github.com/m3db/m3/src/query/functions/utils" 30 "github.com/m3db/m3/src/query/models" 31 "github.com/m3db/m3/src/query/parser" 32 ) 33 34 const ( 35 // SortType returns timeseries elements sorted by their values, in ascending order. 36 SortType = "sort" 37 38 // SortDescType is the same as sort, but sorts in descending order. 39 SortDescType = "sort_desc" 40 ) 41 42 type sortOp struct { 43 opType string 44 lessFn lessFn 45 } 46 47 // OpType for the operator 48 func (o sortOp) OpType() string { 49 return o.opType 50 } 51 52 // String representation 53 func (o sortOp) String() string { 54 return fmt.Sprintf("type: %s", o.opType) 55 } 56 57 type sortNode struct { 58 op sortOp 59 controller *transform.Controller 60 } 61 62 type valueAndMeta struct { 63 val float64 64 seriesMeta block.SeriesMeta 65 } 66 67 type lessFn func (i, j float64) bool 68 69 // Node creates an execution node 70 func (o sortOp) Node( 71 controller *transform.Controller, 72 _ transform.Options, 73 ) transform.OpNode { 74 return &sortNode{ 75 op: o, 76 controller: controller, 77 } 78 } 79 80 func (n *sortNode) Params() parser.Params { 81 return n.op 82 } 83 84 func (n *sortNode) Process(queryCtx *models.QueryContext, ID parser.NodeID, b block.Block) error { 85 return transform.ProcessSimpleBlock(n, n.controller, queryCtx, ID, b) 86 } 87 88 func (n *sortNode) ProcessBlock(queryCtx *models.QueryContext, ID parser.NodeID, b block.Block) (block.Block, error) { 89 if !queryCtx.Options.Instantaneous { 90 return b, nil 91 } 92 stepIter, err := b.StepIter() 93 if err != nil { 94 return nil, err 95 } 96 97 meta := b.Meta() 98 seriesMetas := utils.FlattenMetadata(meta, stepIter.SeriesMeta()) 99 return n.processInstantBlock(queryCtx, stepIter, meta, seriesMetas) 100 } 101 102 func (n *sortNode) processInstantBlock(queryCtx *models.QueryContext, stepIter block.StepIter, meta block.Metadata, seriesMetas []block.SeriesMeta) (block.Block, error) { 103 ixLastStep := stepIter.StepCount() - 1 //we only care for the last step values for the instant query 104 for i := 0; i <= ixLastStep; i++ { 105 if !stepIter.Next() { 106 return nil, fmt.Errorf("invalid step count; expected %d got %d", stepIter.StepCount(), i+1) 107 } 108 } 109 values := stepIter.Current().Values() 110 meta.ResultMetadata.KeepNaNs = true 111 valuesToSort := make([]valueAndMeta, len(values)) 112 for i, value := range values { 113 valuesToSort[i] = valueAndMeta{ 114 val: value, 115 seriesMeta: seriesMetas[i], 116 } 117 } 118 119 sort.Slice(valuesToSort, func(i, j int) bool { 120 return n.op.lessFn(valuesToSort[i].val, valuesToSort[j].val) 121 }) 122 123 for i, sorted := range valuesToSort { 124 values[i] = sorted.val 125 seriesMetas[i] = sorted.seriesMeta 126 } 127 128 //adjust bounds to contain single step 129 time, err := meta.Bounds.TimeForIndex(ixLastStep) 130 if err != nil { 131 return nil, err 132 } 133 meta.Bounds = models.Bounds{ 134 Start: time, 135 Duration: meta.Bounds.StepSize, 136 StepSize: meta.Bounds.StepSize, 137 } 138 139 blockBuilder, err := n.controller.BlockBuilder(queryCtx, meta, seriesMetas) 140 if err != nil { 141 return nil, err 142 } 143 if err = blockBuilder.AddCols(1); err != nil { 144 return nil, err 145 } 146 if err := blockBuilder.AppendValues(0, values); err != nil { 147 return nil, err 148 } 149 if err = stepIter.Err(); err != nil { 150 return nil, err 151 } 152 return blockBuilder.Build(), nil 153 } 154 155 func NewSortOp(opType string) (parser.Params, error) { 156 ascending := opType == SortType 157 if !ascending && opType != SortDescType { 158 return nil, fmt.Errorf("operator not supported: %s", opType) 159 } 160 161 lessFn := utils.GreaterWithNaNs 162 if ascending { 163 lessFn = utils.LesserWithNaNs 164 } 165 166 return sortOp{opType, lessFn}, nil 167 }