github.com/m3db/m3@v1.5.0/src/query/functions/aggregation/absent.go (about) 1 // Copyright (c) 2019 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 aggregation 22 23 import ( 24 "math" 25 26 "github.com/m3db/m3/src/query/block" 27 "github.com/m3db/m3/src/query/executor/transform" 28 "github.com/m3db/m3/src/query/functions/utils" 29 "github.com/m3db/m3/src/query/models" 30 "github.com/m3db/m3/src/query/parser" 31 ) 32 33 const ( 34 // AbsentType returns 1 if there are no elements in this step, or if no series 35 // are present in the current block. 36 AbsentType = "absent" 37 ) 38 39 // NewAbsentOp creates a new absent operation. 40 func NewAbsentOp() parser.Params { 41 return newAbsentOp() 42 } 43 44 // absentOp stores required properties for absent ops. 45 type absentOp struct{} 46 47 // OpType for the operator. 48 func (o absentOp) OpType() string { 49 return AbsentType 50 } 51 52 // String representation. 53 func (o absentOp) String() string { 54 return "type: absent" 55 } 56 57 // Node creates an execution node. 58 func (o absentOp) Node( 59 controller *transform.Controller, 60 _ transform.Options, 61 ) transform.OpNode { 62 return &absentNode{ 63 op: o, 64 controller: controller, 65 } 66 } 67 68 func newAbsentOp() absentOp { 69 return absentOp{} 70 } 71 72 // absentNode is different from base node as it uses no grouping and has 73 // special handling for the 0-series case. 74 type absentNode struct { 75 op parser.Params 76 controller *transform.Controller 77 } 78 79 func (n *absentNode) Params() parser.Params { 80 return n.op 81 } 82 83 func (n *absentNode) Process(queryCtx *models.QueryContext, 84 ID parser.NodeID, b block.Block) error { 85 return transform.ProcessSimpleBlock(n, n.controller, queryCtx, ID, b) 86 } 87 88 func (n *absentNode) ProcessBlock( 89 queryCtx *models.QueryContext, 90 ID parser.NodeID, 91 bl block.Block, 92 ) (block.Block, error) { 93 stepIter, err := bl.StepIter() 94 if err != nil { 95 return nil, err 96 } 97 98 var ( 99 meta = bl.Meta() 100 seriesMetas = stepIter.SeriesMeta() 101 tagOpts = meta.Tags.Opts 102 ) 103 104 // If no series in the input, return a scalar block with value 1. 105 if len(seriesMetas) == 0 { 106 return block.NewScalar(1, meta), nil 107 } 108 109 // NB: pull any common tags out into the created series. 110 dupeTags, _ := utils.DedupeMetadata(seriesMetas, tagOpts) 111 meta.Tags = meta.Tags.Add(dupeTags).Normalize() 112 emptySeriesMeta := []block.SeriesMeta{ 113 block.SeriesMeta{ 114 Tags: models.NewTags(0, tagOpts), 115 Name: []byte{}, 116 }, 117 } 118 119 setupBuilderWithValuesToIndex := func(idx int) (block.Builder, error) { 120 builder, err := n.controller.BlockBuilder(queryCtx, meta, emptySeriesMeta) 121 if err != nil { 122 return nil, err 123 } 124 125 if err = builder.AddCols(stepIter.StepCount()); err != nil { 126 return nil, err 127 } 128 129 for i := 0; i < idx; i++ { 130 if err := builder.AppendValue(i, math.NaN()); err != nil { 131 return nil, err 132 } 133 } 134 135 if err := builder.AppendValue(idx, 1); err != nil { 136 return nil, err 137 } 138 139 return builder, err 140 } 141 142 var builder block.Builder 143 for idx := 0; stepIter.Next(); idx++ { 144 var ( 145 step = stepIter.Current() 146 values = step.Values() 147 val float64 = 1 148 ) 149 for _, v := range values { 150 if !math.IsNaN(v) { 151 val = math.NaN() 152 break 153 } 154 } 155 156 if builder == nil { 157 if !math.IsNaN(val) { 158 builder, err = setupBuilderWithValuesToIndex(idx) 159 if err != nil { 160 return nil, err 161 } 162 } 163 } else { 164 if err := builder.AppendValue(idx, val); err != nil { 165 return nil, err 166 } 167 } 168 } 169 170 if err = stepIter.Err(); err != nil { 171 return nil, err 172 } 173 174 if builder == nil { 175 return block.NewEmptyBlock(meta), nil 176 } 177 178 return builder.Build(), nil 179 }