github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/query/functions/binary/or_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 binary 22 23 import ( 24 "fmt" 25 "math" 26 "testing" 27 "time" 28 29 "github.com/m3db/m3/src/query/block" 30 "github.com/m3db/m3/src/query/executor/transform" 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 xtime "github.com/m3db/m3/src/x/time" 37 38 "github.com/stretchr/testify/assert" 39 "github.com/stretchr/testify/require" 40 ) 41 42 func TestOrWithExactValues(t *testing.T) { 43 values, bounds := test.GenerateValuesAndBounds(nil, nil) 44 block1 := test.NewBlockFromValues(bounds, values) 45 block2 := test.NewBlockFromValues(bounds, values) 46 47 op, err := NewOp( 48 OrType, 49 NodeParams{ 50 LNode: parser.NodeID(rune(0)), 51 RNode: parser.NodeID(rune(1)), 52 VectorMatcherBuilder: emptyVectorMatcherBuilder, 53 }, 54 ) 55 require.NoError(t, err) 56 57 c, sink := executor.NewControllerWithSink(parser.NodeID(rune(2))) 58 node := op.(baseOp).Node(c, transform.Options{}) 59 60 err = node.Process(models.NoopQueryContext(), parser.NodeID(rune(1)), block2) 61 require.NoError(t, err) 62 err = node.Process(models.NoopQueryContext(), parser.NodeID(rune(0)), block1) 63 require.NoError(t, err) 64 assert.Equal(t, values, sink.Values) 65 } 66 67 func TestOrWithSomeValues(t *testing.T) { 68 values1, bounds := test.GenerateValuesAndBounds(nil, nil) 69 block1 := test.NewBlockFromValues(bounds, values1) 70 71 v := [][]float64{ 72 {0, math.NaN(), 2, 3, 4}, 73 {math.NaN(), 6, 7, 8, 9}, 74 } 75 76 block2 := test.NewBlockFromValues(bounds, v) 77 78 op, err := NewOp( 79 OrType, 80 NodeParams{ 81 LNode: parser.NodeID(rune(0)), 82 RNode: parser.NodeID(rune(1)), 83 VectorMatcherBuilder: emptyVectorMatcherBuilder, 84 }, 85 ) 86 require.NoError(t, err) 87 88 c, sink := executor.NewControllerWithSink(parser.NodeID(rune(2))) 89 node := op.(baseOp).Node(c, transform.Options{}) 90 91 err = node.Process(models.NoopQueryContext(), parser.NodeID(rune(1)), block2) 92 require.NoError(t, err) 93 err = node.Process(models.NoopQueryContext(), parser.NodeID(rune(0)), block1) 94 require.NoError(t, err) 95 // NAN values should be filled 96 expected := values1 97 98 compare.EqualsWithNans(t, expected, sink.Values) 99 } 100 101 func generateMetaDataWithTagsInRange( 102 fromRange int, 103 toRange int, 104 ) []block.SeriesMeta { 105 length := toRange - fromRange 106 meta := make([]block.SeriesMeta, length) 107 for i := 0; i < length; i++ { 108 idx := []byte(fmt.Sprint(fromRange + i)) 109 tags := test.TagSliceToTags([]models.Tag{{Name: idx, Value: idx}}) 110 meta[i] = block.SeriesMeta{ 111 Tags: tags, 112 Name: idx, 113 } 114 } 115 return meta 116 } 117 118 var indexMatchingTests = []struct { 119 name string 120 lhs []block.SeriesMeta 121 rhs []block.SeriesMeta 122 expected []int 123 }{ 124 { 125 "equal tags", 126 generateMetaDataWithTagsInRange(0, 5), 127 generateMetaDataWithTagsInRange(0, 5), 128 []int{0, 1, 2, 3, 4}, 129 }, 130 { 131 "empty rhs", 132 generateMetaDataWithTagsInRange(0, 5), 133 []block.SeriesMeta{}, 134 []int{}, 135 }, 136 { 137 "empty lhs", 138 []block.SeriesMeta{}, 139 generateMetaDataWithTagsInRange(0, 5), 140 []int{0, 1, 2, 3, 4}, 141 }, 142 { 143 "longer rhs", 144 generateMetaDataWithTagsInRange(0, 5), 145 generateMetaDataWithTagsInRange(-1, 6), 146 []int{5, 0, 1, 2, 3, 4, 6}, 147 }, 148 { 149 "no overlap", 150 generateMetaDataWithTagsInRange(0, 5), 151 generateMetaDataWithTagsInRange(6, 9), 152 []int{5, 6, 7}, 153 }, 154 } 155 156 func TestIndexMerging(t *testing.T) { 157 matching := VectorMatching{} 158 for _, tt := range indexMatchingTests { 159 t.Run(tt.name, func(t *testing.T) { 160 matching, _ := mergeIndices(matching, tt.lhs, tt.rhs) 161 assert.Equal(t, tt.expected, matching) 162 }) 163 } 164 } 165 166 var orTests = []struct { 167 name string 168 lhsMeta []block.SeriesMeta 169 lhs [][]float64 170 rhsMeta []block.SeriesMeta 171 rhs [][]float64 172 expectedMetas []block.SeriesMeta 173 expected [][]float64 174 err error 175 }{ 176 { 177 "valid, equal tags", 178 test.NewSeriesMeta("a", 2), 179 [][]float64{{1, 2}, {10, 20}}, 180 test.NewSeriesMeta("a", 2), 181 [][]float64{{3, 4}, {30, 40}}, 182 test.NewSeriesMetaWithoutName("a", 2), 183 [][]float64{{1, 2}, {10, 20}}, 184 nil, 185 }, 186 { 187 "valid, some overlap", 188 test.NewSeriesMeta("a", 2), 189 [][]float64{{1, 2}, {10, 20}}, 190 test.NewSeriesMeta("a", 3), 191 [][]float64{{3, 4}, {30, 40}, {50, 60}}, 192 test.NewSeriesMetaWithoutName("a", 3), 193 [][]float64{{1, 2}, {10, 20}, {50, 60}}, 194 nil, 195 }, 196 { 197 "valid, some overlap, updating NaNs", 198 test.NewSeriesMeta("a", 2), 199 [][]float64{{1, math.NaN()}, {math.NaN(), 20}}, 200 test.NewSeriesMeta("a", 3), 201 [][]float64{{3, 4}, {30, 40}, {50, math.NaN()}}, 202 test.NewSeriesMetaWithoutName("a", 3), 203 [][]float64{{1, 4}, {30, 20}, {50, math.NaN()}}, 204 nil, 205 }, 206 { 207 "valid, equal size", 208 test.NewSeriesMeta("a", 2), 209 [][]float64{{1, 2}, {10, 20}}, 210 test.NewSeriesMeta("b", 2), 211 [][]float64{{3, 4}, {30, 40}}, 212 append( 213 test.NewSeriesMetaWithoutName("a", 2), 214 test.NewSeriesMetaWithoutName("b", 2)..., 215 ), 216 [][]float64{{1, 2}, {10, 20}, {3, 4}, {30, 40}}, 217 nil, 218 }, 219 { 220 "valid, longer rhs", 221 test.NewSeriesMeta("a", 2), 222 [][]float64{{1, 2}, {10, 20}}, 223 test.NewSeriesMeta("b", 3), 224 [][]float64{{3, 4}, {30, 40}, {300, 400}}, 225 append( 226 test.NewSeriesMetaWithoutName("a", 2), 227 test.NewSeriesMetaWithoutName("b", 3)..., 228 ), 229 [][]float64{{1, 2}, {10, 20}, {3, 4}, {30, 40}, {300, 400}}, 230 nil, 231 }, 232 { 233 "valid, longer lhs", 234 test.NewSeriesMeta("a", 3), 235 [][]float64{{1, 2}, {10, 20}, {100, 200}}, 236 test.NewSeriesMeta("b", 2), 237 [][]float64{{3, 4}, {30, 40}}, 238 append( 239 test.NewSeriesMetaWithoutName("a", 3), 240 test.NewSeriesMetaWithoutName("b", 2)..., 241 ), 242 [][]float64{{1, 2}, {10, 20}, {100, 200}, {3, 4}, {30, 40}}, 243 nil, 244 }, 245 { 246 "mismatched step counts", 247 test.NewSeriesMeta("a", 2), 248 [][]float64{{1, 2, 3}, {10, 20, 30}}, 249 test.NewSeriesMeta("b", 2), 250 [][]float64{{3, 4}, {30, 40}}, 251 append( 252 test.NewSeriesMetaWithoutName("a", 2), 253 test.NewSeriesMetaWithoutName("b", 2)..., 254 ), 255 [][]float64{{1, 2}, {10, 20}, {3, 4}, {30, 40}}, 256 errMismatchedStepCounts, 257 }, 258 } 259 260 func TestOrs(t *testing.T) { 261 now := xtime.Now() 262 for _, tt := range orTests { 263 t.Run(tt.name, func(t *testing.T) { 264 op, err := NewOp( 265 OrType, 266 NodeParams{ 267 LNode: parser.NodeID(rune(0)), 268 RNode: parser.NodeID(rune(1)), 269 VectorMatcherBuilder: emptyVectorMatcherBuilder, 270 }, 271 ) 272 require.NoError(t, err) 273 274 c, sink := executor.NewControllerWithSink(parser.NodeID(rune(2))) 275 node := op.(baseOp).Node(c, transform.Options{}) 276 bounds := models.Bounds{ 277 Start: now, 278 Duration: time.Minute * time.Duration(len(tt.lhs[0])), 279 StepSize: time.Minute, 280 } 281 282 lhs := test.NewBlockFromValuesWithSeriesMeta(bounds, tt.lhsMeta, tt.lhs) 283 err = node.Process(models.NoopQueryContext(), parser.NodeID(rune(0)), lhs) 284 require.NoError(t, err) 285 286 bounds = models.Bounds{ 287 Start: now, 288 Duration: time.Minute * time.Duration(len(tt.rhs[0])), 289 StepSize: time.Minute, 290 } 291 292 rhs := test.NewBlockFromValuesWithSeriesMeta(bounds, tt.rhsMeta, tt.rhs) 293 err = node.Process(models.NoopQueryContext(), parser.NodeID(rune(1)), rhs) 294 if tt.err != nil { 295 require.EqualError(t, err, tt.err.Error()) 296 return 297 } 298 299 require.NoError(t, err) 300 compare.EqualsWithNans(t, tt.expected, sink.Values) 301 assert.Equal(t, tt.expectedMetas, sink.Metas) 302 }) 303 } 304 } 305 306 func TestOrsBoundsError(t *testing.T) { 307 tt := orTests[0] 308 bounds := models.Bounds{ 309 Start: xtime.Now(), 310 Duration: time.Minute * time.Duration(len(tt.lhs[0])), 311 StepSize: time.Minute, 312 } 313 314 op, err := NewOp( 315 OrType, 316 NodeParams{ 317 LNode: parser.NodeID(rune(0)), 318 RNode: parser.NodeID(rune(1)), 319 VectorMatcherBuilder: emptyVectorMatcherBuilder, 320 }, 321 ) 322 require.NoError(t, err) 323 324 c, _ := executor.NewControllerWithSink(parser.NodeID(rune(2))) 325 node := op.(baseOp).Node(c, transform.Options{}) 326 327 lhs := test.NewBlockFromValuesWithSeriesMeta(bounds, tt.lhsMeta, tt.lhs) 328 err = node.Process(models.NoopQueryContext(), parser.NodeID(rune(0)), lhs) 329 require.NoError(t, err) 330 331 differentBounds := models.Bounds{ 332 Start: bounds.Start.Add(1), 333 Duration: bounds.Duration, 334 StepSize: bounds.StepSize, 335 } 336 rhs := test.NewBlockFromValuesWithSeriesMeta( 337 differentBounds, tt.rhsMeta, tt.rhs) 338 err = node.Process(models.NoopQueryContext(), parser.NodeID(rune(1)), rhs) 339 require.EqualError(t, err, errMismatchedBounds.Error()) 340 } 341 342 func createSeriesMeta() []block.SeriesMeta { 343 return []block.SeriesMeta{ 344 {Tags: test.TagSliceToTags([]models.Tag{ 345 {Name: []byte("foo"), Value: []byte("bar")}, 346 })}, 347 {Tags: test.TagSliceToTags([]models.Tag{ 348 {Name: []byte("baz"), Value: []byte("qux")}, 349 })}, 350 } 351 } 352 353 func TestOrCombinedMetadata(t *testing.T) { 354 op, err := NewOp( 355 OrType, 356 NodeParams{ 357 LNode: parser.NodeID(rune(0)), 358 RNode: parser.NodeID(rune(1)), 359 VectorMatcherBuilder: emptyVectorMatcherBuilder, 360 }, 361 ) 362 require.NoError(t, err) 363 364 c, sink := executor.NewControllerWithSink(parser.NodeID(rune(2))) 365 node := op.(baseOp).Node(c, transform.Options{}) 366 367 bounds := models.Bounds{ 368 Start: xtime.Now(), 369 Duration: time.Minute * 2, 370 StepSize: time.Minute, 371 } 372 373 strTags := test.StringTags{ 374 {N: "a", V: "b"}, {N: "c", V: "d"}, {N: "e", V: "f"}, 375 } 376 lhsMeta := block.Metadata{ 377 Bounds: bounds, 378 Tags: test.StringTagsToTags(strTags), 379 } 380 381 lSeriesMeta := createSeriesMeta() 382 lhs := test.NewBlockFromValuesWithMetaAndSeriesMeta( 383 lhsMeta, 384 lSeriesMeta, 385 [][]float64{{1, 2}, {10, 20}}) 386 387 err = node.Process(models.NoopQueryContext(), parser.NodeID(rune(0)), lhs) 388 require.NoError(t, err) 389 390 strTags = test.StringTags{ 391 {N: "a", V: "b"}, {N: "c", V: "*d"}, {N: "g", V: "h"}, 392 } 393 rhsMeta := block.Metadata{ 394 Bounds: bounds, 395 Tags: test.StringTagsToTags(strTags), 396 } 397 398 // NB (arnikola): since common tags for the series differ, 399 // all four series should be included in the combined 400 // block despite the individual seriesMeta tags being the same. 401 rSeriesMeta := createSeriesMeta() 402 rhs := test.NewBlockFromValuesWithMetaAndSeriesMeta( 403 rhsMeta, 404 rSeriesMeta, 405 [][]float64{{3, 4}, {30, 40}}) 406 407 err = node.Process(models.NoopQueryContext(), parser.NodeID(rune(1)), rhs) 408 require.NoError(t, err) 409 410 compare.EqualsWithNans(t, [][]float64{ 411 {1, 2}, {10, 20}, {3, 4}, {30, 40}, 412 }, sink.Values) 413 414 assert.Equal(t, sink.Meta.Bounds, bounds) 415 exTags := test.TagSliceToTags([]models.Tag{ 416 {Name: []byte("a"), Value: []byte("b")}, 417 }) 418 assert.Equal(t, exTags.Tags, sink.Meta.Tags.Tags) 419 420 stringTags := []test.StringTags{ 421 {{N: "c", V: "d"}, {N: "e", V: "f"}, {N: "foo", V: "bar"}}, 422 {{N: "baz", V: "qux"}, {N: "c", V: "d"}, {N: "e", V: "f"}}, 423 {{N: "c", V: "*d"}, {N: "foo", V: "bar"}, {N: "g", V: "h"}}, 424 {{N: "baz", V: "qux"}, {N: "c", V: "*d"}, {N: "g", V: "h"}}, 425 } 426 427 tags := test.StringTagsSliceToTagSlice(stringTags) 428 expectedMetas := make([]block.SeriesMeta, len(tags)) 429 for i, t := range tags { 430 expectedMetas[i] = block.SeriesMeta{Tags: t} 431 } 432 433 assert.Equal(t, expectedMetas, sink.Metas) 434 }