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  }