github.com/weaviate/weaviate@v1.24.6/usecases/traverser/explorer_validate_sort_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 traverser 13 14 import ( 15 "context" 16 "errors" 17 "testing" 18 19 testLogger "github.com/sirupsen/logrus/hooks/test" 20 "github.com/stretchr/testify/assert" 21 "github.com/stretchr/testify/mock" 22 "github.com/stretchr/testify/require" 23 "github.com/weaviate/weaviate/entities/dto" 24 "github.com/weaviate/weaviate/entities/filters" 25 "github.com/weaviate/weaviate/entities/search" 26 "github.com/weaviate/weaviate/entities/searchparams" 27 ) 28 29 func Test_Explorer_GetClass_WithSort(t *testing.T) { 30 type testData struct { 31 name string 32 params dto.GetParams 33 expectedError error 34 } 35 36 oneSortFilter := []testData{ 37 { 38 name: "invalid order parameter", 39 params: dto.GetParams{ 40 ClassName: "ClassOne", 41 Sort: []filters.Sort{{Path: nil, Order: "asce"}}, 42 }, 43 expectedError: errors.New(`invalid 'sort' parameter: sort parameter at position 0: ` + 44 `invalid order parameter, possible values are: ["asc", "desc"] not: "asce"`), 45 }, 46 { 47 name: "empty path", 48 params: dto.GetParams{ 49 ClassName: "ClassOne", 50 Sort: []filters.Sort{{Path: nil, Order: "asc"}}, 51 }, 52 expectedError: errors.New("invalid 'sort' parameter: sort parameter at position 0: " + 53 "path parameter cannot be empty"), 54 }, 55 { 56 name: "non-existent class", 57 params: dto.GetParams{ 58 ClassName: "NonExistentClass", 59 Sort: []filters.Sort{{Path: []string{"property"}, Order: "asc"}}, 60 }, 61 expectedError: errors.New("invalid 'sort' parameter: sort parameter at position 0: " + 62 "class \"NonExistentClass\" does not exist in schema"), 63 }, 64 { 65 name: "non-existent property in class", 66 params: dto.GetParams{ 67 ClassName: "ClassOne", 68 Sort: []filters.Sort{{Path: []string{"nonexistentproperty"}, Order: "asc"}}, 69 }, 70 expectedError: errors.New("invalid 'sort' parameter: sort parameter at position 0: " + 71 "no such prop with name 'nonexistentproperty' found in class 'ClassOne' in the schema. " + 72 "Check your schema files for which properties in this class are available"), 73 }, 74 { 75 name: "reference property in class", 76 params: dto.GetParams{ 77 ClassName: "ClassOne", 78 Sort: []filters.Sort{{Path: []string{"ref_prop"}, Order: "asc"}}, 79 }, 80 expectedError: errors.New("invalid 'sort' parameter: sort parameter at position 0: " + 81 "sorting by reference not supported, " + 82 "property \"ref_prop\" is a ref prop to the class \"ClassTwo\""), 83 }, 84 { 85 name: "reference property path", 86 params: dto.GetParams{ 87 ClassName: "ClassOne", 88 Sort: []filters.Sort{{Path: []string{"ref", "prop"}, Order: "asc"}}, 89 }, 90 expectedError: errors.New("invalid 'sort' parameter: sort parameter at position 0: " + 91 "sorting by reference not supported, " + 92 "path must have exactly one argument"), 93 }, 94 { 95 name: "invalid order parameter", 96 params: dto.GetParams{ 97 ClassName: "ClassOne", 98 Sort: []filters.Sort{{Path: nil, Order: "asce"}}, 99 }, 100 expectedError: errors.New(`invalid 'sort' parameter: sort parameter at position 0: ` + 101 `invalid order parameter, possible values are: ["asc", "desc"] not: "asce"`), 102 }, 103 } 104 105 twoSortFilters := []testData{ 106 { 107 name: "invalid order parameter", 108 params: dto.GetParams{ 109 ClassName: "ClassOne", 110 Sort: []filters.Sort{ 111 {Path: nil, Order: "asce"}, 112 {Path: nil, Order: "desce"}, 113 }, 114 }, 115 expectedError: errors.New(`invalid 'sort' parameter: ` + 116 `sort parameter at position 0: ` + 117 `invalid order parameter, possible values are: ["asc", "desc"] not: "asce", ` + 118 `sort parameter at position 1: ` + 119 `invalid order parameter, possible values are: ["asc", "desc"] not: "desce"`), 120 }, 121 { 122 name: "empty path", 123 params: dto.GetParams{ 124 ClassName: "ClassOne", 125 Sort: []filters.Sort{ 126 {Path: nil, Order: "asc"}, 127 {Path: []string{}, Order: "asc"}, 128 }, 129 }, 130 expectedError: errors.New("invalid 'sort' parameter: " + 131 "sort parameter at position 0: path parameter cannot be empty, " + 132 "sort parameter at position 1: path parameter cannot be empty"), 133 }, 134 { 135 name: "non-existent class", 136 params: dto.GetParams{ 137 ClassName: "NonExistentClass", 138 Sort: []filters.Sort{ 139 {Path: []string{"property"}, Order: "asc"}, 140 {Path: []string{"property"}, Order: "asc"}, 141 }, 142 }, 143 expectedError: errors.New("invalid 'sort' parameter: " + 144 "sort parameter at position 0: " + 145 "class \"NonExistentClass\" does not exist in schema, " + 146 "sort parameter at position 1: " + 147 "class \"NonExistentClass\" does not exist in schema"), 148 }, 149 { 150 name: "non-existent property in class", 151 params: dto.GetParams{ 152 ClassName: "ClassOne", 153 Sort: []filters.Sort{ 154 {Path: []string{"nonexistentproperty1"}, Order: "asc"}, 155 {Path: []string{"nonexistentproperty2"}, Order: "asc"}, 156 }, 157 }, 158 expectedError: errors.New("invalid 'sort' parameter: " + 159 "sort parameter at position 0: " + 160 "no such prop with name 'nonexistentproperty1' found in class 'ClassOne' in the schema. " + 161 "Check your schema files for which properties in this class are available, " + 162 "sort parameter at position 1: " + 163 "no such prop with name 'nonexistentproperty2' found in class 'ClassOne' in the schema. " + 164 "Check your schema files for which properties in this class are available"), 165 }, 166 { 167 name: "reference property in class", 168 params: dto.GetParams{ 169 ClassName: "ClassOne", 170 Sort: []filters.Sort{ 171 {Path: []string{"ref_prop"}, Order: "asc"}, 172 {Path: []string{"ref_prop"}, Order: "desc"}, 173 }, 174 }, 175 expectedError: errors.New("invalid 'sort' parameter: " + 176 "sort parameter at position 0: " + 177 "sorting by reference not supported, " + 178 "property \"ref_prop\" is a ref prop to the class \"ClassTwo\", " + 179 "sort parameter at position 1: " + 180 "sorting by reference not supported, " + 181 "property \"ref_prop\" is a ref prop to the class \"ClassTwo\""), 182 }, 183 { 184 name: "reference property path", 185 params: dto.GetParams{ 186 ClassName: "ClassOne", 187 Sort: []filters.Sort{ 188 {Path: []string{"ref", "prop"}, Order: "asc"}, 189 {Path: []string{"ref", "prop"}, Order: "asc"}, 190 }, 191 }, 192 expectedError: errors.New("invalid 'sort' parameter: " + 193 "sort parameter at position 0: " + 194 "sorting by reference not supported, " + 195 "path must have exactly one argument, " + 196 "sort parameter at position 1: " + 197 "sorting by reference not supported, " + 198 "path must have exactly one argument"), 199 }, 200 { 201 name: "reference properties path", 202 params: dto.GetParams{ 203 ClassName: "ClassOne", 204 Sort: []filters.Sort{ 205 {Path: []string{"ref_prop"}, Order: "asc"}, 206 {Path: []string{"ref", "prop"}, Order: "asc"}, 207 }, 208 }, 209 expectedError: errors.New("invalid 'sort' parameter: " + 210 "sort parameter at position 0: " + 211 "sorting by reference not supported, " + 212 "property \"ref_prop\" is a ref prop to the class \"ClassTwo\", " + 213 "sort parameter at position 1: " + 214 "sorting by reference not supported, " + 215 "path must have exactly one argument"), 216 }, 217 { 218 name: "reference properties path", 219 params: dto.GetParams{ 220 ClassName: "ClassOne", 221 Sort: []filters.Sort{ 222 {Path: []string{"ref_prop"}, Order: "asc"}, 223 {Path: []string{"ref", "prop"}, Order: "asc"}, 224 }, 225 }, 226 expectedError: errors.New("invalid 'sort' parameter: " + 227 "sort parameter at position 0: " + 228 "sorting by reference not supported, " + 229 "property \"ref_prop\" is a ref prop to the class \"ClassTwo\", " + 230 "sort parameter at position 1: " + 231 "sorting by reference not supported, " + 232 "path must have exactly one argument"), 233 }, 234 } 235 236 oneOfTwoSortFilters := []testData{ 237 { 238 name: "invalid order parameter", 239 params: dto.GetParams{ 240 ClassName: "ClassOne", 241 Sort: []filters.Sort{ 242 {Path: []string{"text_prop"}, Order: "asc"}, 243 {Path: nil, Order: "desce"}, 244 }, 245 }, 246 expectedError: errors.New(`invalid 'sort' parameter: ` + 247 `sort parameter at position 1: ` + 248 `invalid order parameter, possible values are: ["asc", "desc"] not: "desce"`), 249 }, 250 { 251 name: "empty path", 252 params: dto.GetParams{ 253 ClassName: "ClassOne", 254 Sort: []filters.Sort{ 255 {Path: []string{"text_prop"}, Order: "asc"}, 256 {Path: []string{}, Order: "asc"}, 257 }, 258 }, 259 expectedError: errors.New("invalid 'sort' parameter: " + 260 "sort parameter at position 1: path parameter cannot be empty"), 261 }, 262 { 263 name: "non-existent property in class", 264 params: dto.GetParams{ 265 ClassName: "ClassOne", 266 Sort: []filters.Sort{ 267 {Path: []string{"text_prop"}, Order: "asc"}, 268 {Path: []string{"nonexistentproperty2"}, Order: "asc"}, 269 }, 270 }, 271 expectedError: errors.New("invalid 'sort' parameter: " + 272 "sort parameter at position 1: " + 273 "no such prop with name 'nonexistentproperty2' found in class 'ClassOne' in the schema. " + 274 "Check your schema files for which properties in this class are available"), 275 }, 276 { 277 name: "reference property in class", 278 params: dto.GetParams{ 279 ClassName: "ClassOne", 280 Sort: []filters.Sort{ 281 {Path: []string{"text_prop"}, Order: "asc"}, 282 {Path: []string{"ref_prop"}, Order: "desc"}, 283 }, 284 }, 285 expectedError: errors.New("invalid 'sort' parameter: " + 286 "sort parameter at position 1: " + 287 "sorting by reference not supported, " + 288 "property \"ref_prop\" is a ref prop to the class \"ClassTwo\""), 289 }, 290 { 291 name: "reference property path", 292 params: dto.GetParams{ 293 ClassName: "ClassOne", 294 Sort: []filters.Sort{ 295 {Path: []string{"text_prop"}, Order: "asc"}, 296 {Path: []string{"ref", "prop"}, Order: "asc"}, 297 }, 298 }, 299 expectedError: errors.New("invalid 'sort' parameter: " + 300 "sort parameter at position 1: " + 301 "sorting by reference not supported, " + 302 "path must have exactly one argument"), 303 }, 304 { 305 name: "reference properties path", 306 params: dto.GetParams{ 307 ClassName: "ClassOne", 308 Sort: []filters.Sort{ 309 {Path: []string{"text_prop"}, Order: "asc"}, 310 {Path: []string{"ref_prop"}, Order: "asc"}, 311 {Path: []string{"ref", "prop"}, Order: "asc"}, 312 }, 313 }, 314 expectedError: errors.New("invalid 'sort' parameter: " + 315 "sort parameter at position 1: " + 316 "sorting by reference not supported, " + 317 "property \"ref_prop\" is a ref prop to the class \"ClassTwo\", " + 318 "sort parameter at position 2: " + 319 "sorting by reference not supported, " + 320 "path must have exactly one argument"), 321 }, 322 } 323 324 properSortFilters := []testData{ 325 { 326 name: "sort by text_prop", 327 params: dto.GetParams{ 328 ClassName: "ClassOne", 329 NearVector: &searchparams.NearVector{ 330 Vector: []float32{0.8, 0.2, 0.7}, 331 }, 332 Sort: []filters.Sort{ 333 {Path: []string{"text_prop"}, Order: "asc"}, 334 }, 335 }, 336 }, 337 } 338 339 testCases := []struct { 340 name string 341 testData []testData 342 }{ 343 { 344 name: "one sort filter broken", 345 testData: oneSortFilter, 346 }, 347 { 348 name: "two sort filters broken", 349 testData: twoSortFilters, 350 }, 351 { 352 name: "one of two sort filters broken", 353 testData: oneOfTwoSortFilters, 354 }, 355 { 356 name: "proper sort filters", 357 testData: properSortFilters, 358 }, 359 } 360 361 for _, tc := range testCases { 362 t.Run(tc.name, func(t *testing.T) { 363 for _, td := range tc.testData { 364 t.Run(td.name, func(t *testing.T) { 365 params := td.params 366 searchResults := []search.Result{ 367 { 368 ID: "id1", 369 Schema: map[string]interface{}{ 370 "name": "Foo", 371 }, 372 }, 373 } 374 375 search := &fakeVectorSearcher{} 376 sg := &fakeSchemaGetter{ 377 schema: schemaForFiltersValidation(), 378 } 379 log, _ := testLogger.NewNullLogger() 380 metrics := &fakeMetrics{} 381 metrics.On("AddUsageDimensions", mock.Anything, mock.Anything, mock.Anything, 382 mock.Anything) 383 explorer := NewExplorer(search, log, getFakeModulesProvider(), metrics, defaultConfig) 384 explorer.SetSchemaGetter(sg) 385 386 if td.expectedError == nil { 387 search. 388 On("VectorSearch", mock.Anything). 389 Return(searchResults, nil) 390 res, err := explorer.GetClass(context.Background(), params) 391 assert.Nil(t, err) 392 search.AssertExpectations(t) 393 require.Len(t, res, 1) 394 } else { 395 _, err := explorer.GetClass(context.Background(), params) 396 require.NotNil(t, err) 397 assert.Equal(t, err.Error(), td.expectedError.Error()) 398 } 399 }) 400 } 401 }) 402 } 403 }