github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/query/functions/aggregation/take_test.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 aggregation
    22  
    23  import (
    24  	"fmt"
    25  	"math"
    26  	"testing"
    27  
    28  	"github.com/m3db/m3/src/query/block"
    29  	"github.com/m3db/m3/src/query/executor/transform"
    30  	"github.com/m3db/m3/src/query/functions/utils"
    31  	"github.com/m3db/m3/src/query/models"
    32  	"github.com/m3db/m3/src/query/parser"
    33  	"github.com/m3db/m3/src/query/test"
    34  	"github.com/m3db/m3/src/query/test/compare"
    35  	"github.com/m3db/m3/src/query/test/executor"
    36  
    37  	"github.com/stretchr/testify/assert"
    38  	"github.com/stretchr/testify/require"
    39  )
    40  
    41  func TestTakeInstantFn(t *testing.T) {
    42  	valuesMin := []float64{1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 8.1, 9.1}
    43  	buckets := [][]int{{0, 1, 2, 3}, {4}, {5, 6, 7, 8}}
    44  
    45  	var (
    46  		seriesMetasTakeOrdered = []block.SeriesMeta{
    47  			{Tags: test.StringTagsToTags(test.StringTags{{N: "job", V: "api-server"}, {N: "instance", V: "0"}, {N: "group", V: "production"}})},
    48  			{Tags: test.StringTagsToTags(test.StringTags{{N: "job", V: "api-server"}, {N: "instance", V: "1"}, {N: "group", V: "production"}})},
    49  			{Tags: test.StringTagsToTags(test.StringTags{{N: "job", V: "api-server"}, {N: "instance", V: "2"}, {N: "group", V: "production"}})},
    50  			{Tags: test.StringTagsToTags(test.StringTags{{N: "job", V: "api-server"}, {N: "instance", V: "0"}, {N: "group", V: "canary"}})},
    51  			{Tags: test.StringTagsToTags(test.StringTags{{N: "job", V: "api-server"}, {N: "instance", V: "1"}, {N: "group", V: "canary"}})},
    52  			{Tags: test.StringTagsToTags(test.StringTags{{N: "job", V: "app-server"}, {N: "instance", V: "0"}, {N: "group", V: "production"}})},
    53  			{Tags: test.StringTagsToTags(test.StringTags{{N: "job", V: "app-server"}, {N: "instance", V: "1"}, {N: "group", V: "production"}})},
    54  			{Tags: test.StringTagsToTags(test.StringTags{{N: "job", V: "app-server"}, {N: "instance", V: "0"}, {N: "group", V: "canary"}})},
    55  			{Tags: test.StringTagsToTags(test.StringTags{{N: "job", V: "app-server"}, {N: "instance", V: "1"}, {N: "group", V: "canary"}})},
    56  		}
    57  	)
    58  
    59  	expectedMin := []valueAndMeta{
    60  		{val: 1.1, seriesMeta: seriesMetasTakeOrdered[0]},
    61  		{val: 2.1, seriesMeta: seriesMetasTakeOrdered[1]},
    62  		{val: 3.1, seriesMeta: seriesMetasTakeOrdered[2]},
    63  
    64  		{val: 5.1, seriesMeta: seriesMetasTakeOrdered[4]},
    65  
    66  		{val: 6.1, seriesMeta: seriesMetasTakeOrdered[5]},
    67  		{val: 7.1, seriesMeta: seriesMetasTakeOrdered[6]},
    68  		{val: 8.1, seriesMeta: seriesMetasTakeOrdered[7]},
    69  	}
    70  
    71  	size := 3
    72  	minHeap := utils.NewFloatHeap(false, size)
    73  	actual := takeInstantFn(minHeap, valuesMin, buckets, seriesMetasTakeOrdered) //9
    74  
    75  	actualString := fmt.Sprint(actual)
    76  	expectedString := fmt.Sprint(expectedMin)
    77  
    78  	assert.EqualValues(t, expectedString, actualString)
    79  
    80  	valuesMax := []float64{1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 8.1, 9.1}
    81  	expectedMax := []valueAndMeta{
    82  		{val: 4.1, seriesMeta: seriesMetasTakeOrdered[3]},
    83  		{val: 3.1, seriesMeta: seriesMetasTakeOrdered[2]},
    84  		{val: 2.1, seriesMeta: seriesMetasTakeOrdered[1]},
    85  
    86  		{val: 5.1, seriesMeta: seriesMetasTakeOrdered[4]},
    87  
    88  		{val: 9.1, seriesMeta: seriesMetasTakeOrdered[8]},
    89  		{val: 8.1, seriesMeta: seriesMetasTakeOrdered[7]},
    90  		{val: 7.1, seriesMeta: seriesMetasTakeOrdered[6]},
    91  	}
    92  
    93  	maxHeap := utils.NewFloatHeap(true, size)
    94  	actual = takeInstantFn(maxHeap, valuesMax, buckets, seriesMetasTakeOrdered)
    95  	actualString = fmt.Sprint(actual)
    96  	expectedString = fmt.Sprint(expectedMax)
    97  
    98  	assert.EqualValues(t, expectedString, actualString)
    99  }
   100  
   101  func TestTakeFn(t *testing.T) {
   102  	valuesMin := []float64{1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 8.1}
   103  	buckets := [][]int{{0, 1, 2, 3}, {4}, {5, 6, 7}}
   104  	expectedMin := []float64{1.1, 2.1, 3.1, math.NaN(), 5.1, 6.1, 7.1, 8.1}
   105  	size := 3
   106  	minHeap := utils.NewFloatHeap(false, size)
   107  
   108  	actual := takeFn(minHeap, valuesMin, buckets)
   109  	compare.EqualsWithNans(t, expectedMin, actual)
   110  	compare.EqualsWithNans(t, expectedMin, valuesMin)
   111  
   112  	valuesMax := []float64{1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 8.1}
   113  	expectedMax := []float64{math.NaN(), 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 8.1}
   114  
   115  	maxHeap := utils.NewFloatHeap(true, size)
   116  	actual = takeFn(maxHeap, valuesMax, buckets)
   117  	compare.EqualsWithNans(t, expectedMax, actual)
   118  	compare.EqualsWithNans(t, expectedMax, valuesMax)
   119  
   120  	valuesQuantile := []float64{1.1, 2.1, 3.1, 4.1, 5.1}
   121  	actualQ := bucketedQuantileFn(0, valuesQuantile, []int{0, 1, 2, 3})
   122  	compare.EqualsWithNans(t, 1.1, actualQ)
   123  }
   124  
   125  func processTakeOp(t *testing.T, op parser.Params) *executor.SinkNode {
   126  	bl := test.NewBlockFromValuesWithSeriesMeta(bounds, seriesMetas, v)
   127  	c, sink := executor.NewControllerWithSink(parser.NodeID(rune(1)))
   128  	node := op.(takeOp).Node(c, transform.Options{})
   129  	err := node.Process(models.NoopQueryContext(), parser.NodeID(rune(0)), bl)
   130  	require.NoError(t, err)
   131  	return sink
   132  }
   133  
   134  func TestTakeBottomFunctionFilteringWithoutA(t *testing.T) {
   135  	op, err := NewTakeOp(BottomKType, NodeParams{
   136  		MatchingTags: [][]byte{[]byte("a")}, Without: true, Parameter: 1,
   137  	})
   138  	require.NoError(t, err)
   139  	sink := processTakeOp(t, op)
   140  	expected := [][]float64{
   141  		// Taking bottomk(1) of first two series, keeping both series
   142  		{0, math.NaN(), 2, 3, 4},
   143  		{math.NaN(), 6, math.NaN(), math.NaN(), math.NaN()},
   144  		// Taking bottomk(1) of third, fourth, and fifth two series, keeping all series
   145  		{10, 20, 30, 40, 50},
   146  		{math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN()},
   147  		{math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN()},
   148  		// Taking bottomk(1) of last series, keeping it
   149  		{600, 700, 800, 900, 1000},
   150  	}
   151  
   152  	// Should have the same metas as when started
   153  	assert.Equal(t, seriesMetas, sink.Metas)
   154  	compare.EqualsWithNansWithDelta(t, expected, sink.Values, math.Pow10(-5))
   155  	assert.Equal(t, bounds, sink.Meta.Bounds)
   156  }
   157  
   158  func TestTakeTopFunctionFilteringWithoutA(t *testing.T) {
   159  	op, err := NewTakeOp(TopKType, NodeParams{
   160  		MatchingTags: [][]byte{[]byte("a")}, Without: true, Parameter: 1,
   161  	})
   162  	require.NoError(t, err)
   163  	sink := processTakeOp(t, op)
   164  
   165  	expected := [][]float64{
   166  		// Taking bottomk(1) of first two series, keeping both series
   167  		{0, math.NaN(), math.NaN(), math.NaN(), math.NaN()},
   168  		{math.NaN(), 6, 7, 8, 9},
   169  		// Taking bottomk(1) of third, fourth, and fifth two series, keeping all series
   170  		{math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN()},
   171  		{math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN()},
   172  		{100, 200, 300, 400, 500},
   173  		// Taking bottomk(1) of last series, keeping it
   174  		{600, 700, 800, 900, 1000},
   175  	}
   176  
   177  	// Should have the same metas as when started
   178  	assert.Equal(t, seriesMetas, sink.Metas)
   179  	compare.EqualsWithNansWithDelta(t, expected, sink.Values, math.Pow10(-5))
   180  	assert.Equal(t, bounds, sink.Meta.Bounds)
   181  }
   182  
   183  func TestTakeTopFunctionFilteringWithoutALessThanOne(t *testing.T) {
   184  	op, err := NewTakeOp(TopKType, NodeParams{
   185  		MatchingTags: [][]byte{[]byte("a")}, Without: true, Parameter: -1,
   186  	})
   187  	require.NoError(t, err)
   188  	sink := processTakeOp(t, op)
   189  	expected := [][]float64{
   190  		// Taking bottomk(1) of first two series, keeping both series
   191  		{math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN()},
   192  		{math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN()},
   193  		// Taking bottomk(1) of third, fourth, and fifth two series, keeping all series
   194  		{math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN()},
   195  		{math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN()},
   196  		{math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN()},
   197  		// Taking bottomk(1) of last series, keeping it
   198  		{math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN()},
   199  	}
   200  
   201  	// Should have the same metas as when started
   202  	assert.Equal(t, seriesMetas, sink.Metas)
   203  	compare.EqualsWithNansWithDelta(t, expected, sink.Values, math.Pow10(-5))
   204  	assert.Equal(t, bounds, sink.Meta.Bounds)
   205  }
   206  
   207  func TestTakeOpParamIsNaN(t *testing.T) {
   208  	op, err := NewTakeOp(TopKType, NodeParams{
   209  		Parameter: math.NaN(),
   210  	})
   211  	require.NoError(t, err)
   212  	assert.True(t, op.(takeOp).k < 0)
   213  }