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 }