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  }