github.com/weaviate/weaviate@v1.24.6/adapters/repos/db/inverted/objects_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 inverted 13 14 import ( 15 "fmt" 16 "reflect" 17 "testing" 18 "time" 19 20 "github.com/go-openapi/strfmt" 21 "github.com/google/uuid" 22 "github.com/stretchr/testify/assert" 23 "github.com/stretchr/testify/require" 24 "github.com/weaviate/weaviate/adapters/repos/db/helpers" 25 "github.com/weaviate/weaviate/entities/models" 26 "github.com/weaviate/weaviate/entities/schema" 27 ) 28 29 func TestAnalyzeObject(t *testing.T) { 30 a := NewAnalyzer(nil) 31 32 t.Run("with multiple properties", func(t *testing.T) { 33 id1 := uuid.New() 34 id2 := uuid.New() 35 sch := map[string]interface{}{ 36 "description": "I am great!", 37 "email": "john@doe.com", 38 "about_me": "I like reading sci-fi books", 39 "profession": "Mechanical Engineer", 40 "id1": id1, // correctly parsed 41 "id2": id2.String(), // untyped 42 "idArray1": []uuid.UUID{id1}, // correctly parsed 43 "idArray2": []any{id2.String()}, // untyped 44 } 45 46 uuid := "2609f1bc-7693-48f3-b531-6ddc52cd2501" 47 props := []*models.Property{ 48 { 49 Name: "description", 50 DataType: schema.DataTypeText.PropString(), 51 Tokenization: models.PropertyTokenizationWord, 52 }, 53 { 54 Name: "email", 55 DataType: schema.DataTypeText.PropString(), 56 Tokenization: models.PropertyTokenizationWhitespace, 57 }, 58 { 59 Name: "about_me", 60 DataType: schema.DataTypeText.PropString(), 61 Tokenization: models.PropertyTokenizationLowercase, 62 }, 63 { 64 Name: "profession", 65 DataType: schema.DataTypeText.PropString(), 66 Tokenization: models.PropertyTokenizationField, 67 }, 68 { 69 Name: "id1", 70 DataType: []string{"uuid"}, 71 }, 72 { 73 Name: "id2", 74 DataType: []string{"uuid"}, 75 }, 76 { 77 Name: "idArray1", 78 DataType: []string{"uuid[]"}, 79 }, 80 { 81 Name: "idArray2", 82 DataType: []string{"uuid[]"}, 83 }, 84 } 85 res, err := a.Object(sch, props, strfmt.UUID(uuid)) 86 require.Nil(t, err) 87 88 expectedDescription := []Countable{ 89 { 90 Data: []byte("i"), 91 TermFrequency: float32(1), 92 }, 93 { 94 Data: []byte("am"), 95 TermFrequency: float32(1), 96 }, 97 { 98 Data: []byte("great"), 99 TermFrequency: float32(1), 100 }, 101 } 102 103 expectedEmail := []Countable{ 104 { 105 Data: []byte("john@doe.com"), 106 TermFrequency: float32(1), 107 }, 108 } 109 110 expectedAboutMe := []Countable{ 111 { 112 Data: []byte("i"), 113 TermFrequency: float32(1), 114 }, 115 { 116 Data: []byte("like"), 117 TermFrequency: float32(1), 118 }, 119 { 120 Data: []byte("reading"), 121 TermFrequency: float32(1), 122 }, 123 { 124 Data: []byte("sci-fi"), 125 TermFrequency: float32(1), 126 }, 127 { 128 Data: []byte("books"), 129 TermFrequency: float32(1), 130 }, 131 } 132 133 expectedProfession := []Countable{ 134 { 135 Data: []byte("Mechanical Engineer"), 136 TermFrequency: float32(1), 137 }, 138 } 139 140 expectedUUID := []Countable{ 141 { 142 Data: []byte(uuid), 143 TermFrequency: 0, 144 }, 145 } 146 147 expectedID1 := []Countable{ 148 { 149 Data: []byte(id1[:]), 150 TermFrequency: 0, 151 }, 152 } 153 154 expectedID2 := []Countable{ 155 { 156 Data: []byte(id2[:]), 157 TermFrequency: 0, 158 }, 159 } 160 161 expectedIDArray1 := []Countable{ 162 { 163 Data: []byte(id1[:]), 164 TermFrequency: 0, 165 }, 166 } 167 168 expectedIDArray2 := []Countable{ 169 { 170 Data: []byte(id2[:]), 171 TermFrequency: 0, 172 }, 173 } 174 175 require.Len(t, res, 9) 176 var actualDescription []Countable 177 var actualEmail []Countable 178 var actualAboutMe []Countable 179 var actualProfession []Countable 180 var actualUUID []Countable 181 var actualID1 []Countable 182 var actualID2 []Countable 183 var actualIDArray1 []Countable 184 var actualIDArray2 []Countable 185 186 for _, elem := range res { 187 if elem.Name == "email" { 188 actualEmail = elem.Items 189 } 190 191 if elem.Name == "description" { 192 actualDescription = elem.Items 193 } 194 195 if elem.Name == "about_me" { 196 actualAboutMe = elem.Items 197 } 198 199 if elem.Name == "profession" { 200 actualProfession = elem.Items 201 } 202 203 if elem.Name == "_id" { 204 actualUUID = elem.Items 205 } 206 207 if elem.Name == "id1" { 208 actualID1 = elem.Items 209 } 210 211 if elem.Name == "id2" { 212 actualID2 = elem.Items 213 } 214 215 if elem.Name == "idArray1" { 216 actualIDArray1 = elem.Items 217 } 218 219 if elem.Name == "idArray2" { 220 actualIDArray2 = elem.Items 221 } 222 } 223 224 assert.ElementsMatch(t, expectedEmail, actualEmail, res) 225 assert.ElementsMatch(t, expectedDescription, actualDescription, res) 226 assert.ElementsMatch(t, expectedAboutMe, actualAboutMe, res) 227 assert.ElementsMatch(t, expectedProfession, actualProfession, res) 228 assert.ElementsMatch(t, expectedUUID, actualUUID, res) 229 assert.ElementsMatch(t, expectedID1, actualID1, res) 230 assert.ElementsMatch(t, expectedID2, actualID2, res) 231 assert.ElementsMatch(t, expectedIDArray1, actualIDArray1, res) 232 assert.ElementsMatch(t, expectedIDArray2, actualIDArray2, res) 233 }) 234 235 t.Run("with array properties", func(t *testing.T) { 236 sch := map[string]interface{}{ 237 "descriptions": []interface{}{"I am great!", "I am also great!"}, 238 "emails": []interface{}{"john@doe.com", "john2@doe.com"}, 239 "about_me": []interface{}{"I like reading sci-fi books", "I like playing piano"}, 240 "professions": []interface{}{"Mechanical Engineer", "Marketing Analyst"}, 241 "integers": []interface{}{int64(1), int64(2), int64(3), int64(4)}, 242 "numbers": []interface{}{float64(1.1), float64(2.2), float64(3.0), float64(4)}, 243 } 244 245 uuid := "2609f1bc-7693-48f3-b531-6ddc52cd2501" 246 props := []*models.Property{ 247 { 248 Name: "descriptions", 249 DataType: schema.DataTypeTextArray.PropString(), 250 Tokenization: models.PropertyTokenizationWord, 251 }, 252 { 253 Name: "emails", 254 DataType: schema.DataTypeTextArray.PropString(), 255 Tokenization: models.PropertyTokenizationWhitespace, 256 }, 257 { 258 Name: "about_me", 259 DataType: schema.DataTypeTextArray.PropString(), 260 Tokenization: models.PropertyTokenizationLowercase, 261 }, 262 { 263 Name: "professions", 264 DataType: schema.DataTypeTextArray.PropString(), 265 Tokenization: models.PropertyTokenizationField, 266 }, 267 { 268 Name: "integers", 269 DataType: []string{"int[]"}, 270 }, 271 { 272 Name: "numbers", 273 DataType: []string{"number[]"}, 274 }, 275 } 276 res, err := a.Object(sch, props, strfmt.UUID(uuid)) 277 require.Nil(t, err) 278 279 expectedDescriptions := []Countable{ 280 { 281 Data: []byte("i"), 282 TermFrequency: float32(2), 283 }, 284 { 285 Data: []byte("am"), 286 TermFrequency: float32(2), 287 }, 288 { 289 Data: []byte("great"), 290 TermFrequency: float32(2), 291 }, 292 { 293 Data: []byte("also"), 294 TermFrequency: float32(1), 295 }, 296 } 297 298 expectedEmails := []Countable{ 299 { 300 Data: []byte("john@doe.com"), 301 TermFrequency: float32(1), 302 }, 303 { 304 Data: []byte("john2@doe.com"), 305 TermFrequency: float32(1), 306 }, 307 } 308 309 expectedAboutMe := []Countable{ 310 { 311 Data: []byte("i"), 312 TermFrequency: float32(2), 313 }, 314 { 315 Data: []byte("like"), 316 TermFrequency: float32(2), 317 }, 318 { 319 Data: []byte("reading"), 320 TermFrequency: float32(1), 321 }, 322 { 323 Data: []byte("sci-fi"), 324 TermFrequency: float32(1), 325 }, 326 { 327 Data: []byte("books"), 328 TermFrequency: float32(1), 329 }, 330 { 331 Data: []byte("playing"), 332 TermFrequency: float32(1), 333 }, 334 { 335 Data: []byte("piano"), 336 TermFrequency: float32(1), 337 }, 338 } 339 340 expectedProfessions := []Countable{ 341 { 342 Data: []byte("Mechanical Engineer"), 343 TermFrequency: float32(1), 344 }, 345 { 346 Data: []byte("Marketing Analyst"), 347 TermFrequency: float32(1), 348 }, 349 } 350 351 expectedIntegers := []Countable{ 352 { 353 Data: mustGetByteIntNumber(1), 354 }, 355 { 356 Data: mustGetByteIntNumber(2), 357 }, 358 { 359 Data: mustGetByteIntNumber(3), 360 }, 361 { 362 Data: mustGetByteIntNumber(4), 363 }, 364 } 365 366 expectedNumbers := []Countable{ 367 { 368 Data: mustGetByteFloatNumber(1.1), 369 }, 370 { 371 Data: mustGetByteFloatNumber(2.2), 372 }, 373 { 374 Data: mustGetByteFloatNumber(3.0), 375 }, 376 { 377 Data: mustGetByteFloatNumber(4), 378 }, 379 } 380 381 expectedUUID := []Countable{ 382 { 383 Data: []byte(uuid), 384 TermFrequency: 0, 385 }, 386 } 387 388 assert.Len(t, res, 7) 389 var actualDescriptions []Countable 390 var actualEmails []Countable 391 var actualAboutMe []Countable 392 var actualProfessions []Countable 393 var actualIntegers []Countable 394 var actualNumbers []Countable 395 var actualUUID []Countable 396 397 for _, elem := range res { 398 if elem.Name == "emails" { 399 actualEmails = elem.Items 400 } 401 402 if elem.Name == "descriptions" { 403 actualDescriptions = elem.Items 404 } 405 406 if elem.Name == "about_me" { 407 actualAboutMe = elem.Items 408 } 409 410 if elem.Name == "professions" { 411 actualProfessions = elem.Items 412 } 413 414 if elem.Name == "integers" { 415 actualIntegers = elem.Items 416 } 417 418 if elem.Name == "numbers" { 419 actualNumbers = elem.Items 420 } 421 422 if elem.Name == "_id" { 423 actualUUID = elem.Items 424 } 425 } 426 427 assert.ElementsMatch(t, expectedEmails, actualEmails, res) 428 assert.ElementsMatch(t, expectedDescriptions, actualDescriptions, res) 429 assert.ElementsMatch(t, expectedAboutMe, actualAboutMe, res) 430 assert.ElementsMatch(t, expectedProfessions, actualProfessions, res) 431 assert.ElementsMatch(t, expectedIntegers, actualIntegers, res) 432 assert.ElementsMatch(t, expectedNumbers, actualNumbers, res) 433 assert.ElementsMatch(t, expectedUUID, actualUUID, res) 434 }) 435 436 t.Run("with refProps", func(t *testing.T) { 437 t.Run("with a single ref set in the object schema", func(t *testing.T) { 438 beacon := strfmt.URI( 439 "weaviate://localhost/c563d7fa-4a36-4eff-9f39-af1e1db276c4") 440 schema := map[string]interface{}{ 441 "myRef": models.MultipleRef{ 442 &models.SingleRef{ 443 Beacon: beacon, 444 }, 445 }, 446 } 447 448 uuid := "2609f1bc-7693-48f3-b531-6ddc52cd2501" 449 props := []*models.Property{ 450 { 451 Name: "myRef", 452 DataType: []string{"RefClass"}, 453 }, 454 } 455 res, err := a.Object(schema, props, strfmt.UUID(uuid)) 456 require.Nil(t, err) 457 458 expectedRefCount := []Countable{ 459 {Data: []uint8{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}}, 460 } 461 462 expectedRef := []Countable{ 463 {Data: []byte(beacon)}, 464 } 465 466 expectedUUID := []Countable{ 467 { 468 Data: []byte(uuid), 469 TermFrequency: 0, 470 }, 471 } 472 473 require.Len(t, res, 3) 474 var actualRefCount []Countable 475 var actualUUID []Countable 476 var actualRef []Countable 477 478 for _, elem := range res { 479 switch elem.Name { 480 case helpers.MetaCountProp("myRef"): 481 actualRefCount = elem.Items 482 case "_id": 483 actualUUID = elem.Items 484 case "myRef": 485 actualRef = elem.Items 486 } 487 } 488 489 assert.ElementsMatch(t, expectedRefCount, actualRefCount, res) 490 assert.ElementsMatch(t, expectedUUID, actualUUID, res) 491 assert.ElementsMatch(t, expectedRef, actualRef, res) 492 }) 493 494 t.Run("with multiple refs set in the object schema", func(t *testing.T) { 495 beacon1 := strfmt.URI( 496 "weaviate://localhost/c563d7fa-4a36-4eff-9f39-af1e1db276c4") 497 beacon2 := strfmt.URI( 498 "weaviate://localhost/49fe5d33-0b52-4189-8e8d-4268427c4317") 499 500 schema := map[string]interface{}{ 501 "myRef": models.MultipleRef{ 502 {Beacon: beacon1}, 503 {Beacon: beacon2}, 504 }, 505 } 506 507 uuid := "2609f1bc-7693-48f3-b531-6ddc52cd2501" 508 props := []*models.Property{ 509 { 510 Name: "myRef", 511 DataType: []string{"RefClass"}, 512 }, 513 } 514 res, err := a.Object(schema, props, strfmt.UUID(uuid)) 515 require.Nil(t, err) 516 517 expectedRefCount := []Countable{ 518 {Data: []uint8{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}}, 519 } 520 521 expectedRef := []Countable{ 522 {Data: []byte(beacon1)}, 523 {Data: []byte(beacon2)}, 524 } 525 526 expectedUUID := []Countable{ 527 { 528 Data: []byte(uuid), 529 TermFrequency: 0, 530 }, 531 } 532 533 require.Len(t, res, 3) 534 var actualRefCount []Countable 535 var actualUUID []Countable 536 var actualRef []Countable 537 538 for _, elem := range res { 539 switch elem.Name { 540 case helpers.MetaCountProp("myRef"): 541 actualRefCount = elem.Items 542 case "_id": 543 actualUUID = elem.Items 544 case "myRef": 545 actualRef = elem.Items 546 } 547 } 548 549 assert.ElementsMatch(t, expectedRefCount, actualRefCount, res) 550 assert.ElementsMatch(t, expectedUUID, actualUUID, res) 551 assert.ElementsMatch(t, expectedRef, actualRef, res) 552 }) 553 554 t.Run("with the ref omitted in the object schema", func(t *testing.T) { 555 schema := map[string]interface{}{} 556 557 uuid := "2609f1bc-7693-48f3-b531-6ddc52cd2501" 558 props := []*models.Property{ 559 { 560 Name: "myRef", 561 DataType: []string{"RefClass"}, 562 }, 563 } 564 res, err := a.Object(schema, props, strfmt.UUID(uuid)) 565 require.Nil(t, err) 566 567 expectedRefCount := []Countable{ 568 {Data: []uint8{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, 569 } 570 571 expectedUUID := []Countable{ 572 { 573 Data: []byte(uuid), 574 TermFrequency: 0, 575 }, 576 } 577 578 require.Len(t, res, 2) 579 var actualRefCount []Countable 580 var actualUUID []Countable 581 582 for _, elem := range res { 583 if elem.Name == helpers.MetaCountProp("myRef") { 584 actualRefCount = elem.Items 585 } 586 if elem.Name == "_id" { 587 actualUUID = elem.Items 588 } 589 } 590 591 assert.ElementsMatch(t, expectedRefCount, actualRefCount, res) 592 assert.ElementsMatch(t, expectedUUID, actualUUID, res) 593 }) 594 595 // due to the fix introduced in https://github.com/weaviate/weaviate/pull/2320, 596 // MultipleRef's can appear as empty []interface{} when no actual refs are provided for 597 // an object's reference property. 598 // 599 // this test asserts that reference properties do not break when they are unmarshalled 600 // as empty interface{} slices. 601 t.Run("when rep prop is stored as empty interface{} slice", func(t *testing.T) { 602 uuid := "cf768bb0-03d8-4464-8f54-f787cf174c01" 603 name := "Transformers" 604 sch := map[string]interface{}{ 605 "name": name, 606 "reference": []interface{}{}, 607 } 608 609 props := []*models.Property{ 610 { 611 Name: "name", 612 DataType: schema.DataTypeText.PropString(), 613 Tokenization: models.PropertyTokenizationWhitespace, 614 }, 615 { 616 Name: "reference", 617 DataType: []string{"SomeClass"}, 618 }, 619 } 620 res, err := a.Object(sch, props, strfmt.UUID(uuid)) 621 require.Nil(t, err) 622 623 expectedUUID := []Countable{ 624 { 625 Data: []byte(uuid), 626 TermFrequency: 0, 627 }, 628 } 629 630 expectedName := []Countable{ 631 { 632 Data: []byte(name), 633 TermFrequency: 1, 634 }, 635 } 636 637 require.Len(t, res, 2) 638 var actualUUID []Countable 639 var actualName []Countable 640 641 for _, elem := range res { 642 switch elem.Name { 643 case "_id": 644 actualUUID = elem.Items 645 case "name": 646 actualName = elem.Items 647 } 648 } 649 650 assert.ElementsMatch(t, expectedUUID, actualUUID, res) 651 assert.ElementsMatch(t, expectedName, actualName, res) 652 }) 653 }) 654 655 t.Run("when objects are indexed by timestamps", func(t *testing.T) { 656 sch := map[string]interface{}{ 657 "description": "pretty ok if you ask me", 658 "_creationTimeUnix": 1650551406404, 659 "_lastUpdateTimeUnix": 1650551406404, 660 } 661 662 uuid := strfmt.UUID("2609f1bc-7693-48f3-b531-6ddc52cd2501") 663 props := []*models.Property{ 664 { 665 Name: "description", 666 DataType: schema.DataTypeText.PropString(), 667 Tokenization: models.PropertyTokenizationWord, 668 }, 669 } 670 671 res, err := a.Object(sch, props, uuid) 672 require.Nil(t, err) 673 require.Len(t, res, 4) 674 675 expected := []Property{ 676 { 677 Name: "description", 678 Items: []Countable{ 679 {Data: []byte("pretty"), TermFrequency: 1}, 680 {Data: []byte("ok"), TermFrequency: 1}, 681 {Data: []byte("if"), TermFrequency: 1}, 682 {Data: []byte("you"), TermFrequency: 1}, 683 {Data: []byte("ask"), TermFrequency: 1}, 684 {Data: []byte("me"), TermFrequency: 1}, 685 }, 686 HasFilterableIndex: true, 687 HasSearchableIndex: true, 688 }, 689 { 690 Name: "_id", 691 Items: []Countable{{Data: []byte("2609f1bc-7693-48f3-b531-6ddc52cd2501")}}, 692 HasFilterableIndex: true, 693 HasSearchableIndex: false, 694 }, 695 { 696 Name: "_creationTimeUnix", 697 Items: []Countable{{Data: []byte("1650551406404")}}, 698 HasFilterableIndex: true, 699 HasSearchableIndex: false, 700 }, 701 { 702 Name: "_lastUpdateTimeUnix", 703 Items: []Countable{{Data: []byte("1650551406404")}}, 704 HasFilterableIndex: true, 705 HasSearchableIndex: false, 706 }, 707 } 708 709 for i := range res { 710 assert.Equal(t, expected[i].Name, res[i].Name) 711 assert.Equal(t, expected[i].HasFilterableIndex, res[i].HasFilterableIndex) 712 assert.Equal(t, expected[i].HasSearchableIndex, res[i].HasSearchableIndex) 713 assert.ElementsMatch(t, expected[i].Items, res[i].Items) 714 } 715 }) 716 } 717 718 func TestConvertSliceToUntyped(t *testing.T) { 719 tests := []struct { 720 name string 721 input interface{} 722 expectedErr error 723 }{ 724 { 725 name: "interface{} slice", 726 input: []interface{}{map[string]interface{}{}}, 727 }, 728 { 729 name: "string slice", 730 input: []string{"some", "slice"}, 731 }, 732 { 733 name: "int slice", 734 input: []int{1, 2, 3, 4, 5}, 735 }, 736 { 737 name: "time slice", 738 input: []time.Time{time.Now(), time.Now(), time.Now()}, 739 }, 740 { 741 name: "bool slice", 742 input: []bool{false}, 743 }, 744 { 745 name: "float64 slice", 746 input: []float64{1.2, 53555, 4.123, 2, 7.8877887, 0.0001}, 747 }, 748 { 749 name: "empty slice", 750 input: []string{}, 751 }, 752 { 753 name: "unsupported uint8 slice", 754 input: []uint8{1, 2, 3, 4, 5}, 755 expectedErr: fmt.Errorf("unsupported type []uint8"), 756 }, 757 { 758 name: "unsupported struct{}", 759 input: struct{}{}, 760 expectedErr: fmt.Errorf("unsupported type struct {}"), 761 }, 762 } 763 764 for _, test := range tests { 765 t.Run(test.name, func(t *testing.T) { 766 output, err := typedSliceToUntyped(test.input) 767 if test.expectedErr != nil { 768 assert.EqualError(t, err, test.expectedErr.Error()) 769 } else { 770 require.Nil(t, err) 771 assert.Len(t, output, reflect.ValueOf(test.input).Len()) 772 assert.IsType(t, []interface{}{}, output) 773 } 774 }) 775 } 776 } 777 778 func TestIndexInverted(t *testing.T) { 779 vFalse := false 780 vTrue := true 781 782 t.Run("has filterable index", func(t *testing.T) { 783 type testCase struct { 784 name string 785 indexFilterable *bool 786 dataType schema.DataType 787 788 expextedFilterable bool 789 } 790 791 testCases := []testCase{ 792 { 793 name: "int, filterable null", 794 indexFilterable: nil, 795 dataType: schema.DataTypeInt, 796 797 expextedFilterable: true, 798 }, 799 { 800 name: "int, filterable false", 801 indexFilterable: &vFalse, 802 dataType: schema.DataTypeInt, 803 804 expextedFilterable: false, 805 }, 806 { 807 name: "int, filterable true", 808 indexFilterable: &vTrue, 809 dataType: schema.DataTypeInt, 810 811 expextedFilterable: true, 812 }, 813 { 814 name: "text, filterable null", 815 indexFilterable: nil, 816 dataType: schema.DataTypeText, 817 818 expextedFilterable: true, 819 }, 820 { 821 name: "text, filterable false", 822 indexFilterable: &vFalse, 823 dataType: schema.DataTypeText, 824 825 expextedFilterable: false, 826 }, 827 { 828 name: "text, filterable true", 829 indexFilterable: &vTrue, 830 dataType: schema.DataTypeText, 831 832 expextedFilterable: true, 833 }, 834 } 835 836 for _, tc := range testCases { 837 t.Run(tc.name, func(t *testing.T) { 838 hasFilterableIndex := HasFilterableIndex(&models.Property{ 839 Name: "prop", 840 DataType: tc.dataType.PropString(), 841 IndexFilterable: tc.indexFilterable, 842 }) 843 844 assert.Equal(t, tc.expextedFilterable, hasFilterableIndex) 845 }) 846 } 847 }) 848 849 t.Run("has searchable index", func(t *testing.T) { 850 type testCase struct { 851 name string 852 indexSearchable *bool 853 dataType schema.DataType 854 855 expextedSearchable bool 856 } 857 858 testCases := []testCase{ 859 { 860 name: "int, searchable null", 861 indexSearchable: nil, 862 dataType: schema.DataTypeInt, 863 864 expextedSearchable: false, 865 }, 866 { 867 name: "int, searchable false", 868 indexSearchable: &vFalse, 869 dataType: schema.DataTypeInt, 870 871 expextedSearchable: false, 872 }, 873 { 874 name: "int, searchable true", 875 indexSearchable: &vTrue, 876 dataType: schema.DataTypeInt, 877 878 expextedSearchable: false, 879 }, 880 { 881 name: "text, searchable null", 882 indexSearchable: nil, 883 dataType: schema.DataTypeText, 884 885 expextedSearchable: true, 886 }, 887 { 888 name: "text, searchable false", 889 indexSearchable: &vFalse, 890 dataType: schema.DataTypeText, 891 892 expextedSearchable: false, 893 }, 894 { 895 name: "text, searchable true", 896 indexSearchable: &vTrue, 897 dataType: schema.DataTypeText, 898 899 expextedSearchable: true, 900 }, 901 } 902 903 for _, tc := range testCases { 904 t.Run(tc.name, func(t *testing.T) { 905 hasSearchableIndex := HasSearchableIndex(&models.Property{ 906 Name: "prop", 907 DataType: tc.dataType.PropString(), 908 IndexSearchable: tc.indexSearchable, 909 }) 910 911 assert.Equal(t, tc.expextedSearchable, hasSearchableIndex) 912 }) 913 } 914 }) 915 } 916 917 func mustGetByteIntNumber(in int) []byte { 918 out, err := LexicographicallySortableInt64(int64(in)) 919 if err != nil { 920 panic(err) 921 } 922 return out 923 } 924 925 func mustGetByteFloatNumber(in float64) []byte { 926 out, err := LexicographicallySortableFloat64(in) 927 if err != nil { 928 panic(err) 929 } 930 return out 931 }