github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/query/functions/aggregation/count_values_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  	"math"
    25  	"testing"
    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/models"
    30  	"github.com/m3db/m3/src/query/parser"
    31  	"github.com/m3db/m3/src/query/test"
    32  	"github.com/m3db/m3/src/query/test/compare"
    33  	"github.com/m3db/m3/src/query/test/executor"
    34  
    35  	"github.com/stretchr/testify/assert"
    36  	"github.com/stretchr/testify/require"
    37  )
    38  
    39  func TestPadValuesWithNans(t *testing.T) {
    40  	// When padding necessary adds enough NaNs
    41  	vals := bucketColumn{1}
    42  	actual := padValuesWithNaNs(vals, 4)
    43  	compare.EqualsWithNans(t,
    44  		[]float64{1, math.NaN(), math.NaN(), math.NaN()},
    45  		[]float64(actual),
    46  	)
    47  
    48  	// When no padding necessary should do nothing
    49  	vals = bucketColumn{1, 2, 3, 4}
    50  	actual = padValuesWithNaNs(vals, 4)
    51  	compare.EqualsWithNans(t, []float64{1, 2, 3, 4}, []float64(actual))
    52  
    53  	// When vals is longer than padding length, should do nothing
    54  	vals = bucketColumn{1, 2, 3, 4, 5}
    55  	actual = padValuesWithNaNs(vals, 4)
    56  	compare.EqualsWithNans(t, []float64{1, 2, 3, 4, 5}, []float64(actual))
    57  }
    58  
    59  func TestCountValuesFn(t *testing.T) {
    60  	values := []float64{1, 2, 3, 4, 5, 6, 7, 1}
    61  	buckets := []int{0, 1, 7}
    62  	actual := countValuesFn(values, buckets)
    63  	assert.Equal(t, 2.0, actual[1])
    64  	assert.Equal(t, 1.0, actual[2])
    65  }
    66  
    67  func tagsToSeriesMeta(tags []models.Tags) []block.SeriesMeta {
    68  	expectedMetas := make([]block.SeriesMeta, len(tags))
    69  	for i, m := range tags {
    70  		expectedMetas[i] = block.SeriesMeta{
    71  			Name: []byte(CountValuesType),
    72  			Tags: m,
    73  		}
    74  	}
    75  	return expectedMetas
    76  }
    77  
    78  func processCountValuesOp(
    79  	t *testing.T,
    80  	op parser.Params,
    81  	metas []block.SeriesMeta,
    82  	vals [][]float64,
    83  ) *executor.SinkNode {
    84  	bl := test.NewBlockFromValuesWithSeriesMeta(bounds, metas, vals)
    85  	c, sink := executor.NewControllerWithSink(parser.NodeID(rune(1)))
    86  	node := op.(countValuesOp).Node(c, transform.Options{})
    87  	err := node.Process(models.NoopQueryContext(), parser.NodeID(rune(0)), bl)
    88  	require.NoError(t, err)
    89  	return sink
    90  }
    91  
    92  var (
    93  	simpleMetas = []block.SeriesMeta{
    94  		{Tags: test.TagSliceToTags([]models.Tag{{Name: []byte("a"), Value: []byte("1")}, {Name: []byte("b"), Value: []byte("2")}})},
    95  		{Tags: test.TagSliceToTags([]models.Tag{{Name: []byte("a"), Value: []byte("1")}, {Name: []byte("b"), Value: []byte("2")}})},
    96  		{Tags: test.TagSliceToTags([]models.Tag{{Name: []byte("a"), Value: []byte("1")}, {Name: []byte("b"), Value: []byte("3")}})},
    97  		{Tags: test.TagSliceToTags([]models.Tag{{Name: []byte("a"), Value: []byte("1")}, {Name: []byte("b"), Value: []byte("3")}})},
    98  	}
    99  
   100  	simpleVals = [][]float64{
   101  		{0, math.NaN(), 0, 0, 0},
   102  		{0, math.NaN(), 0, 0, 0},
   103  		{math.NaN(), 0, 0, 0, 0},
   104  		{math.NaN(), 0, 0, 0, 0},
   105  	}
   106  )
   107  
   108  func TestSimpleProcessCountValuesFunctionUnfiltered(t *testing.T) {
   109  	tagName := "tag_name"
   110  	op, err := NewCountValuesOp(CountValuesType, NodeParams{
   111  		StringParameter: tagName,
   112  	})
   113  	require.NoError(t, err)
   114  	sink := processCountValuesOp(t, op, simpleMetas, simpleVals)
   115  	expected := [][]float64{{2, 2, 4, 4, 4}}
   116  	expectedTags := []models.Tags{models.EmptyTags()}
   117  
   118  	// Double check expected tags is the same length as expected values
   119  	require.Equal(t, len(expectedTags), len(expected))
   120  	assert.Equal(t, bounds, sink.Meta.Bounds)
   121  	ex := test.TagSliceToTags([]models.Tag{{Name: []byte(tagName), Value: []byte("0")}})
   122  	assert.Equal(t, ex.Tags, sink.Meta.Tags.Tags)
   123  	compare.CompareValuesInOrder(t, sink.Metas, tagsToSeriesMeta(expectedTags), sink.Values, expected)
   124  }
   125  
   126  func TestSimpleProcessCountValuesFunctionFilteringWithoutA(t *testing.T) {
   127  	tagName := "tag_name"
   128  	op, err := NewCountValuesOp(CountValuesType, NodeParams{
   129  		MatchingTags: [][]byte{[]byte("a")}, Without: true, StringParameter: tagName,
   130  	})
   131  	require.NoError(t, err)
   132  	sink := processCountValuesOp(t, op, simpleMetas, simpleVals)
   133  	expected := [][]float64{
   134  		{2, math.NaN(), 2, 2, 2},
   135  		{math.NaN(), 2, 2, 2, 2},
   136  	}
   137  	expectedTags := test.TagSliceSliceToTagSlice([][]models.Tag{
   138  		{{Name: []byte("b"), Value: []byte("2")}},
   139  		{{Name: []byte("b"), Value: []byte("3")}},
   140  	})
   141  
   142  	// Double check expected tags is the same length as expected values
   143  	require.Equal(t, len(expectedTags), len(expected))
   144  	assert.Equal(t, bounds, sink.Meta.Bounds)
   145  	exTags := test.TagSliceToTags([]models.Tag{{Name: []byte(tagName), Value: []byte("0")}})
   146  	assert.Equal(t, exTags.Tags, sink.Meta.Tags.Tags)
   147  	compare.CompareValuesInOrder(t, sink.Metas, tagsToSeriesMeta(expectedTags), sink.Values, expected)
   148  }
   149  
   150  func TestCustomProcessCountValuesFunctionFilteringWithoutA(t *testing.T) {
   151  	tagName := "tag_name"
   152  	op, err := NewCountValuesOp(CountValuesType, NodeParams{
   153  		MatchingTags: [][]byte{[]byte("a")}, Without: true, StringParameter: tagName,
   154  	})
   155  	require.NoError(t, err)
   156  	ms := []block.SeriesMeta{
   157  		{Tags: test.TagSliceToTags([]models.Tag{{Name: []byte("a"), Value: []byte("1")}, {Name: []byte("b"), Value: []byte("2")}})},
   158  		{Tags: test.TagSliceToTags([]models.Tag{{Name: []byte("a"), Value: []byte("1")}, {Name: []byte("b"), Value: []byte("2")}})},
   159  		{Tags: test.TagSliceToTags([]models.Tag{{Name: []byte("a"), Value: []byte("1")}, {Name: []byte("b"), Value: []byte("3")}})},
   160  		{Tags: test.TagSliceToTags([]models.Tag{{Name: []byte("a"), Value: []byte("1")}, {Name: []byte("b"), Value: []byte("3")}})},
   161  	}
   162  
   163  	vs := [][]float64{
   164  		{0, math.NaN(), 0, 2, 0},
   165  		{0, math.NaN(), 1, 0, 3},
   166  		{math.NaN(), 0, 1, 0, 2},
   167  		{math.NaN(), 0, 0, 2, 3},
   168  	}
   169  
   170  	sink := processCountValuesOp(t, op, ms, vs)
   171  	expected := [][]float64{
   172  		{2, math.NaN(), 1, 1, 1},
   173  		{math.NaN(), math.NaN(), 1, math.NaN(), math.NaN()},
   174  		{math.NaN(), math.NaN(), math.NaN(), 1, math.NaN()},
   175  		{math.NaN(), math.NaN(), math.NaN(), math.NaN(), 1},
   176  
   177  		{math.NaN(), 2, 1, 1, math.NaN()},
   178  		{math.NaN(), math.NaN(), 1, math.NaN(), math.NaN()},
   179  		{math.NaN(), math.NaN(), math.NaN(), 1, 1},
   180  		{math.NaN(), math.NaN(), math.NaN(), math.NaN(), 1},
   181  	}
   182  
   183  	expectedTags := test.TagSliceSliceToTagSlice([][]models.Tag{
   184  		{{Name: []byte("b"), Value: []byte("2")}, {Name: []byte(tagName), Value: []byte("0")}},
   185  		{{Name: []byte("b"), Value: []byte("2")}, {Name: []byte(tagName), Value: []byte("1")}},
   186  		{{Name: []byte("b"), Value: []byte("2")}, {Name: []byte(tagName), Value: []byte("2")}},
   187  		{{Name: []byte("b"), Value: []byte("2")}, {Name: []byte(tagName), Value: []byte("3")}},
   188  
   189  		{{Name: []byte("b"), Value: []byte("3")}, {Name: []byte(tagName), Value: []byte("0")}},
   190  		{{Name: []byte("b"), Value: []byte("3")}, {Name: []byte(tagName), Value: []byte("1")}},
   191  		{{Name: []byte("b"), Value: []byte("3")}, {Name: []byte(tagName), Value: []byte("2")}},
   192  		{{Name: []byte("b"), Value: []byte("3")}, {Name: []byte(tagName), Value: []byte("3")}},
   193  	})
   194  
   195  	// Double check expected tags is the same length as expected values
   196  	require.Equal(t, len(expectedTags), len(expected))
   197  	assert.Equal(t, bounds, sink.Meta.Bounds)
   198  	assert.Equal(t, models.EmptyTags(), sink.Meta.Tags)
   199  	compare.CompareValuesInOrder(t, sink.Metas, tagsToSeriesMeta(expectedTags), sink.Values, expected)
   200  }
   201  
   202  func TestSimpleProcessCountValuesFunctionFilteringWithA(t *testing.T) {
   203  	tagName := "tag_name_0"
   204  	op, err := NewCountValuesOp(CountValuesType, NodeParams{
   205  		MatchingTags: [][]byte{[]byte("a")}, Without: false, StringParameter: tagName,
   206  	})
   207  	require.NoError(t, err)
   208  	sink := processCountValuesOp(t, op, simpleMetas, simpleVals)
   209  	expected := [][]float64{{2, 2, 4, 4, 4}}
   210  	expectedTags := []models.Tags{models.EmptyTags()}
   211  
   212  	// Double check expected tags is the same length as expected values
   213  	require.Equal(t, len(expectedTags), len(expected))
   214  	assert.Equal(t, bounds, sink.Meta.Bounds)
   215  	assert.Equal(t, test.TagSliceToTags([]models.Tag{{Name: []byte(tagName), Value: []byte("0")},
   216  		{Name: []byte("a"), Value: []byte("1")}}).Tags, sink.Meta.Tags.Tags)
   217  	compare.CompareValuesInOrder(t, sink.Metas, tagsToSeriesMeta(expectedTags), sink.Values, expected)
   218  }
   219  
   220  func TestProcessCountValuesFunctionFilteringWithoutA(t *testing.T) {
   221  	tagName := "tag_name"
   222  	op, err := NewCountValuesOp(CountValuesType, NodeParams{
   223  		MatchingTags: [][]byte{[]byte("a")}, Without: true, StringParameter: tagName,
   224  	})
   225  	require.NoError(t, err)
   226  	sink := processCountValuesOp(t, op, seriesMetas, v)
   227  
   228  	expected := [][]float64{
   229  		// No shared values between series 1 and 2
   230  		{1, math.NaN(), math.NaN(), math.NaN(), math.NaN()},
   231  		{math.NaN(), 1, math.NaN(), math.NaN(), math.NaN()},
   232  		{math.NaN(), math.NaN(), 1, math.NaN(), math.NaN()},
   233  		{math.NaN(), math.NaN(), 1, math.NaN(), math.NaN()},
   234  		{math.NaN(), math.NaN(), math.NaN(), 1, math.NaN()},
   235  		{math.NaN(), math.NaN(), math.NaN(), 1, math.NaN()},
   236  		{math.NaN(), math.NaN(), math.NaN(), math.NaN(), 1},
   237  		{math.NaN(), math.NaN(), math.NaN(), math.NaN(), 1},
   238  
   239  		// One shared value between series 3, 4 and 5
   240  		{1, math.NaN(), math.NaN(), math.NaN(), math.NaN()},
   241  		{1, math.NaN(), math.NaN(), math.NaN(), 1},
   242  		{1, math.NaN(), math.NaN(), math.NaN(), math.NaN()},
   243  		{math.NaN(), 1, math.NaN(), math.NaN(), math.NaN()},
   244  		{math.NaN(), 1, math.NaN(), math.NaN(), math.NaN()},
   245  		{math.NaN(), 1, math.NaN(), math.NaN(), math.NaN()},
   246  		{math.NaN(), math.NaN(), 1, math.NaN(), math.NaN()},
   247  		{math.NaN(), math.NaN(), 1, math.NaN(), math.NaN()},
   248  		{math.NaN(), math.NaN(), 1, math.NaN(), math.NaN()},
   249  		{math.NaN(), math.NaN(), math.NaN(), 1, math.NaN()},
   250  		{math.NaN(), math.NaN(), math.NaN(), 1, math.NaN()},
   251  		{math.NaN(), math.NaN(), math.NaN(), 1, math.NaN()},
   252  		{math.NaN(), math.NaN(), math.NaN(), math.NaN(), 1},
   253  		{math.NaN(), math.NaN(), math.NaN(), math.NaN(), 1},
   254  
   255  		// No shared values in series 6
   256  		{1, math.NaN(), math.NaN(), math.NaN(), math.NaN()},
   257  		{math.NaN(), 1, math.NaN(), math.NaN(), math.NaN()},
   258  		{math.NaN(), math.NaN(), 1, math.NaN(), math.NaN()},
   259  		{math.NaN(), math.NaN(), math.NaN(), 1, math.NaN()},
   260  		{math.NaN(), math.NaN(), math.NaN(), math.NaN(), 1},
   261  	}
   262  
   263  	expectedTags := test.TagSliceSliceToTagSlice([][]models.Tag{
   264  		// No shared values between series 1 and 2, but two NaNs
   265  		{{Name: []byte(tagName), Value: []byte("0")}},
   266  		{{Name: []byte(tagName), Value: []byte("6")}},
   267  		{{Name: []byte(tagName), Value: []byte("2")}},
   268  		{{Name: []byte(tagName), Value: []byte("7")}},
   269  		{{Name: []byte(tagName), Value: []byte("3")}},
   270  		{{Name: []byte(tagName), Value: []byte("8")}},
   271  		{{Name: []byte(tagName), Value: []byte("4")}},
   272  		{{Name: []byte(tagName), Value: []byte("9")}},
   273  
   274  		// One shared value between series 3, 4 and 5,
   275  		{{Name: []byte("b"), Value: []byte("2")}, {Name: []byte(tagName), Value: []byte("10")}},
   276  		{{Name: []byte("b"), Value: []byte("2")}, {Name: []byte(tagName), Value: []byte("50")}},
   277  		{{Name: []byte("b"), Value: []byte("2")}, {Name: []byte(tagName), Value: []byte("100")}},
   278  		{{Name: []byte("b"), Value: []byte("2")}, {Name: []byte(tagName), Value: []byte("20")}},
   279  		{{Name: []byte("b"), Value: []byte("2")}, {Name: []byte(tagName), Value: []byte("60")}},
   280  		{{Name: []byte("b"), Value: []byte("2")}, {Name: []byte(tagName), Value: []byte("200")}},
   281  		{{Name: []byte("b"), Value: []byte("2")}, {Name: []byte(tagName), Value: []byte("30")}},
   282  		{{Name: []byte("b"), Value: []byte("2")}, {Name: []byte(tagName), Value: []byte("70")}},
   283  		{{Name: []byte("b"), Value: []byte("2")}, {Name: []byte(tagName), Value: []byte("300")}},
   284  		{{Name: []byte("b"), Value: []byte("2")}, {Name: []byte(tagName), Value: []byte("40")}},
   285  		{{Name: []byte("b"), Value: []byte("2")}, {Name: []byte(tagName), Value: []byte("80")}},
   286  		{{Name: []byte("b"), Value: []byte("2")}, {Name: []byte(tagName), Value: []byte("400")}},
   287  		{{Name: []byte("b"), Value: []byte("2")}, {Name: []byte(tagName), Value: []byte("90")}},
   288  		{{Name: []byte("b"), Value: []byte("2")}, {Name: []byte(tagName), Value: []byte("500")}},
   289  
   290  		// No shared values in series 6
   291  		{{Name: []byte("c"), Value: []byte("3")}, {Name: []byte(tagName), Value: []byte("600")}},
   292  		{{Name: []byte("c"), Value: []byte("3")}, {Name: []byte(tagName), Value: []byte("700")}},
   293  		{{Name: []byte("c"), Value: []byte("3")}, {Name: []byte(tagName), Value: []byte("800")}},
   294  		{{Name: []byte("c"), Value: []byte("3")}, {Name: []byte(tagName), Value: []byte("900")}},
   295  		{{Name: []byte("c"), Value: []byte("3")}, {Name: []byte(tagName), Value: []byte("1000")}},
   296  	})
   297  
   298  	// Double check expected tags is the same length as expected values
   299  	require.Equal(t, len(expectedTags), len(expected))
   300  	assert.Equal(t, bounds, sink.Meta.Bounds)
   301  	ex := test.TagSliceToTags([]models.Tag{{Name: []byte("d"), Value: []byte("4")}})
   302  	assert.Equal(t, ex.Tags, sink.Meta.Tags.Tags)
   303  	compare.CompareValues(t, sink.Metas, tagsToSeriesMeta(expectedTags), sink.Values, expected)
   304  }
   305  
   306  func TestShouldFailWhenInvalidLabelName(t *testing.T) {
   307  	tagName := "tag-name"
   308  	op, _ := NewCountValuesOp(CountValuesType, NodeParams{
   309  		StringParameter: tagName,
   310  	})
   311  	bl := test.NewBlockFromValuesWithSeriesMeta(bounds, simpleMetas, simpleVals)
   312  	c, _ := executor.NewControllerWithSink(parser.NodeID(rune(1)))
   313  	node := op.(countValuesOp).Node(c, transform.Options{})
   314  	err := node.Process(models.NoopQueryContext(), parser.NodeID(rune(0)), bl)
   315  	require.Error(t, err)
   316  }