github.com/weaviate/weaviate@v1.24.6/adapters/repos/db/sorter/objects_sorter_test.go (about)

     1  //                           _       _
     2  // __      _____  __ ___   ___  __ _| |_ ___
     3  // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \
     4  //  \ V  V /  __/ (_| |\ V /| | (_| | ||  __/
     5  //   \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___|
     6  //
     7  //  Copyright © 2016 - 2024 Weaviate B.V. All rights reserved.
     8  //
     9  //  CONTACT: hello@weaviate.io
    10  //
    11  
    12  package sorter
    13  
    14  import (
    15  	"reflect"
    16  	"testing"
    17  
    18  	"github.com/stretchr/testify/require"
    19  	"github.com/weaviate/weaviate/entities/filters"
    20  	"github.com/weaviate/weaviate/entities/storobj"
    21  )
    22  
    23  func TestObjectsSorter(t *testing.T) {
    24  	tests := []struct {
    25  		name      string
    26  		sort      []filters.Sort
    27  		limit     int
    28  		wantObjs  []*storobj.Object
    29  		wantDists []float32
    30  	}{
    31  		{
    32  			name:      "sort by string asc",
    33  			sort:      sort1("name", "asc"),
    34  			limit:     3,
    35  			wantObjs:  []*storobj.Object{cityAmsterdam, cityBerlin, cityNewYork, cityNil, cityNil2, cityWroclaw},
    36  			wantDists: []float32{0.4, 0.2, 0.3, 0.0, 0.0, 0.1},
    37  		},
    38  		{
    39  			name:      "sort by string desc",
    40  			sort:      sort1("name", "desc"),
    41  			limit:     4,
    42  			wantObjs:  []*storobj.Object{cityWroclaw, cityNil2, cityNil, cityNewYork, cityBerlin, cityAmsterdam},
    43  			wantDists: []float32{0.1, 0.0, 0.0, 0.3, 0.2, 0.4},
    44  		},
    45  		{
    46  			name:      "sort by text asc",
    47  			sort:      sort1("country", "asc"),
    48  			limit:     5,
    49  			wantObjs:  []*storobj.Object{cityNil2, cityNil, cityBerlin, cityWroclaw, cityAmsterdam, cityNewYork},
    50  			wantDists: []float32{0.0, 0.0, 0.2, 0.1, 0.4, 0.3},
    51  		},
    52  		{
    53  			name:      "sort by text desc",
    54  			sort:      sort1("country", "desc"),
    55  			limit:     3,
    56  			wantObjs:  []*storobj.Object{cityNewYork, cityAmsterdam, cityWroclaw, cityBerlin, cityNil2, cityNil},
    57  			wantDists: []float32{0.3, 0.4, 0.1, 0.2, 0.0, 0.0},
    58  		},
    59  		{
    60  			name:      "sort by int asc",
    61  			sort:      sort1("population", "asc"),
    62  			limit:     4,
    63  			wantObjs:  []*storobj.Object{cityNil2, cityNil, cityWroclaw, cityAmsterdam, cityBerlin, cityNewYork},
    64  			wantDists: []float32{0.0, 0.0, 0.1, 0.4, 0.2, 0.3},
    65  		},
    66  		{
    67  			name:      "sort by int desc",
    68  			sort:      sort1("population", "desc"),
    69  			limit:     5,
    70  			wantObjs:  []*storobj.Object{cityNewYork, cityBerlin, cityAmsterdam, cityWroclaw, cityNil2, cityNil},
    71  			wantDists: []float32{0.3, 0.2, 0.4, 0.1, 0.0, 0.0},
    72  		},
    73  		{
    74  			name:      "sort by number asc",
    75  			sort:      sort1("cityArea", "asc"),
    76  			limit:     3,
    77  			wantObjs:  []*storobj.Object{cityNil2, cityNil, cityAmsterdam, cityWroclaw, cityBerlin, cityNewYork},
    78  			wantDists: []float32{0.0, 0.0, 0.4, 0.1, 0.2, 0.3},
    79  		},
    80  		{
    81  			name:      "sort by number desc",
    82  			sort:      sort1("cityArea", "desc"),
    83  			limit:     4,
    84  			wantObjs:  []*storobj.Object{cityNewYork, cityBerlin, cityWroclaw, cityAmsterdam, cityNil2, cityNil},
    85  			wantDists: []float32{0.3, 0.2, 0.1, 0.4, 0.0, 0.0},
    86  		},
    87  		{
    88  			name:      "sort by date asc",
    89  			sort:      sort1("cityRights", "asc"),
    90  			limit:     5,
    91  			wantObjs:  []*storobj.Object{cityNil2, cityNil, cityAmsterdam, cityWroclaw, cityBerlin, cityNewYork},
    92  			wantDists: []float32{0.0, 0.0, 0.4, 0.1, 0.2, 0.3},
    93  		},
    94  		{
    95  			name:      "sort by date desc",
    96  			sort:      sort1("cityRights", "desc"),
    97  			limit:     3,
    98  			wantObjs:  []*storobj.Object{cityNewYork, cityBerlin, cityWroclaw, cityAmsterdam, cityNil2, cityNil},
    99  			wantDists: []float32{0.3, 0.2, 0.1, 0.4, 0.0, 0.0},
   100  		},
   101  		{
   102  			name:      "sort by string array asc",
   103  			sort:      sort1("timezones", "asc"),
   104  			limit:     4,
   105  			wantObjs:  []*storobj.Object{cityNil2, cityNil, cityWroclaw, cityBerlin, cityAmsterdam, cityNewYork},
   106  			wantDists: []float32{0.0, 0.0, 0.1, 0.2, 0.4, 0.3},
   107  		},
   108  		{
   109  			name:      "sort by string array desc",
   110  			sort:      sort1("timezones", "desc"),
   111  			limit:     5,
   112  			wantObjs:  []*storobj.Object{cityNewYork, cityWroclaw, cityBerlin, cityAmsterdam, cityNil2, cityNil},
   113  			wantDists: []float32{0.3, 0.1, 0.2, 0.4, 0.0, 0.0},
   114  		},
   115  		{
   116  			name:      "sort by text array asc",
   117  			sort:      sort1("timezonesUTC", "asc"),
   118  			limit:     3,
   119  			wantObjs:  []*storobj.Object{cityNil2, cityNil, cityWroclaw, cityBerlin, cityAmsterdam, cityNewYork},
   120  			wantDists: []float32{0.0, 0.0, 0.1, 0.2, 0.4, 0.3},
   121  		},
   122  		{
   123  			name:      "sort by text array desc",
   124  			sort:      sort1("timezonesUTC", "desc"),
   125  			limit:     4,
   126  			wantObjs:  []*storobj.Object{cityNewYork, cityWroclaw, cityBerlin, cityAmsterdam, cityNil2, cityNil},
   127  			wantDists: []float32{0.3, 0.1, 0.2, 0.4, 0.0, 0.0},
   128  		},
   129  		{
   130  			name:      "sort by bool asc",
   131  			sort:      sort1("isCapital", "asc"),
   132  			limit:     5,
   133  			wantObjs:  []*storobj.Object{cityNil2, cityNil, cityWroclaw, cityNewYork, cityBerlin, cityAmsterdam},
   134  			wantDists: []float32{0.0, 0.0, 0.1, 0.3, 0.2, 0.4},
   135  		},
   136  		{
   137  			name:      "sort by bool desc",
   138  			sort:      sort1("isCapital", "desc"),
   139  			limit:     3,
   140  			wantObjs:  []*storobj.Object{cityBerlin, cityAmsterdam, cityWroclaw, cityNewYork, cityNil2, cityNil},
   141  			wantDists: []float32{0.2, 0.4, 0.1, 0.3, 0.0, 0.0},
   142  		},
   143  		{
   144  			name:      "sort by bool array asc",
   145  			sort:      sort1("isCapitalArray", "asc"),
   146  			limit:     4,
   147  			wantObjs:  []*storobj.Object{cityNil2, cityNil, cityWroclaw, cityBerlin, cityAmsterdam, cityNewYork},
   148  			wantDists: []float32{0.0, 0.0, 0.1, 0.2, 0.4, 0.3},
   149  		},
   150  		{
   151  			name:      "sort by bool array desc",
   152  			sort:      sort1("isCapitalArray", "desc"),
   153  			limit:     5,
   154  			wantObjs:  []*storobj.Object{cityNewYork, cityAmsterdam, cityBerlin, cityWroclaw, cityNil2, cityNil},
   155  			wantDists: []float32{0.3, 0.4, 0.2, 0.1, 0.0, 0.0},
   156  		},
   157  		{
   158  			name:      "sort by number array asc",
   159  			sort:      sort1("favoriteNumbers", "asc"),
   160  			limit:     3,
   161  			wantObjs:  []*storobj.Object{cityNil2, cityNil, cityNewYork, cityWroclaw, cityBerlin, cityAmsterdam},
   162  			wantDists: []float32{0.0, 0.0, 0.3, 0.1, 0.2, 0.4},
   163  		},
   164  		{
   165  			name:      "sort by number array desc",
   166  			sort:      sort1("favoriteNumbers", "desc"),
   167  			limit:     4,
   168  			wantObjs:  []*storobj.Object{cityAmsterdam, cityBerlin, cityWroclaw, cityNewYork, cityNil2, cityNil},
   169  			wantDists: []float32{0.4, 0.2, 0.1, 0.3, 0.0, 0.0},
   170  		},
   171  		{
   172  			name:      "sort by int array asc",
   173  			sort:      sort1("favoriteInts", "asc"),
   174  			limit:     5,
   175  			wantObjs:  []*storobj.Object{cityNil2, cityNil, cityNewYork, cityWroclaw, cityBerlin, cityAmsterdam},
   176  			wantDists: []float32{0.0, 0.0, 0.3, 0.1, 0.2, 0.4},
   177  		},
   178  		{
   179  			name:      "sort by int array desc",
   180  			sort:      sort1("favoriteInts", "desc"),
   181  			limit:     3,
   182  			wantObjs:  []*storobj.Object{cityAmsterdam, cityBerlin, cityWroclaw, cityNewYork, cityNil2, cityNil},
   183  			wantDists: []float32{0.4, 0.2, 0.1, 0.3, 0.0, 0.0},
   184  		},
   185  		{
   186  			name:      "sort by date array asc",
   187  			sort:      sort1("favoriteDates", "asc"),
   188  			limit:     4,
   189  			wantObjs:  []*storobj.Object{cityNil2, cityNil, cityAmsterdam, cityWroclaw, cityBerlin, cityNewYork},
   190  			wantDists: []float32{0.0, 0.0, 0.4, 0.1, 0.2, 0.3},
   191  		},
   192  		{
   193  			name:      "sort by date array desc",
   194  			sort:      sort1("favoriteDates", "desc"),
   195  			limit:     5,
   196  			wantObjs:  []*storobj.Object{cityNewYork, cityBerlin, cityWroclaw, cityAmsterdam, cityNil2, cityNil},
   197  			wantDists: []float32{0.3, 0.2, 0.1, 0.4, 0.0, 0.0},
   198  		},
   199  		{
   200  			name:      "sort by phoneNumber asc",
   201  			sort:      sort1("phoneNumber", "asc"),
   202  			limit:     3,
   203  			wantObjs:  []*storobj.Object{cityNil2, cityNil, cityWroclaw, cityAmsterdam, cityNewYork, cityBerlin},
   204  			wantDists: []float32{0.0, 0.0, 0.1, 0.4, 0.3, 0.2},
   205  		},
   206  		{
   207  			name:      "sort by phoneNumber desc",
   208  			sort:      sort1("phoneNumber", "desc"),
   209  			limit:     4,
   210  			wantObjs:  []*storobj.Object{cityBerlin, cityNewYork, cityAmsterdam, cityWroclaw, cityNil2, cityNil},
   211  			wantDists: []float32{0.2, 0.3, 0.4, 0.1, 0.0, 0.0},
   212  		},
   213  		{
   214  			name:      "sort by location asc",
   215  			sort:      sort1("location", "asc"),
   216  			limit:     5,
   217  			wantObjs:  []*storobj.Object{cityNil2, cityNil, cityNewYork, cityAmsterdam, cityBerlin, cityWroclaw},
   218  			wantDists: []float32{0.0, 0.0, 0.3, 0.4, 0.2, 0.1},
   219  		},
   220  		{
   221  			name:      "sort by location desc",
   222  			sort:      sort1("location", "desc"),
   223  			limit:     3,
   224  			wantObjs:  []*storobj.Object{cityWroclaw, cityBerlin, cityAmsterdam, cityNewYork, cityNil2, cityNil},
   225  			wantDists: []float32{0.1, 0.2, 0.4, 0.3, 0.0, 0.0},
   226  		},
   227  		{
   228  			name:      "sort by special id property asc",
   229  			sort:      sort1("id", "asc"),
   230  			limit:     4,
   231  			wantObjs:  []*storobj.Object{cityAmsterdam, cityBerlin, cityNewYork, cityNil, cityNil2, cityWroclaw},
   232  			wantDists: []float32{0.4, 0.2, 0.3, 0.0, 0.0, 0.1},
   233  		},
   234  		{
   235  			name:      "sort by special id property desc",
   236  			sort:      sort1("id", "desc"),
   237  			limit:     5,
   238  			wantObjs:  []*storobj.Object{cityWroclaw, cityNil2, cityNil, cityNewYork, cityBerlin, cityAmsterdam},
   239  			wantDists: []float32{0.1, 0.0, 0.0, 0.3, 0.2, 0.4},
   240  		},
   241  		{
   242  			name:      "sort by special _id property asc",
   243  			sort:      sort1("_id", "asc"),
   244  			limit:     3,
   245  			wantObjs:  []*storobj.Object{cityAmsterdam, cityBerlin, cityNewYork, cityNil, cityNil2, cityWroclaw},
   246  			wantDists: []float32{0.4, 0.2, 0.3, 0.0, 0.0, 0.1},
   247  		},
   248  		{
   249  			name:      "sort by special _id property desc",
   250  			sort:      sort1("_id", "desc"),
   251  			limit:     4,
   252  			wantObjs:  []*storobj.Object{cityWroclaw, cityNil2, cityNil, cityNewYork, cityBerlin, cityAmsterdam},
   253  			wantDists: []float32{0.1, 0.0, 0.0, 0.3, 0.2, 0.4},
   254  		},
   255  		{
   256  			name:      "sort by special _creationTimeUnix property asc",
   257  			sort:      sort1("_creationTimeUnix", "asc"),
   258  			limit:     5,
   259  			wantObjs:  []*storobj.Object{cityAmsterdam, cityBerlin, cityNewYork, cityNil, cityNil2, cityWroclaw},
   260  			wantDists: []float32{0.4, 0.2, 0.3, 0.0, 0.0, 0.1},
   261  		},
   262  		{
   263  			name:      "sort by special _creationTimeUnix property desc",
   264  			sort:      sort1("_creationTimeUnix", "desc"),
   265  			limit:     3,
   266  			wantObjs:  []*storobj.Object{cityWroclaw, cityNil2, cityNil, cityNewYork, cityBerlin, cityAmsterdam},
   267  			wantDists: []float32{0.1, 0.0, 0.0, 0.3, 0.2, 0.4},
   268  		},
   269  		{
   270  			name:      "sort by special _lastUpdateTimeUnix property asc",
   271  			sort:      sort1("_lastUpdateTimeUnix", "asc"),
   272  			limit:     4,
   273  			wantObjs:  []*storobj.Object{cityAmsterdam, cityBerlin, cityNewYork, cityNil, cityNil2, cityWroclaw},
   274  			wantDists: []float32{0.4, 0.2, 0.3, 0.0, 0.0, 0.1},
   275  		},
   276  		{
   277  			name:      "sort by special _lastUpdateTimeUnix property desc",
   278  			sort:      sort1("_lastUpdateTimeUnix", "desc"),
   279  			limit:     5,
   280  			wantObjs:  []*storobj.Object{cityWroclaw, cityNil2, cityNil, cityNewYork, cityBerlin, cityAmsterdam},
   281  			wantDists: []float32{0.1, 0.0, 0.0, 0.3, 0.2, 0.4},
   282  		},
   283  		{
   284  			name:      "sort by isCapital asc & name asc",
   285  			sort:      sort2("isCapital", "asc", "name", "asc"),
   286  			limit:     3,
   287  			wantObjs:  []*storobj.Object{cityNil, cityNil2, cityNewYork, cityWroclaw, cityAmsterdam, cityBerlin},
   288  			wantDists: []float32{0.0, 0.0, 0.3, 0.1, 0.4, 0.2},
   289  		},
   290  		{
   291  			name:      "sort by isCapital desc & name asc",
   292  			sort:      sort2("isCapital", "desc", "name", "asc"),
   293  			limit:     4,
   294  			wantObjs:  []*storobj.Object{cityAmsterdam, cityBerlin, cityNewYork, cityWroclaw, cityNil, cityNil2},
   295  			wantDists: []float32{0.4, 0.2, 0.3, 0.1, 0.0, 0.0},
   296  		},
   297  		{
   298  			name:      "sort by timezones desc & name desc",
   299  			sort:      sort2("timezones", "desc", "name", "desc"),
   300  			limit:     5,
   301  			wantObjs:  []*storobj.Object{cityNewYork, cityWroclaw, cityBerlin, cityAmsterdam, cityNil2, cityNil},
   302  			wantDists: []float32{0.3, 0.1, 0.2, 0.4, 0.0, 0.0},
   303  		},
   304  		{
   305  			name:      "sort by timezones desc & name asc",
   306  			sort:      sort2("timezones", "desc", "name", "asc"),
   307  			limit:     3,
   308  			wantObjs:  []*storobj.Object{cityNewYork, cityAmsterdam, cityBerlin, cityWroclaw, cityNil, cityNil2},
   309  			wantDists: []float32{0.3, 0.4, 0.2, 0.1, 0.0, 0.0},
   310  		},
   311  		{
   312  			name:      "sort by timezonesUTC asc & timezones desc & isCapital asc & population asc",
   313  			sort:      sort4("timezonesUTC", "asc", "timezones", "desc", "isCapital", "asc", "population", "asc"),
   314  			limit:     4,
   315  			wantObjs:  []*storobj.Object{cityNil2, cityNil, cityWroclaw, cityAmsterdam, cityBerlin, cityNewYork},
   316  			wantDists: []float32{0.0, 0.0, 0.1, 0.4, 0.2, 0.3},
   317  		},
   318  		{
   319  			name:      "sort by timezonesUTC asc & timezones desc & isCapital desc & population asc",
   320  			sort:      sort4("timezonesUTC", "asc", "timezones", "desc", "isCapital", "desc", "population", "asc"),
   321  			limit:     5,
   322  			wantObjs:  []*storobj.Object{cityNil2, cityNil, cityAmsterdam, cityBerlin, cityWroclaw, cityNewYork},
   323  			wantDists: []float32{0.0, 0.0, 0.4, 0.2, 0.1, 0.3},
   324  		},
   325  	}
   326  
   327  	for _, tt := range tests {
   328  		t.Run(tt.name, func(t *testing.T) {
   329  			t.Run("with distance", func(t *testing.T) {
   330  				sorter := NewObjectsSorter(sorterCitySchema())
   331  				gotObjs, gotDists, err := sorter.Sort(sorterCitySchemaObjects(), sorterCitySchemaDistances(), 0, tt.sort)
   332  
   333  				require.Nil(t, err)
   334  
   335  				if !reflect.DeepEqual(gotObjs, tt.wantObjs) {
   336  					t.Fatalf("objects got = %v, want %v",
   337  						extractCityNames(gotObjs), extractCityNames(tt.wantObjs))
   338  				}
   339  				if !reflect.DeepEqual(gotDists, tt.wantDists) {
   340  					t.Fatalf("distances got = %v, want %v",
   341  						gotDists, tt.wantDists)
   342  				}
   343  			})
   344  
   345  			t.Run("without distance", func(t *testing.T) {
   346  				sorter := NewObjectsSorter(sorterCitySchema())
   347  				gotObjs, gotDists, err := sorter.Sort(sorterCitySchemaObjects(), nil, 0, tt.sort)
   348  
   349  				require.Nil(t, err)
   350  
   351  				if !reflect.DeepEqual(gotObjs, tt.wantObjs) {
   352  					t.Fatalf("objects got = %v, want %v",
   353  						extractCityNames(gotObjs), extractCityNames(tt.wantObjs))
   354  				}
   355  				if gotDists != nil {
   356  					t.Fatalf("distances got = %v, want nil",
   357  						gotDists)
   358  				}
   359  			})
   360  
   361  			t.Run("with limit", func(t *testing.T) {
   362  				sorter := NewObjectsSorter(sorterCitySchema())
   363  				gotObjs, gotDists, err := sorter.Sort(sorterCitySchemaObjects(), sorterCitySchemaDistances(), tt.limit, tt.sort)
   364  
   365  				require.Nil(t, err)
   366  
   367  				if !reflect.DeepEqual(gotObjs, tt.wantObjs[:tt.limit]) {
   368  					t.Fatalf("objects got = %v, want %v",
   369  						extractCityNames(gotObjs), extractCityNames(tt.wantObjs))
   370  				}
   371  				if !reflect.DeepEqual(gotDists, tt.wantDists[:tt.limit]) {
   372  					t.Fatalf("distances got = %v, want %v",
   373  						gotDists, tt.wantDists)
   374  				}
   375  			})
   376  		})
   377  	}
   378  }
   379  
   380  func createSort(property, order string) filters.Sort {
   381  	return filters.Sort{Path: []string{property}, Order: order}
   382  }
   383  
   384  func sort1(property, order string) []filters.Sort {
   385  	return []filters.Sort{createSort(property, order)}
   386  }
   387  
   388  func sort2(property1, order1, property2, order2 string) []filters.Sort {
   389  	return []filters.Sort{
   390  		createSort(property1, order1),
   391  		createSort(property2, order2),
   392  	}
   393  }
   394  
   395  func sort4(property1, order1, property2, order2, property3, order3, property4, order4 string) []filters.Sort {
   396  	return []filters.Sort{
   397  		createSort(property1, order1),
   398  		createSort(property2, order2),
   399  		createSort(property3, order3),
   400  		createSort(property4, order4),
   401  	}
   402  }
   403  
   404  func extractCityNames(in []*storobj.Object) []string {
   405  	out := make([]string, len(in))
   406  	for i := range in {
   407  		if asMap, ok := in[i].Properties().(map[string]interface{}); ok {
   408  			for k, v := range asMap {
   409  				if k == "name" {
   410  					out[i] = v.(string)
   411  				}
   412  			}
   413  		}
   414  	}
   415  	return out
   416  }