github.com/weaviate/weaviate@v1.24.6/usecases/objects/get_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 objects 13 14 import ( 15 "context" 16 "errors" 17 "testing" 18 19 "github.com/go-openapi/strfmt" 20 "github.com/sirupsen/logrus/hooks/test" 21 "github.com/stretchr/testify/assert" 22 "github.com/stretchr/testify/mock" 23 "github.com/stretchr/testify/require" 24 "github.com/weaviate/weaviate/entities/additional" 25 "github.com/weaviate/weaviate/entities/filters" 26 "github.com/weaviate/weaviate/entities/models" 27 "github.com/weaviate/weaviate/entities/schema" 28 "github.com/weaviate/weaviate/entities/search" 29 "github.com/weaviate/weaviate/usecases/config" 30 ) 31 32 func Test_GetAction(t *testing.T) { 33 var ( 34 vectorRepo *fakeVectorRepo 35 manager *Manager 36 extender *fakeExtender 37 projectorFake *fakeProjector 38 metrics *fakeMetrics 39 ) 40 41 schema := schema.Schema{ 42 Objects: &models.Schema{ 43 Classes: []*models.Class{ 44 { 45 Class: "ActionClass", 46 }, 47 }, 48 }, 49 } 50 51 reset := func() { 52 vectorRepo = &fakeVectorRepo{} 53 schemaManager := &fakeSchemaManager{ 54 GetSchemaResponse: schema, 55 } 56 locks := &fakeLocks{} 57 cfg := &config.WeaviateConfig{} 58 cfg.Config.QueryDefaults.Limit = 20 59 cfg.Config.QueryMaximumResults = 200 60 authorizer := &fakeAuthorizer{} 61 logger, _ := test.NewNullLogger() 62 extender = &fakeExtender{} 63 projectorFake = &fakeProjector{} 64 metrics = &fakeMetrics{} 65 manager = NewManager(locks, schemaManager, cfg, logger, 66 authorizer, vectorRepo, 67 getFakeModulesProviderWithCustomExtenders(extender, projectorFake), metrics) 68 } 69 70 t.Run("get non-existing action by id", func(t *testing.T) { 71 reset() 72 id := strfmt.UUID("99ee9968-22ec-416a-9032-cff80f2f7fdf") 73 74 vectorRepo.On("ObjectByID", id, mock.Anything, mock.Anything).Return((*search.Result)(nil), nil).Once() 75 76 _, err := manager.GetObject(context.Background(), &models.Principal{}, "", 77 id, additional.Properties{}, nil, "") 78 assert.Equal(t, NewErrNotFound("no object with id '99ee9968-22ec-416a-9032-cff80f2f7fdf'"), err) 79 }) 80 81 t.Run("get existing action by id", func(t *testing.T) { 82 reset() 83 id := strfmt.UUID("99ee9968-22ec-416a-9032-cff80f2f7fdf") 84 85 result := &search.Result{ 86 ID: id, 87 ClassName: "ActionClass", 88 Schema: map[string]interface{}{"foo": "bar"}, 89 } 90 vectorRepo.On("ObjectByID", id, mock.Anything, mock.Anything).Return(result, nil).Once() 91 92 expected := &models.Object{ 93 ID: id, 94 Class: "ActionClass", 95 Properties: map[string]interface{}{"foo": "bar"}, 96 VectorWeights: (map[string]string)(nil), 97 } 98 99 res, err := manager.GetObject(context.Background(), &models.Principal{}, "", 100 id, additional.Properties{}, nil, "") 101 require.Nil(t, err) 102 assert.Equal(t, expected, res) 103 }) 104 105 t.Run("get existing object by id with vector without classname (deprecated)", func(t *testing.T) { 106 reset() 107 id := strfmt.UUID("99ee9968-22ec-416a-9032-cff80f2f7fdf") 108 109 result := &search.Result{ 110 ID: id, 111 ClassName: "ActionClass", 112 Schema: map[string]interface{}{"foo": "bar"}, 113 Vector: []float32{1, 2, 3}, 114 Dims: 3, 115 } 116 vectorRepo.On("ObjectByID", id, mock.Anything, mock.Anything).Return(result, nil).Once() 117 118 expected := &models.Object{ 119 ID: id, 120 Class: "ActionClass", 121 Properties: map[string]interface{}{"foo": "bar"}, 122 VectorWeights: (map[string]string)(nil), 123 Vector: []float32{1, 2, 3}, 124 } 125 126 metrics.On("AddUsageDimensions", "ActionClass", "get_rest", "single_include_vector", 3) 127 128 res, err := manager.GetObject(context.Background(), &models.Principal{}, "", 129 id, additional.Properties{Vector: true}, nil, "") 130 require.Nil(t, err) 131 assert.Equal(t, expected, res) 132 }) 133 134 t.Run("get existing object by id with vector with classname", func(t *testing.T) { 135 reset() 136 id := strfmt.UUID("99ee9968-22ec-416a-9032-cff80f2f7fdf") 137 138 result := &search.Result{ 139 ID: id, 140 ClassName: "ActionClass", 141 Schema: map[string]interface{}{"foo": "bar"}, 142 Vector: []float32{1, 2, 3}, 143 Dims: 3, 144 } 145 vectorRepo.On("Object", "ActionClass", id, mock.Anything, mock.Anything, ""). 146 Return(result, nil).Once() 147 148 expected := &models.Object{ 149 ID: id, 150 Class: "ActionClass", 151 Properties: map[string]interface{}{"foo": "bar"}, 152 VectorWeights: (map[string]string)(nil), 153 Vector: []float32{1, 2, 3}, 154 } 155 156 metrics.On("AddUsageDimensions", "ActionClass", "get_rest", "single_include_vector", 3) 157 158 res, err := manager.GetObject(context.Background(), &models.Principal{}, 159 "ActionClass", id, additional.Properties{Vector: true}, nil, "") 160 require.Nil(t, err) 161 assert.Equal(t, expected, res) 162 }) 163 164 t.Run("list all existing actions with all default pagination settings", func(t *testing.T) { 165 reset() 166 id := strfmt.UUID("99ee9968-22ec-416a-9032-cff80f2f7fdf") 167 168 results := []search.Result{ 169 { 170 ID: id, 171 ClassName: "ActionClass", 172 Schema: map[string]interface{}{"foo": "bar"}, 173 }, 174 } 175 vectorRepo.On("ObjectSearch", 0, 20, mock.Anything, mock.Anything, mock.Anything, 176 mock.Anything).Return(results, nil).Once() 177 178 expected := []*models.Object{ 179 { 180 ID: id, 181 Class: "ActionClass", 182 Properties: map[string]interface{}{"foo": "bar"}, 183 VectorWeights: (map[string]string)(nil), 184 }, 185 } 186 187 res, err := manager.GetObjects(context.Background(), &models.Principal{}, nil, nil, nil, nil, nil, additional.Properties{}, "") 188 require.Nil(t, err) 189 assert.Equal(t, expected, res) 190 }) 191 192 t.Run("list all existing objects with vectors", func(t *testing.T) { 193 reset() 194 id := strfmt.UUID("99ee9968-22ec-416a-9032-cff80f2f7fdf") 195 196 results := []search.Result{ 197 { 198 ID: id, 199 ClassName: "ActionClass", 200 Schema: map[string]interface{}{"foo": "bar"}, 201 Vector: []float32{1, 2, 3}, 202 Dims: 3, 203 }, 204 } 205 vectorRepo.On("ObjectSearch", 0, 20, mock.Anything, mock.Anything, mock.Anything, 206 mock.Anything).Return(results, nil).Once() 207 208 metrics.On("AddUsageDimensions", "ActionClass", "get_rest", "list_include_vector", 3) 209 210 expected := []*models.Object{ 211 { 212 ID: id, 213 Class: "ActionClass", 214 Properties: map[string]interface{}{"foo": "bar"}, 215 VectorWeights: (map[string]string)(nil), 216 Vector: []float32{1, 2, 3}, 217 }, 218 } 219 220 res, err := manager.GetObjects(context.Background(), &models.Principal{}, nil, nil, nil, nil, nil, additional.Properties{Vector: true}, "") 221 require.Nil(t, err) 222 assert.Equal(t, expected, res) 223 }) 224 225 t.Run("list all existing actions with all explicit offset and limit", func(t *testing.T) { 226 reset() 227 id := strfmt.UUID("99ee9968-22ec-416a-9032-cff80f2f7fdf") 228 229 results := []search.Result{ 230 { 231 ID: id, 232 ClassName: "ActionClass", 233 Schema: map[string]interface{}{"foo": "bar"}, 234 }, 235 } 236 vectorRepo.On("ObjectSearch", 7, 2, mock.Anything, mock.Anything, mock.Anything, 237 mock.Anything).Return(results, nil).Once() 238 239 expected := []*models.Object{ 240 { 241 ID: id, 242 Class: "ActionClass", 243 Properties: map[string]interface{}{"foo": "bar"}, 244 VectorWeights: (map[string]string)(nil), 245 }, 246 } 247 248 res, err := manager.GetObjects(context.Background(), &models.Principal{}, ptInt64(7), ptInt64(2), nil, nil, nil, additional.Properties{}, "") 249 require.Nil(t, err) 250 assert.Equal(t, expected, res) 251 }) 252 253 t.Run("with an offset greater than the maximum", func(t *testing.T) { 254 reset() 255 256 _, err := manager.GetObjects(context.Background(), &models.Principal{}, ptInt64(201), ptInt64(2), nil, nil, nil, additional.Properties{}, "") 257 require.NotNil(t, err) 258 assert.Contains(t, err.Error(), "query maximum results exceeded") 259 }) 260 261 t.Run("with a limit greater than the minimum", func(t *testing.T) { 262 reset() 263 264 _, err := manager.GetObjects(context.Background(), &models.Principal{}, ptInt64(0), ptInt64(202), nil, nil, nil, additional.Properties{}, "") 265 require.NotNil(t, err) 266 assert.Contains(t, err.Error(), "query maximum results exceeded") 267 }) 268 269 t.Run("with limit and offset individually smaller, but combined greater", func(t *testing.T) { 270 reset() 271 272 _, err := manager.GetObjects(context.Background(), &models.Principal{}, ptInt64(150), ptInt64(150), nil, nil, nil, additional.Properties{}, "") 273 require.NotNil(t, err) 274 assert.Contains(t, err.Error(), "query maximum results exceeded") 275 }) 276 277 t.Run("additional props", func(t *testing.T) { 278 t.Run("on get single requests", func(t *testing.T) { 279 t.Run("feature projection", func(t *testing.T) { 280 reset() 281 id := strfmt.UUID("99ee9968-22ec-416a-9032-cff80f2f7fdf") 282 283 result := &search.Result{ 284 ID: id, 285 ClassName: "ActionClass", 286 Schema: map[string]interface{}{"foo": "bar"}, 287 } 288 vectorRepo.On("ObjectByID", id, mock.Anything, mock.Anything).Return(result, nil).Once() 289 _, err := manager.GetObject(context.Background(), &models.Principal{}, "", 290 id, additional.Properties{ 291 ModuleParams: map[string]interface{}{ 292 "featureProjection": getDefaultParam("featureProjection"), 293 }, 294 }, nil, "") 295 assert.Equal(t, errors.New("get extend: unknown capability: featureProjection"), err) 296 }) 297 298 t.Run("semantic path", func(t *testing.T) { 299 reset() 300 id := strfmt.UUID("99ee9968-22ec-416a-9032-cff80f2f7fdf") 301 302 result := &search.Result{ 303 ID: id, 304 ClassName: "ActionClass", 305 Schema: map[string]interface{}{"foo": "bar"}, 306 } 307 vectorRepo.On("ObjectByID", id, mock.Anything, mock.Anything).Return(result, nil).Once() 308 _, err := manager.GetObject(context.Background(), &models.Principal{}, "", 309 id, additional.Properties{ 310 ModuleParams: map[string]interface{}{ 311 "semanticPath": getDefaultParam("semanticPath"), 312 }, 313 }, nil, "") 314 assert.Equal(t, errors.New("get extend: unknown capability: semanticPath"), err) 315 }) 316 317 t.Run("nearest neighbors", func(t *testing.T) { 318 reset() 319 id := strfmt.UUID("99ee9968-22ec-416a-9032-cff80f2f7fdf") 320 321 result := &search.Result{ 322 ID: id, 323 ClassName: "ActionClass", 324 Schema: map[string]interface{}{"foo": "bar"}, 325 } 326 vectorRepo.On("ObjectByID", id, mock.Anything, mock.Anything).Return(result, nil).Once() 327 extender.multi = []search.Result{ 328 { 329 ID: id, 330 ClassName: "ActionClass", 331 Schema: map[string]interface{}{"foo": "bar"}, 332 AdditionalProperties: models.AdditionalProperties{ 333 "nearestNeighbors": &NearestNeighbors{ 334 Neighbors: []*NearestNeighbor{ 335 { 336 Concept: "foo", 337 Distance: 0.3, 338 }, 339 }, 340 }, 341 }, 342 }, 343 } 344 345 expected := &models.Object{ 346 ID: id, 347 Class: "ActionClass", 348 Properties: map[string]interface{}{"foo": "bar"}, 349 VectorWeights: (map[string]string)(nil), 350 Additional: models.AdditionalProperties{ 351 "nearestNeighbors": &NearestNeighbors{ 352 Neighbors: []*NearestNeighbor{ 353 { 354 Concept: "foo", 355 Distance: 0.3, 356 }, 357 }, 358 }, 359 }, 360 } 361 362 res, err := manager.GetObject(context.Background(), &models.Principal{}, "", 363 id, additional.Properties{ 364 ModuleParams: map[string]interface{}{ 365 "nearestNeighbors": true, 366 }, 367 }, nil, "") 368 require.Nil(t, err) 369 assert.Equal(t, expected, res) 370 }) 371 }) 372 373 t.Run("on list requests", func(t *testing.T) { 374 t.Run("nearest neighbors", func(t *testing.T) { 375 reset() 376 id := strfmt.UUID("99ee9968-22ec-416a-9032-cff80f2f7fdf") 377 378 result := []search.Result{ 379 { 380 ID: id, 381 ClassName: "ActionClass", 382 Schema: map[string]interface{}{"foo": "bar"}, 383 }, 384 } 385 vectorRepo.On("ObjectSearch", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, 386 mock.Anything).Return(result, nil).Once() 387 extender.multi = []search.Result{ 388 { 389 ID: id, 390 ClassName: "ActionClass", 391 Schema: map[string]interface{}{"foo": "bar"}, 392 AdditionalProperties: models.AdditionalProperties{ 393 "nearestNeighbors": &NearestNeighbors{ 394 Neighbors: []*NearestNeighbor{ 395 { 396 Concept: "foo", 397 Distance: 0.3, 398 }, 399 }, 400 }, 401 }, 402 }, 403 } 404 405 expected := []*models.Object{ 406 { 407 ID: id, 408 Class: "ActionClass", 409 Properties: map[string]interface{}{"foo": "bar"}, 410 VectorWeights: (map[string]string)(nil), 411 Additional: models.AdditionalProperties{ 412 "nearestNeighbors": &NearestNeighbors{ 413 Neighbors: []*NearestNeighbor{ 414 { 415 Concept: "foo", 416 Distance: 0.3, 417 }, 418 }, 419 }, 420 }, 421 }, 422 } 423 424 res, err := manager.GetObjects(context.Background(), &models.Principal{}, nil, ptInt64(10), nil, nil, nil, additional.Properties{ 425 ModuleParams: map[string]interface{}{ 426 "nearestNeighbors": true, 427 }, 428 }, "") 429 require.Nil(t, err) 430 assert.Equal(t, expected, res) 431 }) 432 433 t.Run("feature projection", func(t *testing.T) { 434 reset() 435 id := strfmt.UUID("99ee9968-22ec-416a-9032-cff80f2f7fdf") 436 437 result := []search.Result{ 438 { 439 ID: id, 440 ClassName: "ActionClass", 441 Schema: map[string]interface{}{"foo": "bar"}, 442 }, 443 } 444 vectorRepo.On("ObjectSearch", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, 445 mock.Anything).Return(result, nil).Once() 446 projectorFake.multi = []search.Result{ 447 { 448 ID: id, 449 ClassName: "ActionClass", 450 Schema: map[string]interface{}{"foo": "bar"}, 451 AdditionalProperties: models.AdditionalProperties{ 452 "featureProjection": &FeatureProjection{ 453 Vector: []float32{1, 2, 3}, 454 }, 455 }, 456 }, 457 } 458 459 expected := []*models.Object{ 460 { 461 ID: id, 462 Class: "ActionClass", 463 Properties: map[string]interface{}{"foo": "bar"}, 464 VectorWeights: (map[string]string)(nil), 465 Additional: models.AdditionalProperties{ 466 "featureProjection": &FeatureProjection{ 467 Vector: []float32{1, 2, 3}, 468 }, 469 }, 470 }, 471 } 472 473 res, err := manager.GetObjects(context.Background(), &models.Principal{}, nil, ptInt64(10), nil, nil, nil, additional.Properties{ 474 ModuleParams: map[string]interface{}{ 475 "featureProjection": getDefaultParam("featureProjection"), 476 }, 477 }, "") 478 require.Nil(t, err) 479 assert.Equal(t, expected, res) 480 }) 481 }) 482 }) 483 484 t.Run("sort props", func(t *testing.T) { 485 t.Run("sort=foo,number&order=asc,desc", func(t *testing.T) { 486 reset() 487 id := strfmt.UUID("99ee9968-22ec-416a-9032-cff80f2f7fdf") 488 sort := "foo,number" 489 asc := "asc,desc" 490 expectedSort := []filters.Sort{ 491 {Path: []string{"foo"}, Order: "asc"}, 492 {Path: []string{"number"}, Order: "desc"}, 493 } 494 495 result := []search.Result{ 496 { 497 ID: id, 498 ClassName: "ActionClass", 499 Schema: map[string]interface{}{ 500 "foo": "bar", 501 "number": float64(1), 502 }, 503 }, 504 } 505 vectorRepo.On("ObjectSearch", mock.AnythingOfType("int"), mock.AnythingOfType("int"), expectedSort, 506 mock.Anything, mock.Anything, mock.Anything).Return(result, nil).Once() 507 projectorFake.multi = []search.Result{ 508 { 509 ID: id, 510 ClassName: "ActionClass", 511 Schema: map[string]interface{}{ 512 "foo": "bar", 513 "number": float64(1), 514 }, 515 }, 516 } 517 518 expected := []*models.Object{ 519 { 520 ID: id, 521 Class: "ActionClass", 522 Properties: map[string]interface{}{ 523 "foo": "bar", 524 "number": float64(1), 525 }, 526 VectorWeights: (map[string]string)(nil), 527 }, 528 } 529 530 res, err := manager.GetObjects(context.Background(), &models.Principal{}, nil, ptInt64(10), &sort, &asc, nil, additional.Properties{}, "") 531 require.Nil(t, err) 532 assert.Equal(t, expected, res) 533 }) 534 535 t.Run("sort=foo,number,prop1,prop2&order=desc", func(t *testing.T) { 536 reset() 537 id := strfmt.UUID("99ee9968-22ec-416a-9032-cff80f2f7fdf") 538 sort := "foo,number,prop1,prop2" 539 asc := "desc" 540 expectedSort := []filters.Sort{ 541 {Path: []string{"foo"}, Order: "desc"}, 542 {Path: []string{"number"}, Order: "asc"}, 543 {Path: []string{"prop1"}, Order: "asc"}, 544 {Path: []string{"prop2"}, Order: "asc"}, 545 } 546 547 result := []search.Result{ 548 { 549 ID: id, 550 ClassName: "ActionClass", 551 Schema: map[string]interface{}{ 552 "foo": "bar", 553 "number": float64(1), 554 }, 555 }, 556 } 557 vectorRepo.On("ObjectSearch", mock.Anything, mock.Anything, expectedSort, mock.Anything, mock.Anything, 558 mock.Anything).Return(result, nil).Once() 559 projectorFake.multi = []search.Result{ 560 { 561 ID: id, 562 ClassName: "ActionClass", 563 Schema: map[string]interface{}{ 564 "foo": "bar", 565 "number": float64(1), 566 }, 567 }, 568 } 569 570 expected := []*models.Object{ 571 { 572 ID: id, 573 Class: "ActionClass", 574 Properties: map[string]interface{}{ 575 "foo": "bar", 576 "number": float64(1), 577 }, 578 VectorWeights: (map[string]string)(nil), 579 }, 580 } 581 582 res, err := manager.GetObjects(context.Background(), &models.Principal{}, nil, ptInt64(10), &sort, &asc, nil, additional.Properties{}, "") 583 require.Nil(t, err) 584 assert.Equal(t, expected, res) 585 }) 586 587 t.Run("sort=foo,number", func(t *testing.T) { 588 reset() 589 sort := "foo,number" 590 expectedSort := []filters.Sort{ 591 {Path: []string{"foo"}, Order: "asc"}, 592 {Path: []string{"number"}, Order: "asc"}, 593 } 594 result := []search.Result{ 595 { 596 ID: "uuid", 597 ClassName: "ActionClass", 598 Schema: map[string]interface{}{ 599 "foo": "bar", 600 "number": float64(1), 601 }, 602 }, 603 } 604 605 vectorRepo.On("ObjectSearch", mock.Anything, mock.Anything, expectedSort, mock.Anything, mock.Anything, 606 mock.Anything).Return(result, nil).Once() 607 608 _, err := manager.GetObjects(context.Background(), &models.Principal{}, nil, ptInt64(10), &sort, nil, nil, additional.Properties{}, "") 609 require.Nil(t, err) 610 }) 611 612 t.Run("sort=foo,number,prop", func(t *testing.T) { 613 reset() 614 sort := "foo,number,prop" 615 expectedSort := []filters.Sort{ 616 {Path: []string{"foo"}, Order: "asc"}, 617 {Path: []string{"number"}, Order: "asc"}, 618 {Path: []string{"prop"}, Order: "asc"}, 619 } 620 result := []search.Result{ 621 { 622 ID: "uuid", 623 ClassName: "ActionClass", 624 Schema: map[string]interface{}{ 625 "foo": "bar", 626 "number": float64(1), 627 }, 628 }, 629 } 630 631 vectorRepo.On("ObjectSearch", mock.Anything, mock.Anything, expectedSort, mock.Anything, mock.Anything, 632 mock.Anything).Return(result, nil).Once() 633 634 _, err := manager.GetObjects(context.Background(), &models.Principal{}, nil, ptInt64(10), &sort, nil, nil, additional.Properties{}, "") 635 require.Nil(t, err) 636 }) 637 638 t.Run("order=asc", func(t *testing.T) { 639 reset() 640 order := "asc" 641 var expectedSort []filters.Sort 642 result := []search.Result{ 643 { 644 ID: "uuid", 645 ClassName: "ActionClass", 646 Schema: map[string]interface{}{ 647 "foo": "bar", 648 "number": float64(1), 649 }, 650 }, 651 } 652 653 vectorRepo.On("ObjectSearch", mock.Anything, mock.Anything, expectedSort, mock.Anything, mock.Anything, 654 mock.Anything).Return(result, nil).Once() 655 656 _, err := manager.GetObjects(context.Background(), &models.Principal{}, nil, ptInt64(10), nil, &order, nil, additional.Properties{}, "") 657 require.Nil(t, err) 658 }) 659 }) 660 } 661 662 func Test_GetThing(t *testing.T) { 663 var ( 664 vectorRepo *fakeVectorRepo 665 manager *Manager 666 extender *fakeExtender 667 projectorFake *fakeProjector 668 ) 669 670 schema := schema.Schema{ 671 Objects: &models.Schema{ 672 Classes: []*models.Class{ 673 { 674 Class: "ThingClass", 675 }, 676 }, 677 }, 678 } 679 680 reset := func() { 681 vectorRepo = &fakeVectorRepo{} 682 schemaManager := &fakeSchemaManager{ 683 GetSchemaResponse: schema, 684 } 685 locks := &fakeLocks{} 686 cfg := &config.WeaviateConfig{} 687 cfg.Config.QueryDefaults.Limit = 20 688 cfg.Config.QueryMaximumResults = 200 689 authorizer := &fakeAuthorizer{} 690 logger, _ := test.NewNullLogger() 691 extender = &fakeExtender{} 692 projectorFake = &fakeProjector{} 693 metrics := &fakeMetrics{} 694 manager = NewManager(locks, schemaManager, cfg, logger, 695 authorizer, vectorRepo, 696 getFakeModulesProviderWithCustomExtenders(extender, projectorFake), metrics) 697 } 698 699 t.Run("get non-existing thing by id", func(t *testing.T) { 700 reset() 701 id := strfmt.UUID("99ee9968-22ec-416a-9032-cff80f2f7fdf") 702 703 vectorRepo.On("ObjectByID", id, mock.Anything, mock.Anything).Return((*search.Result)(nil), nil).Once() 704 705 _, err := manager.GetObject(context.Background(), &models.Principal{}, "", id, 706 additional.Properties{}, nil, "") 707 assert.Equal(t, NewErrNotFound("no object with id '99ee9968-22ec-416a-9032-cff80f2f7fdf'"), err) 708 }) 709 710 t.Run("get existing thing by id", func(t *testing.T) { 711 reset() 712 id := strfmt.UUID("99ee9968-22ec-416a-9032-cff80f2f7fdf") 713 714 result := &search.Result{ 715 ID: id, 716 ClassName: "ThingClass", 717 Schema: map[string]interface{}{"foo": "bar"}, 718 } 719 vectorRepo.On("ObjectByID", id, mock.Anything, mock.Anything).Return(result, nil).Once() 720 721 expected := &models.Object{ 722 ID: id, 723 Class: "ThingClass", 724 Properties: map[string]interface{}{"foo": "bar"}, 725 VectorWeights: (map[string]string)(nil), 726 } 727 728 res, err := manager.GetObject(context.Background(), &models.Principal{}, "", id, 729 additional.Properties{}, nil, "") 730 require.Nil(t, err) 731 assert.Equal(t, expected, res) 732 }) 733 734 t.Run("list all existing things", func(t *testing.T) { 735 reset() 736 id := strfmt.UUID("99ee9968-22ec-416a-9032-cff80f2f7fdf") 737 738 results := []search.Result{ 739 { 740 ID: id, 741 ClassName: "ThingClass", 742 Schema: map[string]interface{}{"foo": "bar"}, 743 }, 744 } 745 vectorRepo.On("ObjectSearch", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, 746 mock.Anything).Return(results, nil).Once() 747 748 expected := []*models.Object{ 749 { 750 ID: id, 751 Class: "ThingClass", 752 Properties: map[string]interface{}{"foo": "bar"}, 753 VectorWeights: (map[string]string)(nil), 754 }, 755 } 756 757 res, err := manager.GetObjects(context.Background(), &models.Principal{}, nil, nil, nil, nil, nil, additional.Properties{}, "") 758 require.Nil(t, err) 759 assert.Equal(t, expected, res) 760 }) 761 762 t.Run("additional props", func(t *testing.T) { 763 t.Run("on get single requests", func(t *testing.T) { 764 t.Run("feature projection", func(t *testing.T) { 765 reset() 766 id := strfmt.UUID("99ee9968-22ec-416a-9032-cff80f2f7fdf") 767 768 result := &search.Result{ 769 ID: id, 770 ClassName: "ThingClass", 771 Schema: map[string]interface{}{"foo": "bar"}, 772 } 773 vectorRepo.On("ObjectByID", id, mock.Anything, mock.Anything).Return(result, nil).Once() 774 _, err := manager.GetObject(context.Background(), &models.Principal{}, "", 775 id, additional.Properties{ 776 ModuleParams: map[string]interface{}{ 777 "featureProjection": getDefaultParam("featureProjection"), 778 }, 779 }, nil, "") 780 assert.Equal(t, errors.New("get extend: unknown capability: featureProjection"), err) 781 }) 782 783 t.Run("nearest neighbors", func(t *testing.T) { 784 reset() 785 id := strfmt.UUID("99ee9968-22ec-416a-9032-cff80f2f7fdf") 786 787 result := &search.Result{ 788 ID: id, 789 ClassName: "ThingClass", 790 Schema: map[string]interface{}{"foo": "bar"}, 791 } 792 vectorRepo.On("ObjectByID", id, mock.Anything, mock.Anything).Return(result, nil).Once() 793 extender.multi = []search.Result{ 794 { 795 ID: id, 796 ClassName: "ThingClass", 797 Schema: map[string]interface{}{"foo": "bar"}, 798 AdditionalProperties: models.AdditionalProperties{ 799 "nearestNeighbors": &NearestNeighbors{ 800 Neighbors: []*NearestNeighbor{ 801 { 802 Concept: "foo", 803 Distance: 0.3, 804 }, 805 }, 806 }, 807 }, 808 }, 809 } 810 811 expected := &models.Object{ 812 ID: id, 813 Class: "ThingClass", 814 Properties: map[string]interface{}{"foo": "bar"}, 815 VectorWeights: (map[string]string)(nil), 816 Additional: models.AdditionalProperties{ 817 "nearestNeighbors": &NearestNeighbors{ 818 Neighbors: []*NearestNeighbor{ 819 { 820 Concept: "foo", 821 Distance: 0.3, 822 }, 823 }, 824 }, 825 }, 826 } 827 828 res, err := manager.GetObject(context.Background(), &models.Principal{}, "", 829 id, additional.Properties{ 830 ModuleParams: map[string]interface{}{ 831 "nearestNeighbors": true, 832 }, 833 }, nil, "") 834 require.Nil(t, err) 835 assert.Equal(t, expected, res) 836 }) 837 }) 838 839 t.Run("on list requests", func(t *testing.T) { 840 t.Run("nearest neighbors", func(t *testing.T) { 841 reset() 842 id := strfmt.UUID("99ee9968-22ec-416a-9032-cff80f2f7fdf") 843 844 result := []search.Result{ 845 { 846 ID: id, 847 ClassName: "ThingClass", 848 Schema: map[string]interface{}{"foo": "bar"}, 849 }, 850 } 851 vectorRepo.On("ObjectSearch", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, 852 mock.Anything).Return(result, nil).Once() 853 extender.multi = []search.Result{ 854 { 855 ID: id, 856 ClassName: "ThingClass", 857 Schema: map[string]interface{}{"foo": "bar"}, 858 AdditionalProperties: models.AdditionalProperties{ 859 "nearestNeighbors": &NearestNeighbors{ 860 Neighbors: []*NearestNeighbor{ 861 { 862 Concept: "foo", 863 Distance: 0.3, 864 }, 865 }, 866 }, 867 }, 868 }, 869 } 870 871 expected := []*models.Object{ 872 { 873 ID: id, 874 Class: "ThingClass", 875 Properties: map[string]interface{}{"foo": "bar"}, 876 VectorWeights: (map[string]string)(nil), 877 Additional: models.AdditionalProperties{ 878 "nearestNeighbors": &NearestNeighbors{ 879 Neighbors: []*NearestNeighbor{ 880 { 881 Concept: "foo", 882 Distance: 0.3, 883 }, 884 }, 885 }, 886 }, 887 }, 888 } 889 890 res, err := manager.GetObjects(context.Background(), &models.Principal{}, nil, ptInt64(10), nil, nil, nil, additional.Properties{ 891 ModuleParams: map[string]interface{}{ 892 "nearestNeighbors": true, 893 }, 894 }, "") 895 require.Nil(t, err) 896 assert.Equal(t, expected, res) 897 }) 898 899 t.Run("feature projection", func(t *testing.T) { 900 reset() 901 id := strfmt.UUID("99ee9968-22ec-416a-9032-cff80f2f7fdf") 902 903 result := []search.Result{ 904 { 905 ID: id, 906 ClassName: "ThingClass", 907 Schema: map[string]interface{}{"foo": "bar"}, 908 }, 909 } 910 vectorRepo.On("ObjectSearch", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, 911 mock.Anything).Return(result, nil).Once() 912 projectorFake.multi = []search.Result{ 913 { 914 ID: id, 915 ClassName: "ThingClass", 916 Schema: map[string]interface{}{"foo": "bar"}, 917 AdditionalProperties: models.AdditionalProperties{ 918 "featureProjection": &FeatureProjection{ 919 Vector: []float32{1, 2, 3}, 920 }, 921 }, 922 }, 923 } 924 925 expected := []*models.Object{ 926 { 927 ID: id, 928 Class: "ThingClass", 929 Properties: map[string]interface{}{"foo": "bar"}, 930 VectorWeights: (map[string]string)(nil), 931 Additional: models.AdditionalProperties{ 932 "featureProjection": &FeatureProjection{ 933 Vector: []float32{1, 2, 3}, 934 }, 935 }, 936 }, 937 } 938 939 res, err := manager.GetObjects(context.Background(), &models.Principal{}, nil, ptInt64(10), nil, nil, nil, additional.Properties{ 940 ModuleParams: map[string]interface{}{ 941 "featureProjection": getDefaultParam("featureProjection"), 942 }, 943 }, "") 944 require.Nil(t, err) 945 assert.Equal(t, expected, res) 946 }) 947 }) 948 }) 949 } 950 951 func Test_GetObject(t *testing.T) { 952 var ( 953 principal = models.Principal{} 954 adds = additional.Properties{} 955 className = "MyClass" 956 id = strfmt.UUID("99ee9968-22ec-416a-9032-cff80f2f7fdf") 957 schema = schema.Schema{ 958 Objects: &models.Schema{ 959 Classes: []*models.Class{ 960 { 961 Class: className, 962 }, 963 }, 964 }, 965 } 966 result = &search.Result{ 967 ID: id, 968 ClassName: className, 969 Schema: map[string]interface{}{"foo": "bar"}, 970 } 971 ) 972 973 t.Run("without projection", func(t *testing.T) { 974 m := newFakeGetManager(schema) 975 m.repo.On("Object", className, id, mock.Anything, mock.Anything, "").Return((*search.Result)(nil), nil).Once() 976 _, err := m.GetObject(context.Background(), &principal, className, id, adds, nil, "") 977 if err == nil { 978 t.Errorf("GetObject() must return an error for non existing object") 979 } 980 981 m.repo.On("Object", className, id, mock.Anything, mock.Anything, "").Return(result, nil).Once() 982 expected := &models.Object{ 983 ID: id, 984 Class: className, 985 Properties: map[string]interface{}{"foo": "bar"}, 986 VectorWeights: (map[string]string)(nil), 987 } 988 989 got, err := m.GetObject(context.Background(), &principal, className, id, adds, nil, "") 990 require.Nil(t, err) 991 assert.Equal(t, expected, got) 992 }) 993 994 t.Run("with projection", func(t *testing.T) { 995 m := newFakeGetManager(schema) 996 m.extender.multi = []search.Result{ 997 { 998 ID: id, 999 ClassName: className, 1000 Schema: map[string]interface{}{"foo": "bar"}, 1001 AdditionalProperties: models.AdditionalProperties{ 1002 "nearestNeighbors": &NearestNeighbors{ 1003 Neighbors: []*NearestNeighbor{ 1004 { 1005 Concept: "foo", 1006 Distance: 0.3, 1007 }, 1008 }, 1009 }, 1010 }, 1011 }, 1012 } 1013 m.repo.On("Object", className, id, mock.Anything, mock.Anything, "").Return(result, nil).Once() 1014 _, err := m.GetObject(context.Background(), &principal, className, id, 1015 additional.Properties{ 1016 ModuleParams: map[string]interface{}{ 1017 "Unknown": getDefaultParam("Unknown"), 1018 }, 1019 }, nil, "") 1020 if err == nil { 1021 t.Errorf("GetObject() must return unknown feature projection error") 1022 } 1023 1024 m.repo.On("Object", className, id, mock.Anything, mock.Anything, "").Return(result, nil).Once() 1025 expected := &models.Object{ 1026 ID: id, 1027 Class: className, 1028 Properties: map[string]interface{}{"foo": "bar"}, 1029 VectorWeights: (map[string]string)(nil), 1030 Additional: models.AdditionalProperties{ 1031 "nearestNeighbors": &NearestNeighbors{ 1032 Neighbors: []*NearestNeighbor{ 1033 { 1034 Concept: "foo", 1035 Distance: 0.3, 1036 }, 1037 }, 1038 }, 1039 }, 1040 } 1041 1042 res, err := m.GetObject(context.Background(), &principal, className, id, 1043 additional.Properties{ 1044 ModuleParams: map[string]interface{}{ 1045 "nearestNeighbors": true, 1046 }, 1047 }, nil, "") 1048 require.Nil(t, err) 1049 assert.Equal(t, expected, res) 1050 }) 1051 } 1052 1053 func ptInt64(in int64) *int64 { 1054 return &in 1055 } 1056 1057 type fakeGetManager struct { 1058 *Manager 1059 repo *fakeVectorRepo 1060 extender *fakeExtender 1061 projector *fakeProjector 1062 authorizer *fakeAuthorizer 1063 locks *fakeLocks 1064 metrics *fakeMetrics 1065 modulesProvider *fakeModulesProvider 1066 } 1067 1068 func newFakeGetManager(schema schema.Schema, opts ...func(*fakeGetManager)) fakeGetManager { 1069 r := fakeGetManager{ 1070 repo: new(fakeVectorRepo), 1071 extender: new(fakeExtender), 1072 projector: new(fakeProjector), 1073 authorizer: new(fakeAuthorizer), 1074 locks: new(fakeLocks), 1075 metrics: new(fakeMetrics), 1076 modulesProvider: new(fakeModulesProvider), 1077 } 1078 1079 for _, opt := range opts { 1080 opt(&r) 1081 } 1082 1083 schemaManager := &fakeSchemaManager{ 1084 GetSchemaResponse: schema, 1085 } 1086 cfg := &config.WeaviateConfig{} 1087 cfg.Config.QueryDefaults.Limit = 20 1088 cfg.Config.QueryMaximumResults = 200 1089 cfg.Config.TrackVectorDimensions = true 1090 logger, _ := test.NewNullLogger() 1091 r.modulesProvider = getFakeModulesProviderWithCustomExtenders(r.extender, r.projector) 1092 r.Manager = NewManager(r.locks, schemaManager, cfg, logger, 1093 r.authorizer, r.repo, r.modulesProvider, r.metrics) 1094 1095 return r 1096 }