github.com/weaviate/weaviate@v1.24.6/adapters/handlers/graphql/local/get/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 // These tests verify that the parameters to the resolver are properly extracted from a GraphQL query. 13 14 package get 15 16 import ( 17 "fmt" 18 "testing" 19 "time" 20 21 "github.com/go-openapi/strfmt" 22 "github.com/google/uuid" 23 "github.com/stretchr/testify/assert" 24 "github.com/stretchr/testify/require" 25 "github.com/tailor-inc/graphql/language/ast" 26 test_helper "github.com/weaviate/weaviate/adapters/handlers/graphql/test/helper" 27 "github.com/weaviate/weaviate/entities/additional" 28 "github.com/weaviate/weaviate/entities/dto" 29 "github.com/weaviate/weaviate/entities/filters" 30 "github.com/weaviate/weaviate/entities/models" 31 "github.com/weaviate/weaviate/entities/search" 32 "github.com/weaviate/weaviate/entities/searchparams" 33 helper "github.com/weaviate/weaviate/test/helper" 34 ) 35 36 func TestSimpleFieldParamsOK(t *testing.T) { 37 t.Parallel() 38 resolver := newMockResolver() 39 expectedParams := dto.GetParams{ 40 ClassName: "SomeAction", 41 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 42 } 43 44 resolver.On("GetClass", expectedParams). 45 Return(test_helper.EmptyList(), nil).Once() 46 47 resolver.AssertResolve(t, "{ Get { SomeAction { intField } } }") 48 } 49 50 func TestExtractIntField(t *testing.T) { 51 t.Parallel() 52 53 resolver := newMockResolver() 54 55 expectedParams := dto.GetParams{ 56 ClassName: "SomeAction", 57 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 58 } 59 60 resolver.On("GetClass", expectedParams). 61 Return(test_helper.EmptyList(), nil).Once() 62 63 query := "{ Get { SomeAction { intField } } }" 64 resolver.AssertResolve(t, query) 65 } 66 67 func TestExtractGeoCoordinatesField(t *testing.T) { 68 t.Parallel() 69 70 resolver := newMockResolver() 71 72 expectedParams := dto.GetParams{ 73 ClassName: "SomeAction", 74 Properties: []search.SelectProperty{{Name: "location", IsPrimitive: true}}, 75 } 76 77 resolverReturn := []interface{}{ 78 map[string]interface{}{ 79 "location": &models.GeoCoordinates{Latitude: ptFloat32(0.5), Longitude: ptFloat32(0.6)}, 80 }, 81 } 82 83 resolver.On("GetClass", expectedParams). 84 Return(resolverReturn, nil).Once() 85 86 query := "{ Get { SomeAction { location { latitude longitude } } } }" 87 result := resolver.AssertResolve(t, query) 88 89 expectedLocation := map[string]interface{}{ 90 "location": map[string]interface{}{ 91 "latitude": float32(0.5), 92 "longitude": float32(0.6), 93 }, 94 } 95 96 assert.Equal(t, expectedLocation, result.Get("Get", "SomeAction").Result.([]interface{})[0]) 97 } 98 99 func TestExtractUUIDField(t *testing.T) { 100 t.Parallel() 101 102 resolver := newMockResolver() 103 104 expectedParams := dto.GetParams{ 105 ClassName: "SomeAction", 106 Properties: []search.SelectProperty{{Name: "uuidField", IsPrimitive: true}}, 107 } 108 109 id := uuid.New() 110 111 resolverReturn := []interface{}{ 112 map[string]interface{}{ 113 "uuidField": id, 114 }, 115 } 116 117 resolver.On("GetClass", expectedParams). 118 Return(resolverReturn, nil).Once() 119 120 query := "{ Get { SomeAction { uuidField } } }" 121 result := resolver.AssertResolve(t, query) 122 123 expectedProps := map[string]interface{}{ 124 "uuidField": id.String(), 125 } 126 127 assert.Equal(t, expectedProps, result.Get("Get", "SomeAction").Result.([]interface{})[0]) 128 } 129 130 func TestExtractUUIDArrayField(t *testing.T) { 131 t.Parallel() 132 133 resolver := newMockResolver() 134 135 expectedParams := dto.GetParams{ 136 ClassName: "SomeAction", 137 Properties: []search.SelectProperty{{Name: "uuidArrayField", IsPrimitive: true}}, 138 } 139 140 id1 := uuid.New() 141 id2 := uuid.New() 142 143 resolverReturn := []interface{}{ 144 map[string]interface{}{ 145 "uuidArrayField": []uuid.UUID{id1, id2}, 146 }, 147 } 148 149 resolver.On("GetClass", expectedParams). 150 Return(resolverReturn, nil).Once() 151 152 query := "{ Get { SomeAction { uuidArrayField } } }" 153 result := resolver.AssertResolve(t, query) 154 155 expectedProps := map[string]interface{}{ 156 "uuidArrayField": []any{id1.String(), id2.String()}, 157 } 158 159 assert.Equal(t, expectedProps, result.Get("Get", "SomeAction").Result.([]interface{})[0]) 160 } 161 162 func TestExtractPhoneNumberField(t *testing.T) { 163 // We need to explicitly test all cases of asking for just one sub-property 164 // at a time, because the AST-parsing uses known fields of known props to 165 // distinguish a complex primitive prop from a reference prop 166 // 167 // See "isPrimitive()" and "fieldNameIsOfObjectButNonReferenceType" in 168 // class_builder_fields.go for more details 169 170 type test struct { 171 name string 172 query string 173 expectedParams dto.GetParams 174 resolverReturn interface{} 175 expectedResult interface{} 176 } 177 178 tests := []test{ 179 { 180 name: "with only input requested", 181 query: "{ Get { SomeAction { phone { input } } } }", 182 expectedParams: dto.GetParams{ 183 ClassName: "SomeAction", 184 Properties: []search.SelectProperty{{Name: "phone", IsPrimitive: true}}, 185 }, 186 resolverReturn: []interface{}{ 187 map[string]interface{}{ 188 "phone": &models.PhoneNumber{Input: "+49 171 1234567"}, 189 }, 190 }, 191 expectedResult: map[string]interface{}{ 192 "phone": map[string]interface{}{ 193 "input": "+49 171 1234567", 194 }, 195 }, 196 }, 197 { 198 name: "with only internationalFormatted requested", 199 query: "{ Get { SomeAction { phone { internationalFormatted } } } }", 200 expectedParams: dto.GetParams{ 201 ClassName: "SomeAction", 202 Properties: []search.SelectProperty{{Name: "phone", IsPrimitive: true}}, 203 }, 204 resolverReturn: []interface{}{ 205 map[string]interface{}{ 206 "phone": &models.PhoneNumber{InternationalFormatted: "+49 171 1234567"}, 207 }, 208 }, 209 expectedResult: map[string]interface{}{ 210 "phone": map[string]interface{}{ 211 "internationalFormatted": "+49 171 1234567", 212 }, 213 }, 214 }, 215 { 216 name: "with only nationalFormatted requested", 217 query: "{ Get { SomeAction { phone { nationalFormatted } } } }", 218 expectedParams: dto.GetParams{ 219 ClassName: "SomeAction", 220 Properties: []search.SelectProperty{{Name: "phone", IsPrimitive: true}}, 221 }, 222 resolverReturn: []interface{}{ 223 map[string]interface{}{ 224 "phone": &models.PhoneNumber{NationalFormatted: "0171 1234567"}, 225 }, 226 }, 227 expectedResult: map[string]interface{}{ 228 "phone": map[string]interface{}{ 229 "nationalFormatted": "0171 1234567", 230 }, 231 }, 232 }, 233 { 234 name: "with only national requested", 235 query: "{ Get { SomeAction { phone { national } } } }", 236 expectedParams: dto.GetParams{ 237 ClassName: "SomeAction", 238 Properties: []search.SelectProperty{{Name: "phone", IsPrimitive: true}}, 239 }, 240 resolverReturn: []interface{}{ 241 map[string]interface{}{ 242 "phone": &models.PhoneNumber{National: 0o1711234567}, 243 }, 244 }, 245 expectedResult: map[string]interface{}{ 246 "phone": map[string]interface{}{ 247 "national": 0o1711234567, 248 }, 249 }, 250 }, 251 { 252 name: "with only valid requested", 253 query: "{ Get { SomeAction { phone { valid } } } }", 254 expectedParams: dto.GetParams{ 255 ClassName: "SomeAction", 256 Properties: []search.SelectProperty{{Name: "phone", IsPrimitive: true}}, 257 }, 258 resolverReturn: []interface{}{ 259 map[string]interface{}{ 260 "phone": &models.PhoneNumber{Valid: true}, 261 }, 262 }, 263 expectedResult: map[string]interface{}{ 264 "phone": map[string]interface{}{ 265 "valid": true, 266 }, 267 }, 268 }, 269 { 270 name: "with only countryCode requested", 271 query: "{ Get { SomeAction { phone { countryCode } } } }", 272 expectedParams: dto.GetParams{ 273 ClassName: "SomeAction", 274 Properties: []search.SelectProperty{{Name: "phone", IsPrimitive: true}}, 275 }, 276 resolverReturn: []interface{}{ 277 map[string]interface{}{ 278 "phone": &models.PhoneNumber{CountryCode: 49}, 279 }, 280 }, 281 expectedResult: map[string]interface{}{ 282 "phone": map[string]interface{}{ 283 "countryCode": 49, 284 }, 285 }, 286 }, 287 { 288 name: "with only defaultCountry requested", 289 query: "{ Get { SomeAction { phone { defaultCountry } } } }", 290 expectedParams: dto.GetParams{ 291 ClassName: "SomeAction", 292 Properties: []search.SelectProperty{{Name: "phone", IsPrimitive: true}}, 293 }, 294 resolverReturn: []interface{}{ 295 map[string]interface{}{ 296 "phone": &models.PhoneNumber{DefaultCountry: "DE"}, 297 }, 298 }, 299 expectedResult: map[string]interface{}{ 300 "phone": map[string]interface{}{ 301 "defaultCountry": "DE", 302 }, 303 }, 304 }, 305 { 306 name: "with multiple fields set", 307 query: "{ Get { SomeAction { phone { input internationalFormatted " + 308 "nationalFormatted defaultCountry national countryCode valid } } } }", 309 expectedParams: dto.GetParams{ 310 ClassName: "SomeAction", 311 Properties: []search.SelectProperty{{Name: "phone", IsPrimitive: true}}, 312 }, 313 resolverReturn: []interface{}{ 314 map[string]interface{}{ 315 "phone": &models.PhoneNumber{ 316 DefaultCountry: "DE", 317 CountryCode: 49, 318 NationalFormatted: "0171 123456", 319 InternationalFormatted: "+49 171 123456", 320 National: 171123456, 321 Input: "0171123456", 322 Valid: true, 323 }, 324 }, 325 }, 326 expectedResult: map[string]interface{}{ 327 "phone": map[string]interface{}{ 328 "defaultCountry": "DE", 329 "countryCode": 49, 330 "nationalFormatted": "0171 123456", 331 "internationalFormatted": "+49 171 123456", 332 "national": 171123456, 333 "input": "0171123456", 334 "valid": true, 335 }, 336 }, 337 }, 338 } 339 340 for _, test := range tests { 341 t.Run(test.name, func(t *testing.T) { 342 resolver := newMockResolver() 343 344 resolver.On("GetClass", test.expectedParams). 345 Return(test.resolverReturn, nil).Once() 346 result := resolver.AssertResolve(t, test.query) 347 assert.Equal(t, test.expectedResult, result.Get("Get", "SomeAction").Result.([]interface{})[0]) 348 }) 349 } 350 } 351 352 func TestExtractAdditionalFields(t *testing.T) { 353 // We don't need to explicitly test every subselection as we did on 354 // phoneNumber as these fields have fixed keys. So we can simply check for 355 // the prop 356 357 type test struct { 358 name string 359 query string 360 expectedParams dto.GetParams 361 resolverReturn interface{} 362 expectedResult interface{} 363 } 364 365 // To facilitate testing timestamps 366 nowString := fmt.Sprint(time.Now().UnixNano() / int64(time.Millisecond)) 367 368 tests := []test{ 369 { 370 name: "with _additional distance", 371 query: "{ Get { SomeAction { _additional { distance } } } }", 372 expectedParams: dto.GetParams{ 373 ClassName: "SomeAction", 374 AdditionalProperties: additional.Properties{ 375 Distance: true, 376 }, 377 }, 378 resolverReturn: []interface{}{ 379 map[string]interface{}{ 380 "_additional": map[string]interface{}{ 381 "distance": helper.CertaintyToDist(t, 0.69), 382 }, 383 }, 384 }, 385 expectedResult: map[string]interface{}{ 386 "_additional": map[string]interface{}{ 387 "distance": helper.CertaintyToDist(t, 0.69), 388 }, 389 }, 390 }, 391 { 392 name: "with _additional certainty", 393 query: "{ Get { SomeAction { _additional { certainty } } } }", 394 expectedParams: dto.GetParams{ 395 ClassName: "SomeAction", 396 AdditionalProperties: additional.Properties{ 397 Certainty: true, 398 }, 399 }, 400 resolverReturn: []interface{}{ 401 map[string]interface{}{ 402 "_additional": map[string]interface{}{ 403 "certainty": 0.69, 404 "distance": helper.CertaintyToDist(t, 0.69), 405 }, 406 }, 407 }, 408 expectedResult: map[string]interface{}{ 409 "_additional": map[string]interface{}{ 410 "certainty": 0.69, 411 }, 412 }, 413 }, 414 { 415 name: "with _additional vector", 416 query: "{ Get { SomeAction { _additional { vector } } } }", 417 expectedParams: dto.GetParams{ 418 ClassName: "SomeAction", 419 AdditionalProperties: additional.Properties{ 420 Vector: true, 421 }, 422 }, 423 resolverReturn: []interface{}{ 424 map[string]interface{}{ 425 "_additional": map[string]interface{}{ 426 "vector": []float32{0.1, -0.3}, 427 }, 428 }, 429 }, 430 expectedResult: map[string]interface{}{ 431 "_additional": map[string]interface{}{ 432 "vector": []interface{}{float32(0.1), float32(-0.3)}, 433 }, 434 }, 435 }, 436 { 437 name: "with _additional creationTimeUnix", 438 query: "{ Get { SomeAction { _additional { creationTimeUnix } } } }", 439 expectedParams: dto.GetParams{ 440 ClassName: "SomeAction", 441 AdditionalProperties: additional.Properties{ 442 CreationTimeUnix: true, 443 }, 444 }, 445 resolverReturn: []interface{}{ 446 map[string]interface{}{ 447 "_additional": map[string]interface{}{ 448 "creationTimeUnix": nowString, 449 }, 450 }, 451 }, 452 expectedResult: map[string]interface{}{ 453 "_additional": map[string]interface{}{ 454 "creationTimeUnix": nowString, 455 }, 456 }, 457 }, 458 { 459 name: "with _additional lastUpdateTimeUnix", 460 query: "{ Get { SomeAction { _additional { lastUpdateTimeUnix } } } }", 461 expectedParams: dto.GetParams{ 462 ClassName: "SomeAction", 463 AdditionalProperties: additional.Properties{ 464 LastUpdateTimeUnix: true, 465 }, 466 }, 467 resolverReturn: []interface{}{ 468 map[string]interface{}{ 469 "_additional": map[string]interface{}{ 470 "lastUpdateTimeUnix": nowString, 471 }, 472 }, 473 }, 474 expectedResult: map[string]interface{}{ 475 "_additional": map[string]interface{}{ 476 "lastUpdateTimeUnix": nowString, 477 }, 478 }, 479 }, 480 { 481 name: "with _additional classification", 482 query: "{ Get { SomeAction { _additional { classification { id completed classifiedFields scope basedOn } } } } }", 483 expectedParams: dto.GetParams{ 484 ClassName: "SomeAction", 485 AdditionalProperties: additional.Properties{ 486 Classification: true, 487 }, 488 }, 489 resolverReturn: []interface{}{ 490 map[string]interface{}{ 491 "_additional": models.AdditionalProperties{ 492 "classification": &additional.Classification{ 493 ID: "12345", 494 BasedOn: []string{"primitiveProp"}, 495 Scope: []string{"refprop1", "refprop2", "refprop3"}, 496 ClassifiedFields: []string{"refprop3"}, 497 Completed: timeMust(strfmt.ParseDateTime("2006-01-02T15:04:05.000Z")), 498 }, 499 }, 500 }, 501 }, 502 expectedResult: map[string]interface{}{ 503 "_additional": map[string]interface{}{ 504 "classification": map[string]interface{}{ 505 "id": "12345", 506 "basedOn": []interface{}{"primitiveProp"}, 507 "scope": []interface{}{"refprop1", "refprop2", "refprop3"}, 508 "classifiedFields": []interface{}{"refprop3"}, 509 "completed": "2006-01-02T15:04:05.000Z", 510 }, 511 }, 512 }, 513 }, 514 { 515 name: "with _additional interpretation", 516 query: "{ Get { SomeAction { _additional { interpretation { source { concept weight occurrence } } } } } }", 517 expectedParams: dto.GetParams{ 518 ClassName: "SomeAction", 519 AdditionalProperties: additional.Properties{ 520 ModuleParams: map[string]interface{}{ 521 "interpretation": true, 522 }, 523 }, 524 }, 525 resolverReturn: []interface{}{ 526 map[string]interface{}{ 527 "_additional": map[string]interface{}{ 528 "interpretation": &Interpretation{ 529 Source: []*InterpretationSource{ 530 { 531 Concept: "foo", 532 Weight: 0.6, 533 Occurrence: 1200, 534 }, 535 { 536 Concept: "bar", 537 Weight: 0.9, 538 Occurrence: 800, 539 }, 540 }, 541 }, 542 }, 543 }, 544 }, 545 expectedResult: map[string]interface{}{ 546 "_additional": map[string]interface{}{ 547 "interpretation": map[string]interface{}{ 548 "source": []interface{}{ 549 map[string]interface{}{ 550 "concept": "foo", 551 "weight": 0.6, 552 "occurrence": 1200, 553 }, 554 map[string]interface{}{ 555 "concept": "bar", 556 "weight": 0.9, 557 "occurrence": 800, 558 }, 559 }, 560 }, 561 }, 562 }, 563 }, 564 { 565 name: "with _additional nearestNeighbors", 566 query: "{ Get { SomeAction { _additional { nearestNeighbors { neighbors { concept distance } } } } } }", 567 expectedParams: dto.GetParams{ 568 ClassName: "SomeAction", 569 AdditionalProperties: additional.Properties{ 570 ModuleParams: map[string]interface{}{ 571 "nearestNeighbors": true, 572 }, 573 }, 574 }, 575 resolverReturn: []interface{}{ 576 map[string]interface{}{ 577 "_additional": map[string]interface{}{ 578 "nearestNeighbors": &NearestNeighbors{ 579 Neighbors: []*NearestNeighbor{ 580 { 581 Concept: "foo", 582 Distance: 0.1, 583 }, 584 { 585 Concept: "bar", 586 Distance: 0.2, 587 }, 588 }, 589 }, 590 }, 591 }, 592 }, 593 expectedResult: map[string]interface{}{ 594 "_additional": map[string]interface{}{ 595 "nearestNeighbors": map[string]interface{}{ 596 "neighbors": []interface{}{ 597 map[string]interface{}{ 598 "concept": "foo", 599 "distance": float32(0.1), 600 }, 601 map[string]interface{}{ 602 "concept": "bar", 603 "distance": float32(0.2), 604 }, 605 }, 606 }, 607 }, 608 }, 609 }, 610 { 611 name: "with _additional featureProjection without any optional parameters", 612 query: "{ Get { SomeAction { _additional { featureProjection { vector } } } } }", 613 expectedParams: dto.GetParams{ 614 ClassName: "SomeAction", 615 AdditionalProperties: additional.Properties{ 616 ModuleParams: map[string]interface{}{ 617 "featureProjection": extractAdditionalParam("featureProjection", nil), 618 }, 619 }, 620 }, 621 resolverReturn: []interface{}{ 622 map[string]interface{}{ 623 "_additional": models.AdditionalProperties{ 624 "featureProjection": &FeatureProjection{ 625 Vector: []float32{0.0, 1.1, 2.2}, 626 }, 627 }, 628 }, 629 }, 630 expectedResult: map[string]interface{}{ 631 "_additional": map[string]interface{}{ 632 "featureProjection": map[string]interface{}{ 633 "vector": []interface{}{float32(0.0), float32(1.1), float32(2.2)}, 634 }, 635 }, 636 }, 637 }, 638 { 639 name: "with _additional featureProjection with optional parameters", 640 query: `{ Get { SomeAction { _additional { featureProjection(algorithm: "tsne", dimensions: 3, learningRate: 15, iterations: 100, perplexity: 10) { vector } } } } }`, 641 expectedParams: dto.GetParams{ 642 ClassName: "SomeAction", 643 AdditionalProperties: additional.Properties{ 644 ModuleParams: map[string]interface{}{ 645 "featureProjection": extractAdditionalParam("featureProjection", 646 []*ast.Argument{ 647 createArg("algorithm", "tsne"), 648 createArg("dimensions", "3"), 649 createArg("iterations", "100"), 650 createArg("learningRate", "15"), 651 createArg("perplexity", "10"), 652 }, 653 ), 654 }, 655 }, 656 }, 657 resolverReturn: []interface{}{ 658 map[string]interface{}{ 659 "_additional": map[string]interface{}{ 660 "featureProjection": &FeatureProjection{ 661 Vector: []float32{0.0, 1.1, 2.2}, 662 }, 663 }, 664 }, 665 }, 666 expectedResult: map[string]interface{}{ 667 "_additional": map[string]interface{}{ 668 "featureProjection": map[string]interface{}{ 669 "vector": []interface{}{float32(0.0), float32(1.1), float32(2.2)}, 670 }, 671 }, 672 }, 673 }, 674 { 675 name: "with _additional semanticPath set", 676 query: `{ Get { SomeAction { _additional { semanticPath { path { concept distanceToQuery distanceToResult distanceToPrevious distanceToNext } } } } } }`, 677 expectedParams: dto.GetParams{ 678 ClassName: "SomeAction", 679 AdditionalProperties: additional.Properties{ 680 ModuleParams: map[string]interface{}{ 681 "semanticPath": extractAdditionalParam("semanticPath", nil), 682 }, 683 }, 684 }, 685 resolverReturn: []interface{}{ 686 map[string]interface{}{ 687 "_additional": models.AdditionalProperties{ 688 "semanticPath": &SemanticPath{ 689 Path: []*SemanticPathElement{ 690 { 691 Concept: "foo", 692 DistanceToNext: ptFloat32(0.5), 693 DistanceToPrevious: nil, 694 DistanceToQuery: 0.1, 695 DistanceToResult: 0.1, 696 }, 697 { 698 Concept: "bar", 699 DistanceToPrevious: ptFloat32(0.5), 700 DistanceToNext: nil, 701 DistanceToQuery: 0.1, 702 DistanceToResult: 0.1, 703 }, 704 }, 705 }, 706 }, 707 }, 708 }, 709 expectedResult: map[string]interface{}{ 710 "_additional": map[string]interface{}{ 711 "semanticPath": map[string]interface{}{ 712 "path": []interface{}{ 713 map[string]interface{}{ 714 "concept": "foo", 715 "distanceToNext": float32(0.5), 716 "distanceToPrevious": nil, 717 "distanceToQuery": float32(0.1), 718 "distanceToResult": float32(0.1), 719 }, 720 map[string]interface{}{ 721 "concept": "bar", 722 "distanceToPrevious": float32(0.5), 723 "distanceToNext": nil, 724 "distanceToQuery": float32(0.1), 725 "distanceToResult": float32(0.1), 726 }, 727 }, 728 }, 729 }, 730 }, 731 }, 732 } 733 734 for _, test := range tests { 735 t.Run(test.name, func(t *testing.T) { 736 resolver := newMockResolverWithVectorizer("mock-custom-near-text-module") 737 738 resolver.On("GetClass", test.expectedParams). 739 Return(test.resolverReturn, nil).Once() 740 result := resolver.AssertResolve(t, test.query) 741 assert.Equal(t, test.expectedResult, result.Get("Get", "SomeAction").Result.([]interface{})[0]) 742 }) 743 } 744 } 745 746 func TestNearCustomTextRanker(t *testing.T) { 747 t.Parallel() 748 749 resolver := newMockResolverWithVectorizer("mock-custom-near-text-module") 750 751 t.Run("for actions", func(t *testing.T) { 752 query := `{ Get { SomeAction(nearCustomText: { 753 concepts: ["c1", "c2", "c3"], 754 moveTo: { 755 concepts:["positive"], 756 force: 0.5 757 } 758 moveAwayFrom: { 759 concepts:["epic"] 760 force: 0.25 761 } 762 }) { intField } } }` 763 764 expectedParams := dto.GetParams{ 765 ClassName: "SomeAction", 766 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 767 ModuleParams: map[string]interface{}{ 768 "nearCustomText": extractNearTextParam(map[string]interface{}{ 769 "concepts": []interface{}{"c1", "c2", "c3"}, 770 "moveTo": map[string]interface{}{ 771 "concepts": []interface{}{"positive"}, 772 "force": float64(0.5), 773 }, 774 "moveAwayFrom": map[string]interface{}{ 775 "concepts": []interface{}{"epic"}, 776 "force": float64(0.25), 777 }, 778 }), 779 }, 780 } 781 782 resolver.On("GetClass", expectedParams). 783 Return([]interface{}{}, nil).Once() 784 785 resolver.AssertResolve(t, query) 786 }) 787 788 t.Run("for a class that does not have a text2vec module", func(t *testing.T) { 789 query := `{ Get { CustomVectorClass(nearCustomText: { 790 concepts: ["c1", "c2", "c3"], 791 moveTo: { 792 concepts:["positive"], 793 force: 0.5 794 } 795 moveAwayFrom: { 796 concepts:["epic"] 797 force: 0.25 798 } 799 }) { intField } } }` 800 801 res := resolver.Resolve(query) 802 require.Len(t, res.Errors, 1) 803 assert.Contains(t, res.Errors[0].Message, "Unknown argument \"nearCustomText\" on field \"CustomVectorClass\"") 804 }) 805 806 t.Run("for things with optional distance set", func(t *testing.T) { 807 query := `{ Get { SomeThing(nearCustomText: { 808 concepts: ["c1", "c2", "c3"], 809 distance: 0.6, 810 moveTo: { 811 concepts:["positive"], 812 force: 0.5 813 } 814 moveAwayFrom: { 815 concepts:["epic"] 816 force: 0.25 817 } 818 }) { intField } } }` 819 820 expectedParams := dto.GetParams{ 821 ClassName: "SomeThing", 822 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 823 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 824 ModuleParams: map[string]interface{}{ 825 "nearCustomText": extractNearTextParam(map[string]interface{}{ 826 "concepts": []interface{}{"c1", "c2", "c3"}, 827 "distance": float64(0.6), 828 "moveTo": map[string]interface{}{ 829 "concepts": []interface{}{"positive"}, 830 "force": float64(0.5), 831 }, 832 "moveAwayFrom": map[string]interface{}{ 833 "concepts": []interface{}{"epic"}, 834 "force": float64(0.25), 835 }, 836 }), 837 }, 838 } 839 resolver.On("GetClass", expectedParams). 840 Return([]interface{}{}, nil).Once() 841 842 resolver.AssertResolve(t, query) 843 }) 844 845 t.Run("for things with optional certainty set", func(t *testing.T) { 846 query := `{ Get { SomeThing(nearCustomText: { 847 concepts: ["c1", "c2", "c3"], 848 certainty: 0.4, 849 moveTo: { 850 concepts:["positive"], 851 force: 0.5 852 } 853 moveAwayFrom: { 854 concepts:["epic"] 855 force: 0.25 856 } 857 }) { intField } } }` 858 859 expectedParams := dto.GetParams{ 860 ClassName: "SomeThing", 861 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 862 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 863 ModuleParams: map[string]interface{}{ 864 "nearCustomText": extractNearTextParam(map[string]interface{}{ 865 "concepts": []interface{}{"c1", "c2", "c3"}, 866 "certainty": float64(0.4), 867 "moveTo": map[string]interface{}{ 868 "concepts": []interface{}{"positive"}, 869 "force": float64(0.5), 870 }, 871 "moveAwayFrom": map[string]interface{}{ 872 "concepts": []interface{}{"epic"}, 873 "force": float64(0.25), 874 }, 875 }), 876 }, 877 } 878 resolver.On("GetClass", expectedParams). 879 Return([]interface{}{}, nil).Once() 880 881 resolver.AssertResolve(t, query) 882 }) 883 884 t.Run("for things with optional distance and objects set", func(t *testing.T) { 885 query := `{ Get { SomeThing(nearCustomText: { 886 concepts: ["c1", "c2", "c3"], 887 distance: 0.4, 888 moveTo: { 889 concepts:["positive"], 890 force: 0.5 891 objects: [ 892 { id: "moveTo-uuid1" } 893 { beacon: "weaviate://localhost/moveTo-uuid1" }, 894 { beacon: "weaviate://localhost/moveTo-uuid2" } 895 ] 896 } 897 moveAwayFrom: { 898 concepts:["epic"] 899 force: 0.25 900 objects: [ 901 { id: "moveAway-uuid1" } 902 { beacon: "weaviate://localhost/moveAway-uuid2" } 903 ] 904 } 905 }) { intField } } }` 906 907 expectedParams := dto.GetParams{ 908 ClassName: "SomeThing", 909 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 910 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 911 ModuleParams: map[string]interface{}{ 912 "nearCustomText": extractNearTextParam(map[string]interface{}{ 913 "concepts": []interface{}{"c1", "c2", "c3"}, 914 "distance": float64(0.4), 915 "moveTo": map[string]interface{}{ 916 "concepts": []interface{}{"positive"}, 917 "force": float64(0.5), 918 "objects": []interface{}{ 919 map[string]interface{}{ 920 "id": "moveTo-uuid1", 921 }, 922 map[string]interface{}{ 923 "beacon": "weaviate://localhost/moveTo-uuid1", 924 }, 925 map[string]interface{}{ 926 "beacon": "weaviate://localhost/moveTo-uuid2", 927 }, 928 }, 929 }, 930 "moveAwayFrom": map[string]interface{}{ 931 "concepts": []interface{}{"epic"}, 932 "force": float64(0.25), 933 "objects": []interface{}{ 934 map[string]interface{}{ 935 "id": "moveAway-uuid1", 936 }, 937 map[string]interface{}{ 938 "beacon": "weaviate://localhost/moveAway-uuid2", 939 }, 940 }, 941 }, 942 }), 943 }, 944 } 945 resolver.On("GetClass", expectedParams). 946 Return([]interface{}{}, nil).Once() 947 948 resolver.AssertResolve(t, query) 949 }) 950 951 t.Run("for things with optional certainty and objects set", func(t *testing.T) { 952 query := `{ Get { SomeThing(nearCustomText: { 953 concepts: ["c1", "c2", "c3"], 954 certainty: 0.4, 955 moveTo: { 956 concepts:["positive"], 957 force: 0.5 958 objects: [ 959 { id: "moveTo-uuid1" } 960 { beacon: "weaviate://localhost/moveTo-uuid1" }, 961 { beacon: "weaviate://localhost/moveTo-uuid2" } 962 ] 963 } 964 moveAwayFrom: { 965 concepts:["epic"] 966 force: 0.25 967 objects: [ 968 { id: "moveAway-uuid1" } 969 { beacon: "weaviate://localhost/moveAway-uuid2" } 970 ] 971 } 972 }) { intField } } }` 973 974 expectedParams := dto.GetParams{ 975 ClassName: "SomeThing", 976 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 977 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 978 ModuleParams: map[string]interface{}{ 979 "nearCustomText": extractNearTextParam(map[string]interface{}{ 980 "concepts": []interface{}{"c1", "c2", "c3"}, 981 "certainty": float64(0.4), 982 "moveTo": map[string]interface{}{ 983 "concepts": []interface{}{"positive"}, 984 "force": float64(0.5), 985 "objects": []interface{}{ 986 map[string]interface{}{ 987 "id": "moveTo-uuid1", 988 }, 989 map[string]interface{}{ 990 "beacon": "weaviate://localhost/moveTo-uuid1", 991 }, 992 map[string]interface{}{ 993 "beacon": "weaviate://localhost/moveTo-uuid2", 994 }, 995 }, 996 }, 997 "moveAwayFrom": map[string]interface{}{ 998 "concepts": []interface{}{"epic"}, 999 "force": float64(0.25), 1000 "objects": []interface{}{ 1001 map[string]interface{}{ 1002 "id": "moveAway-uuid1", 1003 }, 1004 map[string]interface{}{ 1005 "beacon": "weaviate://localhost/moveAway-uuid2", 1006 }, 1007 }, 1008 }, 1009 }), 1010 }, 1011 } 1012 resolver.On("GetClass", expectedParams). 1013 Return([]interface{}{}, nil).Once() 1014 1015 resolver.AssertResolve(t, query) 1016 }) 1017 1018 t.Run("for things with optional distance and limit set", func(t *testing.T) { 1019 query := `{ Get { SomeThing( 1020 limit: 6 1021 nearCustomText: { 1022 concepts: ["c1", "c2", "c3"], 1023 distance: 0.4, 1024 moveTo: { 1025 concepts:["positive"], 1026 force: 0.5 1027 } 1028 moveAwayFrom: { 1029 concepts:["epic"] 1030 force: 0.25 1031 } 1032 }) { intField } } }` 1033 1034 expectedParams := dto.GetParams{ 1035 ClassName: "SomeThing", 1036 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1037 Pagination: &filters.Pagination{Limit: 6}, 1038 ModuleParams: map[string]interface{}{ 1039 "nearCustomText": extractNearTextParam(map[string]interface{}{ 1040 "concepts": []interface{}{"c1", "c2", "c3"}, 1041 "distance": float64(0.4), 1042 "moveTo": map[string]interface{}{ 1043 "concepts": []interface{}{"positive"}, 1044 "force": float64(0.5), 1045 }, 1046 "moveAwayFrom": map[string]interface{}{ 1047 "concepts": []interface{}{"epic"}, 1048 "force": float64(0.25), 1049 }, 1050 }), 1051 }, 1052 } 1053 resolver.On("GetClass", expectedParams). 1054 Return([]interface{}{}, nil).Once() 1055 1056 resolver.AssertResolve(t, query) 1057 }) 1058 1059 t.Run("for things with optional certainty and limit set", func(t *testing.T) { 1060 query := `{ Get { SomeThing( 1061 limit: 6 1062 nearCustomText: { 1063 concepts: ["c1", "c2", "c3"], 1064 certainty: 0.4, 1065 moveTo: { 1066 concepts:["positive"], 1067 force: 0.5 1068 } 1069 moveAwayFrom: { 1070 concepts:["epic"] 1071 force: 0.25 1072 } 1073 }) { intField } } }` 1074 1075 expectedParams := dto.GetParams{ 1076 ClassName: "SomeThing", 1077 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1078 Pagination: &filters.Pagination{Limit: 6}, 1079 ModuleParams: map[string]interface{}{ 1080 "nearCustomText": extractNearTextParam(map[string]interface{}{ 1081 "concepts": []interface{}{"c1", "c2", "c3"}, 1082 "certainty": float64(0.4), 1083 "moveTo": map[string]interface{}{ 1084 "concepts": []interface{}{"positive"}, 1085 "force": float64(0.5), 1086 }, 1087 "moveAwayFrom": map[string]interface{}{ 1088 "concepts": []interface{}{"epic"}, 1089 "force": float64(0.25), 1090 }, 1091 }), 1092 }, 1093 } 1094 resolver.On("GetClass", expectedParams). 1095 Return([]interface{}{}, nil).Once() 1096 1097 resolver.AssertResolve(t, query) 1098 }) 1099 1100 t.Run("for things with optional distance and negative limit set", func(t *testing.T) { 1101 query := `{ Get { SomeThing( 1102 limit: -1 1103 nearCustomText: { 1104 concepts: ["c1", "c2", "c3"], 1105 distance: 0.4, 1106 moveTo: { 1107 concepts:["positive"], 1108 force: 0.5 1109 } 1110 moveAwayFrom: { 1111 concepts:["epic"] 1112 force: 0.25 1113 } 1114 }) { intField } } }` 1115 1116 expectedParams := dto.GetParams{ 1117 ClassName: "SomeThing", 1118 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1119 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 1120 ModuleParams: map[string]interface{}{ 1121 "nearCustomText": extractNearTextParam(map[string]interface{}{ 1122 "concepts": []interface{}{"c1", "c2", "c3"}, 1123 "distance": float64(0.4), 1124 "moveTo": map[string]interface{}{ 1125 "concepts": []interface{}{"positive"}, 1126 "force": float64(0.5), 1127 }, 1128 "moveAwayFrom": map[string]interface{}{ 1129 "concepts": []interface{}{"epic"}, 1130 "force": float64(0.25), 1131 }, 1132 }), 1133 }, 1134 } 1135 resolver.On("GetClass", expectedParams). 1136 Return([]interface{}{}, nil).Once() 1137 1138 resolver.AssertResolve(t, query) 1139 }) 1140 1141 t.Run("for things with optional certainty and negative limit set", func(t *testing.T) { 1142 query := `{ Get { SomeThing( 1143 limit: -1 1144 nearCustomText: { 1145 concepts: ["c1", "c2", "c3"], 1146 certainty: 0.4, 1147 moveTo: { 1148 concepts:["positive"], 1149 force: 0.5 1150 } 1151 moveAwayFrom: { 1152 concepts:["epic"] 1153 force: 0.25 1154 } 1155 }) { intField } } }` 1156 1157 expectedParams := dto.GetParams{ 1158 ClassName: "SomeThing", 1159 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1160 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 1161 ModuleParams: map[string]interface{}{ 1162 "nearCustomText": extractNearTextParam(map[string]interface{}{ 1163 "concepts": []interface{}{"c1", "c2", "c3"}, 1164 "certainty": float64(0.4), 1165 "moveTo": map[string]interface{}{ 1166 "concepts": []interface{}{"positive"}, 1167 "force": float64(0.5), 1168 }, 1169 "moveAwayFrom": map[string]interface{}{ 1170 "concepts": []interface{}{"epic"}, 1171 "force": float64(0.25), 1172 }, 1173 }), 1174 }, 1175 } 1176 resolver.On("GetClass", expectedParams). 1177 Return([]interface{}{}, nil).Once() 1178 1179 resolver.AssertResolve(t, query) 1180 }) 1181 } 1182 1183 func TestNearVectorRanker(t *testing.T) { 1184 t.Parallel() 1185 1186 resolver := newMockResolver() 1187 1188 t.Run("for actions", func(t *testing.T) { 1189 query := `{ Get { SomeAction(nearVector: { 1190 vector: [0.123, 0.984] 1191 }) { intField } } }` 1192 1193 expectedParams := dto.GetParams{ 1194 ClassName: "SomeAction", 1195 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1196 NearVector: &searchparams.NearVector{ 1197 Vector: []float32{0.123, 0.984}, 1198 }, 1199 } 1200 1201 resolver.On("GetClass", expectedParams). 1202 Return([]interface{}{}, nil).Once() 1203 1204 resolver.AssertResolve(t, query) 1205 }) 1206 1207 t.Run("for things with optional distance set", func(t *testing.T) { 1208 query := `{ Get { SomeThing(nearVector: { 1209 vector: [0.123, 0.984] 1210 distance: 0.4 1211 }) { intField } } }` 1212 1213 expectedParams := dto.GetParams{ 1214 ClassName: "SomeThing", 1215 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1216 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 1217 NearVector: &searchparams.NearVector{ 1218 Vector: []float32{0.123, 0.984}, 1219 Distance: 0.4, 1220 WithDistance: true, 1221 }, 1222 } 1223 resolver.On("GetClass", expectedParams). 1224 Return([]interface{}{}, nil).Once() 1225 1226 resolver.AssertResolve(t, query) 1227 }) 1228 1229 t.Run("for things with optional certainty set", func(t *testing.T) { 1230 query := `{ Get { SomeThing(nearVector: { 1231 vector: [0.123, 0.984] 1232 certainty: 0.4 1233 }) { intField } } }` 1234 1235 expectedParams := dto.GetParams{ 1236 ClassName: "SomeThing", 1237 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1238 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 1239 NearVector: &searchparams.NearVector{ 1240 Vector: []float32{0.123, 0.984}, 1241 Certainty: 0.4, 1242 }, 1243 } 1244 resolver.On("GetClass", expectedParams). 1245 Return([]interface{}{}, nil).Once() 1246 1247 resolver.AssertResolve(t, query) 1248 }) 1249 1250 t.Run("for things with optional distance and limit set", func(t *testing.T) { 1251 query := `{ Get { SomeThing( 1252 limit: 4 1253 nearVector: { 1254 vector: [0.123, 0.984] 1255 distance: 0.1 1256 }) { intField } } }` 1257 1258 expectedParams := dto.GetParams{ 1259 ClassName: "SomeThing", 1260 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1261 Pagination: &filters.Pagination{Limit: 4}, 1262 NearVector: &searchparams.NearVector{ 1263 Vector: []float32{0.123, 0.984}, 1264 Distance: 0.1, 1265 WithDistance: true, 1266 }, 1267 } 1268 1269 resolver.On("GetClass", expectedParams). 1270 Return([]interface{}{}, nil).Once() 1271 1272 resolver.AssertResolve(t, query) 1273 }) 1274 1275 t.Run("for things with optional certainty and limit set", func(t *testing.T) { 1276 query := `{ Get { SomeThing( 1277 limit: 4 1278 nearVector: { 1279 vector: [0.123, 0.984] 1280 certainty: 0.1 1281 }) { intField } } }` 1282 1283 expectedParams := dto.GetParams{ 1284 ClassName: "SomeThing", 1285 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1286 Pagination: &filters.Pagination{Limit: 4}, 1287 NearVector: &searchparams.NearVector{ 1288 Vector: []float32{0.123, 0.984}, 1289 Certainty: 0.1, 1290 }, 1291 } 1292 1293 resolver.On("GetClass", expectedParams). 1294 Return([]interface{}{}, nil).Once() 1295 1296 resolver.AssertResolve(t, query) 1297 }) 1298 1299 t.Run("for things with optional distance and negative limit set", func(t *testing.T) { 1300 query := `{ Get { SomeThing( 1301 limit: -1 1302 nearVector: { 1303 vector: [0.123, 0.984] 1304 distance: 0.1 1305 }) { intField } } }` 1306 1307 expectedParams := dto.GetParams{ 1308 ClassName: "SomeThing", 1309 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1310 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 1311 NearVector: &searchparams.NearVector{ 1312 Vector: []float32{0.123, 0.984}, 1313 Distance: 0.1, 1314 WithDistance: true, 1315 }, 1316 } 1317 1318 resolver.On("GetClass", expectedParams). 1319 Return([]interface{}{}, nil).Once() 1320 1321 resolver.AssertResolve(t, query) 1322 }) 1323 1324 t.Run("for things with optional certainty and negative limit set", func(t *testing.T) { 1325 query := `{ Get { SomeThing( 1326 limit: -1 1327 nearVector: { 1328 vector: [0.123, 0.984] 1329 certainty: 0.1 1330 }) { intField } } }` 1331 1332 expectedParams := dto.GetParams{ 1333 ClassName: "SomeThing", 1334 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1335 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 1336 NearVector: &searchparams.NearVector{ 1337 Vector: []float32{0.123, 0.984}, 1338 Certainty: 0.1, 1339 }, 1340 } 1341 1342 resolver.On("GetClass", expectedParams). 1343 Return([]interface{}{}, nil).Once() 1344 1345 resolver.AssertResolve(t, query) 1346 }) 1347 } 1348 1349 func TestExtractPagination(t *testing.T) { 1350 t.Parallel() 1351 1352 resolver := newMockResolver() 1353 1354 expectedParams := dto.GetParams{ 1355 ClassName: "SomeAction", 1356 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1357 Pagination: &filters.Pagination{ 1358 Limit: 10, 1359 }, 1360 } 1361 1362 resolver.On("GetClass", expectedParams). 1363 Return(test_helper.EmptyList(), nil).Once() 1364 1365 query := "{ Get { SomeAction(limit: 10) { intField } } }" 1366 resolver.AssertResolve(t, query) 1367 } 1368 1369 func TestExtractPaginationWithOffset(t *testing.T) { 1370 t.Parallel() 1371 1372 resolver := newMockResolver() 1373 1374 expectedParams := dto.GetParams{ 1375 ClassName: "SomeAction", 1376 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1377 Pagination: &filters.Pagination{ 1378 Offset: 5, 1379 Limit: 10, 1380 }, 1381 } 1382 1383 resolver.On("GetClass", expectedParams). 1384 Return(test_helper.EmptyList(), nil).Once() 1385 1386 query := "{ Get { SomeAction(offset: 5 limit: 10) { intField } } }" 1387 resolver.AssertResolve(t, query) 1388 } 1389 1390 func TestExtractPaginationWithOnlyOffset(t *testing.T) { 1391 t.Parallel() 1392 1393 resolver := newMockResolver() 1394 1395 expectedParams := dto.GetParams{ 1396 ClassName: "SomeAction", 1397 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1398 Pagination: &filters.Pagination{ 1399 Offset: 5, 1400 Limit: -1, 1401 }, 1402 } 1403 1404 resolver.On("GetClass", expectedParams). 1405 Return(test_helper.EmptyList(), nil).Once() 1406 1407 query := "{ Get { SomeAction(offset: 5) { intField } } }" 1408 resolver.AssertResolve(t, query) 1409 } 1410 1411 func TestExtractCursor(t *testing.T) { 1412 t.Parallel() 1413 1414 resolver := newMockResolver() 1415 1416 expectedParams := dto.GetParams{ 1417 ClassName: "SomeAction", 1418 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1419 Cursor: &filters.Cursor{ 1420 After: "8ef8d5cc-c101-4fbd-a016-84e766b93ecf", 1421 Limit: 2, 1422 }, 1423 Pagination: &filters.Pagination{ 1424 Offset: 0, 1425 Limit: 2, 1426 }, 1427 } 1428 1429 resolver.On("GetClass", expectedParams). 1430 Return(test_helper.EmptyList(), nil).Once() 1431 1432 query := `{ Get { SomeAction(after: "8ef8d5cc-c101-4fbd-a016-84e766b93ecf" limit: 2) { intField } } }` 1433 resolver.AssertResolve(t, query) 1434 } 1435 1436 func TestExtractGroupParams(t *testing.T) { 1437 t.Parallel() 1438 1439 resolver := newMockResolver() 1440 1441 expectedParams := dto.GetParams{ 1442 ClassName: "SomeAction", 1443 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1444 Group: &dto.GroupParams{ 1445 Strategy: "closest", 1446 Force: 0.3, 1447 }, 1448 } 1449 1450 resolver.On("GetClass", expectedParams). 1451 Return(test_helper.EmptyList(), nil).Once() 1452 1453 query := "{ Get { SomeAction(group: {type: closest, force: 0.3}) { intField } } }" 1454 resolver.AssertResolve(t, query) 1455 } 1456 1457 func TestGetRelation(t *testing.T) { 1458 t.Parallel() 1459 1460 t.Run("without using custom fragments", func(t *testing.T) { 1461 resolver := newMockResolver() 1462 1463 expectedParams := dto.GetParams{ 1464 ClassName: "SomeAction", 1465 Properties: []search.SelectProperty{ 1466 { 1467 Name: "hasAction", 1468 IsPrimitive: false, 1469 Refs: []search.SelectClass{ 1470 { 1471 ClassName: "SomeAction", 1472 RefProperties: []search.SelectProperty{ 1473 { 1474 Name: "intField", 1475 IsPrimitive: true, 1476 }, 1477 { 1478 Name: "hasAction", 1479 IsPrimitive: false, 1480 Refs: []search.SelectClass{ 1481 { 1482 ClassName: "SomeAction", 1483 RefProperties: []search.SelectProperty{ 1484 { 1485 Name: "intField", 1486 IsPrimitive: true, 1487 }, 1488 }, 1489 }, 1490 }, 1491 }, 1492 }, 1493 }, 1494 }, 1495 }, 1496 }, 1497 } 1498 1499 resolver.On("GetClass", expectedParams). 1500 Return(test_helper.EmptyList(), nil).Once() 1501 1502 query := "{ Get { SomeAction { hasAction { ... on SomeAction { intField, hasAction { ... on SomeAction { intField } } } } } } }" 1503 resolver.AssertResolve(t, query) 1504 }) 1505 1506 t.Run("with a custom fragment one level deep", func(t *testing.T) { 1507 resolver := newMockResolver() 1508 1509 expectedParams := dto.GetParams{ 1510 ClassName: "SomeAction", 1511 Properties: []search.SelectProperty{ 1512 { 1513 Name: "hasAction", 1514 IsPrimitive: false, 1515 Refs: []search.SelectClass{ 1516 { 1517 ClassName: "SomeAction", 1518 RefProperties: []search.SelectProperty{ 1519 { 1520 Name: "intField", 1521 IsPrimitive: true, 1522 }, 1523 }, 1524 }, 1525 }, 1526 }, 1527 }, 1528 } 1529 1530 resolver.On("GetClass", expectedParams). 1531 Return(test_helper.EmptyList(), nil).Once() 1532 1533 query := "fragment actionFragment on SomeAction { intField } { Get { SomeAction { hasAction { ...actionFragment } } } }" 1534 resolver.AssertResolve(t, query) 1535 }) 1536 1537 t.Run("with a custom fragment multiple levels deep", func(t *testing.T) { 1538 resolver := newMockResolver() 1539 1540 expectedParams := dto.GetParams{ 1541 ClassName: "SomeAction", 1542 Properties: []search.SelectProperty{ 1543 { 1544 Name: "hasAction", 1545 IsPrimitive: false, 1546 Refs: []search.SelectClass{ 1547 { 1548 ClassName: "SomeAction", 1549 RefProperties: []search.SelectProperty{ 1550 { 1551 Name: "intField", 1552 IsPrimitive: true, 1553 }, 1554 { 1555 Name: "hasAction", 1556 IsPrimitive: false, 1557 Refs: []search.SelectClass{ 1558 { 1559 ClassName: "SomeAction", 1560 RefProperties: []search.SelectProperty{ 1561 { 1562 Name: "intField", 1563 IsPrimitive: true, 1564 }, 1565 }, 1566 }, 1567 }, 1568 }, 1569 }, 1570 }, 1571 }, 1572 }, 1573 }, 1574 } 1575 1576 resolver.On("GetClass", expectedParams). 1577 Return(test_helper.EmptyList(), nil).Once() 1578 1579 query := ` 1580 fragment innerFragment on SomeAction { intField } 1581 fragment actionFragment on SomeAction { intField hasAction { ...innerFragment } } 1582 1583 { Get { SomeAction { hasAction { ...actionFragment } } } }` 1584 resolver.AssertResolve(t, query) 1585 }) 1586 } 1587 1588 func TestNearObject(t *testing.T) { 1589 t.Parallel() 1590 1591 resolver := newMockResolver() 1592 1593 t.Run("for objects with beacon", func(t *testing.T) { 1594 query := `{ Get { SomeAction( 1595 nearObject: { 1596 beacon: "weaviate://localhost/some-uuid" 1597 }) { intField } } }` 1598 1599 expectedParams := dto.GetParams{ 1600 ClassName: "SomeAction", 1601 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1602 NearObject: &searchparams.NearObject{ 1603 Beacon: "weaviate://localhost/some-uuid", 1604 }, 1605 } 1606 1607 resolver.On("GetClass", expectedParams). 1608 Return([]interface{}{}, nil).Once() 1609 1610 resolver.AssertResolve(t, query) 1611 }) 1612 1613 t.Run("for objects with beacon and optional distance set", func(t *testing.T) { 1614 query := `{ Get { SomeThing( 1615 nearObject: { 1616 beacon: "weaviate://localhost/some-other-uuid" 1617 distance: 0.7 1618 }) { intField } } }` 1619 1620 expectedParams := dto.GetParams{ 1621 ClassName: "SomeThing", 1622 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1623 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 1624 NearObject: &searchparams.NearObject{ 1625 Beacon: "weaviate://localhost/some-other-uuid", 1626 Distance: 0.7, 1627 WithDistance: true, 1628 }, 1629 } 1630 resolver.On("GetClass", expectedParams). 1631 Return([]interface{}{}, nil).Once() 1632 1633 resolver.AssertResolve(t, query) 1634 }) 1635 1636 t.Run("for objects with beacon and optional certainty set", func(t *testing.T) { 1637 query := `{ Get { SomeThing( 1638 nearObject: { 1639 beacon: "weaviate://localhost/some-other-uuid" 1640 certainty: 0.7 1641 }) { intField } } }` 1642 1643 expectedParams := dto.GetParams{ 1644 ClassName: "SomeThing", 1645 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1646 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 1647 NearObject: &searchparams.NearObject{ 1648 Beacon: "weaviate://localhost/some-other-uuid", 1649 Certainty: 0.7, 1650 }, 1651 } 1652 resolver.On("GetClass", expectedParams). 1653 Return([]interface{}{}, nil).Once() 1654 1655 resolver.AssertResolve(t, query) 1656 }) 1657 1658 t.Run("for objects with id set", func(t *testing.T) { 1659 query := `{ Get { SomeAction( 1660 nearObject: { 1661 id: "some-uuid" 1662 }) { intField } } }` 1663 1664 expectedParams := dto.GetParams{ 1665 ClassName: "SomeAction", 1666 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1667 NearObject: &searchparams.NearObject{ 1668 ID: "some-uuid", 1669 }, 1670 } 1671 1672 resolver.On("GetClass", expectedParams). 1673 Return([]interface{}{}, nil).Once() 1674 1675 resolver.AssertResolve(t, query) 1676 }) 1677 1678 t.Run("for objects with id and optional distance set", func(t *testing.T) { 1679 query := `{ Get { SomeThing( 1680 nearObject: { 1681 id: "some-other-uuid" 1682 distance: 0.7 1683 }) { intField } } }` 1684 1685 expectedParams := dto.GetParams{ 1686 ClassName: "SomeThing", 1687 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 1688 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1689 NearObject: &searchparams.NearObject{ 1690 ID: "some-other-uuid", 1691 Distance: 0.7, 1692 WithDistance: true, 1693 }, 1694 } 1695 resolver.On("GetClass", expectedParams). 1696 Return([]interface{}{}, nil).Once() 1697 1698 resolver.AssertResolve(t, query) 1699 }) 1700 1701 t.Run("for objects with id and optional certainty set", func(t *testing.T) { 1702 query := `{ Get { SomeThing( 1703 nearObject: { 1704 id: "some-other-uuid" 1705 certainty: 0.7 1706 }) { intField } } }` 1707 1708 expectedParams := dto.GetParams{ 1709 ClassName: "SomeThing", 1710 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 1711 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1712 NearObject: &searchparams.NearObject{ 1713 ID: "some-other-uuid", 1714 Certainty: 0.7, 1715 }, 1716 } 1717 resolver.On("GetClass", expectedParams). 1718 Return([]interface{}{}, nil).Once() 1719 1720 resolver.AssertResolve(t, query) 1721 }) 1722 1723 t.Run("for objects with optional distance and limit set", func(t *testing.T) { 1724 query := `{ Get { SomeThing( 1725 limit: 5 1726 nearObject: { 1727 id: "some-other-uuid" 1728 distance: 0.7 1729 }) { intField } } }` 1730 1731 expectedParams := dto.GetParams{ 1732 ClassName: "SomeThing", 1733 Pagination: &filters.Pagination{Limit: 5}, 1734 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1735 NearObject: &searchparams.NearObject{ 1736 ID: "some-other-uuid", 1737 Distance: 0.7, 1738 WithDistance: true, 1739 }, 1740 } 1741 resolver.On("GetClass", expectedParams). 1742 Return([]interface{}{}, nil).Once() 1743 1744 resolver.AssertResolve(t, query) 1745 }) 1746 1747 t.Run("for objects with optional certainty and limit set", func(t *testing.T) { 1748 query := `{ Get { SomeThing( 1749 limit: 5 1750 nearObject: { 1751 id: "some-other-uuid" 1752 certainty: 0.7 1753 }) { intField } } }` 1754 1755 expectedParams := dto.GetParams{ 1756 ClassName: "SomeThing", 1757 Pagination: &filters.Pagination{Limit: 5}, 1758 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1759 NearObject: &searchparams.NearObject{ 1760 ID: "some-other-uuid", 1761 Certainty: 0.7, 1762 }, 1763 } 1764 resolver.On("GetClass", expectedParams). 1765 Return([]interface{}{}, nil).Once() 1766 1767 resolver.AssertResolve(t, query) 1768 }) 1769 1770 t.Run("for objects with optional distance and negative limit set", func(t *testing.T) { 1771 query := `{ Get { SomeThing( 1772 limit: -1 1773 nearObject: { 1774 id: "some-other-uuid" 1775 distance: 0.7 1776 }) { intField } } }` 1777 1778 expectedParams := dto.GetParams{ 1779 ClassName: "SomeThing", 1780 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 1781 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1782 NearObject: &searchparams.NearObject{ 1783 ID: "some-other-uuid", 1784 Distance: 0.7, 1785 WithDistance: true, 1786 }, 1787 } 1788 resolver.On("GetClass", expectedParams). 1789 Return([]interface{}{}, nil).Once() 1790 1791 resolver.AssertResolve(t, query) 1792 }) 1793 1794 t.Run("for objects with optional certainty and negative limit set", func(t *testing.T) { 1795 query := `{ Get { SomeThing( 1796 limit: -1 1797 nearObject: { 1798 id: "some-other-uuid" 1799 certainty: 0.7 1800 }) { intField } } }` 1801 1802 expectedParams := dto.GetParams{ 1803 ClassName: "SomeThing", 1804 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 1805 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1806 NearObject: &searchparams.NearObject{ 1807 ID: "some-other-uuid", 1808 Certainty: 0.7, 1809 }, 1810 } 1811 resolver.On("GetClass", expectedParams). 1812 Return([]interface{}{}, nil).Once() 1813 1814 resolver.AssertResolve(t, query) 1815 }) 1816 } 1817 1818 func TestNearTextNoNoModules(t *testing.T) { 1819 t.Parallel() 1820 1821 resolver := newMockResolverWithNoModules() 1822 1823 t.Run("for nearText that is not available", func(t *testing.T) { 1824 query := `{ Get { SomeAction(nearText: { 1825 concepts: ["c1", "c2", "c3"], 1826 moveTo: { 1827 concepts:["positive"], 1828 force: 0.5 1829 }, 1830 moveAwayFrom: { 1831 concepts:["epic"], 1832 force: 0.25 1833 } 1834 }) { intField } } }` 1835 1836 expectedParams := dto.GetParams{ 1837 ClassName: "SomeAction", 1838 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1839 } 1840 1841 resolver.On("GetClass", expectedParams). 1842 Return(nil, nil).Once() 1843 1844 resolver.AssertFailToResolve(t, query) 1845 }) 1846 } 1847 1848 func TestBM25WithSort(t *testing.T) { 1849 t.Parallel() 1850 resolver := newMockResolverWithNoModules() 1851 query := `{Get{SomeAction(bm25:{query:"apple",properties:["name"]},sort:[{path:["name"],order:desc}]){intField}}}` 1852 resolver.AssertFailToResolve(t, query, "bm25 search is not compatible with sort") 1853 } 1854 1855 func TestHybridWithSort(t *testing.T) { 1856 t.Parallel() 1857 resolver := newMockResolverWithNoModules() 1858 query := `{Get{SomeAction(hybrid:{query:"apple"},sort:[{path:["name"],order:desc}]){intField}}}` 1859 resolver.AssertFailToResolve(t, query, "hybrid search is not compatible with sort") 1860 } 1861 1862 func TestNearObjectNoModules(t *testing.T) { 1863 t.Parallel() 1864 1865 resolver := newMockResolverWithNoModules() 1866 1867 t.Run("for objects with beacon", func(t *testing.T) { 1868 query := `{ Get { SomeAction( 1869 nearObject: { 1870 beacon: "weaviate://localhost/some-uuid" 1871 }) { intField } } }` 1872 1873 expectedParams := dto.GetParams{ 1874 ClassName: "SomeAction", 1875 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1876 NearObject: &searchparams.NearObject{ 1877 Beacon: "weaviate://localhost/some-uuid", 1878 }, 1879 } 1880 1881 resolver.On("GetClass", expectedParams). 1882 Return([]interface{}{}, nil).Once() 1883 1884 resolver.AssertResolve(t, query) 1885 }) 1886 1887 t.Run("for objects with ID and distance set", func(t *testing.T) { 1888 query := `{ Get { SomeThing( 1889 nearObject: { 1890 id: "some-uuid" 1891 distance: 0.7 1892 }) { intField } } }` 1893 1894 expectedParams := dto.GetParams{ 1895 ClassName: "SomeThing", 1896 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 1897 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1898 NearObject: &searchparams.NearObject{ 1899 ID: "some-uuid", 1900 Distance: 0.7, 1901 WithDistance: true, 1902 }, 1903 } 1904 1905 resolver.On("GetClass", expectedParams). 1906 Return([]interface{}{}, nil).Once() 1907 1908 resolver.AssertResolve(t, query) 1909 }) 1910 1911 t.Run("for objects with ID and certainty set", func(t *testing.T) { 1912 query := `{ Get { SomeThing( 1913 nearObject: { 1914 id: "some-uuid" 1915 certainty: 0.7 1916 }) { intField } } }` 1917 1918 expectedParams := dto.GetParams{ 1919 ClassName: "SomeThing", 1920 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 1921 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1922 NearObject: &searchparams.NearObject{ 1923 ID: "some-uuid", 1924 Certainty: 0.7, 1925 }, 1926 } 1927 1928 resolver.On("GetClass", expectedParams). 1929 Return([]interface{}{}, nil).Once() 1930 1931 resolver.AssertResolve(t, query) 1932 }) 1933 1934 t.Run("for objects with distance and limit set", func(t *testing.T) { 1935 query := `{ Get { SomeThing( 1936 limit: 12 1937 nearObject: { 1938 id: "some-uuid" 1939 distance: 0.7 1940 }) { intField } } }` 1941 1942 expectedParams := dto.GetParams{ 1943 ClassName: "SomeThing", 1944 Pagination: &filters.Pagination{Limit: 12}, 1945 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1946 NearObject: &searchparams.NearObject{ 1947 ID: "some-uuid", 1948 Distance: 0.7, 1949 WithDistance: true, 1950 }, 1951 } 1952 1953 resolver.On("GetClass", expectedParams). 1954 Return([]interface{}{}, nil).Once() 1955 1956 resolver.AssertResolve(t, query) 1957 }) 1958 1959 t.Run("for objects with certainty and limit set", func(t *testing.T) { 1960 query := `{ Get { SomeThing( 1961 limit: 12 1962 nearObject: { 1963 id: "some-uuid" 1964 certainty: 0.7 1965 }) { intField } } }` 1966 1967 expectedParams := dto.GetParams{ 1968 ClassName: "SomeThing", 1969 Pagination: &filters.Pagination{Limit: 12}, 1970 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1971 NearObject: &searchparams.NearObject{ 1972 ID: "some-uuid", 1973 Certainty: 0.7, 1974 }, 1975 } 1976 1977 resolver.On("GetClass", expectedParams). 1978 Return([]interface{}{}, nil).Once() 1979 1980 resolver.AssertResolve(t, query) 1981 }) 1982 1983 t.Run("for objects with distance and negative limit set", func(t *testing.T) { 1984 query := `{ Get { SomeThing( 1985 limit: -1 1986 nearObject: { 1987 id: "some-uuid" 1988 distance: 0.7 1989 }) { intField } } }` 1990 1991 expectedParams := dto.GetParams{ 1992 ClassName: "SomeThing", 1993 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 1994 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 1995 NearObject: &searchparams.NearObject{ 1996 ID: "some-uuid", 1997 Distance: 0.7, 1998 WithDistance: true, 1999 }, 2000 } 2001 2002 resolver.On("GetClass", expectedParams). 2003 Return([]interface{}{}, nil).Once() 2004 2005 resolver.AssertResolve(t, query) 2006 }) 2007 2008 t.Run("for objects with certainty and negative limit set", func(t *testing.T) { 2009 query := `{ Get { SomeThing( 2010 limit: -1 2011 nearObject: { 2012 id: "some-uuid" 2013 certainty: 0.7 2014 }) { intField } } }` 2015 2016 expectedParams := dto.GetParams{ 2017 ClassName: "SomeThing", 2018 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 2019 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 2020 NearObject: &searchparams.NearObject{ 2021 ID: "some-uuid", 2022 Certainty: 0.7, 2023 }, 2024 } 2025 2026 resolver.On("GetClass", expectedParams). 2027 Return([]interface{}{}, nil).Once() 2028 2029 resolver.AssertResolve(t, query) 2030 }) 2031 } 2032 2033 func TestNearVectorNoModules(t *testing.T) { 2034 t.Parallel() 2035 2036 resolver := newMockResolverWithNoModules() 2037 2038 t.Run("for actions", func(t *testing.T) { 2039 query := `{ Get { SomeAction(nearVector: { 2040 vector: [0.123, 0.984] 2041 }) { intField } } }` 2042 2043 expectedParams := dto.GetParams{ 2044 ClassName: "SomeAction", 2045 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 2046 NearVector: &searchparams.NearVector{ 2047 Vector: []float32{0.123, 0.984}, 2048 }, 2049 } 2050 2051 resolver.On("GetClass", expectedParams). 2052 Return([]interface{}{}, nil).Once() 2053 2054 resolver.AssertResolve(t, query) 2055 }) 2056 2057 t.Run("for things with optional distance set", func(t *testing.T) { 2058 query := `{ Get { SomeThing(nearVector: { 2059 vector: [0.123, 0.984] 2060 distance: 0.4 2061 }) { intField } } }` 2062 2063 expectedParams := dto.GetParams{ 2064 ClassName: "SomeThing", 2065 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 2066 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 2067 NearVector: &searchparams.NearVector{ 2068 Vector: []float32{0.123, 0.984}, 2069 Distance: 0.4, 2070 WithDistance: true, 2071 }, 2072 } 2073 resolver.On("GetClass", expectedParams). 2074 Return([]interface{}{}, nil).Once() 2075 2076 resolver.AssertResolve(t, query) 2077 }) 2078 2079 t.Run("for things with optional certainty set", func(t *testing.T) { 2080 query := `{ Get { SomeThing(nearVector: { 2081 vector: [0.123, 0.984] 2082 certainty: 0.4 2083 }) { intField } } }` 2084 2085 expectedParams := dto.GetParams{ 2086 ClassName: "SomeThing", 2087 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 2088 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 2089 NearVector: &searchparams.NearVector{ 2090 Vector: []float32{0.123, 0.984}, 2091 Certainty: 0.4, 2092 }, 2093 } 2094 resolver.On("GetClass", expectedParams). 2095 Return([]interface{}{}, nil).Once() 2096 2097 resolver.AssertResolve(t, query) 2098 }) 2099 2100 t.Run("for things with optional certainty and limit set", func(t *testing.T) { 2101 query := `{ Get { SomeThing( 2102 limit: 4 2103 nearVector: { 2104 vector: [0.123, 0.984] 2105 certainty: 0.4 2106 }) { intField } } }` 2107 2108 expectedParams := dto.GetParams{ 2109 ClassName: "SomeThing", 2110 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 2111 Pagination: &filters.Pagination{Limit: 4}, 2112 NearVector: &searchparams.NearVector{ 2113 Vector: []float32{0.123, 0.984}, 2114 Certainty: 0.4, 2115 }, 2116 } 2117 resolver.On("GetClass", expectedParams). 2118 Return([]interface{}{}, nil).Once() 2119 2120 resolver.AssertResolve(t, query) 2121 }) 2122 2123 t.Run("for things with optional distance and negative limit set", func(t *testing.T) { 2124 query := `{ Get { SomeThing( 2125 limit: -1 2126 nearVector: { 2127 vector: [0.123, 0.984] 2128 distance: 0.4 2129 }) { intField } } }` 2130 2131 expectedParams := dto.GetParams{ 2132 ClassName: "SomeThing", 2133 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 2134 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 2135 NearVector: &searchparams.NearVector{ 2136 Vector: []float32{0.123, 0.984}, 2137 Distance: 0.4, 2138 WithDistance: true, 2139 }, 2140 } 2141 resolver.On("GetClass", expectedParams). 2142 Return([]interface{}{}, nil).Once() 2143 2144 resolver.AssertResolve(t, query) 2145 }) 2146 2147 t.Run("for things with optional certainty and negative limit set", func(t *testing.T) { 2148 query := `{ Get { SomeThing( 2149 limit: -1 2150 nearVector: { 2151 vector: [0.123, 0.984] 2152 certainty: 0.4 2153 }) { intField } } }` 2154 2155 expectedParams := dto.GetParams{ 2156 ClassName: "SomeThing", 2157 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 2158 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 2159 NearVector: &searchparams.NearVector{ 2160 Vector: []float32{0.123, 0.984}, 2161 Certainty: 0.4, 2162 }, 2163 } 2164 resolver.On("GetClass", expectedParams). 2165 Return([]interface{}{}, nil).Once() 2166 2167 resolver.AssertResolve(t, query) 2168 }) 2169 } 2170 2171 func TestSort(t *testing.T) { 2172 t.Parallel() 2173 2174 tests := []struct { 2175 name string 2176 resolver *mockResolver 2177 }{ 2178 { 2179 name: "with modules", 2180 resolver: newMockResolver(), 2181 }, 2182 { 2183 name: "with no modules", 2184 resolver: newMockResolverWithNoModules(), 2185 }, 2186 } 2187 for _, tt := range tests { 2188 t.Run(tt.name, func(t *testing.T) { 2189 t.Run("simple sort", func(t *testing.T) { 2190 query := `{ Get { SomeAction(sort:[{ 2191 path: ["path"] order: asc 2192 }]) { intField } } }` 2193 2194 expectedParams := dto.GetParams{ 2195 ClassName: "SomeAction", 2196 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 2197 Sort: []filters.Sort{{Path: []string{"path"}, Order: "asc"}}, 2198 } 2199 2200 tt.resolver.On("GetClass", expectedParams). 2201 Return([]interface{}{}, nil).Once() 2202 2203 tt.resolver.AssertResolve(t, query) 2204 }) 2205 2206 t.Run("simple sort with two paths", func(t *testing.T) { 2207 query := `{ Get { SomeAction(sort:[{ 2208 path: ["path1", "path2"] order: desc 2209 }]) { intField } } }` 2210 2211 expectedParams := dto.GetParams{ 2212 ClassName: "SomeAction", 2213 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 2214 Sort: []filters.Sort{{Path: []string{"path1", "path2"}, Order: "desc"}}, 2215 } 2216 2217 tt.resolver.On("GetClass", expectedParams). 2218 Return([]interface{}{}, nil).Once() 2219 2220 tt.resolver.AssertResolve(t, query) 2221 }) 2222 2223 t.Run("simple sort with two sort filters", func(t *testing.T) { 2224 query := `{ Get { SomeAction(sort:[{ 2225 path: ["first1", "first2", "first3", "first4"] order: asc 2226 } { 2227 path: ["second1"] order: desc 2228 }]) { intField } } }` 2229 2230 expectedParams := dto.GetParams{ 2231 ClassName: "SomeAction", 2232 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 2233 Sort: []filters.Sort{ 2234 {Path: []string{"first1", "first2", "first3", "first4"}, Order: "asc"}, 2235 {Path: []string{"second1"}, Order: "desc"}, 2236 }, 2237 } 2238 2239 tt.resolver.On("GetClass", expectedParams). 2240 Return([]interface{}{}, nil).Once() 2241 2242 tt.resolver.AssertResolve(t, query) 2243 }) 2244 }) 2245 } 2246 } 2247 2248 func TestGroupBy(t *testing.T) { 2249 t.Parallel() 2250 2251 tests := []struct { 2252 name string 2253 resolver *mockResolver 2254 }{ 2255 { 2256 name: "with modules", 2257 resolver: newMockResolver(), 2258 }, 2259 { 2260 name: "with no modules", 2261 resolver: newMockResolverWithNoModules(), 2262 }, 2263 } 2264 for _, tt := range tests { 2265 t.Run(tt.name, func(t *testing.T) { 2266 t.Run("simple groupBy", func(t *testing.T) { 2267 query := `{ Get { 2268 SomeAction( 2269 groupBy:{path: ["path"] groups: 2 objectsPerGroup:3} 2270 ) { 2271 _additional{group{count groupedBy {value path} maxDistance minDistance hits {_additional{distance}}} 2272 } 2273 } } }` 2274 2275 expectedParams := dto.GetParams{ 2276 ClassName: "SomeAction", 2277 GroupBy: &searchparams.GroupBy{Property: "path", Groups: 2, ObjectsPerGroup: 3}, 2278 AdditionalProperties: additional.Properties{Group: true}, 2279 } 2280 2281 tt.resolver.On("GetClass", expectedParams). 2282 Return([]interface{}{}, nil).Once() 2283 2284 tt.resolver.AssertResolve(t, query) 2285 }) 2286 }) 2287 } 2288 } 2289 2290 func ptFloat32(in float32) *float32 { 2291 return &in 2292 } 2293 2294 func timeMust(t strfmt.DateTime, err error) strfmt.DateTime { 2295 if err != nil { 2296 panic(err) 2297 } 2298 2299 return t 2300 }