github.com/weaviate/weaviate@v1.24.6/modules/text2vec-contextionary/module_graphql_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 modcontextionary 13 14 import ( 15 "testing" 16 17 "github.com/stretchr/testify/assert" 18 "github.com/stretchr/testify/require" 19 "github.com/tailor-inc/graphql/language/ast" 20 "github.com/weaviate/weaviate/entities/additional" 21 "github.com/weaviate/weaviate/entities/dto" 22 "github.com/weaviate/weaviate/entities/filters" 23 "github.com/weaviate/weaviate/entities/search" 24 "github.com/weaviate/weaviate/modules/text2vec-contextionary/additional/models" 25 helper "github.com/weaviate/weaviate/test/helper" 26 "github.com/weaviate/weaviate/usecases/traverser" 27 ) 28 29 type testCase struct { 30 name string 31 query string 32 expectedParamsToTraverser traverser.ExploreParams 33 resolverReturn []search.Result 34 expectedResults []result 35 } 36 37 type testCases []testCase 38 39 type result struct { 40 pathToField []string 41 expectedValue interface{} 42 } 43 44 func TestExtractAdditionalFields(t *testing.T) { 45 // We don't need to explicitly test every subselection as we did on 46 // phoneNumber as these fields have fixed keys. So we can simply check for 47 // the prop 48 49 type test struct { 50 name string 51 query string 52 expectedParams dto.GetParams 53 resolverReturn interface{} 54 expectedResult interface{} 55 } 56 57 tests := []test{ 58 { 59 name: "with _additional certainty", 60 query: "{ Get { SomeAction { _additional { certainty distance } } } }", 61 expectedParams: dto.GetParams{ 62 ClassName: "SomeAction", 63 AdditionalProperties: additional.Properties{ 64 Certainty: true, 65 Distance: true, 66 }, 67 }, 68 resolverReturn: []interface{}{ 69 map[string]interface{}{ 70 "_additional": map[string]interface{}{ 71 "certainty": 0.69, 72 "distance": helper.CertaintyToDist(t, 0.69), 73 }, 74 }, 75 }, 76 expectedResult: map[string]interface{}{ 77 "_additional": map[string]interface{}{ 78 "certainty": 0.69, 79 "distance": helper.CertaintyToDist(t, 0.69), 80 }, 81 }, 82 }, 83 { 84 name: "with _additional interpretation", 85 query: "{ Get { SomeAction { _additional { interpretation { source { concept weight occurrence } } } } } }", 86 expectedParams: dto.GetParams{ 87 ClassName: "SomeAction", 88 AdditionalProperties: additional.Properties{ 89 ModuleParams: map[string]interface{}{ 90 "interpretation": true, 91 }, 92 }, 93 }, 94 resolverReturn: []interface{}{ 95 map[string]interface{}{ 96 "_additional": map[string]interface{}{ 97 "interpretation": &models.Interpretation{ 98 Source: []*models.InterpretationSource{ 99 { 100 Concept: "foo", 101 Weight: 0.6, 102 Occurrence: 1200, 103 }, 104 { 105 Concept: "bar", 106 Weight: 0.9, 107 Occurrence: 800, 108 }, 109 }, 110 }, 111 }, 112 }, 113 }, 114 expectedResult: map[string]interface{}{ 115 "_additional": map[string]interface{}{ 116 "interpretation": map[string]interface{}{ 117 "source": []interface{}{ 118 map[string]interface{}{ 119 "concept": "foo", 120 "weight": 0.6, 121 "occurrence": 1200, 122 }, 123 map[string]interface{}{ 124 "concept": "bar", 125 "weight": 0.9, 126 "occurrence": 800, 127 }, 128 }, 129 }, 130 }, 131 }, 132 }, 133 { 134 name: "with _additional nearestNeighbors", 135 query: "{ Get { SomeAction { _additional { nearestNeighbors { neighbors { concept distance } } } } } }", 136 expectedParams: dto.GetParams{ 137 ClassName: "SomeAction", 138 AdditionalProperties: additional.Properties{ 139 ModuleParams: map[string]interface{}{ 140 "nearestNeighbors": true, 141 }, 142 }, 143 }, 144 resolverReturn: []interface{}{ 145 map[string]interface{}{ 146 "_additional": map[string]interface{}{ 147 "nearestNeighbors": &models.NearestNeighbors{ 148 Neighbors: []*models.NearestNeighbor{ 149 { 150 Concept: "foo", 151 Distance: 0.1, 152 }, 153 { 154 Concept: "bar", 155 Distance: 0.2, 156 }, 157 }, 158 }, 159 }, 160 }, 161 }, 162 expectedResult: map[string]interface{}{ 163 "_additional": map[string]interface{}{ 164 "nearestNeighbors": map[string]interface{}{ 165 "neighbors": []interface{}{ 166 map[string]interface{}{ 167 "concept": "foo", 168 "distance": float32(0.1), 169 }, 170 map[string]interface{}{ 171 "concept": "bar", 172 "distance": float32(0.2), 173 }, 174 }, 175 }, 176 }, 177 }, 178 }, 179 { 180 name: "with _additional featureProjection without any optional parameters", 181 query: "{ Get { SomeAction { _additional { featureProjection { vector } } } } }", 182 expectedParams: dto.GetParams{ 183 ClassName: "SomeAction", 184 AdditionalProperties: additional.Properties{ 185 ModuleParams: map[string]interface{}{ 186 "featureProjection": extractAdditionalParam("featureProjection", nil), 187 }, 188 }, 189 }, 190 resolverReturn: []interface{}{ 191 map[string]interface{}{ 192 "_additional": map[string]interface{}{ 193 "featureProjection": &models.FeatureProjection{ 194 Vector: []float32{0.0, 1.1, 2.2}, 195 }, 196 }, 197 }, 198 }, 199 expectedResult: map[string]interface{}{ 200 "_additional": map[string]interface{}{ 201 "featureProjection": map[string]interface{}{ 202 "vector": []interface{}{float32(0.0), float32(1.1), float32(2.2)}, 203 }, 204 }, 205 }, 206 }, 207 { 208 name: "with _additional featureProjection with optional parameters", 209 query: `{ Get { SomeAction { _additional { featureProjection(algorithm: "tsne", dimensions: 3, learningRate: 15, iterations: 100, perplexity: 10) { vector } } } } }`, 210 expectedParams: dto.GetParams{ 211 ClassName: "SomeAction", 212 AdditionalProperties: additional.Properties{ 213 ModuleParams: map[string]interface{}{ 214 "featureProjection": extractAdditionalParam("featureProjection", 215 []*ast.Argument{ 216 createArg("algorithm", "tsne"), 217 createArg("dimensions", "3"), 218 createArg("iterations", "100"), 219 createArg("learningRate", "15"), 220 createArg("perplexity", "10"), 221 }, 222 ), 223 }, 224 }, 225 }, 226 resolverReturn: []interface{}{ 227 map[string]interface{}{ 228 "_additional": map[string]interface{}{ 229 "featureProjection": &models.FeatureProjection{ 230 Vector: []float32{0.0, 1.1, 2.2}, 231 }, 232 }, 233 }, 234 }, 235 expectedResult: map[string]interface{}{ 236 "_additional": map[string]interface{}{ 237 "featureProjection": map[string]interface{}{ 238 "vector": []interface{}{float32(0.0), float32(1.1), float32(2.2)}, 239 }, 240 }, 241 }, 242 }, 243 { 244 name: "with _additional semanticPath set", 245 query: `{ Get { SomeAction { _additional { semanticPath { path { concept distanceToQuery distanceToResult distanceToPrevious distanceToNext } } } } } }`, 246 expectedParams: dto.GetParams{ 247 ClassName: "SomeAction", 248 AdditionalProperties: additional.Properties{ 249 ModuleParams: map[string]interface{}{ 250 "semanticPath": extractAdditionalParam("semanticPath", nil), 251 }, 252 }, 253 }, 254 resolverReturn: []interface{}{ 255 map[string]interface{}{ 256 "_additional": map[string]interface{}{ 257 "semanticPath": &models.SemanticPath{ 258 Path: []*models.SemanticPathElement{ 259 { 260 Concept: "foo", 261 DistanceToNext: ptFloat32(0.5), 262 DistanceToPrevious: nil, 263 DistanceToQuery: 0.1, 264 DistanceToResult: 0.1, 265 }, 266 { 267 Concept: "bar", 268 DistanceToPrevious: ptFloat32(0.5), 269 DistanceToNext: nil, 270 DistanceToQuery: 0.1, 271 DistanceToResult: 0.1, 272 }, 273 }, 274 }, 275 }, 276 }, 277 }, 278 expectedResult: map[string]interface{}{ 279 "_additional": map[string]interface{}{ 280 "semanticPath": map[string]interface{}{ 281 "path": []interface{}{ 282 map[string]interface{}{ 283 "concept": "foo", 284 "distanceToNext": float32(0.5), 285 "distanceToPrevious": nil, 286 "distanceToQuery": float32(0.1), 287 "distanceToResult": float32(0.1), 288 }, 289 map[string]interface{}{ 290 "concept": "bar", 291 "distanceToPrevious": float32(0.5), 292 "distanceToNext": nil, 293 "distanceToQuery": float32(0.1), 294 "distanceToResult": float32(0.1), 295 }, 296 }, 297 }, 298 }, 299 }, 300 }, 301 } 302 303 for _, test := range tests { 304 t.Run(test.name, func(t *testing.T) { 305 resolver := newMockResolver() 306 307 resolver.On("GetClass", test.expectedParams). 308 Return(test.resolverReturn, nil).Once() 309 result := resolver.AssertResolve(t, test.query) 310 assert.Equal(t, test.expectedResult, result.Get("Get", "SomeAction").Result.([]interface{})[0]) 311 }) 312 } 313 } 314 315 func TestNearTextRanker(t *testing.T) { 316 t.Parallel() 317 318 resolver := newMockResolver() 319 320 t.Run("for actions", func(t *testing.T) { 321 query := `{ Get { SomeAction(nearText: { 322 concepts: ["c1", "c2", "c3"], 323 moveTo: { 324 concepts:["positive"], 325 force: 0.5 326 }, 327 moveAwayFrom: { 328 concepts:["epic"], 329 force: 0.25 330 } 331 }) { intField } } }` 332 333 expectedParams := dto.GetParams{ 334 ClassName: "SomeAction", 335 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 336 ModuleParams: map[string]interface{}{ 337 "nearText": extractNearTextParam(map[string]interface{}{ 338 "concepts": []interface{}{"c1", "c2", "c3"}, 339 "moveTo": map[string]interface{}{ 340 "concepts": []interface{}{"positive"}, 341 "force": float64(0.5), 342 }, 343 "moveAwayFrom": map[string]interface{}{ 344 "concepts": []interface{}{"epic"}, 345 "force": float64(0.25), 346 }, 347 }), 348 }, 349 } 350 351 resolver.On("GetClass", expectedParams). 352 Return([]interface{}{}, nil).Once() 353 354 resolver.AssertResolve(t, query) 355 }) 356 357 t.Run("for a class that does not have a text2vec module", func(t *testing.T) { 358 query := `{ Get { CustomVectorClass(nearText: { 359 concepts: ["c1", "c2", "c3"], 360 moveTo: { 361 concepts:["positive"], 362 force: 0.5 363 }, 364 moveAwayFrom: { 365 concepts:["epic"], 366 force: 0.25 367 } 368 }) { intField } } }` 369 370 res := resolver.Resolve(query) 371 require.Len(t, res.Errors, 1) 372 assert.Contains(t, res.Errors[0].Message, "Unknown argument \"nearText\" on field \"CustomVectorClass\"") 373 }) 374 375 t.Run("for things with optional distance set", func(t *testing.T) { 376 query := `{ Get { SomeThing(nearText: { 377 concepts: ["c1", "c2", "c3"], 378 distance: 0.6, 379 moveTo: { 380 concepts:["positive"], 381 force: 0.5 382 }, 383 moveAwayFrom: { 384 concepts:["epic"], 385 force: 0.25 386 } 387 }) { intField } } }` 388 389 expectedParams := dto.GetParams{ 390 ClassName: "SomeThing", 391 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 392 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 393 ModuleParams: map[string]interface{}{ 394 "nearText": extractNearTextParam(map[string]interface{}{ 395 "concepts": []interface{}{"c1", "c2", "c3"}, 396 "distance": float64(0.6), 397 "moveTo": map[string]interface{}{ 398 "concepts": []interface{}{"positive"}, 399 "force": float64(0.5), 400 }, 401 "moveAwayFrom": map[string]interface{}{ 402 "concepts": []interface{}{"epic"}, 403 "force": float64(0.25), 404 }, 405 }), 406 }, 407 } 408 resolver.On("GetClass", expectedParams). 409 Return([]interface{}{}, nil).Once() 410 411 resolver.AssertResolve(t, query) 412 }) 413 414 t.Run("for things with optional certainty set", func(t *testing.T) { 415 query := `{ Get { SomeThing(nearText: { 416 concepts: ["c1", "c2", "c3"], 417 certainty: 0.4, 418 moveTo: { 419 concepts:["positive"], 420 force: 0.5 421 }, 422 moveAwayFrom: { 423 concepts:["epic"], 424 force: 0.25 425 } 426 }) { intField } } }` 427 428 expectedParams := dto.GetParams{ 429 ClassName: "SomeThing", 430 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 431 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 432 ModuleParams: map[string]interface{}{ 433 "nearText": extractNearTextParam(map[string]interface{}{ 434 "concepts": []interface{}{"c1", "c2", "c3"}, 435 "certainty": float64(0.4), 436 "moveTo": map[string]interface{}{ 437 "concepts": []interface{}{"positive"}, 438 "force": float64(0.5), 439 }, 440 "moveAwayFrom": map[string]interface{}{ 441 "concepts": []interface{}{"epic"}, 442 "force": float64(0.25), 443 }, 444 }), 445 }, 446 } 447 resolver.On("GetClass", expectedParams). 448 Return([]interface{}{}, nil).Once() 449 450 resolver.AssertResolve(t, query) 451 }) 452 453 t.Run("for things with optional distance and objects set", func(t *testing.T) { 454 query := `{ Get { SomeThing(nearText: { 455 concepts: ["c1", "c2", "c3"], 456 distance: 0.4, 457 moveTo: { 458 concepts:["positive"], 459 force: 0.5 460 objects: [ 461 { id: "moveTo-uuid1" } 462 { beacon: "weaviate://localhost/moveTo-uuid3" } 463 ] 464 }, 465 moveAwayFrom: { 466 concepts:["epic"], 467 force: 0.25 468 objects: [ 469 { id: "moveAway-uuid1" } 470 { beacon: "weaviate://localhost/moveAway-uuid2" } 471 { beacon: "weaviate://localhost/moveAway-uuid3" } 472 ] 473 } 474 }) { intField } } }` 475 476 expectedParams := dto.GetParams{ 477 ClassName: "SomeThing", 478 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 479 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 480 ModuleParams: map[string]interface{}{ 481 "nearText": extractNearTextParam(map[string]interface{}{ 482 "concepts": []interface{}{"c1", "c2", "c3"}, 483 "distance": float64(0.4), 484 "moveTo": map[string]interface{}{ 485 "concepts": []interface{}{"positive"}, 486 "force": float64(0.5), 487 "objects": []interface{}{ 488 map[string]interface{}{ 489 "id": "moveTo-uuid1", 490 }, 491 map[string]interface{}{ 492 "beacon": "weaviate://localhost/moveTo-uuid3", 493 }, 494 }, 495 }, 496 "moveAwayFrom": map[string]interface{}{ 497 "concepts": []interface{}{"epic"}, 498 "force": float64(0.25), 499 "objects": []interface{}{ 500 map[string]interface{}{ 501 "id": "moveAway-uuid1", 502 }, 503 map[string]interface{}{ 504 "beacon": "weaviate://localhost/moveAway-uuid2", 505 }, 506 map[string]interface{}{ 507 "beacon": "weaviate://localhost/moveAway-uuid3", 508 }, 509 }, 510 }, 511 }), 512 }, 513 } 514 resolver.On("GetClass", expectedParams). 515 Return([]interface{}{}, nil).Once() 516 517 resolver.AssertResolve(t, query) 518 }) 519 520 t.Run("for things with optional certainty and objects set", func(t *testing.T) { 521 query := `{ Get { SomeThing(nearText: { 522 concepts: ["c1", "c2", "c3"], 523 certainty: 0.4, 524 moveTo: { 525 concepts:["positive"], 526 force: 0.5 527 objects: [ 528 { id: "moveTo-uuid1" } 529 { beacon: "weaviate://localhost/moveTo-uuid3" } 530 ] 531 }, 532 moveAwayFrom: { 533 concepts:["epic"], 534 force: 0.25 535 objects: [ 536 { id: "moveAway-uuid1" } 537 { beacon: "weaviate://localhost/moveAway-uuid2" } 538 { beacon: "weaviate://localhost/moveAway-uuid3" } 539 ] 540 } 541 }) { intField } } }` 542 543 expectedParams := dto.GetParams{ 544 ClassName: "SomeThing", 545 Properties: []search.SelectProperty{{Name: "intField", IsPrimitive: true}}, 546 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 547 ModuleParams: map[string]interface{}{ 548 "nearText": extractNearTextParam(map[string]interface{}{ 549 "concepts": []interface{}{"c1", "c2", "c3"}, 550 "certainty": float64(0.4), 551 "moveTo": map[string]interface{}{ 552 "concepts": []interface{}{"positive"}, 553 "force": float64(0.5), 554 "objects": []interface{}{ 555 map[string]interface{}{ 556 "id": "moveTo-uuid1", 557 }, 558 map[string]interface{}{ 559 "beacon": "weaviate://localhost/moveTo-uuid3", 560 }, 561 }, 562 }, 563 "moveAwayFrom": map[string]interface{}{ 564 "concepts": []interface{}{"epic"}, 565 "force": float64(0.25), 566 "objects": []interface{}{ 567 map[string]interface{}{ 568 "id": "moveAway-uuid1", 569 }, 570 map[string]interface{}{ 571 "beacon": "weaviate://localhost/moveAway-uuid2", 572 }, 573 map[string]interface{}{ 574 "beacon": "weaviate://localhost/moveAway-uuid3", 575 }, 576 }, 577 }, 578 }), 579 }, 580 } 581 resolver.On("GetClass", expectedParams). 582 Return([]interface{}{}, nil).Once() 583 584 resolver.AssertResolve(t, query) 585 }) 586 } 587 588 func Test_ResolveExplore(t *testing.T) { 589 t.Parallel() 590 591 testsNearText := testCases{ 592 testCase{ 593 name: "Resolve Explore with nearText", 594 query: ` 595 { 596 Explore(nearText: {concepts: ["car", "best brand"]}) { 597 beacon className certainty distance 598 } 599 }`, 600 expectedParamsToTraverser: traverser.ExploreParams{ 601 ModuleParams: map[string]interface{}{ 602 "nearText": extractNearTextParam(map[string]interface{}{ 603 "concepts": []interface{}{"car", "best brand"}, 604 }), 605 }, 606 WithCertaintyProp: true, 607 }, 608 resolverReturn: []search.Result{ 609 { 610 Beacon: "weaviate://localhost/some-uuid", 611 ClassName: "bestClass", 612 Certainty: 0.7, 613 Dist: helper.CertaintyToDist(t, 0.7), 614 }, 615 }, 616 expectedResults: []result{{ 617 pathToField: []string{"Explore"}, 618 expectedValue: []interface{}{ 619 map[string]interface{}{ 620 "beacon": "weaviate://localhost/some-uuid", 621 "className": "bestClass", 622 "certainty": float32(0.7), 623 "distance": helper.CertaintyToDist(t, 0.7), 624 }, 625 }, 626 }}, 627 }, 628 629 testCase{ 630 name: "with nearText with optional limit and distance set", 631 query: ` 632 { 633 Explore( 634 nearText: {concepts: ["car", "best brand"], distance: 0.6}, limit: 17 635 ){ 636 beacon className 637 } 638 }`, 639 expectedParamsToTraverser: traverser.ExploreParams{ 640 ModuleParams: map[string]interface{}{ 641 "nearText": extractNearTextParam(map[string]interface{}{ 642 "concepts": []interface{}{"car", "best brand"}, 643 "distance": float64(0.6), 644 }), 645 }, 646 Limit: 17, 647 }, 648 resolverReturn: []search.Result{ 649 { 650 Beacon: "weaviate://localhost/some-uuid", 651 ClassName: "bestClass", 652 Dist: 0.6, 653 }, 654 }, 655 expectedResults: []result{{ 656 pathToField: []string{"Explore"}, 657 expectedValue: []interface{}{ 658 map[string]interface{}{ 659 "beacon": "weaviate://localhost/some-uuid", 660 "className": "bestClass", 661 }, 662 }, 663 }}, 664 }, 665 666 testCase{ 667 name: "with nearText with optional limit and certainty set", 668 query: ` 669 { 670 Explore( 671 nearText: {concepts: ["car", "best brand"], certainty: 0.6}, limit: 17 672 ){ 673 beacon className 674 } 675 }`, 676 expectedParamsToTraverser: traverser.ExploreParams{ 677 ModuleParams: map[string]interface{}{ 678 "nearText": extractNearTextParam(map[string]interface{}{ 679 "concepts": []interface{}{"car", "best brand"}, 680 "certainty": float64(0.6), 681 }), 682 }, 683 Limit: 17, 684 }, 685 resolverReturn: []search.Result{ 686 { 687 Beacon: "weaviate://localhost/some-uuid", 688 ClassName: "bestClass", 689 }, 690 }, 691 expectedResults: []result{{ 692 pathToField: []string{"Explore"}, 693 expectedValue: []interface{}{ 694 map[string]interface{}{ 695 "beacon": "weaviate://localhost/some-uuid", 696 "className": "bestClass", 697 }, 698 }, 699 }}, 700 }, 701 702 testCase{ 703 name: "with moveTo set", 704 query: ` 705 { 706 Explore( 707 limit: 17 708 nearText: { 709 concepts: ["car", "best brand"] 710 moveTo: { 711 concepts: ["mercedes"] 712 force: 0.7 713 } 714 } 715 ) { 716 beacon className 717 } 718 }`, 719 expectedParamsToTraverser: traverser.ExploreParams{ 720 Limit: 17, 721 ModuleParams: map[string]interface{}{ 722 "nearText": extractNearTextParam(map[string]interface{}{ 723 "concepts": []interface{}{"car", "best brand"}, 724 "moveTo": map[string]interface{}{ 725 "concepts": []interface{}{"mercedes"}, 726 "force": float64(0.7), 727 }, 728 }), 729 }, 730 }, 731 resolverReturn: []search.Result{ 732 { 733 Beacon: "weaviate://localhost/some-uuid", 734 ClassName: "bestClass", 735 }, 736 }, 737 expectedResults: []result{{ 738 pathToField: []string{"Explore"}, 739 expectedValue: []interface{}{ 740 map[string]interface{}{ 741 "beacon": "weaviate://localhost/some-uuid", 742 "className": "bestClass", 743 }, 744 }, 745 }}, 746 }, 747 748 testCase{ 749 name: "with moveTo and moveAwayFrom set", 750 query: ` 751 { 752 Explore( 753 limit: 17 754 nearText: { 755 concepts: ["car", "best brand"] 756 moveTo: { 757 concepts: ["mercedes"] 758 force: 0.7 759 } 760 moveAwayFrom: { 761 concepts: ["van"] 762 force: 0.7 763 } 764 } 765 ) { 766 beacon className 767 } 768 }`, 769 expectedParamsToTraverser: traverser.ExploreParams{ 770 Limit: 17, 771 ModuleParams: map[string]interface{}{ 772 "nearText": extractNearTextParam(map[string]interface{}{ 773 "concepts": []interface{}{"car", "best brand"}, 774 "moveTo": map[string]interface{}{ 775 "concepts": []interface{}{"mercedes"}, 776 "force": float64(0.7), 777 }, 778 "moveAwayFrom": map[string]interface{}{ 779 "concepts": []interface{}{"van"}, 780 "force": float64(0.7), 781 }, 782 }), 783 }, 784 }, 785 resolverReturn: []search.Result{ 786 { 787 Beacon: "weaviate://localhost/some-uuid", 788 ClassName: "bestClass", 789 }, 790 }, 791 expectedResults: []result{{ 792 pathToField: []string{"Explore"}, 793 expectedValue: []interface{}{ 794 map[string]interface{}{ 795 "beacon": "weaviate://localhost/some-uuid", 796 "className": "bestClass", 797 }, 798 }, 799 }}, 800 }, 801 802 testCase{ 803 name: "with moveTo and objects set", 804 query: ` 805 { 806 Explore( 807 limit: 17 808 nearText: { 809 concepts: ["car", "best brand"] 810 moveTo: { 811 concepts: ["mercedes"] 812 force: 0.7 813 objects: [ 814 {id: "moveto-uuid"}, 815 {beacon: "weaviate://localhost/other-moveto-uuid"}, 816 ] 817 } 818 } 819 ) { 820 beacon className 821 } 822 }`, 823 expectedParamsToTraverser: traverser.ExploreParams{ 824 Limit: 17, 825 ModuleParams: map[string]interface{}{ 826 "nearText": extractNearTextParam(map[string]interface{}{ 827 "concepts": []interface{}{"car", "best brand"}, 828 "moveTo": map[string]interface{}{ 829 "concepts": []interface{}{"mercedes"}, 830 "force": float64(0.7), 831 "objects": []interface{}{ 832 map[string]interface{}{ 833 "id": "moveto-uuid", 834 }, 835 map[string]interface{}{ 836 "beacon": "weaviate://localhost/other-moveto-uuid", 837 }, 838 }, 839 }, 840 }), 841 }, 842 }, 843 resolverReturn: []search.Result{ 844 { 845 Beacon: "weaviate://localhost/some-uuid", 846 ClassName: "bestClass", 847 }, 848 }, 849 expectedResults: []result{{ 850 pathToField: []string{"Explore"}, 851 expectedValue: []interface{}{ 852 map[string]interface{}{ 853 "beacon": "weaviate://localhost/some-uuid", 854 "className": "bestClass", 855 }, 856 }, 857 }}, 858 }, 859 860 testCase{ 861 name: "with moveTo and moveAwayFrom and objects set", 862 query: ` 863 { 864 Explore( 865 limit: 17 866 nearText: { 867 concepts: ["car", "best brand"] 868 moveTo: { 869 concepts: ["mercedes"] 870 force: 0.7 871 objects: [ 872 {id: "moveto-uuid1"}, 873 {beacon: "weaviate://localhost/moveto-uuid2"}, 874 ] 875 } 876 moveAwayFrom: { 877 concepts: ["van"] 878 force: 0.7 879 objects: [ 880 {id: "moveAway-uuid1"}, 881 {beacon: "weaviate://localhost/moveAway-uuid2"}, 882 {id: "moveAway-uuid3"}, 883 {id: "moveAway-uuid4"}, 884 ] 885 } 886 } 887 ) { 888 beacon className 889 } 890 }`, 891 expectedParamsToTraverser: traverser.ExploreParams{ 892 Limit: 17, 893 ModuleParams: map[string]interface{}{ 894 "nearText": extractNearTextParam(map[string]interface{}{ 895 "concepts": []interface{}{"car", "best brand"}, 896 "moveTo": map[string]interface{}{ 897 "concepts": []interface{}{"mercedes"}, 898 "force": float64(0.7), 899 "objects": []interface{}{ 900 map[string]interface{}{ 901 "id": "moveto-uuid1", 902 }, 903 map[string]interface{}{ 904 "beacon": "weaviate://localhost/moveto-uuid2", 905 }, 906 }, 907 }, 908 "moveAwayFrom": map[string]interface{}{ 909 "concepts": []interface{}{"van"}, 910 "force": float64(0.7), 911 "objects": []interface{}{ 912 map[string]interface{}{ 913 "id": "moveAway-uuid1", 914 }, 915 map[string]interface{}{ 916 "beacon": "weaviate://localhost/moveAway-uuid2", 917 }, 918 map[string]interface{}{ 919 "id": "moveAway-uuid3", 920 }, 921 map[string]interface{}{ 922 "id": "moveAway-uuid4", 923 }, 924 }, 925 }, 926 }), 927 }, 928 }, 929 resolverReturn: []search.Result{ 930 { 931 Beacon: "weaviate://localhost/some-uuid", 932 ClassName: "bestClass", 933 }, 934 }, 935 expectedResults: []result{{ 936 pathToField: []string{"Explore"}, 937 expectedValue: []interface{}{ 938 map[string]interface{}{ 939 "beacon": "weaviate://localhost/some-uuid", 940 "className": "bestClass", 941 }, 942 }, 943 }}, 944 }, 945 } 946 947 testsNearText.AssertExtraction(t, newExploreMockResolver()) 948 } 949 950 func (tests testCases) AssertExtraction(t *testing.T, resolver *mockResolver) { 951 for _, testCase := range tests { 952 t.Run(testCase.name, func(t *testing.T) { 953 resolver.On("Explore", testCase.expectedParamsToTraverser). 954 Return(testCase.resolverReturn, nil).Once() 955 956 result := resolver.AssertResolve(t, testCase.query) 957 958 for _, expectedResult := range testCase.expectedResults { 959 value := result.Get(expectedResult.pathToField...).Result 960 961 assert.Equal(t, expectedResult.expectedValue, value) 962 } 963 }) 964 } 965 } 966 967 func ptFloat32(in float32) *float32 { 968 return &in 969 }