github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/processors/query/operator-order-impl_test.go (about) 1 /* 2 * Copyright (c) 2021-present unTill Pro, Ltd. 3 */ 4 5 package queryprocessor 6 7 import ( 8 "context" 9 "testing" 10 11 "github.com/stretchr/testify/require" 12 13 "github.com/voedger/voedger/pkg/pipeline" 14 ) 15 16 func TestOrderOperator_Flush(t *testing.T) { 17 work := func(id int64, name string, departmentNumber int64, weight float64) pipeline.IWorkpiece { 18 return rowsWorkpiece{ 19 outputRow: &outputRow{ 20 keyToIdx: map[string]int{rootDocument: 0}, 21 values: []interface{}{ 22 []IOutputRow{ 23 &outputRow{ 24 keyToIdx: map[string]int{ 25 "id": 0, 26 "name": 1, 27 "department_number": 2, 28 "weight": 3, 29 }, 30 values: []interface{}{id, name, departmentNumber, weight}, 31 }, 32 }, 33 }, 34 }, 35 } 36 } 37 id := func(work pipeline.IWorkpiece) int64 { 38 return work.(rowsWorkpiece).OutputRow().Value(rootDocument).([]IOutputRow)[0].Values()[0].(int64) 39 } 40 name := func(work pipeline.IWorkpiece) string { 41 return work.(rowsWorkpiece).OutputRow().Value(rootDocument).([]IOutputRow)[0].Values()[1].(string) 42 } 43 departmentNumber := func(work pipeline.IWorkpiece) int64 { 44 return work.(rowsWorkpiece).OutputRow().Value(rootDocument).([]IOutputRow)[0].Values()[2].(int64) 45 } 46 weight := func(work pipeline.IWorkpiece) float64 { 47 return work.(rowsWorkpiece).OutputRow().Value(rootDocument).([]IOutputRow)[0].Values()[3].(float64) 48 } 49 t.Run("Should order by one int64 field asc", func(t *testing.T) { 50 require := require.New(t) 51 orders := []IOrderBy{ 52 orderBy{ 53 field: "id", 54 desc: false, 55 }} 56 operator := newOrderOperator(orders, &testMetrics{}) 57 works := make([]pipeline.IWorkpiece, 0) 58 59 _, _ = operator.DoAsync(context.Background(), work(1, "Cola", 100, 1.15)) 60 _, _ = operator.DoAsync(context.Background(), work(42, "Sprite", 100, 2.0)) 61 _, _ = operator.DoAsync(context.Background(), work(5, "Pepsi", 100, 1.75)) 62 63 _ = operator.Flush(func(work pipeline.IWorkpiece) { 64 works = append(works, work) 65 }) 66 67 require.Equal(int64(1), id(works[0])) 68 require.Equal("Cola", name(works[0])) 69 require.Equal(int64(5), id(works[1])) 70 require.Equal("Pepsi", name(works[1])) 71 require.Equal(int64(42), id(works[2])) 72 require.Equal("Sprite", name(works[2])) 73 }) 74 t.Run("Should order by one int64 field desc", func(t *testing.T) { 75 require := require.New(t) 76 orders := []IOrderBy{ 77 orderBy{ 78 field: "id", 79 desc: true, 80 }} 81 operator := newOrderOperator(orders, &testMetrics{}) 82 works := make([]pipeline.IWorkpiece, 0) 83 84 _, _ = operator.DoAsync(context.Background(), work(1, "Cola", 100, 1.15)) 85 _, _ = operator.DoAsync(context.Background(), work(42, "Sprite", 100, 2.0)) 86 _, _ = operator.DoAsync(context.Background(), work(5, "Pepsi", 100, 1.75)) 87 88 _ = operator.Flush(func(work pipeline.IWorkpiece) { 89 works = append(works, work) 90 }) 91 92 require.Equal(int64(42), id(works[0])) 93 require.Equal("Sprite", name(works[0])) 94 require.Equal(int64(5), id(works[1])) 95 require.Equal("Pepsi", name(works[1])) 96 require.Equal(int64(1), id(works[2])) 97 require.Equal("Cola", name(works[2])) 98 }) 99 t.Run("Should order by one string field asc", func(t *testing.T) { 100 require := require.New(t) 101 orders := []IOrderBy{ 102 orderBy{ 103 field: "name", 104 desc: false, 105 }} 106 operator := newOrderOperator(orders, &testMetrics{}) 107 works := make([]pipeline.IWorkpiece, 0) 108 109 _, _ = operator.DoAsync(context.Background(), work(1, "Cola", 100, 1.15)) 110 _, _ = operator.DoAsync(context.Background(), work(42, "Sprite", 100, 2.0)) 111 _, _ = operator.DoAsync(context.Background(), work(5, "Pepsi", 100, 1.75)) 112 113 _ = operator.Flush(func(work pipeline.IWorkpiece) { 114 works = append(works, work) 115 }) 116 117 require.Equal(int64(1), id(works[0])) 118 require.Equal("Cola", name(works[0])) 119 require.Equal(int64(5), id(works[1])) 120 require.Equal("Pepsi", name(works[1])) 121 require.Equal(int64(42), id(works[2])) 122 require.Equal("Sprite", name(works[2])) 123 }) 124 t.Run("Should order by one string field desc", func(t *testing.T) { 125 require := require.New(t) 126 orders := []IOrderBy{ 127 orderBy{ 128 field: "name", 129 desc: true, 130 }} 131 operator := newOrderOperator(orders, &testMetrics{}) 132 works := make([]pipeline.IWorkpiece, 0) 133 134 _, _ = operator.DoAsync(context.Background(), work(1, "Cola", 100, 1.15)) 135 _, _ = operator.DoAsync(context.Background(), work(42, "Sprite", 100, 2.0)) 136 _, _ = operator.DoAsync(context.Background(), work(5, "Pepsi", 100, 1.75)) 137 138 _ = operator.Flush(func(work pipeline.IWorkpiece) { 139 works = append(works, work) 140 }) 141 142 require.Equal(int64(42), id(works[0])) 143 require.Equal("Sprite", name(works[0])) 144 require.Equal(int64(5), id(works[1])) 145 require.Equal("Pepsi", name(works[1])) 146 require.Equal(int64(1), id(works[2])) 147 require.Equal("Cola", name(works[2])) 148 }) 149 t.Run("Should order by one float64 field asc", func(t *testing.T) { 150 require := require.New(t) 151 orders := []IOrderBy{ 152 orderBy{ 153 field: "weight", 154 desc: false, 155 }} 156 operator := newOrderOperator(orders, &testMetrics{}) 157 works := make([]pipeline.IWorkpiece, 0) 158 159 _, _ = operator.DoAsync(context.Background(), work(1, "Cola", 100, 1.15)) 160 _, _ = operator.DoAsync(context.Background(), work(42, "Sprite", 100, 2.0)) 161 _, _ = operator.DoAsync(context.Background(), work(5, "Pepsi", 100, 1.75)) 162 163 _ = operator.Flush(func(work pipeline.IWorkpiece) { 164 works = append(works, work) 165 }) 166 167 require.Equal(1.15, weight(works[0])) 168 require.Equal("Cola", name(works[0])) 169 require.Equal(1.75, weight(works[1])) 170 require.Equal("Pepsi", name(works[1])) 171 require.Equal(2.0, weight(works[2])) 172 require.Equal("Sprite", name(works[2])) 173 }) 174 t.Run("Should order by one float64 field desc", func(t *testing.T) { 175 require := require.New(t) 176 orders := []IOrderBy{ 177 orderBy{ 178 field: "weight", 179 desc: true, 180 }} 181 operator := newOrderOperator(orders, &testMetrics{}) 182 works := make([]pipeline.IWorkpiece, 0) 183 184 _, _ = operator.DoAsync(context.Background(), work(1, "Cola", 100, 1.15)) 185 _, _ = operator.DoAsync(context.Background(), work(42, "Sprite", 100, 2.0)) 186 _, _ = operator.DoAsync(context.Background(), work(5, "Pepsi", 100, 1.75)) 187 188 _ = operator.Flush(func(work pipeline.IWorkpiece) { 189 works = append(works, work) 190 }) 191 192 require.Equal(2.0, weight(works[0])) 193 require.Equal("Sprite", name(works[0])) 194 require.Equal(1.75, weight(works[1])) 195 require.Equal("Pepsi", name(works[1])) 196 require.Equal(1.15, weight(works[2])) 197 require.Equal("Cola", name(works[2])) 198 }) 199 t.Run("Should order by two fields asc", func(t *testing.T) { 200 require := require.New(t) 201 orders := []IOrderBy{ 202 orderBy{ 203 field: "department_number", 204 desc: false, 205 }, 206 orderBy{ 207 field: "name", 208 desc: false, 209 }} 210 operator := newOrderOperator(orders, &testMetrics{}) 211 works := make([]pipeline.IWorkpiece, 0) 212 213 _, _ = operator.DoAsync(context.Background(), work(1, "Xenta", 100, 1.45)) 214 _, _ = operator.DoAsync(context.Background(), work(2, "Amaretto", 100, 2.0)) 215 _, _ = operator.DoAsync(context.Background(), work(3, "Vodka", 200, 2.13)) 216 _, _ = operator.DoAsync(context.Background(), work(4, "Sherry", 200, 1.7)) 217 218 _ = operator.Flush(func(work pipeline.IWorkpiece) { 219 works = append(works, work) 220 }) 221 222 require.Equal(int64(100), departmentNumber(works[0])) 223 require.Equal("Amaretto", name(works[0])) 224 require.Equal(int64(100), departmentNumber(works[1])) 225 require.Equal("Xenta", name(works[1])) 226 require.Equal(int64(200), departmentNumber(works[2])) 227 require.Equal("Sherry", name(works[2])) 228 require.Equal(int64(200), departmentNumber(works[3])) 229 require.Equal("Vodka", name(works[3])) 230 }) 231 t.Run("Should order by two fields desc", func(t *testing.T) { 232 require := require.New(t) 233 orders := []IOrderBy{ 234 orderBy{ 235 field: "department_number", 236 desc: true, 237 }, 238 orderBy{ 239 field: "name", 240 desc: true, 241 }} 242 operator := newOrderOperator(orders, &testMetrics{}) 243 works := make([]pipeline.IWorkpiece, 0) 244 245 _, _ = operator.DoAsync(context.Background(), work(1, "Xenta", 100, 1.45)) 246 _, _ = operator.DoAsync(context.Background(), work(2, "Amaretto", 100, 2.0)) 247 _, _ = operator.DoAsync(context.Background(), work(3, "Vodka", 200, 2.13)) 248 _, _ = operator.DoAsync(context.Background(), work(4, "Sherry", 200, 1.7)) 249 250 _ = operator.Flush(func(work pipeline.IWorkpiece) { 251 works = append(works, work) 252 }) 253 254 require.Equal(int64(200), departmentNumber(works[0])) 255 require.Equal("Vodka", name(works[0])) 256 require.Equal(int64(200), departmentNumber(works[1])) 257 require.Equal("Sherry", name(works[1])) 258 require.Equal(int64(100), departmentNumber(works[2])) 259 require.Equal("Xenta", name(works[2])) 260 require.Equal(int64(100), departmentNumber(works[3])) 261 require.Equal("Amaretto", name(works[3])) 262 }) 263 t.Run("Should order by two fields first field is asc second filed is desc", func(t *testing.T) { 264 require := require.New(t) 265 orders := []IOrderBy{ 266 orderBy{ 267 field: "department_number", 268 desc: false, 269 }, 270 orderBy{ 271 field: "name", 272 desc: true, 273 }} 274 operator := newOrderOperator(orders, &testMetrics{}) 275 works := make([]pipeline.IWorkpiece, 0) 276 277 _, _ = operator.DoAsync(context.Background(), work(1, "Xenta", 100, 1.45)) 278 _, _ = operator.DoAsync(context.Background(), work(2, "Amaretto", 100, 2.0)) 279 _, _ = operator.DoAsync(context.Background(), work(3, "Vodka", 200, 2.13)) 280 _, _ = operator.DoAsync(context.Background(), work(4, "Sherry", 200, 1.7)) 281 282 _ = operator.Flush(func(work pipeline.IWorkpiece) { 283 works = append(works, work) 284 }) 285 286 require.Equal(int64(100), departmentNumber(works[0])) 287 require.Equal("Xenta", name(works[0])) 288 require.Equal(int64(100), departmentNumber(works[1])) 289 require.Equal("Amaretto", name(works[1])) 290 require.Equal(int64(200), departmentNumber(works[2])) 291 require.Equal("Vodka", name(works[2])) 292 require.Equal(int64(200), departmentNumber(works[3])) 293 require.Equal("Sherry", name(works[3])) 294 }) 295 t.Run("Should order by two fields first field is desc second filed is asc", func(t *testing.T) { 296 require := require.New(t) 297 orders := []IOrderBy{ 298 orderBy{ 299 field: "department_number", 300 desc: true, 301 }, 302 orderBy{ 303 field: "name", 304 desc: false, 305 }} 306 operator := newOrderOperator(orders, &testMetrics{}) 307 works := make([]pipeline.IWorkpiece, 0) 308 309 _, _ = operator.DoAsync(context.Background(), work(1, "Xenta", 100, 1.45)) 310 _, _ = operator.DoAsync(context.Background(), work(2, "Amaretto", 100, 2.0)) 311 _, _ = operator.DoAsync(context.Background(), work(3, "Vodka", 200, 2.13)) 312 _, _ = operator.DoAsync(context.Background(), work(4, "Sherry", 200, 1.7)) 313 314 _ = operator.Flush(func(work pipeline.IWorkpiece) { 315 works = append(works, work) 316 }) 317 318 require.Equal(int64(200), departmentNumber(works[0])) 319 require.Equal("Sherry", name(works[0])) 320 require.Equal(int64(200), departmentNumber(works[1])) 321 require.Equal("Vodka", name(works[1])) 322 require.Equal(int64(100), departmentNumber(works[2])) 323 require.Equal("Amaretto", name(works[2])) 324 require.Equal(int64(100), departmentNumber(works[3])) 325 require.Equal("Xenta", name(works[3])) 326 }) 327 t.Run("Should return data type unspecified error", func(t *testing.T) { 328 require := require.New(t) 329 work := func(flag bool) pipeline.IWorkpiece { 330 return rowsWorkpiece{ 331 outputRow: &outputRow{ 332 keyToIdx: map[string]int{rootDocument: 0}, 333 values: []interface{}{ 334 []IOutputRow{ 335 &outputRow{ 336 keyToIdx: map[string]int{"flag": 0}, 337 values: []interface{}{flag}, 338 }, 339 }, 340 }, 341 }, 342 } 343 } 344 orders := []IOrderBy{ 345 orderBy{ 346 field: "flag", 347 desc: false, 348 }, 349 } 350 operator := newOrderOperator(orders, &testMetrics{}) 351 352 _, _ = operator.DoAsync(context.Background(), work(true)) 353 _, _ = operator.DoAsync(context.Background(), work(false)) 354 355 err := operator.Flush(func(work pipeline.IWorkpiece) { 356 t.Fatal("must not be call") 357 }) 358 359 require.ErrorIs(err, ErrWrongType) 360 }) 361 t.Run("Should order by int32 field", func(t *testing.T) { 362 work := func(x, y int32) pipeline.IWorkpiece { 363 return rowsWorkpiece{outputRow: &outputRow{ 364 keyToIdx: map[string]int{rootDocument: 0}, 365 values: []interface{}{ 366 []IOutputRow{&outputRow{ 367 keyToIdx: map[string]int{ 368 "x": 0, 369 "y": 1, 370 }, 371 values: []interface{}{x, y}, 372 }}, 373 }, 374 }} 375 } 376 x := func(work pipeline.IWorkpiece) int32 { 377 return work.(rowsWorkpiece).OutputRow().Value(rootDocument).([]IOutputRow)[0].Values()[0].(int32) 378 } 379 y := func(work pipeline.IWorkpiece) int32 { 380 return work.(rowsWorkpiece).OutputRow().Value(rootDocument).([]IOutputRow)[0].Values()[1].(int32) 381 } 382 t.Run("Asc order", func(t *testing.T) { 383 require := require.New(t) 384 orders := []IOrderBy{ 385 orderBy{ 386 field: "x", 387 desc: false, 388 }} 389 operator := newOrderOperator(orders, &testMetrics{}) 390 works := make([]pipeline.IWorkpiece, 0) 391 392 _, _ = operator.DoAsync(context.Background(), work(0, 1)) 393 _, _ = operator.DoAsync(context.Background(), work(2, 3)) 394 395 _ = operator.Flush(func(work pipeline.IWorkpiece) { 396 works = append(works, work) 397 }) 398 399 require.Equal(int32(0), x(works[0])) 400 require.Equal(int32(1), y(works[0])) 401 require.Equal(int32(2), x(works[1])) 402 require.Equal(int32(3), y(works[1])) 403 }) 404 t.Run("Desc order", func(t *testing.T) { 405 require := require.New(t) 406 orders := []IOrderBy{ 407 orderBy{ 408 field: "x", 409 desc: true, 410 }} 411 operator := newOrderOperator(orders, &testMetrics{}) 412 works := make([]pipeline.IWorkpiece, 0) 413 414 _, _ = operator.DoAsync(context.Background(), work(0, 1)) 415 _, _ = operator.DoAsync(context.Background(), work(2, 3)) 416 417 _ = operator.Flush(func(work pipeline.IWorkpiece) { 418 works = append(works, work) 419 }) 420 421 require.Equal(int32(2), x(works[0])) 422 require.Equal(int32(3), y(works[0])) 423 require.Equal(int32(0), x(works[1])) 424 require.Equal(int32(1), y(works[1])) 425 }) 426 }) 427 t.Run("Should order by float32 field", func(t *testing.T) { 428 work := func(temperature float32) pipeline.IWorkpiece { 429 return rowsWorkpiece{outputRow: &outputRow{ 430 keyToIdx: map[string]int{rootDocument: 0}, 431 values: []interface{}{ 432 []IOutputRow{&outputRow{ 433 keyToIdx: map[string]int{"temperature": 0}, 434 values: []interface{}{temperature}, 435 }}, 436 }, 437 }} 438 } 439 temperature := func(work pipeline.IWorkpiece) float32 { 440 return work.(rowsWorkpiece).OutputRow().Value(rootDocument).([]IOutputRow)[0].Values()[0].(float32) 441 } 442 t.Run("Asc order", func(t *testing.T) { 443 require := require.New(t) 444 orders := []IOrderBy{ 445 orderBy{ 446 field: "x", 447 desc: false, 448 }} 449 operator := newOrderOperator(orders, &testMetrics{}) 450 works := make([]pipeline.IWorkpiece, 0) 451 452 _, _ = operator.DoAsync(context.Background(), work(22.5)) 453 _, _ = operator.DoAsync(context.Background(), work(-7.2)) 454 _, _ = operator.DoAsync(context.Background(), work(15.3)) 455 456 _ = operator.Flush(func(work pipeline.IWorkpiece) { 457 works = append(works, work) 458 }) 459 460 require.Equal(float32(-7.2), temperature(works[0])) 461 require.Equal(float32(15.3), temperature(works[1])) 462 require.Equal(float32(22.5), temperature(works[2])) 463 }) 464 t.Run("Desc order", func(t *testing.T) { 465 require := require.New(t) 466 orders := []IOrderBy{ 467 orderBy{ 468 field: "x", 469 desc: true, 470 }} 471 operator := newOrderOperator(orders, &testMetrics{}) 472 works := make([]pipeline.IWorkpiece, 0) 473 474 _, _ = operator.DoAsync(context.Background(), work(22.5)) 475 _, _ = operator.DoAsync(context.Background(), work(-7.2)) 476 _, _ = operator.DoAsync(context.Background(), work(15.3)) 477 478 _ = operator.Flush(func(work pipeline.IWorkpiece) { 479 works = append(works, work) 480 }) 481 482 require.Equal(float32(22.5), temperature(works[0])) 483 require.Equal(float32(15.3), temperature(works[1])) 484 require.Equal(float32(-7.2), temperature(works[2])) 485 }) 486 }) 487 } 488 489 func TestOrderOperator_DoAsync(t *testing.T) { 490 //TODO 491 require := require.New(t) 492 release := false 493 work := testWorkpiece{ 494 outputRow: &testOutputRow{}, 495 release: func() { 496 release = true 497 }, 498 } 499 operator := newOrderOperator(nil, &testMetrics{}) 500 501 _, _ = operator.DoAsync(context.Background(), work) 502 503 require.Len(operator.(*OrderOperator).rows, 1) 504 require.True(release) 505 }