github.com/weaviate/weaviate@v1.24.6/adapters/repos/db/crud_integration_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 //go:build integrationTest 13 14 package db 15 16 import ( 17 "context" 18 "fmt" 19 "math/rand" 20 "testing" 21 "time" 22 23 "github.com/go-openapi/strfmt" 24 "github.com/google/uuid" 25 "github.com/sirupsen/logrus/hooks/test" 26 "github.com/stretchr/testify/assert" 27 "github.com/stretchr/testify/require" 28 "github.com/weaviate/weaviate/entities/additional" 29 "github.com/weaviate/weaviate/entities/dto" 30 "github.com/weaviate/weaviate/entities/filters" 31 "github.com/weaviate/weaviate/entities/models" 32 "github.com/weaviate/weaviate/entities/multi" 33 "github.com/weaviate/weaviate/entities/schema" 34 "github.com/weaviate/weaviate/entities/schema/crossref" 35 "github.com/weaviate/weaviate/entities/search" 36 "github.com/weaviate/weaviate/entities/searchparams" 37 enthnsw "github.com/weaviate/weaviate/entities/vectorindex/hnsw" 38 "github.com/weaviate/weaviate/usecases/objects" 39 "github.com/weaviate/weaviate/usecases/replica" 40 ) 41 42 func TestCRUD(t *testing.T) { 43 dirName := t.TempDir() 44 45 logger, _ := test.NewNullLogger() 46 thingclass := &models.Class{ 47 VectorIndexConfig: enthnsw.NewDefaultUserConfig(), 48 InvertedIndexConfig: invertedConfig(), 49 Class: "TheBestThingClass", 50 Properties: []*models.Property{ 51 { 52 Name: "stringProp", 53 DataType: schema.DataTypeText.PropString(), 54 Tokenization: models.PropertyTokenizationWhitespace, 55 }, 56 { 57 Name: "location", 58 DataType: []string{string(schema.DataTypeGeoCoordinates)}, 59 }, 60 { 61 Name: "phone", 62 DataType: []string{string(schema.DataTypePhoneNumber)}, 63 }, 64 }, 65 } 66 actionclass := &models.Class{ 67 Class: "TheBestActionClass", 68 VectorIndexConfig: enthnsw.NewDefaultUserConfig(), 69 InvertedIndexConfig: invertedConfig(), 70 Properties: []*models.Property{ 71 { 72 Name: "stringProp", 73 DataType: schema.DataTypeText.PropString(), 74 Tokenization: models.PropertyTokenizationWhitespace, 75 }, 76 { 77 Name: "refProp", 78 DataType: []string{"TheBestThingClass"}, 79 }, 80 { 81 Name: "phone", 82 DataType: []string{string(schema.DataTypePhoneNumber)}, 83 }, 84 }, 85 } 86 schemaGetter := &fakeSchemaGetter{ 87 schema: schema.Schema{Objects: &models.Schema{Classes: nil}}, 88 shardState: singleShardState(), 89 } 90 repo, err := New(logger, Config{ 91 MemtablesFlushDirtyAfter: 60, 92 RootPath: dirName, 93 QueryMaximumResults: 10, 94 MaxImportGoroutinesFactor: 1, 95 }, &fakeRemoteClient{}, &fakeNodeResolver{}, &fakeRemoteNodeClient{}, &fakeReplicationClient{}, nil) 96 require.Nil(t, err) 97 repo.SetSchemaGetter(schemaGetter) 98 require.Nil(t, repo.WaitForStartup(testCtx())) 99 defer repo.Shutdown(context.Background()) 100 migrator := NewMigrator(repo, logger) 101 102 t.Run("creating the thing class", func(t *testing.T) { 103 require.Nil(t, 104 migrator.AddClass(context.Background(), thingclass, schemaGetter.shardState)) 105 }) 106 107 t.Run("creating the action class", func(t *testing.T) { 108 require.Nil(t, 109 migrator.AddClass(context.Background(), actionclass, schemaGetter.shardState)) 110 }) 111 112 // update schema getter so it's in sync with class 113 schemaGetter.schema = schema.Schema{ 114 Objects: &models.Schema{ 115 Classes: []*models.Class{actionclass, thingclass}, 116 }, 117 } 118 119 thingID := strfmt.UUID("a0b55b05-bc5b-4cc9-b646-1452d1390a62") 120 121 t.Run("validating that the thing doesn't exist prior", func(t *testing.T) { 122 ok, err := repo.Exists(context.Background(), "TheBestThingClass", thingID, nil, "") 123 require.Nil(t, err) 124 assert.False(t, ok) 125 }) 126 127 t.Run("adding a thing", func(t *testing.T) { 128 thing := &models.Object{ 129 CreationTimeUnix: 1565612833955, 130 LastUpdateTimeUnix: 1000001, 131 ID: thingID, 132 Class: "TheBestThingClass", 133 Properties: map[string]interface{}{ 134 "stringProp": "some value", 135 "phone": &models.PhoneNumber{ 136 CountryCode: 49, 137 DefaultCountry: "DE", 138 Input: "0171 1234567", 139 Valid: true, 140 InternationalFormatted: "+49 171 1234567", 141 National: 1234567, 142 NationalFormatted: "0171 1234567", 143 }, 144 "location": &models.GeoCoordinates{ 145 Latitude: ptFloat32(1), 146 Longitude: ptFloat32(2), 147 }, 148 }, 149 Additional: models.AdditionalProperties{ 150 "interpretation": map[string]interface{}{ 151 "source": []interface{}{ 152 map[string]interface{}{ 153 "concept": "some", 154 "occurrence": float64(1), 155 "weight": float64(1), 156 }, 157 map[string]interface{}{ 158 "concept": "value", 159 "occurrence": float64(1), 160 "weight": float64(1), 161 }, 162 }, 163 }, 164 }, 165 } 166 vector := []float32{1, 3, 5, 0.4} 167 168 err := repo.PutObject(context.Background(), thing, vector, nil, nil) 169 170 assert.Nil(t, err) 171 }) 172 173 t.Run("validating that the thing exists now", func(t *testing.T) { 174 ok, err := repo.Exists(context.Background(), "TheBestThingClass", thingID, nil, "") 175 require.Nil(t, err) 176 assert.True(t, ok) 177 }) 178 179 t.Run("trying to add a thing to a non-existing class", func(t *testing.T) { 180 thing := &models.Object{ 181 CreationTimeUnix: 1565612833955, 182 LastUpdateTimeUnix: 1000001, 183 ID: thingID, 184 Class: "WrongClass", 185 Properties: map[string]interface{}{ 186 "stringProp": "some value", 187 }, 188 } 189 vector := []float32{1, 3, 5, 0.4} 190 191 err := repo.PutObject(context.Background(), thing, vector, nil, nil) 192 assert.Equal(t, 193 fmt.Errorf("import into non-existing index for WrongClass"), err) 194 }) 195 196 timeMust := func(t strfmt.DateTime, err error) strfmt.DateTime { 197 if err != nil { 198 panic(err) 199 } 200 201 return t 202 } 203 204 t.Run("updating the thing", func(t *testing.T) { 205 thing := &models.Object{ 206 CreationTimeUnix: 1565612833955, 207 LastUpdateTimeUnix: 10000020, 208 ID: thingID, 209 Class: "TheBestThingClass", 210 Properties: map[string]interface{}{ 211 "stringProp": "updated value", 212 "phone": &models.PhoneNumber{ 213 CountryCode: 49, 214 DefaultCountry: "DE", 215 Input: "0171 1234567", 216 Valid: true, 217 InternationalFormatted: "+49 171 1234567", 218 National: 1234567, 219 NationalFormatted: "0171 1234567", 220 }, 221 "location": &models.GeoCoordinates{ 222 Latitude: ptFloat32(1), 223 Longitude: ptFloat32(2), 224 }, 225 }, 226 } 227 vector := []float32{1, 3, 5, 0.4} 228 229 err := repo.PutObject(context.Background(), thing, vector, nil, nil) 230 assert.Nil(t, err) 231 }) 232 233 t.Run("validating the updates are reflected", func(t *testing.T) { 234 expected := &models.Object{ 235 CreationTimeUnix: 1565612833955, 236 LastUpdateTimeUnix: 10000020, 237 ID: thingID, 238 Class: "TheBestThingClass", 239 VectorWeights: map[string]string(nil), 240 Properties: map[string]interface{}{ 241 "stringProp": "updated value", 242 "phone": &models.PhoneNumber{ 243 CountryCode: 49, 244 DefaultCountry: "DE", 245 Input: "0171 1234567", 246 Valid: true, 247 InternationalFormatted: "+49 171 1234567", 248 National: 1234567, 249 NationalFormatted: "0171 1234567", 250 }, 251 "location": &models.GeoCoordinates{ 252 Latitude: ptFloat32(1), 253 Longitude: ptFloat32(2), 254 }, 255 }, 256 Additional: models.AdditionalProperties{}, 257 } 258 res, err := repo.ObjectByID(context.Background(), thingID, nil, additional.Properties{}, "") 259 require.Nil(t, err) 260 assert.Equal(t, expected, res.ObjectWithVector(false)) 261 262 res, err = repo.Object(context.Background(), expected.Class, thingID, nil, 263 additional.Properties{}, nil, "") 264 require.Nil(t, err) 265 assert.Equal(t, expected, res.ObjectWithVector(false)) 266 }) 267 268 t.Run("finding the updated object by querying for an updated value", 269 func(t *testing.T) { 270 // This is to verify the inverted index was updated correctly 271 res, err := repo.Search(context.Background(), dto.GetParams{ 272 ClassName: "TheBestThingClass", 273 Pagination: &filters.Pagination{Limit: 10}, 274 Filters: &filters.LocalFilter{ 275 Root: &filters.Clause{ 276 Operator: filters.OperatorEqual, 277 On: &filters.Path{ 278 Class: "TheBestThingClass", 279 Property: "stringProp", 280 }, 281 Value: &filters.Value{ 282 // we would not have found this object before using "updated", as 283 // this string was only introduced as part of the update 284 Value: "updated", 285 Type: schema.DataTypeText, 286 }, 287 }, 288 }, 289 }) 290 require.Nil(t, err) 291 require.Len(t, res, 1) 292 assert.Equal(t, thingID, res[0].ID) 293 }) 294 295 t.Run("NOT finding the previous version by querying for an outdated value", 296 func(t *testing.T) { 297 // This is to verify the inverted index was cleaned up correctly 298 res, err := repo.Search(context.Background(), dto.GetParams{ 299 ClassName: "TheBestThingClass", 300 Pagination: &filters.Pagination{Limit: 10}, 301 Filters: &filters.LocalFilter{ 302 Root: &filters.Clause{ 303 Operator: filters.OperatorEqual, 304 On: &filters.Path{ 305 Class: "TheBestThingClass", 306 Property: "stringProp", 307 }, 308 Value: &filters.Value{ 309 Value: "some", 310 Type: schema.DataTypeText, 311 }, 312 }, 313 }, 314 }) 315 require.Nil(t, err) 316 require.Len(t, res, 0) 317 }) 318 319 t.Run("still finding it for an unchanged term", 320 func(t *testing.T) { 321 // This is to verify that while we're adding new links and cleaning up 322 // old ones, we don't actually touch those that were present and still 323 // should be 324 res, err := repo.Search(context.Background(), dto.GetParams{ 325 ClassName: "TheBestThingClass", 326 Pagination: &filters.Pagination{Limit: 10}, 327 Filters: &filters.LocalFilter{ 328 Root: &filters.Clause{ 329 Operator: filters.OperatorEqual, 330 On: &filters.Path{ 331 Class: "TheBestThingClass", 332 Property: "stringProp", 333 }, 334 Value: &filters.Value{ 335 // we would not have found this object before using "updated", as 336 // this string was only introduced as part of the update 337 Value: "value", 338 Type: schema.DataTypeText, 339 }, 340 }, 341 }, 342 }) 343 require.Nil(t, err) 344 require.Len(t, res, 1) 345 assert.Equal(t, thingID, res[0].ID) 346 }) 347 348 t.Run("updating the thing back to its original value", func(t *testing.T) { 349 thing := &models.Object{ 350 CreationTimeUnix: 1565612833955, 351 LastUpdateTimeUnix: 1000001, 352 ID: thingID, 353 Class: "TheBestThingClass", 354 Properties: map[string]interface{}{ 355 "stringProp": "some value", 356 "phone": &models.PhoneNumber{ 357 CountryCode: 49, 358 DefaultCountry: "DE", 359 Input: "0171 1234567", 360 Valid: true, 361 InternationalFormatted: "+49 171 1234567", 362 National: 1234567, 363 NationalFormatted: "0171 1234567", 364 }, 365 "location": &models.GeoCoordinates{ 366 Latitude: ptFloat32(1), 367 Longitude: ptFloat32(2), 368 }, 369 }, 370 } 371 vector := []float32{1, 3, 5, 0.4} 372 373 err := repo.PutObject(context.Background(), thing, vector, nil, nil) 374 assert.Nil(t, err) 375 }) 376 377 actionID := strfmt.UUID("022ca5ba-7c0b-4a78-85bf-26346bbcfae7") 378 t.Run("adding an action", func(t *testing.T) { 379 action := &models.Object{ 380 CreationTimeUnix: 1000002, 381 LastUpdateTimeUnix: 1000003, 382 ID: actionID, 383 Class: "TheBestActionClass", 384 Properties: map[string]interface{}{ 385 "stringProp": "some act-citing value", 386 "refProp": models.MultipleRef{ 387 &models.SingleRef{ 388 Classification: &models.ReferenceMetaClassification{ 389 LosingDistance: ptFloat64(0.7), 390 MeanLosingDistance: ptFloat64(0.7), 391 ClosestLosingDistance: ptFloat64(0.65), 392 WinningDistance: 0.3, 393 MeanWinningDistance: 0.3, 394 ClosestWinningDistance: 0.25, 395 ClosestOverallDistance: 0.25, 396 OverallCount: 3, 397 WinningCount: 2, 398 LosingCount: 1, 399 }, 400 Beacon: strfmt.URI( 401 crossref.NewLocalhost("", thingID).String()), 402 }, 403 }, 404 }, 405 Additional: models.AdditionalProperties{ 406 "classification": &additional.Classification{ 407 ID: "foo", 408 Scope: []string{"scope1", "scope2"}, 409 ClassifiedFields: []string{"field1", "field2"}, 410 Completed: timeMust(strfmt.ParseDateTime("2006-01-02T15:04:05.000Z")), 411 }, 412 }, 413 } 414 vector := []float32{3, 1, 0.3, 12} 415 416 err := repo.PutObject(context.Background(), action, vector, nil, nil) 417 418 assert.Nil(t, err) 419 }) 420 421 t.Run("searching by vector", func(t *testing.T) { 422 // the search vector is designed to be very close to the action, but 423 // somewhat far from the thing. So it should match the action closer 424 searchVector := []float32{2.9, 1.1, 0.5, 8.01} 425 426 res, err := repo.CrossClassVectorSearch(context.Background(), searchVector, "", 0, 10, nil) 427 428 require.Nil(t, err) 429 require.Equal(t, true, len(res) >= 2) 430 assert.Equal(t, actionID, res[0].ID) 431 assert.Equal(t, "TheBestActionClass", res[0].ClassName) 432 assert.Equal(t, "TheBestActionClass", res[0].ClassName) 433 assert.Equal(t, int64(1000002), res[0].Created) 434 assert.Equal(t, int64(1000003), res[0].Updated) 435 assert.Equal(t, thingID, res[1].ID) 436 437 assert.Equal(t, "TheBestThingClass", res[1].ClassName) 438 assert.Equal(t, int64(1565612833955), res[1].Created) 439 assert.Equal(t, int64(1000001), res[1].Updated) 440 }) 441 442 t.Run("searching by vector for a single class", func(t *testing.T) { 443 // the search vector is designed to be very close to the action, but 444 // somewhat far from the thing. So it should match the action closer 445 searchVector := []float32{2.9, 1.1, 0.5, 8.01} 446 447 params := dto.GetParams{ 448 SearchVector: searchVector, 449 ClassName: "TheBestThingClass", 450 Pagination: &filters.Pagination{Limit: 10}, 451 Filters: nil, 452 } 453 res, err := repo.VectorSearch(context.Background(), params) 454 455 require.Nil(t, err) 456 require.Len(t, res, 1, "got exactly one result") 457 assert.Equal(t, thingID, res[0].ID, "extracted the ID") 458 assert.Equal(t, "TheBestThingClass", res[0].ClassName, "matches the class name") 459 schema := res[0].Schema.(map[string]interface{}) 460 assert.Equal(t, "some value", schema["stringProp"], "has correct string prop") 461 assert.Equal(t, &models.GeoCoordinates{ptFloat32(1), ptFloat32(2)}, schema["location"], "has correct geo prop") 462 assert.Equal(t, &models.PhoneNumber{ 463 CountryCode: 49, 464 DefaultCountry: "DE", 465 Input: "0171 1234567", 466 Valid: true, 467 InternationalFormatted: "+49 171 1234567", 468 National: 1234567, 469 NationalFormatted: "0171 1234567", 470 }, schema["phone"], "has correct phone prop") 471 assert.Equal(t, models.AdditionalProperties{}, res[0].AdditionalProperties, "no meta information should be included unless explicitly asked for") 472 assert.Equal(t, thingID, schema["id"], "has id in schema as uuid field") 473 }) 474 475 t.Run("searching by class type", func(t *testing.T) { 476 params := dto.GetParams{ 477 SearchVector: nil, 478 ClassName: "TheBestThingClass", 479 Pagination: &filters.Pagination{Limit: 10}, 480 Filters: nil, 481 } 482 res, err := repo.Search(context.Background(), params) 483 484 require.Nil(t, err) 485 require.Len(t, res, 1, "got exactly one result") 486 assert.Equal(t, thingID, res[0].ID, "extracted the ID") 487 assert.Equal(t, "TheBestThingClass", res[0].ClassName, "matches the class name") 488 schema := res[0].Schema.(map[string]interface{}) 489 assert.Equal(t, "some value", schema["stringProp"], "has correct string prop") 490 assert.Equal(t, &models.GeoCoordinates{ptFloat32(1), ptFloat32(2)}, schema["location"], "has correct geo prop") 491 assert.Equal(t, thingID, schema["id"], "has id in schema as uuid field") 492 }) 493 494 t.Run("adding a thing with interpretation additional property", func(t *testing.T) { 495 thing := &models.Object{ 496 CreationTimeUnix: 1565612833955, 497 LastUpdateTimeUnix: 1000001, 498 ID: thingID, 499 Class: "TheBestThingClass", 500 Properties: map[string]interface{}{ 501 "stringProp": "some value", 502 "phone": &models.PhoneNumber{ 503 CountryCode: 49, 504 DefaultCountry: "DE", 505 Input: "0171 1234567", 506 Valid: true, 507 InternationalFormatted: "+49 171 1234567", 508 National: 1234567, 509 NationalFormatted: "0171 1234567", 510 }, 511 "location": &models.GeoCoordinates{ 512 Latitude: ptFloat32(1), 513 Longitude: ptFloat32(2), 514 }, 515 }, 516 Additional: models.AdditionalProperties{ 517 "interpretation": map[string]interface{}{ 518 "source": []interface{}{ 519 map[string]interface{}{ 520 "concept": "some", 521 "occurrence": float64(1), 522 "weight": float64(1), 523 }, 524 map[string]interface{}{ 525 "concept": "value", 526 "occurrence": float64(1), 527 "weight": float64(1), 528 }, 529 }, 530 }, 531 }, 532 } 533 vector := []float32{1, 3, 5, 0.4} 534 535 err := repo.PutObject(context.Background(), thing, vector, nil, nil) 536 537 assert.Nil(t, err) 538 }) 539 540 t.Run("searching all things", func(t *testing.T) { 541 // as the test suits grow we might have to extend the limit 542 res, err := repo.ObjectSearch(context.Background(), 0, 100, nil, nil, additional.Properties{}, "") 543 require.Nil(t, err) 544 545 item, ok := findID(res, thingID) 546 require.Equal(t, true, ok, "results should contain our desired thing id") 547 548 assert.Equal(t, thingID, item.ID, "extracted the ID") 549 assert.Equal(t, "TheBestThingClass", item.ClassName, "matches the class name") 550 schema := item.Schema.(map[string]interface{}) 551 assert.Equal(t, "some value", schema["stringProp"], "has correct string prop") 552 assert.Equal(t, &models.GeoCoordinates{ptFloat32(1), ptFloat32(2)}, schema["location"], "has correct geo prop") 553 assert.Equal(t, thingID, schema["id"], "has id in schema as uuid field") 554 assert.Equal(t, models.AdditionalProperties{}, item.AdditionalProperties, "has no additional properties unless explicitly asked for") 555 }) 556 557 t.Run("searching all things with Vector additional props", func(t *testing.T) { 558 // as the test suits grow we might have to extend the limit 559 res, err := repo.ObjectSearch(context.Background(), 0, 100, nil, nil, additional.Properties{Vector: true}, "") 560 require.Nil(t, err) 561 562 item, ok := findID(res, thingID) 563 require.Equal(t, true, ok, "results should contain our desired thing id") 564 565 assert.Equal(t, thingID, item.ID, "extracted the ID") 566 assert.Equal(t, "TheBestThingClass", item.ClassName, "matches the class name") 567 schema := item.Schema.(map[string]interface{}) 568 assert.Equal(t, "some value", schema["stringProp"], "has correct string prop") 569 assert.Equal(t, &models.GeoCoordinates{ptFloat32(1), ptFloat32(2)}, schema["location"], "has correct geo prop") 570 assert.Equal(t, thingID, schema["id"], "has id in schema as uuid field") 571 assert.Equal(t, []float32{1, 3, 5, 0.4}, item.Vector, "has Vector property") 572 }) 573 574 t.Run("searching all things with Vector and Interpretation additional props", func(t *testing.T) { 575 // as the test suits grow we might have to extend the limit 576 params := additional.Properties{ 577 Vector: true, 578 ModuleParams: map[string]interface{}{ 579 "interpretation": true, 580 }, 581 } 582 res, err := repo.ObjectSearch(context.Background(), 0, 100, nil, nil, params, "") 583 require.Nil(t, err) 584 585 item, ok := findID(res, thingID) 586 require.Equal(t, true, ok, "results should contain our desired thing id") 587 588 assert.Equal(t, thingID, item.ID, "extracted the ID") 589 assert.Equal(t, "TheBestThingClass", item.ClassName, "matches the class name") 590 schema := item.Schema.(map[string]interface{}) 591 assert.Equal(t, "some value", schema["stringProp"], "has correct string prop") 592 assert.Equal(t, &models.GeoCoordinates{ptFloat32(1), ptFloat32(2)}, schema["location"], "has correct geo prop") 593 assert.Equal(t, thingID, schema["id"], "has id in schema as uuid field") 594 assert.Equal(t, []float32{1, 3, 5, 0.4}, item.Vector, "has Vector property") 595 assert.Equal(t, models.AdditionalProperties{ 596 "interpretation": map[string]interface{}{ 597 "source": []interface{}{ 598 map[string]interface{}{ 599 "concept": "some", 600 "occurrence": float64(1), 601 "weight": float64(1), 602 }, 603 map[string]interface{}{ 604 "concept": "value", 605 "occurrence": float64(1), 606 "weight": float64(1), 607 }, 608 }, 609 }, 610 }, item.AdditionalProperties, "has Vector and Interpretation additional property") 611 }) 612 613 t.Run("searching a thing by ID", func(t *testing.T) { 614 item, err := repo.ObjectByID(context.Background(), thingID, search.SelectProperties{}, additional.Properties{}, "") 615 require.Nil(t, err) 616 require.NotNil(t, item, "must have a result") 617 618 assert.Equal(t, thingID, item.ID, "extracted the ID") 619 assert.Equal(t, "TheBestThingClass", item.ClassName, "matches the class name") 620 schema := item.Schema.(map[string]interface{}) 621 assert.Equal(t, "some value", schema["stringProp"], "has correct string prop") 622 assert.Equal(t, &models.GeoCoordinates{ptFloat32(1), ptFloat32(2)}, schema["location"], "has correct geo prop") 623 assert.Equal(t, thingID, schema["id"], "has id in schema as uuid field") 624 }) 625 626 // Check the same, but with Object() 627 t.Run("searching a thing by ID", func(t *testing.T) { 628 item, err := repo.Object(context.Background(), "TheBestThingClass", 629 thingID, search.SelectProperties{}, additional.Properties{}, nil, "") 630 require.Nil(t, err) 631 require.NotNil(t, item, "must have a result") 632 633 assert.Equal(t, thingID, item.ID, "extracted the ID") 634 assert.Equal(t, "TheBestThingClass", item.ClassName, "matches the class name") 635 schema := item.Schema.(map[string]interface{}) 636 assert.Equal(t, "some value", schema["stringProp"], "has correct string prop") 637 assert.Equal(t, &models.GeoCoordinates{ptFloat32(1), ptFloat32(2)}, schema["location"], "has correct geo prop") 638 assert.Equal(t, thingID, schema["id"], "has id in schema as uuid field") 639 }) 640 641 t.Run("listing multiple things by IDs (MultiGet)", func(t *testing.T) { 642 query := []multi.Identifier{ 643 { 644 ID: "be685717-e61e-450d-8d5c-f44f32d0336c", // this id does not exist 645 ClassName: "TheBestThingClass", 646 }, 647 { 648 ID: thingID.String(), 649 ClassName: "TheBestThingClass", 650 }, 651 } 652 res, err := repo.MultiGet(context.Background(), query, additional.Properties{}, "") 653 require.Nil(t, err) 654 require.Len(t, res, 2, "length must match even with nil-items") 655 656 assert.Equal(t, strfmt.UUID(""), res[0].ID, "empty object for the not-found item") 657 658 item := res[1] 659 assert.Equal(t, thingID, item.ID, "extracted the ID") 660 assert.Equal(t, "TheBestThingClass", item.ClassName, "matches the class name") 661 schema := item.Schema.(map[string]interface{}) 662 assert.Equal(t, "some value", schema["stringProp"], "has correct string prop") 663 assert.Equal(t, &models.GeoCoordinates{ptFloat32(1), ptFloat32(2)}, schema["location"], "has correct geo prop") 664 assert.Equal(t, thingID, schema["id"], "has id in schema as uuid field") 665 }) 666 667 t.Run("searching an action by ID without meta", func(t *testing.T) { 668 item, err := repo.ObjectByID(context.Background(), actionID, search.SelectProperties{}, additional.Properties{}, "") 669 require.Nil(t, err) 670 require.NotNil(t, item, "must have a result") 671 672 assert.Equal(t, actionID, item.ID, "extracted the ID") 673 assert.Equal(t, "TheBestActionClass", item.ClassName, "matches the class name") 674 schema := item.Schema.(map[string]interface{}) 675 assert.Equal(t, "some act-citing value", schema["stringProp"], "has correct string prop") 676 assert.Equal(t, models.AdditionalProperties{}, item.AdditionalProperties, "not meta information should be included unless explicitly asked for") 677 expectedRefProp := models.MultipleRef{ 678 &models.SingleRef{ 679 Beacon: strfmt.URI( 680 crossref.NewLocalhost("", thingID).String()), 681 }, 682 } 683 assert.Equal(t, expectedRefProp, schema["refProp"]) 684 }) 685 686 t.Run("searching an action by ID with Classification and Vector additional properties", func(t *testing.T) { 687 item, err := repo.ObjectByID(context.Background(), actionID, search.SelectProperties{}, additional.Properties{Classification: true, Vector: true, RefMeta: true}, "") 688 require.Nil(t, err) 689 require.NotNil(t, item, "must have a result") 690 691 assert.Equal(t, actionID, item.ID, "extracted the ID") 692 assert.Equal(t, "TheBestActionClass", item.ClassName, "matches the class name") 693 schema := item.Schema.(map[string]interface{}) 694 assert.Equal(t, "some act-citing value", schema["stringProp"], "has correct string prop") 695 assert.Equal(t, models.AdditionalProperties{ 696 "classification": &additional.Classification{ 697 ID: "foo", 698 Scope: []string{"scope1", "scope2"}, 699 ClassifiedFields: []string{"field1", "field2"}, 700 Completed: timeMust(strfmt.ParseDateTime("2006-01-02T15:04:05.000Z")), 701 }, 702 }, item.AdditionalProperties, "it should include the object meta as it was explicitly specified") 703 assert.Equal(t, []float32{3, 1, 0.3, 12}, item.Vector, "has Vector property") 704 705 expectedRefProp := models.MultipleRef{ 706 &models.SingleRef{ 707 Classification: &models.ReferenceMetaClassification{ 708 LosingDistance: ptFloat64(0.7), 709 MeanLosingDistance: ptFloat64(0.7), 710 ClosestLosingDistance: ptFloat64(0.65), 711 WinningDistance: 0.3, 712 MeanWinningDistance: 0.3, 713 ClosestWinningDistance: 0.25, 714 ClosestOverallDistance: 0.25, 715 OverallCount: 3, 716 WinningCount: 2, 717 LosingCount: 1, 718 }, 719 Beacon: strfmt.URI( 720 crossref.NewLocalhost("", thingID).String()), 721 }, 722 } 723 assert.Equal(t, expectedRefProp, schema["refProp"]) 724 }) 725 726 t.Run("searching an action by ID with only Vector additional property", func(t *testing.T) { 727 item, err := repo.ObjectByID(context.Background(), actionID, search.SelectProperties{}, additional.Properties{Vector: true}, "") 728 require.Nil(t, err) 729 require.NotNil(t, item, "must have a result") 730 731 assert.Equal(t, actionID, item.ID, "extracted the ID") 732 assert.Equal(t, "TheBestActionClass", item.ClassName, "matches the class name") 733 schema := item.Schema.(map[string]interface{}) 734 assert.Equal(t, "some act-citing value", schema["stringProp"], "has correct string prop") 735 assert.Equal(t, []float32{3, 1, 0.3, 12}, item.Vector, "it should include the object meta as it was explicitly specified") 736 }) 737 738 t.Run("searching all actions", func(t *testing.T) { 739 res, err := repo.ObjectSearch(context.Background(), 0, 10, nil, nil, additional.Properties{}, "") 740 require.Nil(t, err) 741 742 item, ok := findID(res, actionID) 743 require.Equal(t, true, ok, "results should contain our desired action id") 744 745 assert.Equal(t, actionID, item.ID, "extracted the ID") 746 assert.Equal(t, "TheBestActionClass", item.ClassName, "matches the class name") 747 schema := item.Schema.(map[string]interface{}) 748 assert.Equal(t, "some act-citing value", schema["stringProp"], "has correct string prop") 749 }) 750 751 t.Run("sorting all objects", func(t *testing.T) { 752 // prepare 753 thingID1 := strfmt.UUID("7c8183ae-150d-433f-92b6-ed095b000001") 754 thingID2 := strfmt.UUID("7c8183ae-150d-433f-92b6-ed095b000002") 755 thingID3 := strfmt.UUID("7c8183ae-150d-433f-92b6-ed095b000003") 756 thingID4 := strfmt.UUID("7c8183ae-150d-433f-92b6-ed095b000004") 757 actionID1 := strfmt.UUID("7c8183ae-150d-433f-92b6-ed095b100001") 758 actionID2 := strfmt.UUID("7c8183ae-150d-433f-92b6-ed095b100002") 759 actionID3 := strfmt.UUID("7c8183ae-150d-433f-92b6-ed095b100003") 760 testData := []struct { 761 id strfmt.UUID 762 className string 763 stringProp string 764 phone uint64 765 longitude float32 766 }{ 767 { 768 id: thingID1, 769 className: "TheBestThingClass", 770 stringProp: "a very short text", 771 phone: 1234900, 772 longitude: 10, 773 }, 774 { 775 id: thingID2, 776 className: "TheBestThingClass", 777 stringProp: "zebra lives in Zoo", 778 phone: 1234800, 779 longitude: 111, 780 }, 781 { 782 id: thingID3, 783 className: "TheBestThingClass", 784 stringProp: "the best thing class", 785 phone: 1234910, 786 longitude: 2, 787 }, 788 { 789 id: thingID4, 790 className: "TheBestThingClass", 791 stringProp: "car", 792 phone: 1234901, 793 longitude: 11, 794 }, 795 { 796 id: actionID1, 797 className: "TheBestActionClass", 798 stringProp: "a very short text", 799 phone: 1234000, 800 longitude: 10, 801 }, 802 { 803 id: actionID2, 804 className: "TheBestActionClass", 805 stringProp: "zebra lives in Zoo", 806 phone: 1234002, 807 longitude: 5, 808 }, 809 { 810 id: actionID3, 811 className: "TheBestActionClass", 812 stringProp: "fossil fuels", 813 phone: 1234010, 814 longitude: 6, 815 }, 816 } 817 for _, td := range testData { 818 object := &models.Object{ 819 CreationTimeUnix: 1565612833990, 820 LastUpdateTimeUnix: 1000001, 821 ID: td.id, 822 Class: td.className, 823 Properties: map[string]interface{}{ 824 "stringProp": td.stringProp, 825 "phone": &models.PhoneNumber{ 826 CountryCode: 49, 827 DefaultCountry: "DE", 828 Input: fmt.Sprintf("0171 %d", td.phone), 829 Valid: true, 830 InternationalFormatted: fmt.Sprintf("+49 171 %d", td.phone), 831 National: td.phone, 832 NationalFormatted: fmt.Sprintf("0171 %d", td.phone), 833 }, 834 "location": &models.GeoCoordinates{ 835 Latitude: ptFloat32(1), 836 Longitude: ptFloat32(td.longitude), 837 }, 838 }, 839 } 840 vector := []float32{1.1, 1.3, 1.5, 1.4} 841 err := repo.PutObject(context.Background(), object, vector, nil, nil) 842 assert.Nil(t, err) 843 } 844 // run sorting tests 845 tests := []struct { 846 name string 847 sort []filters.Sort 848 expectedThingIDs []strfmt.UUID 849 expectedActionIDs []strfmt.UUID 850 constainsErrorMsgs []string 851 }{ 852 { 853 name: "by stringProp asc", 854 sort: []filters.Sort{{Path: []string{"stringProp"}, Order: "asc"}}, 855 expectedThingIDs: []strfmt.UUID{thingID1, thingID4, thingID, thingID3, thingID2}, 856 expectedActionIDs: []strfmt.UUID{actionID1, actionID3, actionID, actionID2}, 857 }, 858 { 859 name: "by stringProp desc", 860 sort: []filters.Sort{{Path: []string{"stringProp"}, Order: "desc"}}, 861 expectedThingIDs: []strfmt.UUID{thingID2, thingID3, thingID, thingID4, thingID1}, 862 expectedActionIDs: []strfmt.UUID{actionID2, actionID, actionID3, actionID1}, 863 }, 864 { 865 name: "by phone asc", 866 sort: []filters.Sort{{Path: []string{"phone"}, Order: "asc"}}, 867 expectedThingIDs: []strfmt.UUID{thingID, thingID2, thingID1, thingID4, thingID3}, 868 expectedActionIDs: []strfmt.UUID{actionID, actionID1, actionID2, actionID3}, 869 }, 870 { 871 name: "by phone desc", 872 sort: []filters.Sort{{Path: []string{"phone"}, Order: "desc"}}, 873 expectedThingIDs: []strfmt.UUID{thingID3, thingID4, thingID1, thingID2, thingID}, 874 expectedActionIDs: []strfmt.UUID{actionID3, actionID2, actionID1, actionID}, 875 }, 876 { 877 name: "by phone and stringProp asc", 878 sort: []filters.Sort{ 879 {Path: []string{"phone"}, Order: "asc"}, 880 {Path: []string{"stringProp"}, Order: "asc"}, 881 }, 882 expectedThingIDs: []strfmt.UUID{thingID, thingID2, thingID1, thingID4, thingID3}, 883 expectedActionIDs: []strfmt.UUID{actionID, actionID1, actionID2, actionID3}, 884 }, 885 { 886 name: "by location asc", 887 sort: []filters.Sort{{Path: []string{"location"}, Order: "asc"}}, 888 constainsErrorMsgs: []string{"search: search index thebestactionclass: sort parameter at position 0: " + 889 "no such prop with name 'location' found in class 'TheBestActionClass' in the schema. " + 890 "Check your schema files for which properties in this class are available"}, 891 }, 892 } 893 for _, tt := range tests { 894 t.Run(tt.name, func(t *testing.T) { 895 res, err := repo.ObjectSearch(context.Background(), 0, 100, nil, tt.sort, additional.Properties{Vector: true}, "") 896 if len(tt.constainsErrorMsgs) > 0 { 897 require.NotNil(t, err) 898 for _, errorMsg := range tt.constainsErrorMsgs { 899 assert.Contains(t, err.Error(), errorMsg) 900 } 901 } else { 902 require.Nil(t, err) 903 require.Len(t, res, 9) 904 905 var thingIds, actionIds []strfmt.UUID 906 for i := range res { 907 if res[i].ClassName == "TheBestThingClass" { 908 thingIds = append(thingIds, res[i].ID) 909 } else { 910 actionIds = append(actionIds, res[i].ID) 911 } 912 } 913 assert.EqualValues(t, thingIds, tt.expectedThingIDs, "thing ids don't match") 914 assert.EqualValues(t, actionIds, tt.expectedActionIDs, "action ids don't match") 915 } 916 }) 917 } 918 // clean up 919 for _, td := range testData { 920 err := repo.DeleteObject(context.Background(), td.className, td.id, nil, "") 921 assert.Nil(t, err) 922 } 923 }) 924 925 t.Run("verifying the thing is indexed in the inverted index", func(t *testing.T) { 926 // This is a control for the upcoming deletion, after the deletion it should not 927 // be indexed anymore. 928 res, err := repo.Search(context.Background(), dto.GetParams{ 929 ClassName: "TheBestThingClass", 930 Pagination: &filters.Pagination{Limit: 10}, 931 Filters: &filters.LocalFilter{ 932 Root: &filters.Clause{ 933 Operator: filters.OperatorEqual, 934 On: &filters.Path{ 935 Class: "TheBestThingClass", 936 Property: "stringProp", 937 }, 938 Value: &filters.Value{ 939 Value: "some", 940 Type: schema.DataTypeText, 941 }, 942 }, 943 }, 944 }) 945 require.Nil(t, err) 946 require.Len(t, res, 1) 947 }) 948 949 t.Run("verifying the action is indexed in the inverted index", func(t *testing.T) { 950 // This is a control for the upcoming deletion, after the deletion it should not 951 // be indexed anymore. 952 res, err := repo.Search(context.Background(), dto.GetParams{ 953 ClassName: "TheBestActionClass", 954 Pagination: &filters.Pagination{Limit: 10}, 955 Filters: &filters.LocalFilter{ 956 Root: &filters.Clause{ 957 Operator: filters.OperatorEqual, 958 On: &filters.Path{ 959 Class: "TheBestActionClass", 960 Property: "stringProp", 961 }, 962 Value: &filters.Value{ 963 Value: "some", 964 Type: schema.DataTypeText, 965 }, 966 }, 967 }, 968 }) 969 require.Nil(t, err) 970 require.Len(t, res, 1) 971 }) 972 973 t.Run("deleting a thing again", func(t *testing.T) { 974 err := repo.DeleteObject(context.Background(), "TheBestThingClass", thingID, nil, "") 975 976 assert.Nil(t, err) 977 }) 978 979 t.Run("deleting a action again", func(t *testing.T) { 980 err := repo.DeleteObject(context.Background(), "TheBestActionClass", actionID, nil, "") 981 982 assert.Nil(t, err) 983 }) 984 985 t.Run("trying to delete from a non-existing class", func(t *testing.T) { 986 err := repo.DeleteObject(context.Background(), "WrongClass", thingID, nil, "") 987 988 assert.Equal(t, fmt.Errorf( 989 "delete from non-existing index for WrongClass"), err) 990 }) 991 992 t.Run("verifying the thing is NOT indexed in the inverted index", 993 func(t *testing.T) { 994 res, err := repo.Search(context.Background(), dto.GetParams{ 995 ClassName: "TheBestThingClass", 996 Pagination: &filters.Pagination{Limit: 10}, 997 Filters: &filters.LocalFilter{ 998 Root: &filters.Clause{ 999 Operator: filters.OperatorEqual, 1000 On: &filters.Path{ 1001 Class: "TheBestThingClass", 1002 Property: "stringProp", 1003 }, 1004 Value: &filters.Value{ 1005 Value: "some", 1006 Type: schema.DataTypeText, 1007 }, 1008 }, 1009 }, 1010 }) 1011 require.Nil(t, err) 1012 require.Len(t, res, 0) 1013 }) 1014 1015 t.Run("verifying the action is NOT indexed in the inverted index", 1016 func(t *testing.T) { 1017 res, err := repo.Search(context.Background(), dto.GetParams{ 1018 ClassName: "TheBestActionClass", 1019 Pagination: &filters.Pagination{Limit: 10}, 1020 Filters: &filters.LocalFilter{ 1021 Root: &filters.Clause{ 1022 Operator: filters.OperatorEqual, 1023 On: &filters.Path{ 1024 Class: "TheBestActionClass", 1025 Property: "stringProp", 1026 }, 1027 Value: &filters.Value{ 1028 Value: "some", 1029 Type: schema.DataTypeText, 1030 }, 1031 }, 1032 }, 1033 }) 1034 require.Nil(t, err) 1035 require.Len(t, res, 0) 1036 }) 1037 1038 t.Run("trying to get the deleted thing by ID", func(t *testing.T) { 1039 item, err := repo.ObjectByID(context.Background(), thingID, search.SelectProperties{}, additional.Properties{}, "") 1040 require.Nil(t, err) 1041 require.Nil(t, item, "must not have a result") 1042 }) 1043 1044 t.Run("trying to get the deleted action by ID", func(t *testing.T) { 1045 item, err := repo.ObjectByID(context.Background(), actionID, search.SelectProperties{}, additional.Properties{}, "") 1046 require.Nil(t, err) 1047 require.Nil(t, item, "must not have a result") 1048 }) 1049 1050 t.Run("searching by vector for a single thing class again after deletion", 1051 func(t *testing.T) { 1052 searchVector := []float32{2.9, 1.1, 0.5, 8.01} 1053 params := dto.GetParams{ 1054 SearchVector: searchVector, 1055 ClassName: "TheBestThingClass", 1056 Pagination: &filters.Pagination{Limit: 10}, 1057 Filters: nil, 1058 } 1059 1060 res, err := repo.VectorSearch(context.Background(), params) 1061 1062 require.Nil(t, err) 1063 assert.Len(t, res, 0) 1064 }) 1065 1066 t.Run("searching by vector for a single action class again after deletion", func(t *testing.T) { 1067 searchVector := []float32{2.9, 1.1, 0.5, 8.01} 1068 params := dto.GetParams{ 1069 SearchVector: searchVector, 1070 ClassName: "TheBestActionClass", 1071 Pagination: &filters.Pagination{Limit: 10}, 1072 Filters: nil, 1073 } 1074 1075 res, err := repo.VectorSearch(context.Background(), params) 1076 1077 require.Nil(t, err) 1078 assert.Len(t, res, 0) 1079 }) 1080 1081 t.Run("ensure referenced class searches are not limited", func(t *testing.T) { 1082 numThings := int(repo.config.QueryMaximumResults * 10) 1083 createdActionIDs := make([]strfmt.UUID, numThings) 1084 createdThingIDs := make([]strfmt.UUID, numThings) 1085 1086 t.Run("add new action objects", func(t *testing.T) { 1087 actionBatch := make([]objects.BatchObject, numThings) 1088 for i := 0; i < len(createdActionIDs); i++ { 1089 newID := strfmt.UUID(uuid.NewString()) 1090 actionBatch[i] = objects.BatchObject{ 1091 UUID: newID, 1092 Object: &models.Object{ 1093 ID: newID, 1094 Class: "TheBestActionClass", 1095 Properties: map[string]interface{}{ 1096 "stringProp": fmt.Sprintf("action#%d", i), 1097 }, 1098 }, 1099 } 1100 createdActionIDs[i] = newID 1101 } 1102 batchObjResp, err := repo.BatchPutObjects(context.Background(), actionBatch, nil) 1103 require.Len(t, batchObjResp, numThings) 1104 require.Nil(t, err) 1105 for _, r := range batchObjResp { 1106 require.Nil(t, r.Err) 1107 } 1108 }) 1109 1110 t.Run("add more thing objects to reference", func(t *testing.T) { 1111 thingBatch := make([]objects.BatchObject, numThings) 1112 for i := 0; i < len(createdThingIDs); i++ { 1113 newID := strfmt.UUID(uuid.NewString()) 1114 thingBatch[i] = objects.BatchObject{ 1115 UUID: newID, 1116 Object: &models.Object{ 1117 ID: newID, 1118 Class: "TheBestThingClass", 1119 Properties: map[string]interface{}{ 1120 "stringProp": fmt.Sprintf("thing#%d", i), 1121 }, 1122 }, 1123 } 1124 createdThingIDs[i] = newID 1125 } 1126 batchObjResp, err := repo.BatchPutObjects(context.Background(), thingBatch, nil) 1127 require.Len(t, batchObjResp, numThings) 1128 require.Nil(t, err) 1129 for _, r := range batchObjResp { 1130 require.Nil(t, r.Err) 1131 } 1132 }) 1133 1134 t.Run("reference each thing from an action", func(t *testing.T) { 1135 refBatch := make([]objects.BatchReference, numThings) 1136 for i := range refBatch { 1137 ref := objects.BatchReference{ 1138 From: &crossref.RefSource{ 1139 Local: true, 1140 PeerName: "localhost", 1141 Class: "TheBestActionClass", 1142 Property: schema.PropertyName("refProp"), 1143 TargetID: createdActionIDs[i], 1144 }, 1145 To: &crossref.Ref{ 1146 Local: true, 1147 PeerName: "localhost", 1148 TargetID: createdThingIDs[i], 1149 }, 1150 } 1151 refBatch[i] = ref 1152 } 1153 batchRefResp, err := repo.AddBatchReferences(context.Background(), refBatch, nil) 1154 require.Nil(t, err) 1155 require.Len(t, batchRefResp, numThings) 1156 for _, r := range batchRefResp { 1157 require.Nil(t, r.Err) 1158 } 1159 }) 1160 1161 t.Run("query every action for its referenced thing", func(t *testing.T) { 1162 for i := range createdActionIDs { 1163 resp, err := repo.Search(context.Background(), dto.GetParams{ 1164 ClassName: "TheBestActionClass", 1165 Pagination: &filters.Pagination{Limit: 5}, 1166 AdditionalProperties: additional.Properties{ID: true}, 1167 Properties: search.SelectProperties{ 1168 { 1169 Name: "refProp", 1170 Refs: []search.SelectClass{ 1171 { 1172 ClassName: "TheBestThingClass", 1173 RefProperties: search.SelectProperties{ 1174 { 1175 Name: "stringProp", 1176 IsPrimitive: true, 1177 }, 1178 }, 1179 }, 1180 }, 1181 }, 1182 }, 1183 Filters: &filters.LocalFilter{ 1184 Root: &filters.Clause{ 1185 Operator: filters.OperatorAnd, 1186 Operands: []filters.Clause{ 1187 { 1188 Operator: filters.OperatorEqual, 1189 On: &filters.Path{ 1190 Class: "TheBestActionClass", 1191 Property: "stringProp", 1192 }, 1193 Value: &filters.Value{ 1194 Value: fmt.Sprintf("action#%d", i), 1195 Type: schema.DataTypeText, 1196 }, 1197 }, 1198 { 1199 Operator: filters.OperatorLike, 1200 On: &filters.Path{ 1201 Class: "TheBestActionClass", 1202 Property: "refProp", 1203 Child: &filters.Path{ 1204 Class: "TheBestThingClass", 1205 Property: "stringProp", 1206 }, 1207 }, 1208 Value: &filters.Value{ 1209 Value: "thing#*", 1210 Type: schema.DataTypeText, 1211 }, 1212 }, 1213 }, 1214 }, 1215 }, 1216 }) 1217 1218 require.Nil(t, err) 1219 require.Len(t, resp, 1) 1220 assert.Len(t, resp[0].Schema.(map[string]interface{})["refProp"], 1) 1221 } 1222 }) 1223 }) 1224 1225 t.Run("query obj by id which has no props", func(t *testing.T) { 1226 id := strfmt.UUID("2cd8a381-6568-4724-9d5c-1ef28d439e94") 1227 1228 t.Run("insert test obj", func(t *testing.T) { 1229 vec := []float32{0.1, 0.2, 0.3, 0.4} 1230 1231 obj := &models.Object{ 1232 ID: id, 1233 Class: "TheBestActionClass", 1234 Vector: vec, 1235 } 1236 require.Nil(t, repo.PutObject(context.Background(), obj, vec, nil, nil)) 1237 }) 1238 1239 t.Run("perform search with id filter", func(t *testing.T) { 1240 res, err := repo.Search(context.Background(), dto.GetParams{ 1241 Pagination: &filters.Pagination{Limit: 10}, 1242 ClassName: "TheBestActionClass", 1243 Filters: &filters.LocalFilter{ 1244 Root: &filters.Clause{ 1245 Operator: filters.OperatorEqual, 1246 On: &filters.Path{ 1247 Class: "TheBestActionClass", 1248 Property: filters.InternalPropID, 1249 }, 1250 Value: &filters.Value{ 1251 Value: id.String(), 1252 Type: schema.DataTypeText, 1253 }, 1254 }, 1255 }, 1256 }) 1257 1258 require.Nil(t, err) 1259 1260 expected := []search.Result{ 1261 { 1262 ID: id, 1263 ClassName: "TheBestActionClass", 1264 Schema: map[string]interface{}{ 1265 "id": id, 1266 }, 1267 Score: 0, 1268 AdditionalProperties: models.AdditionalProperties{}, 1269 Dims: 4, 1270 }, 1271 } 1272 1273 for i := range expected { 1274 expected[i].DocID = res[i].DocID 1275 } 1276 1277 assert.Equal(t, expected, res) 1278 }) 1279 }) 1280 } 1281 1282 func TestCRUD_Query(t *testing.T) { 1283 dirName := t.TempDir() 1284 1285 logger, _ := test.NewNullLogger() 1286 thingclass := &models.Class{ 1287 VectorIndexConfig: enthnsw.NewDefaultUserConfig(), 1288 InvertedIndexConfig: invertedConfig(), 1289 Class: "TheBestThingClass", 1290 Properties: []*models.Property{ 1291 { 1292 Name: "stringProp", 1293 DataType: schema.DataTypeText.PropString(), 1294 Tokenization: models.PropertyTokenizationWhitespace, 1295 }, 1296 }, 1297 } 1298 schemaGetter := &fakeSchemaGetter{ 1299 schema: schema.Schema{Objects: &models.Schema{Classes: nil}}, 1300 shardState: singleShardState(), 1301 } 1302 repo, err := New(logger, Config{ 1303 MemtablesFlushDirtyAfter: 60, 1304 RootPath: dirName, 1305 QueryMaximumResults: 10, 1306 MaxImportGoroutinesFactor: 1, 1307 }, &fakeRemoteClient{}, &fakeNodeResolver{}, &fakeRemoteNodeClient{}, &fakeReplicationClient{}, nil) 1308 require.Nil(t, err) 1309 repo.SetSchemaGetter(schemaGetter) 1310 require.Nil(t, repo.WaitForStartup(testCtx())) 1311 defer repo.Shutdown(context.Background()) 1312 migrator := NewMigrator(repo, logger) 1313 1314 t.Run("creating the thing class", func(t *testing.T) { 1315 require.Nil(t, 1316 migrator.AddClass(context.Background(), thingclass, schemaGetter.shardState)) 1317 }) 1318 1319 // update schema getter so it's in sync with class 1320 schemaGetter.schema = schema.Schema{ 1321 Objects: &models.Schema{ 1322 Classes: []*models.Class{thingclass}, 1323 }, 1324 } 1325 1326 t.Run("scroll through all objects", func(t *testing.T) { 1327 // prepare 1328 className := "TheBestThingClass" 1329 thingID1 := strfmt.UUID("7c8183ae-150d-433f-92b6-ed095b000001") 1330 thingID2 := strfmt.UUID("7c8183ae-150d-433f-92b6-ed095b000002") 1331 thingID3 := strfmt.UUID("7c8183ae-150d-433f-92b6-ed095b000003") 1332 thingID4 := strfmt.UUID("7c8183ae-150d-433f-92b6-ed095b000004") 1333 thingID5 := strfmt.UUID("7c8183ae-150d-433f-92b6-ed095b000005") 1334 thingID6 := strfmt.UUID("7c8183ae-150d-433f-92b6-ed095b000006") 1335 thingID7 := strfmt.UUID("7c8183ae-150d-433f-92b6-ed095b000007") 1336 testData := []struct { 1337 id strfmt.UUID 1338 className string 1339 stringProp string 1340 phone uint64 1341 longitude float32 1342 }{ 1343 { 1344 id: thingID1, 1345 className: className, 1346 stringProp: "a very short text", 1347 }, 1348 { 1349 id: thingID2, 1350 className: className, 1351 stringProp: "zebra lives in Zoo", 1352 }, 1353 { 1354 id: thingID3, 1355 className: className, 1356 stringProp: "the best thing class", 1357 }, 1358 { 1359 id: thingID4, 1360 className: className, 1361 stringProp: "car", 1362 }, 1363 { 1364 id: thingID5, 1365 className: className, 1366 stringProp: "a very short text", 1367 }, 1368 { 1369 id: thingID6, 1370 className: className, 1371 stringProp: "zebra lives in Zoo", 1372 }, 1373 { 1374 id: thingID7, 1375 className: className, 1376 stringProp: "fossil fuels", 1377 }, 1378 } 1379 for _, td := range testData { 1380 object := &models.Object{ 1381 CreationTimeUnix: 1565612833990, 1382 LastUpdateTimeUnix: 1000001, 1383 ID: td.id, 1384 Class: td.className, 1385 Properties: map[string]interface{}{ 1386 "stringProp": td.stringProp, 1387 }, 1388 } 1389 vector := []float32{1.1, 1.3, 1.5, 1.4} 1390 err := repo.PutObject(context.Background(), object, vector, nil, nil) 1391 assert.Nil(t, err) 1392 } 1393 // toParams helper method 1394 toParams := func(className string, offset, limit int, 1395 cursor *filters.Cursor, filters *filters.LocalFilter, sort []filters.Sort, 1396 ) *objects.QueryInput { 1397 return &objects.QueryInput{ 1398 Class: className, 1399 Offset: offset, 1400 Limit: limit, 1401 Cursor: cursor, 1402 Filters: filters, 1403 Sort: sort, 1404 Additional: additional.Properties{}, 1405 } 1406 } 1407 // run scrolling through all results 1408 tests := []struct { 1409 name string 1410 className string 1411 cursor *filters.Cursor 1412 query *objects.QueryInput 1413 expectedThingIDs []strfmt.UUID 1414 constainsErrorMsgs []string 1415 }{ 1416 { 1417 name: "all results with step limit: 100", 1418 query: toParams(className, 0, 100, &filters.Cursor{After: "", Limit: 100}, nil, nil), 1419 expectedThingIDs: []strfmt.UUID{thingID1, thingID2, thingID3, thingID4, thingID5, thingID6, thingID7}, 1420 }, 1421 { 1422 name: "all results with step limit: 1", 1423 query: toParams(className, 0, 1, &filters.Cursor{After: "", Limit: 1}, nil, nil), 1424 expectedThingIDs: []strfmt.UUID{thingID1, thingID2, thingID3, thingID4, thingID5, thingID6, thingID7}, 1425 }, 1426 { 1427 name: "all results with step limit: 1 after: thingID4", 1428 query: toParams(className, 0, 1, &filters.Cursor{After: thingID4.String(), Limit: 1}, nil, nil), 1429 expectedThingIDs: []strfmt.UUID{thingID5, thingID6, thingID7}, 1430 }, 1431 { 1432 name: "all results with step limit: 1 after: thingID7", 1433 query: toParams(className, 0, 1, &filters.Cursor{After: thingID7.String(), Limit: 1}, nil, nil), 1434 expectedThingIDs: []strfmt.UUID{}, 1435 }, 1436 { 1437 name: "all results with step limit: 3", 1438 query: toParams(className, 0, 3, &filters.Cursor{After: "", Limit: 3}, nil, nil), 1439 expectedThingIDs: []strfmt.UUID{thingID1, thingID2, thingID3, thingID4, thingID5, thingID6, thingID7}, 1440 }, 1441 { 1442 name: "all results with step limit: 7", 1443 query: toParams(className, 0, 7, &filters.Cursor{After: "", Limit: 7}, nil, nil), 1444 expectedThingIDs: []strfmt.UUID{thingID1, thingID2, thingID3, thingID4, thingID5, thingID6, thingID7}, 1445 }, 1446 { 1447 name: "error on empty class", 1448 query: toParams("", 0, 7, &filters.Cursor{After: "", Limit: 7}, nil, nil), 1449 constainsErrorMsgs: []string{"class not found"}, 1450 }, 1451 { 1452 name: "error on sort parameter", 1453 query: toParams(className, 0, 7, 1454 &filters.Cursor{After: "", Limit: 7}, nil, 1455 []filters.Sort{{Path: []string{"stringProp"}, Order: "asc"}}, 1456 ), 1457 cursor: &filters.Cursor{After: "", Limit: 7}, 1458 constainsErrorMsgs: []string{"sort cannot be set with after and limit parameters"}, 1459 }, 1460 { 1461 name: "error on offset parameter", 1462 query: toParams(className, 10, 7, 1463 &filters.Cursor{After: "", Limit: 7}, nil, 1464 nil, 1465 ), 1466 cursor: &filters.Cursor{After: "", Limit: 7}, 1467 constainsErrorMsgs: []string{"offset cannot be set with after and limit parameters"}, 1468 }, 1469 { 1470 name: "error on offset and sort parameter", 1471 query: toParams(className, 10, 7, 1472 &filters.Cursor{After: "", Limit: 7}, nil, 1473 []filters.Sort{{Path: []string{"stringProp"}, Order: "asc"}}, 1474 ), 1475 cursor: &filters.Cursor{After: "", Limit: 7}, 1476 constainsErrorMsgs: []string{"offset,sort cannot be set with after and limit parameters"}, 1477 }, 1478 } 1479 for _, tt := range tests { 1480 t.Run(tt.name, func(t *testing.T) { 1481 if len(tt.constainsErrorMsgs) > 0 { 1482 res, err := repo.Query(context.Background(), tt.query) 1483 require.NotNil(t, err) 1484 assert.Nil(t, res) 1485 for _, errorMsg := range tt.constainsErrorMsgs { 1486 assert.Contains(t, err.Error(), errorMsg) 1487 } 1488 } else { 1489 cursorSearch := func(t *testing.T, className string, cursor *filters.Cursor) []strfmt.UUID { 1490 res, err := repo.Query(context.Background(), toParams(className, 0, cursor.Limit, cursor, nil, nil)) 1491 require.Nil(t, err) 1492 var ids []strfmt.UUID 1493 for i := range res { 1494 ids = append(ids, res[i].ID) 1495 } 1496 return ids 1497 } 1498 1499 var thingIds []strfmt.UUID 1500 cursor := tt.query.Cursor 1501 for { 1502 result := cursorSearch(t, tt.query.Class, cursor) 1503 thingIds = append(thingIds, result...) 1504 if len(result) == 0 { 1505 break 1506 } 1507 after := result[len(result)-1] 1508 cursor = &filters.Cursor{After: after.String(), Limit: cursor.Limit} 1509 } 1510 1511 require.Equal(t, len(tt.expectedThingIDs), len(thingIds)) 1512 for i := range tt.expectedThingIDs { 1513 assert.Equal(t, tt.expectedThingIDs[i], thingIds[i]) 1514 } 1515 } 1516 }) 1517 } 1518 // clean up 1519 for _, td := range testData { 1520 err := repo.DeleteObject(context.Background(), td.className, td.id, nil, "") 1521 assert.Nil(t, err) 1522 } 1523 }) 1524 } 1525 1526 func Test_ImportWithoutVector_UpdateWithVectorLater(t *testing.T) { 1527 r := getRandomSeed() 1528 total := 100 1529 individual := total / 4 1530 className := "DeferredVector" 1531 var data []*models.Object 1532 var class *models.Class 1533 1534 dirName := t.TempDir() 1535 logger, _ := test.NewNullLogger() 1536 1537 schemaGetter := &fakeSchemaGetter{ 1538 schema: schema.Schema{Objects: &models.Schema{Classes: nil}}, 1539 shardState: singleShardState(), 1540 } 1541 repo, err := New(logger, Config{ 1542 MemtablesFlushDirtyAfter: 60, 1543 RootPath: dirName, 1544 QueryMaximumResults: 10000, 1545 MaxImportGoroutinesFactor: 1, 1546 }, &fakeRemoteClient{}, &fakeNodeResolver{}, &fakeRemoteNodeClient{}, &fakeReplicationClient{}, nil) 1547 require.Nil(t, err) 1548 repo.SetSchemaGetter(schemaGetter) 1549 require.Nil(t, repo.WaitForStartup(testCtx())) 1550 defer repo.Shutdown(context.Background()) 1551 migrator := NewMigrator(repo, logger) 1552 1553 t.Run("prepare data for test", func(t *testing.T) { 1554 data = make([]*models.Object, total) 1555 for i := range data { 1556 data[i] = &models.Object{ 1557 ID: strfmt.UUID(uuid.Must(uuid.NewRandom()).String()), 1558 Class: className, 1559 Properties: map[string]interface{}{ 1560 "int_prop": int64(i), 1561 }, 1562 Vector: nil, 1563 } 1564 } 1565 }) 1566 1567 t.Run("create required schema", func(t *testing.T) { 1568 class = &models.Class{ 1569 Class: className, 1570 Properties: []*models.Property{ 1571 { 1572 DataType: []string{string(schema.DataTypeInt)}, 1573 Name: "int_prop", 1574 }, 1575 }, 1576 VectorIndexConfig: enthnsw.NewDefaultUserConfig(), 1577 InvertedIndexConfig: invertedConfig(), 1578 } 1579 require.Nil(t, 1580 migrator.AddClass(context.Background(), class, schemaGetter.shardState)) 1581 }) 1582 1583 // update schema getter so it's in sync with class 1584 schemaGetter.schema = schema.Schema{ 1585 Objects: &models.Schema{ 1586 Classes: []*models.Class{class}, 1587 }, 1588 } 1589 1590 t.Run("import individual objects without vector", func(t *testing.T) { 1591 for i := 0; i < individual; i++ { 1592 err := repo.PutObject(context.Background(), data[i], nil, nil, nil) // nil vector ! 1593 require.Nil(t, err) 1594 } 1595 }) 1596 1597 t.Run("import batch objects without vector", func(t *testing.T) { 1598 batch := make(objects.BatchObjects, total-individual) 1599 1600 for i := range batch { 1601 batch[i] = objects.BatchObject{ 1602 OriginalIndex: i, 1603 Err: nil, 1604 Object: data[i+individual], 1605 UUID: data[i+individual].ID, 1606 } 1607 } 1608 1609 res, err := repo.BatchPutObjects(context.Background(), batch, nil) 1610 require.Nil(t, err) 1611 1612 for _, obj := range res { 1613 require.Nil(t, obj.Err) 1614 } 1615 }) 1616 1617 t.Run("verify inverted index works correctly", func(t *testing.T) { 1618 res, err := repo.Search(context.Background(), dto.GetParams{ 1619 Filters: buildFilter("int_prop", total+1, lte, dtInt), 1620 ClassName: className, 1621 Pagination: &filters.Pagination{ 1622 Offset: 0, 1623 Limit: total, 1624 }, 1625 }) 1626 require.Nil(t, err) 1627 assert.Len(t, res, total) 1628 }) 1629 1630 t.Run("perform unfiltered vector search and verify there are no matches", func(t *testing.T) { 1631 res, err := repo.VectorSearch(context.Background(), dto.GetParams{ 1632 Filters: nil, 1633 ClassName: className, 1634 Pagination: &filters.Pagination{ 1635 Offset: 0, 1636 Limit: total, 1637 }, 1638 SearchVector: randomVector(r, 7), 1639 }) 1640 require.Nil(t, err) 1641 assert.Len(t, res, 0) // we skipped the vector on half the elements, so we should now match half 1642 }) 1643 1644 t.Run("update some of the objects to add vectors", func(t *testing.T) { 1645 for i := range data { 1646 if i%2 == 1 { 1647 continue 1648 } 1649 1650 data[i].Vector = randomVector(r, 7) 1651 err := repo.PutObject(context.Background(), data[i], data[i].Vector, nil, nil) 1652 require.Nil(t, err) 1653 } 1654 }) 1655 1656 t.Run("perform unfiltered vector search and verify correct matches", func(t *testing.T) { 1657 res, err := repo.VectorSearch(context.Background(), dto.GetParams{ 1658 Filters: nil, 1659 ClassName: className, 1660 Pagination: &filters.Pagination{ 1661 Offset: 0, 1662 Limit: total, 1663 }, 1664 SearchVector: randomVector(r, 7), 1665 }) 1666 require.Nil(t, err) 1667 assert.Len(t, res, total/2) // we skipped the vector on half the elements, so we should now match half 1668 }) 1669 1670 t.Run("perform filtered vector search and verify correct matches", func(t *testing.T) { 1671 res, err := repo.VectorSearch(context.Background(), dto.GetParams{ 1672 Filters: buildFilter("int_prop", 50, lt, dtInt), 1673 ClassName: className, 1674 Pagination: &filters.Pagination{ 1675 Offset: 0, 1676 Limit: total, 1677 }, 1678 SearchVector: randomVector(r, 7), 1679 }) 1680 require.Nil(t, err) 1681 // we skipped the vector on half the elements, and cut the list in half with 1682 // the filter, so we're only expected a quarter of the total size now 1683 assert.Len(t, res, total/4) 1684 }) 1685 } 1686 1687 func TestVectorSearch_ByDistance(t *testing.T) { 1688 className := "SomeClass" 1689 var class *models.Class 1690 1691 dirName := t.TempDir() 1692 logger, _ := test.NewNullLogger() 1693 1694 schemaGetter := &fakeSchemaGetter{ 1695 schema: schema.Schema{Objects: &models.Schema{Classes: nil}}, 1696 shardState: singleShardState(), 1697 } 1698 repo, err := New(logger, Config{ 1699 MemtablesFlushDirtyAfter: 60, 1700 RootPath: dirName, 1701 // this is set really low to ensure that search 1702 // by distance is conducted, which executes 1703 // without regard to this value 1704 QueryMaximumResults: 1, 1705 MaxImportGoroutinesFactor: 1, 1706 }, &fakeRemoteClient{}, &fakeNodeResolver{}, &fakeRemoteNodeClient{}, &fakeReplicationClient{}, nil) 1707 require.Nil(t, err) 1708 repo.SetSchemaGetter(schemaGetter) 1709 require.Nil(t, repo.WaitForStartup(testCtx())) 1710 defer repo.Shutdown(context.Background()) 1711 migrator := NewMigrator(repo, logger) 1712 1713 t.Run("create required schema", func(t *testing.T) { 1714 class = &models.Class{ 1715 Class: className, 1716 Properties: []*models.Property{ 1717 { 1718 DataType: []string{string(schema.DataTypeInt)}, 1719 Name: "int_prop", 1720 }, 1721 }, 1722 VectorIndexConfig: enthnsw.NewDefaultUserConfig(), 1723 InvertedIndexConfig: invertedConfig(), 1724 } 1725 require.Nil(t, 1726 migrator.AddClass(context.Background(), class, schemaGetter.shardState)) 1727 }) 1728 1729 // update schema getter so it's in sync with class 1730 schemaGetter.schema = schema.Schema{ 1731 Objects: &models.Schema{ 1732 Classes: []*models.Class{class}, 1733 }, 1734 } 1735 1736 searchVector := []float32{-0.10190568, -0.06259751, 0.05616188, -0.19249836, 0.09714927, -0.1902525, -0.064424865, -0.0387358, 0.17581701, 0.4476738, 0.29261824, 0.12026761, -0.19975126, 0.023600178, 0.17348698, 0.12701738, -0.36018127, -0.12051587, -0.17620522, 0.060741074, -0.064512916, 0.18640806, -0.1529852, 0.08211839, -0.02558465, -0.11369845, 0.0924098, -0.10544433, -0.14728987, -0.041860342, -0.08533595, 0.25886244, 0.2963937, 0.26010615, 0.2111097, 0.029396622, 0.01429563, 0.06410264, -0.119665794, 0.33583277, -0.05802661, 0.023306102, 0.14435922, -0.003951336, -0.13870825, 0.07140894, 0.10469943, -0.059021875, -0.065911904, 0.024216041, -0.26282874, 0.04896568, -0.08291928, -0.12793182, -0.077824734, 0.08843151, 0.31247458, -0.066301286, 0.006904921, -0.08277095, 0.13936226, -0.64392364, -0.19566211, 0.047227614, 0.086121306, -0.20725192, -0.096485816, -0.16436341, -0.06559169, -0.019639932, -0.012729637, 0.08901619, 0.0015896161, -0.24789932, 0.35496348, -0.16272856, -0.01648429, 0.11247674, 0.08099968, 0.13339259, 0.055829972, -0.34662855, 0.068509, 0.13880715, 0.3201848, -0.055557363, 0.22142135, -0.12867308, 0.0037871755, 0.24888979, -0.007443307, 0.08906625, -0.02022331, 0.11510742, -0.2385861, 0.16177008, -0.16214795, -0.28715602, 0.016784908, 0.19386634, -0.07731616, -0.100485384, 0.4100712, 0.061834496, -0.2325293, -0.026056025, -0.11632323, -0.17040555, -0.081960455, -0.0061040106, -0.05949373, 0.044952348, -0.079565264, 0.024430245, -0.09375341, -0.30249637, 0.115205586, -0.13083287, -0.04264671, -0.089810364, 0.16227561, 0.07318055, -0.10496504, 0.00063501706, -0.04936106, -0.0022282854, 1.0893154, 0.1698662, -0.019563455, -0.011128426, 0.04477475, -0.15656771, -0.056911886, -0.5759019, -0.1881429, 0.17088258, 0.24124439, 0.111288875, -0.0015475494, -0.021278847, -0.08362156, 0.09997524, -0.094385885, -0.1674031, 0.061180864, 0.28517494, -0.016217072, 0.025866214, -0.22854298, -0.17924422, -0.037767246, 0.12252907, -0.31698978, -0.038031228, 0.055408552, 0.1743545, -0.040576655, 0.1293942, -0.56650764, -0.10306195, -0.19548112, -0.245544, -0.018241389, -0.039024632, -0.31659162, 0.1565075, 0.08412337, 0.13177724, -0.13766576, -0.15355161, -0.16960397, -0.012436442, 0.04828157, 0.12566057, -0.35308784, -0.37520224, -0.1265899, -0.13991497, 0.14402144, 0.117542416, -0.20750546, -0.5849919, -0.010469457, -0.19677396, 0.011365964, 0.00666846, -0.083470255, 0.24928358, 0.07026387, 0.19082819, 0.24557637, 0.014292963, 0.14846677, 0.031625308, -0.20398879, 0.19507346, -0.18119761, -0.045725327, -0.042455163, -0.099733196, -0.33636123, -0.28447086, 0.30274838, -0.01603988, -0.0529655, 0.15784146, 0.08746072, -0.1703993, 0.2414512, 0.060322937, -0.00812057, 0.031162385, -0.1764905, 0.22107981, -0.016657066, 0.31948856, 0.07282925, -0.036991462, 0.01266936, -0.009106514, -0.038732465, 0.20973183, 0.033236098, -0.10673938, -0.06880061, 0.115524575, -0.39688373, 0.08749971, -0.21816005, -0.22100002, -0.3716853, -0.14720486, 0.24316181, 0.29673144, 0.020808747, 0.07658521, 0.16310681, 0.38785335, 0.0992224, 0.14177811, 0.025954131, -0.08690783, 0.19653428, 0.09584941, 0.040072605, -0.00038361162, -0.094546966, 0.1910902, 0.13217318, 0.060072783, -0.0655816, 0.2777626, 0.1799169, 0.20187178, -0.0996889, -0.01932122, -0.13133621, 0.057482753, -0.36892185, -0.032093313, 0.14607865, 0.12033318, -0.041683596, -0.2048406, -0.041777443, -0.14975598, -0.2526341, 0.12659752, 0.010567178, -0.297333, -0.27522174, 0.06923473, 0.043150593, -0.017045585, -0.2400216, 0.11413547, -0.40081662, -0.0018820907, 0.13800722, 0.085972115, -0.01519989, -0.10491216, 0.09170084, 0.063085504, 0.046743374, -0.014466267, 0.09880224, 0.027706565, 0.09951337, 0.17317492, -0.025654864, 0.14658073, 0.042377427, -0.08402882, -0.12423425, 0.32714987, -0.1527207, 0.106094465, 0.017378228, -0.06302387} 1737 searchObject := strfmt.UUID("fe687bf4-f10f-4c23-948d-0746ea2927b3") 1738 1739 tests := map[strfmt.UUID]struct { 1740 inputVec []float32 1741 expected bool 1742 }{ 1743 strfmt.UUID("88460290-03b2-44a3-9adb-9fa3ae11d9e6"): { 1744 inputVec: []float32{-0.11015724, -0.05380307, 0.027512914, -0.16925375, 0.08306809, -0.19312492, -0.08910436, -0.011051652, 0.17981204, 0.40469593, 0.28226805, 0.09381516, -0.18380599, 0.03102771, 0.1645333, 0.1530153, -0.3187937, -0.10800173, -0.18466279, 0.0004336393, -0.0495677, 0.19905856, -0.11614494, 0.08834681, -0.011200292, -0.11969374, 0.12497086, -0.12427251, -0.13395442, -0.0060353535, -0.07504816, 0.23205791, 0.2982508, 0.2517544, 0.176147, -0.036871903, 0.017852835, 0.040007118, -0.118621, 0.3648693, -0.058933854, 0.04004229, 0.11871147, -0.019860389, -0.12701912, 0.106662825, 0.086498804, -0.04303973, -0.0742352, 0.018250324, -0.26544014, 0.029228423, -0.087171465, -0.1282789, -0.06403083, 0.09680911, 0.31433868, -0.081510685, -0.011283603, -0.041624587, 0.16530018, -0.6714878, -0.2436993, 0.03173918, 0.106117725, -0.20803581, -0.10429562, -0.16975354, -0.078582145, -0.0065962705, -0.06840946, 0.094937086, -0.020617036, -0.23795949, 0.34785536, -0.19834635, -0.015064479, 0.11930141, 0.090962164, 0.120560184, 0.054095767, -0.38602966, 0.057141174, 0.12039684, 0.32000408, -0.05146908, 0.20762976, -0.09342379, 0.037577383, 0.23894139, -0.0075003104, 0.104791366, -0.015841056, 0.102840215, -0.20813248, 0.1855997, -0.12594056, -0.27132365, -0.0055563124, 0.21954241, -0.10798524, -0.111896284, 0.44049335, 0.049884494, -0.22339955, -0.005374135, -0.120713554, -0.22275059, -0.09146004, 0.017188415, -0.106493734, 0.045247544, -0.07725446, 0.056848228, -0.10294392, -0.2896642, 0.112891, -0.13773362, -0.089911595, -0.13500965, 0.14051703, 0.040092673, -0.13896292, 0.04580957, -0.014300959, 0.03737215, 1.0661443, 0.19767477, -0.07703914, -0.012910904, -0.0037716173, -0.14437087, -0.06938004, -0.5348036, -0.16047458, 0.19416414, 0.21938956, 0.092242256, -0.012630808, -0.021863988, -0.051702406, 0.08780951, -0.0815602, -0.15332024, 0.077632725, 0.25709584, -0.025725808, 0.042116437, -0.22687604, -0.11791685, -0.028626656, 0.16734225, -0.3017483, -0.03236202, 0.02888077, 0.18193199, -0.009032297, 0.14454253, -0.511494, -0.12119192, -0.20757924, -0.2561716, -0.03904554, -0.07348411, -0.28547177, 0.15967208, 0.079396725, 0.14358875, -0.12829632, -0.18175666, -0.15540425, -0.020419862, 0.019070208, 0.12168836, -0.3428434, -0.357543, -0.11218741, -0.12834033, 0.13564876, 0.12768728, -0.1817461, -0.61235875, -0.029409664, -0.19765733, 0.03872163, 0.0074950717, -0.10025679, 0.2872255, 0.033995092, 0.12945211, 0.21831632, 0.04666009, 0.14233032, 0.016767867, -0.2039244, 0.2000191, -0.13099428, -0.020210614, -0.06286195, -0.0948797, -0.34830436, -0.21595824, 0.32722405, -0.024735296, -0.07859145, 0.16975155, 0.08186461, -0.19249061, 0.23405583, 0.04837592, 0.021467948, -0.022215014, -0.14892808, 0.23908867, -0.050126728, 0.2867907, 0.07740656, -0.01714987, -0.0046314416, -0.0048108613, -0.007407311, 0.1807499, 0.049772616, -0.14680666, -0.07335314, 0.09023705, -0.40600133, 0.05522128, -0.20085222, -0.20410904, -0.34319055, -0.10792269, 0.2297779, 0.30397663, 0.05230268, 0.06408224, 0.13797496, 0.3691112, 0.083033495, 0.13695791, -0.015612457, -0.06413475, 0.18117142, 0.12928344, 0.049171276, 0.016104931, -0.102417335, 0.19589683, 0.14380622, 0.0748065, -0.005402455, 0.27243868, 0.14925551, 0.19564849, -0.10738364, -0.054175537, -0.10068278, 0.06004795, -0.38755924, -0.01654251, 0.1394104, 0.0968949, 0.004271706, -0.17105898, -0.050423585, -0.15311627, -0.24458972, 0.12665795, -0.022814916, -0.23887472, -0.289588, 0.05521137, 0.041259795, -0.021133862, -0.23674431, 0.08424598, -0.37863016, 0.017239956, 0.13776784, 0.060790475, 0.057887543, -0.08784489, 0.08803934, 0.027996546, 0.085972995, -0.014455558, 0.11668073, 0.03812387, 0.088413864, 0.22228678, -0.015599858, 0.11000236, 0.035271563, -0.08088438, -0.13092226, 0.29378533, -0.12311522, 0.09377676, 0.02948718, -0.09136077}, 1745 expected: true, 1746 }, 1747 strfmt.UUID("c99bc97d-7035-4311-94f3-947dc6471f51"): { 1748 inputVec: []float32{-0.07545987, -0.046643265, 0.044445477, -0.18531442, 0.07922216, -0.19388637, -0.069393866, -0.036144026, 0.1713317, 0.41803706, 0.23576374, 0.073170714, -0.14085358, 0.012535708, 0.17439325, 0.10057567, -0.33506152, -0.06800867, -0.18882714, 0.002687021, -0.03276807, 0.17267752, -0.13951558, 0.071382746, 0.020254405, -0.10178502, 0.13977699, -0.107296936, -0.113307, -0.002506761, -0.092065684, 0.21008658, 0.31157792, 0.19640765, 0.15769793, -0.0050196033, 0.0022481605, 0.015436289, -0.11822955, 0.31494477, -0.07425527, 0.051401984, 0.11648046, -0.00016831602, -0.12758006, 0.06814693, 0.06108981, -0.025454175, -0.018695071, 0.041827776, -0.23480764, 0.06652185, -0.078328855, -0.121668324, -0.04341973, 0.114403985, 0.32614416, -0.07992741, -0.019665314, -0.017408244, 0.12615794, -0.6350545, -0.17056493, 0.07171332, 0.047071394, -0.18428493, -0.09011123, -0.15995751, -0.03345579, -0.014678, -0.038699757, 0.044125225, 0.0042562615, -0.29445595, 0.30460796, -0.13630153, 0.00014055961, 0.08996278, 0.08948901, 0.12164838, 0.079090506, -0.36153567, 0.02817218, 0.11914518, 0.29805067, -0.07431765, 0.16793592, -0.099549234, 0.045226075, 0.22235383, -0.045654725, 0.09233901, -0.004902314, 0.08621588, -0.19723448, 0.19557382, -0.13199815, -0.22924824, -0.015981175, 0.19762704, -0.08940076, -0.084909916, 0.43372774, 0.026998578, -0.20827708, 0.037450224, -0.078997016, -0.18065391, -0.071308024, 0.00870316, -0.114981964, 0.017085023, -0.07696264, 0.009330409, -0.097458, -0.25530958, 0.118254915, -0.12516825, -0.008301536, -0.20432962, 0.15235707, 0.012840041, -0.18034773, 0.039270073, -0.03131139, 0.013706253, 0.98688674, 0.18840753, -0.055119563, 0.00836046, 0.019445436, -0.10701598, -0.024610046, -0.50088006, -0.15488546, 0.14209819, 0.1798376, 0.08615982, -0.0119235935, -0.0060070297, -0.08406098, 0.10096481, -0.09077014, -0.15957798, 0.10556352, 0.2100476, -0.036947068, 0.05316554, -0.20480183, -0.14873864, -0.0069070593, 0.16027303, -0.288908, -0.04487129, 0.0705415, 0.11973847, -0.0017247469, 0.14092937, -0.5262047, -0.094283305, -0.19120996, -0.2816572, -0.010916339, -0.07984056, -0.28659204, 0.13706332, 0.07364347, 0.12300072, -0.17554194, -0.16378267, -0.15244205, 0.00075927645, 0.017289847, 0.12072629, -0.33452734, -0.33727616, -0.12780978, -0.09350711, 0.105674624, 0.10770573, -0.17278843, -0.5760599, -0.013741414, -0.15395893, 0.009837732, 0.015417911, -0.11384676, 0.24567491, 0.04905973, 0.10762609, 0.2131752, 0.019281652, 0.11665857, 0.022718405, -0.2234067, 0.23241606, -0.12194457, -0.049972955, -0.012225418, -0.14856412, -0.386102, -0.23018965, 0.28920102, -0.023396742, -0.114672944, 0.12130062, 0.05654803, -0.16194181, 0.24095012, 0.03644393, 0.028024165, -0.008832254, -0.16496961, 0.19496499, -0.035887964, 0.25981775, 0.0970074, 0.0013458093, -0.009548204, 0.040741496, -0.019192837, 0.20718361, -0.004034228, -0.1343262, -0.06990001, 0.09888768, -0.35942966, 0.043895893, -0.19182123, -0.17963983, -0.3222771, -0.10223457, 0.23866613, 0.25855777, 0.04051543, 0.08756274, 0.15683484, 0.37856522, 0.04853359, 0.10198129, -0.0061066896, -0.049892712, 0.17087941, 0.14563805, 0.06984385, 0.0071270005, -0.11838641, 0.18716812, 0.14013803, 0.05242403, 0.034357738, 0.3083466, 0.14742611, 0.17841975, -0.124118194, -0.014102871, -0.052544866, 0.037493005, -0.33485797, -0.013164912, 0.1066288, 0.11141791, -0.04029921, -0.16429856, -0.032241724, -0.15965424, -0.2430594, 0.13654563, 0.009401224, -0.2045843, -0.28467956, 0.07325551, 0.027996557, -0.033877768, -0.24350801, 0.08329816, -0.35555813, 0.006908567, 0.07227365, 0.03188268, 0.032559503, -0.09180395, 0.05601515, 0.0047281734, 0.06878795, -0.018943194, 0.08251342, 0.042039152, 0.12902294, 0.20526606, -0.014881293, 0.11723917, 0.0115632, -0.09016013, -0.12117223, 0.31020245, -0.111444525, 0.077845715, 0.00046715315, -0.104099475}, 1749 expected: true, 1750 }, 1751 strfmt.UUID("fe687bf4-f10f-4c23-948d-0746ea2927b3"): { 1752 inputVec: []float32{-0.20739016, -0.19551805, 0.06645163, 0.008650202, 0.03700748, -0.04132599, -0.029881354, 0.04684896, 0.096614264, 0.42888844, 0.10003969, 0.026234219, -0.051639702, -0.118660435, 0.14473079, 0.2911885, -0.1180539, -0.16804434, -0.48081538, 0.021702053, 0.12612472, 0.15442817, -0.05836532, 0.074295096, -0.28077397, -0.24297802, 0.047836643, -0.36753318, -0.30482984, 0.09265357, 0.25571078, 0.41130066, 0.46177864, 0.34033778, 0.20721313, -0.37726295, 0.07721501, 0.08009689, 0.00027321206, 0.5168123, -0.15305339, 0.0937765, 0.096195236, -0.21120761, 0.014014921, 0.3133104, 0.20773117, 0.08483507, -0.27784437, -0.17281856, -0.6050923, -0.22439326, -0.16914369, -0.3149047, -0.13828672, 0.16334395, -0.0018224253, -0.024342008, 0.3511251, 0.04979151, 0.34223744, -0.6965703, -0.36211932, -0.27092442, 0.34418032, -0.09667905, 0.13344757, -0.15622364, -0.24129291, 0.06958589, -0.2681816, -0.09497071, -0.08923615, -0.06642436, 0.48688608, -0.33535984, 0.014242731, 0.079838976, 0.32949054, 0.09051045, -0.2653392, -0.47393548, 0.07508276, 0.0062832804, 0.724184, -0.18929236, 0.11718613, 0.049603477, 0.08766128, 0.31040704, 0.04038693, -0.0017023507, -0.18986607, 0.056264438, -0.20978904, -0.107441366, -0.30505633, -0.45781082, -0.11571784, 0.32160303, -0.1347523, -0.08090298, 0.51651996, -0.023250414, -0.18725531, -0.14222279, 0.009277832, -0.49789724, -0.25156206, 0.0042495225, 0.0038805408, -0.031416763, 0.10277136, 0.14383446, -0.23241928, -0.42357358, 0.027033398, -0.2262604, -0.2685295, -0.14510548, 0.18256307, 0.063297585, 0.027636252, 0.081166506, 0.06726344, 0.1677495, 1.5217289, 0.33152232, -0.2209926, 0.051426213, 0.15640806, -0.30210486, -0.32857975, -0.4170022, -0.028293105, 0.28772062, 0.50510746, 0.09162247, -0.12383193, -0.25066972, -0.1441897, 0.107192926, -0.07404076, 0.0042472635, 0.11014519, 0.22332853, 0.09434378, -0.3278343, 0.041899726, 0.06838457, 0.10983681, 0.11864574, -0.25336757, -0.047530346, -0.027303243, 0.37403497, 0.13420461, 0.14946426, -0.41996637, -0.037703935, -0.47961184, -0.29839846, -0.103934005, -0.12058302, -0.12806267, 0.22814582, 0.3904893, -0.16044962, -0.17479864, -0.33139735, -0.29185295, 0.0653074, 0.042426735, 0.06092335, -0.18776153, -0.52555144, -0.15889317, -0.20644087, 0.2293067, 0.26668283, -0.15607063, -0.696593, -0.08224992, -0.4283747, 0.26883888, -0.031052848, -0.1311875, 0.26636878, 0.16457985, 0.15660451, 0.10629464, 0.17345549, 0.23963387, 0.22997221, -0.111713186, -0.08499592, -0.2274625, 0.19285984, -0.08285016, -0.02692149, -0.3426618, -0.13361897, 0.2870389, -0.12032792, -0.22944619, 0.25588584, 0.24607788, -0.2762531, 0.30983892, 0.011088746, -0.15739818, 0.053215, -0.21660997, 0.033805694, -0.17886437, 0.2979239, 0.2163545, -0.08381542, 0.19666128, -0.28977823, -0.20994817, -0.012160099, 0.057499636, -0.12549455, 0.19303595, -0.14420606, -0.51937664, 0.23400985, -0.27893808, -0.2660984, -0.27870297, -0.32149136, 0.19958079, 0.34468395, 0.18947665, -0.16529581, 0.101419374, 0.30195153, 0.09030288, 0.12496541, 0.02999903, -0.016697621, 0.15314853, 0.27848768, 0.24102053, 0.06933273, 0.08923653, 0.10477832, 0.4389032, 0.15679164, -0.11119637, 0.134823, 0.30230528, 0.20818473, -0.005579584, -0.3474488, -0.44394243, 0.22270252, -0.3668763, 0.07474772, 0.011691334, 0.088187896, 0.23832949, -0.07960201, 0.066471875, 0.034641538, -0.39984587, 0.0032980456, -0.28492525, -0.46358657, -0.2148288, -0.107226945, 0.02734428, -0.24686679, -0.123900555, 0.18174778, -0.31248868, 0.13808723, 0.31549984, 0.21521719, 0.13966985, -0.27272752, 0.12091104, 0.14257833, 0.23175247, 0.15639938, 0.40828535, 0.31916845, 0.023645567, 0.20658277, -0.20365283, 0.113746524, 0.13173752, -0.050343305, -0.31581175, 0.09704622, -0.014172505, 0.16924341, 0.30327854, -0.17770194}, 1753 expected: false, 1754 }, 1755 strfmt.UUID("e7bf6c45-de72-493a-b273-5ef198974d61"): { 1756 inputVec: []float32{0.089313604, -0.050221898, 0.18352903, 0.16257699, 0.14520381, 0.17993976, 0.14594483, 0.019256027, -0.15505213, 0.23606326, -0.14456263, 0.2679586, -0.112208664, 0.12997514, 0.0051072896, 0.28151348, -0.10495799, 0.026782967, -0.38603118, 0.16190273, -0.0428943, -0.16265322, -0.17910561, 0.0746288, -0.3117934, -0.15871756, -0.11377734, -0.06822346, -0.13829489, 0.13019162, 0.30741218, 0.16194165, 0.013218932, 0.054517113, 0.12490437, -0.07709048, 0.02556826, -0.21159878, -0.09082174, 0.24629511, 0.05013666, 0.25168124, -0.14423938, -0.0937688, -0.07811525, -0.049346007, 0.3592527, 0.30411252, -0.1168557, 0.18870471, 0.06614835, -0.20099068, -0.084436245, 0.073036775, -0.03448665, -0.11147946, -0.10862863, -0.012393957, 0.18990599, 0.060957544, 0.19518377, -0.027541652, -0.26750082, -0.12780671, 0.09570065, -0.03541132, 0.094820626, -0.13539355, -0.09468136, 0.18476579, -0.20970085, -0.20989786, -0.12084438, -0.04517079, -0.008074663, 0.02824076, 0.114496395, -0.20462593, 0.103516705, -0.101554185, -0.1374868, -0.24884155, -0.08101618, -0.016105993, 0.22608215, -0.007247754, -0.17246912, 0.058247145, -0.041018173, 0.19471274, -0.022576109, 0.032828204, -0.079321206, -0.09259324, 0.041115705, -0.25280195, -0.28517374, -0.19496292, 0.18070905, 0.06384923, -0.004056949, 0.1536253, 0.17861623, -0.033833142, 0.12039968, 0.04458716, 0.08793809, -0.15683243, -0.1087904, 0.1741014, 0.007256374, -0.20265253, 0.034111258, 0.03311363, -0.09449356, -0.13161612, -0.026084669, 0.07609202, 0.03452338, 0.08840356, -0.044566724, 0.1507175, 0.089273594, 0.18872644, 0.18333815, -0.023196407, 0.63831943, 0.20309874, 0.10217627, 0.11445079, 0.18965706, -0.16809432, -0.343172, -0.06439529, 0.08362327, 0.32746288, 0.38483366, 0.020372175, -0.25239283, 0.019468365, -0.016367752, 0.016749177, 0.024621855, 0.030529505, 0.20601188, -0.100692995, -0.16414656, -0.23193358, 0.26616478, 0.06166736, 0.14341855, 0.1294041, 0.045133967, 0.0014262896, -0.0194398, 0.040737696, 0.10099013, -0.10838136, -0.28768313, -0.073719576, -0.15836753, -0.10482511, -0.1349642, -0.107005455, 0.01957546, 0.13799994, 0.056444198, -0.38841644, -0.07585945, -0.018703599, -0.19934878, 0.15176265, 0.04133126, 0.063531734, 0.09720055, -0.29999572, 0.04765686, -0.23604262, 0.081500284, 0.056092553, -0.13664724, -0.37729686, 0.031137427, -0.052083906, 0.117984496, -0.14562207, -0.029609507, 0.13725121, 0.090367764, 0.12787215, 0.11026589, 0.25123242, 0.12911159, 0.055398554, 0.0032232201, 0.026706887, 0.14584258, 0.019900957, -0.12197998, -0.087177716, -0.24649806, -0.17869286, 0.07139921, -0.09633085, -0.16027117, 0.23617831, 0.05429949, -0.061085824, 0.040451035, 0.052443117, -0.14255014, 0.15598148, -0.2336374, 0.08394173, -0.34318882, 0.3419207, 0.18282516, -0.03709172, 0.10525048, -0.1871602, -0.22663523, 0.01635051, 0.16996534, -0.18056048, -0.169894, -0.18467705, -0.3641231, 0.060861763, -0.080082566, -0.08888943, 0.11629789, -0.00973362, 0.07452957, 0.25680214, 0.042024083, -0.024963235, 0.1743134, 0.10921186, 0.25191578, 0.028438354, 0.004781374, -0.08364819, 0.051807538, 0.1165724, 0.29184434, -0.21512283, 0.12515399, -0.08803969, 0.41930157, -0.10181762, 0.038189832, 0.085555896, -0.026453126, 0.04717047, 0.12667313, 0.023158737, -0.45877644, 0.18732828, 0.062374037, -0.21956007, -0.04449947, 0.19028638, 0.1359094, 0.26384917, 0.077602044, 0.35136092, 0.069637895, 0.048263475, -0.02498448, -0.09221205, -0.012142404, -0.124592446, 0.14599627, -0.050875153, -0.25454503, -0.069588415, -0.29793787, -0.13407284, 0.25388947, 0.35565627, -0.034204755, 0.0024766966, 0.086427726, -0.054318108, 0.063218184, -0.037823644, 0.108287826, 0.14440496, 0.025134278, 0.14978257, -0.03355889, 0.02980915, -0.13764386, 0.4167542, -0.03938922, 0.026970355, 0.24595529, 0.111741625, -0.074567944, -0.057232533}, 1757 expected: false, 1758 }, 1759 strfmt.UUID("0999d109-1d5f-465a-bd8b-e3fbd46f10aa"): { 1760 inputVec: []float32{-0.10486144, -0.07437922, 0.069469325, -0.1438278, 0.07740161, -0.18606456, -0.09991434, -0.020051572, 0.19863395, 0.4347328, 0.297606, 0.07853262, -0.16025662, 0.023596637, 0.16935731, 0.17052403, -0.29870638, -0.10309007, -0.20055692, 0.0027809117, -0.03928043, 0.21178603, -0.13793766, 0.08118157, 0.006693433, -0.13829204, 0.14778963, -0.13180175, -0.21128704, -0.0026104634, -0.076393716, 0.22200249, 0.32417125, 0.26045212, 0.1783609, -0.114116184, 0.0100981165, 0.07233143, -0.15913877, 0.4238603, -0.036907215, 0.0595873, 0.0807002, -0.07637312, -0.12889846, 0.111177936, 0.091114685, -0.018454906, -0.12132672, 0.056664582, -0.30461523, 0.020763714, -0.10992191, -0.14430659, -0.092879646, 0.13615008, 0.33039626, -0.115675874, 0.03607886, -0.027918883, 0.19531779, -0.7211654, -0.23073879, 0.011791817, 0.1315166, -0.22779183, -0.13773227, -0.1814997, -0.09008116, 0.021698939, -0.102921166, 0.090760864, 0.011856942, -0.25561005, 0.40769714, -0.21286584, -0.018059848, 0.13812906, 0.079457305, 0.12631191, 0.0024881593, -0.4282836, 0.0619608, 0.12207897, 0.39083096, -0.009502015, 0.19990632, -0.06503092, 0.0635979, 0.27579078, -0.020699967, 0.068474516, 0.0043831975, 0.10303624, -0.1885405, 0.22989234, -0.15952443, -0.29842895, 0.006752088, 0.22831629, -0.13150804, -0.13695218, 0.5357904, 0.050116863, -0.24064547, -0.01375713, -0.096647836, -0.24984525, -0.10429946, 0.002098812, -0.08113263, 0.05237009, -0.10246039, 0.05234802, -0.13899775, -0.3439524, 0.12522809, -0.18406768, -0.09022853, -0.19954625, 0.15810682, 0.039185096, -0.13576287, 0.045047805, 0.0035671506, 0.055920787, 1.1730403, 0.24019612, -0.13423051, -0.008052084, -0.00431602, -0.17079304, -0.09064658, -0.58728856, -0.1365065, 0.22919424, 0.22795208, 0.13396585, 0.018962797, -0.0075796233, -0.072394304, 0.10908417, -0.10881145, -0.16565171, 0.10378018, 0.27296618, -0.059810717, 0.03355443, -0.22429268, -0.12499127, -0.0441017, 0.20800696, -0.29992488, -0.003536096, 0.0026575085, 0.2427503, -0.007395092, 0.13233404, -0.5494433, -0.13144702, -0.2899963, -0.27367246, -0.05257514, -0.0939783, -0.267614, 0.16651331, 0.13891254, 0.08047202, -0.14046521, -0.19062972, -0.1433134, 0.0067776316, 0.00207368, 0.12986982, -0.35847133, -0.41852546, -0.15541135, -0.09865207, 0.14805861, 0.17072491, -0.22655731, -0.6473966, -0.007884447, -0.2060257, 0.035390265, 0.02781265, -0.09760371, 0.30535778, 0.047540557, 0.14565119, 0.21733035, 0.06558403, 0.13184759, 0.044231005, -0.22218557, 0.1897204, -0.1596938, 0.017510587, -0.030249557, -0.082377456, -0.39669412, -0.18365891, 0.34806964, -0.024830062, -0.06955674, 0.21521395, 0.1201222, -0.21855503, 0.23522708, 0.038058903, -0.019610198, -0.025448406, -0.18122384, 0.26068974, -0.055872105, 0.29595166, 0.11005987, -0.00841942, 0.006325112, -0.0013332894, -0.025598384, 0.17320716, 0.03480282, -0.1504056, -0.07133905, 0.08367911, -0.41866872, 0.062191408, -0.14972427, -0.18488628, -0.37027854, -0.14803104, 0.23587811, 0.33285886, 0.059688937, 0.030515533, 0.16795416, 0.3813925, 0.0755207, 0.15504116, -0.003507182, -0.08249321, 0.24292688, 0.13771294, 0.08057683, 0.016365156, -0.12878628, 0.1833687, 0.17496476, 0.050333332, 0.008188007, 0.32129762, 0.15476923, 0.2052587, -0.060781036, -0.1502798, -0.10187848, 0.11062117, -0.41137248, 0.016532877, 0.107270226, 0.08759128, 0.011842419, -0.17039144, -0.0139911, -0.13244899, -0.23845059, 0.075682834, -0.052250806, -0.30011725, -0.28581655, -0.00055503653, 0.022204043, -0.08598292, -0.24763824, 0.08245162, -0.39607832, 0.008443992, 0.16124122, 0.08812278, 0.0335653, -0.09692297, 0.07613783, 0.033542078, 0.11447116, -0.0069911424, 0.09004892, 0.09898015, 0.14595516, 0.24977732, -0.0018444546, 0.06290809, 0.013354713, -0.10336537, -0.1028908, 0.31109008, -0.110210516, 0.07165067, 0.050161615, -0.11413514}, 1761 expected: true, 1762 }, 1763 } 1764 1765 t.Run("insert test objects", func(t *testing.T) { 1766 for id, props := range tests { 1767 err := repo.PutObject(context.Background(), &models.Object{Class: className, ID: id}, props.inputVec, nil, nil) 1768 require.Nil(t, err) 1769 } 1770 }) 1771 1772 t.Run("perform nearVector search by distance", func(t *testing.T) { 1773 results, err := repo.VectorSearch(context.Background(), dto.GetParams{ 1774 ClassName: className, 1775 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 1776 NearVector: &searchparams.NearVector{ 1777 Distance: 0.1, 1778 }, 1779 SearchVector: searchVector, 1780 AdditionalProperties: additional.Properties{Distance: true}, 1781 }) 1782 require.Nil(t, err) 1783 require.NotEmpty(t, results) 1784 // ensure that we receive more results than 1785 // the `QueryMaximumResults`, as this should 1786 // only apply to limited vector searches 1787 require.Greater(t, len(results), 1) 1788 1789 for _, res := range results { 1790 if props, ok := tests[res.ID]; !ok { 1791 t.Fatalf("received unexpected result: %+v", res) 1792 } else { 1793 assert.True(t, props.expected, "result id was not intended to meet threshold %s", res.ID) 1794 } 1795 } 1796 }) 1797 1798 t.Run("perform nearObject search by distance", func(t *testing.T) { 1799 results, err := repo.VectorSearch(context.Background(), dto.GetParams{ 1800 ClassName: className, 1801 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 1802 NearObject: &searchparams.NearObject{ 1803 Distance: 0.1, 1804 ID: searchObject.String(), 1805 }, 1806 SearchVector: searchVector, 1807 AdditionalProperties: additional.Properties{Distance: true}, 1808 }) 1809 require.Nil(t, err) 1810 require.NotEmpty(t, results) 1811 // ensure that we receive more results than 1812 // the `QueryMaximumResults`, as this should 1813 // only apply to limited vector searches 1814 require.Greater(t, len(results), 1) 1815 1816 for _, res := range results { 1817 if props, ok := tests[res.ID]; !ok { 1818 t.Fatalf("received unexpected result: %+v", res) 1819 } else { 1820 assert.True(t, props.expected, "result id was not intended to meet threshold %s", res.ID) 1821 } 1822 } 1823 }) 1824 } 1825 1826 func TestVectorSearch_ByCertainty(t *testing.T) { 1827 className := "SomeClass" 1828 var class *models.Class 1829 1830 dirName := t.TempDir() 1831 logger, _ := test.NewNullLogger() 1832 1833 schemaGetter := &fakeSchemaGetter{ 1834 schema: schema.Schema{Objects: &models.Schema{Classes: nil}}, 1835 shardState: singleShardState(), 1836 } 1837 repo, err := New(logger, Config{ 1838 MemtablesFlushDirtyAfter: 60, 1839 RootPath: dirName, 1840 // this is set really low to ensure that search 1841 // by distance is conducted, which executes 1842 // without regard to this value 1843 QueryMaximumResults: 1, 1844 MaxImportGoroutinesFactor: 1, 1845 }, &fakeRemoteClient{}, &fakeNodeResolver{}, &fakeRemoteNodeClient{}, &fakeReplicationClient{}, nil) 1846 require.Nil(t, err) 1847 repo.SetSchemaGetter(schemaGetter) 1848 require.Nil(t, repo.WaitForStartup(testCtx())) 1849 defer repo.Shutdown(context.Background()) 1850 migrator := NewMigrator(repo, logger) 1851 1852 t.Run("create required schema", func(t *testing.T) { 1853 class = &models.Class{ 1854 Class: className, 1855 Properties: []*models.Property{ 1856 { 1857 DataType: []string{string(schema.DataTypeInt)}, 1858 Name: "int_prop", 1859 }, 1860 }, 1861 VectorIndexConfig: enthnsw.NewDefaultUserConfig(), 1862 InvertedIndexConfig: invertedConfig(), 1863 } 1864 require.Nil(t, 1865 migrator.AddClass(context.Background(), class, schemaGetter.shardState)) 1866 }) 1867 1868 // update schema getter so it's in sync with class 1869 schemaGetter.schema = schema.Schema{ 1870 Objects: &models.Schema{ 1871 Classes: []*models.Class{class}, 1872 }, 1873 } 1874 1875 searchVector := []float32{-0.10190568, -0.06259751, 0.05616188, -0.19249836, 0.09714927, -0.1902525, -0.064424865, -0.0387358, 0.17581701, 0.4476738, 0.29261824, 0.12026761, -0.19975126, 0.023600178, 0.17348698, 0.12701738, -0.36018127, -0.12051587, -0.17620522, 0.060741074, -0.064512916, 0.18640806, -0.1529852, 0.08211839, -0.02558465, -0.11369845, 0.0924098, -0.10544433, -0.14728987, -0.041860342, -0.08533595, 0.25886244, 0.2963937, 0.26010615, 0.2111097, 0.029396622, 0.01429563, 0.06410264, -0.119665794, 0.33583277, -0.05802661, 0.023306102, 0.14435922, -0.003951336, -0.13870825, 0.07140894, 0.10469943, -0.059021875, -0.065911904, 0.024216041, -0.26282874, 0.04896568, -0.08291928, -0.12793182, -0.077824734, 0.08843151, 0.31247458, -0.066301286, 0.006904921, -0.08277095, 0.13936226, -0.64392364, -0.19566211, 0.047227614, 0.086121306, -0.20725192, -0.096485816, -0.16436341, -0.06559169, -0.019639932, -0.012729637, 0.08901619, 0.0015896161, -0.24789932, 0.35496348, -0.16272856, -0.01648429, 0.11247674, 0.08099968, 0.13339259, 0.055829972, -0.34662855, 0.068509, 0.13880715, 0.3201848, -0.055557363, 0.22142135, -0.12867308, 0.0037871755, 0.24888979, -0.007443307, 0.08906625, -0.02022331, 0.11510742, -0.2385861, 0.16177008, -0.16214795, -0.28715602, 0.016784908, 0.19386634, -0.07731616, -0.100485384, 0.4100712, 0.061834496, -0.2325293, -0.026056025, -0.11632323, -0.17040555, -0.081960455, -0.0061040106, -0.05949373, 0.044952348, -0.079565264, 0.024430245, -0.09375341, -0.30249637, 0.115205586, -0.13083287, -0.04264671, -0.089810364, 0.16227561, 0.07318055, -0.10496504, 0.00063501706, -0.04936106, -0.0022282854, 1.0893154, 0.1698662, -0.019563455, -0.011128426, 0.04477475, -0.15656771, -0.056911886, -0.5759019, -0.1881429, 0.17088258, 0.24124439, 0.111288875, -0.0015475494, -0.021278847, -0.08362156, 0.09997524, -0.094385885, -0.1674031, 0.061180864, 0.28517494, -0.016217072, 0.025866214, -0.22854298, -0.17924422, -0.037767246, 0.12252907, -0.31698978, -0.038031228, 0.055408552, 0.1743545, -0.040576655, 0.1293942, -0.56650764, -0.10306195, -0.19548112, -0.245544, -0.018241389, -0.039024632, -0.31659162, 0.1565075, 0.08412337, 0.13177724, -0.13766576, -0.15355161, -0.16960397, -0.012436442, 0.04828157, 0.12566057, -0.35308784, -0.37520224, -0.1265899, -0.13991497, 0.14402144, 0.117542416, -0.20750546, -0.5849919, -0.010469457, -0.19677396, 0.011365964, 0.00666846, -0.083470255, 0.24928358, 0.07026387, 0.19082819, 0.24557637, 0.014292963, 0.14846677, 0.031625308, -0.20398879, 0.19507346, -0.18119761, -0.045725327, -0.042455163, -0.099733196, -0.33636123, -0.28447086, 0.30274838, -0.01603988, -0.0529655, 0.15784146, 0.08746072, -0.1703993, 0.2414512, 0.060322937, -0.00812057, 0.031162385, -0.1764905, 0.22107981, -0.016657066, 0.31948856, 0.07282925, -0.036991462, 0.01266936, -0.009106514, -0.038732465, 0.20973183, 0.033236098, -0.10673938, -0.06880061, 0.115524575, -0.39688373, 0.08749971, -0.21816005, -0.22100002, -0.3716853, -0.14720486, 0.24316181, 0.29673144, 0.020808747, 0.07658521, 0.16310681, 0.38785335, 0.0992224, 0.14177811, 0.025954131, -0.08690783, 0.19653428, 0.09584941, 0.040072605, -0.00038361162, -0.094546966, 0.1910902, 0.13217318, 0.060072783, -0.0655816, 0.2777626, 0.1799169, 0.20187178, -0.0996889, -0.01932122, -0.13133621, 0.057482753, -0.36892185, -0.032093313, 0.14607865, 0.12033318, -0.041683596, -0.2048406, -0.041777443, -0.14975598, -0.2526341, 0.12659752, 0.010567178, -0.297333, -0.27522174, 0.06923473, 0.043150593, -0.017045585, -0.2400216, 0.11413547, -0.40081662, -0.0018820907, 0.13800722, 0.085972115, -0.01519989, -0.10491216, 0.09170084, 0.063085504, 0.046743374, -0.014466267, 0.09880224, 0.027706565, 0.09951337, 0.17317492, -0.025654864, 0.14658073, 0.042377427, -0.08402882, -0.12423425, 0.32714987, -0.1527207, 0.106094465, 0.017378228, -0.06302387} 1876 searchObject := strfmt.UUID("fe687bf4-f10f-4c23-948d-0746ea2927b3") 1877 1878 tests := map[strfmt.UUID]struct { 1879 inputVec []float32 1880 expected bool 1881 }{ 1882 strfmt.UUID("88460290-03b2-44a3-9adb-9fa3ae11d9e6"): { 1883 inputVec: []float32{-0.11015724, -0.05380307, 0.027512914, -0.16925375, 0.08306809, -0.19312492, -0.08910436, -0.011051652, 0.17981204, 0.40469593, 0.28226805, 0.09381516, -0.18380599, 0.03102771, 0.1645333, 0.1530153, -0.3187937, -0.10800173, -0.18466279, 0.0004336393, -0.0495677, 0.19905856, -0.11614494, 0.08834681, -0.011200292, -0.11969374, 0.12497086, -0.12427251, -0.13395442, -0.0060353535, -0.07504816, 0.23205791, 0.2982508, 0.2517544, 0.176147, -0.036871903, 0.017852835, 0.040007118, -0.118621, 0.3648693, -0.058933854, 0.04004229, 0.11871147, -0.019860389, -0.12701912, 0.106662825, 0.086498804, -0.04303973, -0.0742352, 0.018250324, -0.26544014, 0.029228423, -0.087171465, -0.1282789, -0.06403083, 0.09680911, 0.31433868, -0.081510685, -0.011283603, -0.041624587, 0.16530018, -0.6714878, -0.2436993, 0.03173918, 0.106117725, -0.20803581, -0.10429562, -0.16975354, -0.078582145, -0.0065962705, -0.06840946, 0.094937086, -0.020617036, -0.23795949, 0.34785536, -0.19834635, -0.015064479, 0.11930141, 0.090962164, 0.120560184, 0.054095767, -0.38602966, 0.057141174, 0.12039684, 0.32000408, -0.05146908, 0.20762976, -0.09342379, 0.037577383, 0.23894139, -0.0075003104, 0.104791366, -0.015841056, 0.102840215, -0.20813248, 0.1855997, -0.12594056, -0.27132365, -0.0055563124, 0.21954241, -0.10798524, -0.111896284, 0.44049335, 0.049884494, -0.22339955, -0.005374135, -0.120713554, -0.22275059, -0.09146004, 0.017188415, -0.106493734, 0.045247544, -0.07725446, 0.056848228, -0.10294392, -0.2896642, 0.112891, -0.13773362, -0.089911595, -0.13500965, 0.14051703, 0.040092673, -0.13896292, 0.04580957, -0.014300959, 0.03737215, 1.0661443, 0.19767477, -0.07703914, -0.012910904, -0.0037716173, -0.14437087, -0.06938004, -0.5348036, -0.16047458, 0.19416414, 0.21938956, 0.092242256, -0.012630808, -0.021863988, -0.051702406, 0.08780951, -0.0815602, -0.15332024, 0.077632725, 0.25709584, -0.025725808, 0.042116437, -0.22687604, -0.11791685, -0.028626656, 0.16734225, -0.3017483, -0.03236202, 0.02888077, 0.18193199, -0.009032297, 0.14454253, -0.511494, -0.12119192, -0.20757924, -0.2561716, -0.03904554, -0.07348411, -0.28547177, 0.15967208, 0.079396725, 0.14358875, -0.12829632, -0.18175666, -0.15540425, -0.020419862, 0.019070208, 0.12168836, -0.3428434, -0.357543, -0.11218741, -0.12834033, 0.13564876, 0.12768728, -0.1817461, -0.61235875, -0.029409664, -0.19765733, 0.03872163, 0.0074950717, -0.10025679, 0.2872255, 0.033995092, 0.12945211, 0.21831632, 0.04666009, 0.14233032, 0.016767867, -0.2039244, 0.2000191, -0.13099428, -0.020210614, -0.06286195, -0.0948797, -0.34830436, -0.21595824, 0.32722405, -0.024735296, -0.07859145, 0.16975155, 0.08186461, -0.19249061, 0.23405583, 0.04837592, 0.021467948, -0.022215014, -0.14892808, 0.23908867, -0.050126728, 0.2867907, 0.07740656, -0.01714987, -0.0046314416, -0.0048108613, -0.007407311, 0.1807499, 0.049772616, -0.14680666, -0.07335314, 0.09023705, -0.40600133, 0.05522128, -0.20085222, -0.20410904, -0.34319055, -0.10792269, 0.2297779, 0.30397663, 0.05230268, 0.06408224, 0.13797496, 0.3691112, 0.083033495, 0.13695791, -0.015612457, -0.06413475, 0.18117142, 0.12928344, 0.049171276, 0.016104931, -0.102417335, 0.19589683, 0.14380622, 0.0748065, -0.005402455, 0.27243868, 0.14925551, 0.19564849, -0.10738364, -0.054175537, -0.10068278, 0.06004795, -0.38755924, -0.01654251, 0.1394104, 0.0968949, 0.004271706, -0.17105898, -0.050423585, -0.15311627, -0.24458972, 0.12665795, -0.022814916, -0.23887472, -0.289588, 0.05521137, 0.041259795, -0.021133862, -0.23674431, 0.08424598, -0.37863016, 0.017239956, 0.13776784, 0.060790475, 0.057887543, -0.08784489, 0.08803934, 0.027996546, 0.085972995, -0.014455558, 0.11668073, 0.03812387, 0.088413864, 0.22228678, -0.015599858, 0.11000236, 0.035271563, -0.08088438, -0.13092226, 0.29378533, -0.12311522, 0.09377676, 0.02948718, -0.09136077}, 1884 expected: true, 1885 }, 1886 strfmt.UUID("c99bc97d-7035-4311-94f3-947dc6471f51"): { 1887 inputVec: []float32{-0.07545987, -0.046643265, 0.044445477, -0.18531442, 0.07922216, -0.19388637, -0.069393866, -0.036144026, 0.1713317, 0.41803706, 0.23576374, 0.073170714, -0.14085358, 0.012535708, 0.17439325, 0.10057567, -0.33506152, -0.06800867, -0.18882714, 0.002687021, -0.03276807, 0.17267752, -0.13951558, 0.071382746, 0.020254405, -0.10178502, 0.13977699, -0.107296936, -0.113307, -0.002506761, -0.092065684, 0.21008658, 0.31157792, 0.19640765, 0.15769793, -0.0050196033, 0.0022481605, 0.015436289, -0.11822955, 0.31494477, -0.07425527, 0.051401984, 0.11648046, -0.00016831602, -0.12758006, 0.06814693, 0.06108981, -0.025454175, -0.018695071, 0.041827776, -0.23480764, 0.06652185, -0.078328855, -0.121668324, -0.04341973, 0.114403985, 0.32614416, -0.07992741, -0.019665314, -0.017408244, 0.12615794, -0.6350545, -0.17056493, 0.07171332, 0.047071394, -0.18428493, -0.09011123, -0.15995751, -0.03345579, -0.014678, -0.038699757, 0.044125225, 0.0042562615, -0.29445595, 0.30460796, -0.13630153, 0.00014055961, 0.08996278, 0.08948901, 0.12164838, 0.079090506, -0.36153567, 0.02817218, 0.11914518, 0.29805067, -0.07431765, 0.16793592, -0.099549234, 0.045226075, 0.22235383, -0.045654725, 0.09233901, -0.004902314, 0.08621588, -0.19723448, 0.19557382, -0.13199815, -0.22924824, -0.015981175, 0.19762704, -0.08940076, -0.084909916, 0.43372774, 0.026998578, -0.20827708, 0.037450224, -0.078997016, -0.18065391, -0.071308024, 0.00870316, -0.114981964, 0.017085023, -0.07696264, 0.009330409, -0.097458, -0.25530958, 0.118254915, -0.12516825, -0.008301536, -0.20432962, 0.15235707, 0.012840041, -0.18034773, 0.039270073, -0.03131139, 0.013706253, 0.98688674, 0.18840753, -0.055119563, 0.00836046, 0.019445436, -0.10701598, -0.024610046, -0.50088006, -0.15488546, 0.14209819, 0.1798376, 0.08615982, -0.0119235935, -0.0060070297, -0.08406098, 0.10096481, -0.09077014, -0.15957798, 0.10556352, 0.2100476, -0.036947068, 0.05316554, -0.20480183, -0.14873864, -0.0069070593, 0.16027303, -0.288908, -0.04487129, 0.0705415, 0.11973847, -0.0017247469, 0.14092937, -0.5262047, -0.094283305, -0.19120996, -0.2816572, -0.010916339, -0.07984056, -0.28659204, 0.13706332, 0.07364347, 0.12300072, -0.17554194, -0.16378267, -0.15244205, 0.00075927645, 0.017289847, 0.12072629, -0.33452734, -0.33727616, -0.12780978, -0.09350711, 0.105674624, 0.10770573, -0.17278843, -0.5760599, -0.013741414, -0.15395893, 0.009837732, 0.015417911, -0.11384676, 0.24567491, 0.04905973, 0.10762609, 0.2131752, 0.019281652, 0.11665857, 0.022718405, -0.2234067, 0.23241606, -0.12194457, -0.049972955, -0.012225418, -0.14856412, -0.386102, -0.23018965, 0.28920102, -0.023396742, -0.114672944, 0.12130062, 0.05654803, -0.16194181, 0.24095012, 0.03644393, 0.028024165, -0.008832254, -0.16496961, 0.19496499, -0.035887964, 0.25981775, 0.0970074, 0.0013458093, -0.009548204, 0.040741496, -0.019192837, 0.20718361, -0.004034228, -0.1343262, -0.06990001, 0.09888768, -0.35942966, 0.043895893, -0.19182123, -0.17963983, -0.3222771, -0.10223457, 0.23866613, 0.25855777, 0.04051543, 0.08756274, 0.15683484, 0.37856522, 0.04853359, 0.10198129, -0.0061066896, -0.049892712, 0.17087941, 0.14563805, 0.06984385, 0.0071270005, -0.11838641, 0.18716812, 0.14013803, 0.05242403, 0.034357738, 0.3083466, 0.14742611, 0.17841975, -0.124118194, -0.014102871, -0.052544866, 0.037493005, -0.33485797, -0.013164912, 0.1066288, 0.11141791, -0.04029921, -0.16429856, -0.032241724, -0.15965424, -0.2430594, 0.13654563, 0.009401224, -0.2045843, -0.28467956, 0.07325551, 0.027996557, -0.033877768, -0.24350801, 0.08329816, -0.35555813, 0.006908567, 0.07227365, 0.03188268, 0.032559503, -0.09180395, 0.05601515, 0.0047281734, 0.06878795, -0.018943194, 0.08251342, 0.042039152, 0.12902294, 0.20526606, -0.014881293, 0.11723917, 0.0115632, -0.09016013, -0.12117223, 0.31020245, -0.111444525, 0.077845715, 0.00046715315, -0.104099475}, 1888 expected: true, 1889 }, 1890 strfmt.UUID("fe687bf4-f10f-4c23-948d-0746ea2927b3"): { 1891 inputVec: []float32{-0.20739016, -0.19551805, 0.06645163, 0.008650202, 0.03700748, -0.04132599, -0.029881354, 0.04684896, 0.096614264, 0.42888844, 0.10003969, 0.026234219, -0.051639702, -0.118660435, 0.14473079, 0.2911885, -0.1180539, -0.16804434, -0.48081538, 0.021702053, 0.12612472, 0.15442817, -0.05836532, 0.074295096, -0.28077397, -0.24297802, 0.047836643, -0.36753318, -0.30482984, 0.09265357, 0.25571078, 0.41130066, 0.46177864, 0.34033778, 0.20721313, -0.37726295, 0.07721501, 0.08009689, 0.00027321206, 0.5168123, -0.15305339, 0.0937765, 0.096195236, -0.21120761, 0.014014921, 0.3133104, 0.20773117, 0.08483507, -0.27784437, -0.17281856, -0.6050923, -0.22439326, -0.16914369, -0.3149047, -0.13828672, 0.16334395, -0.0018224253, -0.024342008, 0.3511251, 0.04979151, 0.34223744, -0.6965703, -0.36211932, -0.27092442, 0.34418032, -0.09667905, 0.13344757, -0.15622364, -0.24129291, 0.06958589, -0.2681816, -0.09497071, -0.08923615, -0.06642436, 0.48688608, -0.33535984, 0.014242731, 0.079838976, 0.32949054, 0.09051045, -0.2653392, -0.47393548, 0.07508276, 0.0062832804, 0.724184, -0.18929236, 0.11718613, 0.049603477, 0.08766128, 0.31040704, 0.04038693, -0.0017023507, -0.18986607, 0.056264438, -0.20978904, -0.107441366, -0.30505633, -0.45781082, -0.11571784, 0.32160303, -0.1347523, -0.08090298, 0.51651996, -0.023250414, -0.18725531, -0.14222279, 0.009277832, -0.49789724, -0.25156206, 0.0042495225, 0.0038805408, -0.031416763, 0.10277136, 0.14383446, -0.23241928, -0.42357358, 0.027033398, -0.2262604, -0.2685295, -0.14510548, 0.18256307, 0.063297585, 0.027636252, 0.081166506, 0.06726344, 0.1677495, 1.5217289, 0.33152232, -0.2209926, 0.051426213, 0.15640806, -0.30210486, -0.32857975, -0.4170022, -0.028293105, 0.28772062, 0.50510746, 0.09162247, -0.12383193, -0.25066972, -0.1441897, 0.107192926, -0.07404076, 0.0042472635, 0.11014519, 0.22332853, 0.09434378, -0.3278343, 0.041899726, 0.06838457, 0.10983681, 0.11864574, -0.25336757, -0.047530346, -0.027303243, 0.37403497, 0.13420461, 0.14946426, -0.41996637, -0.037703935, -0.47961184, -0.29839846, -0.103934005, -0.12058302, -0.12806267, 0.22814582, 0.3904893, -0.16044962, -0.17479864, -0.33139735, -0.29185295, 0.0653074, 0.042426735, 0.06092335, -0.18776153, -0.52555144, -0.15889317, -0.20644087, 0.2293067, 0.26668283, -0.15607063, -0.696593, -0.08224992, -0.4283747, 0.26883888, -0.031052848, -0.1311875, 0.26636878, 0.16457985, 0.15660451, 0.10629464, 0.17345549, 0.23963387, 0.22997221, -0.111713186, -0.08499592, -0.2274625, 0.19285984, -0.08285016, -0.02692149, -0.3426618, -0.13361897, 0.2870389, -0.12032792, -0.22944619, 0.25588584, 0.24607788, -0.2762531, 0.30983892, 0.011088746, -0.15739818, 0.053215, -0.21660997, 0.033805694, -0.17886437, 0.2979239, 0.2163545, -0.08381542, 0.19666128, -0.28977823, -0.20994817, -0.012160099, 0.057499636, -0.12549455, 0.19303595, -0.14420606, -0.51937664, 0.23400985, -0.27893808, -0.2660984, -0.27870297, -0.32149136, 0.19958079, 0.34468395, 0.18947665, -0.16529581, 0.101419374, 0.30195153, 0.09030288, 0.12496541, 0.02999903, -0.016697621, 0.15314853, 0.27848768, 0.24102053, 0.06933273, 0.08923653, 0.10477832, 0.4389032, 0.15679164, -0.11119637, 0.134823, 0.30230528, 0.20818473, -0.005579584, -0.3474488, -0.44394243, 0.22270252, -0.3668763, 0.07474772, 0.011691334, 0.088187896, 0.23832949, -0.07960201, 0.066471875, 0.034641538, -0.39984587, 0.0032980456, -0.28492525, -0.46358657, -0.2148288, -0.107226945, 0.02734428, -0.24686679, -0.123900555, 0.18174778, -0.31248868, 0.13808723, 0.31549984, 0.21521719, 0.13966985, -0.27272752, 0.12091104, 0.14257833, 0.23175247, 0.15639938, 0.40828535, 0.31916845, 0.023645567, 0.20658277, -0.20365283, 0.113746524, 0.13173752, -0.050343305, -0.31581175, 0.09704622, -0.014172505, 0.16924341, 0.30327854, -0.17770194}, 1892 expected: false, 1893 }, 1894 strfmt.UUID("e7bf6c45-de72-493a-b273-5ef198974d61"): { 1895 inputVec: []float32{0.089313604, -0.050221898, 0.18352903, 0.16257699, 0.14520381, 0.17993976, 0.14594483, 0.019256027, -0.15505213, 0.23606326, -0.14456263, 0.2679586, -0.112208664, 0.12997514, 0.0051072896, 0.28151348, -0.10495799, 0.026782967, -0.38603118, 0.16190273, -0.0428943, -0.16265322, -0.17910561, 0.0746288, -0.3117934, -0.15871756, -0.11377734, -0.06822346, -0.13829489, 0.13019162, 0.30741218, 0.16194165, 0.013218932, 0.054517113, 0.12490437, -0.07709048, 0.02556826, -0.21159878, -0.09082174, 0.24629511, 0.05013666, 0.25168124, -0.14423938, -0.0937688, -0.07811525, -0.049346007, 0.3592527, 0.30411252, -0.1168557, 0.18870471, 0.06614835, -0.20099068, -0.084436245, 0.073036775, -0.03448665, -0.11147946, -0.10862863, -0.012393957, 0.18990599, 0.060957544, 0.19518377, -0.027541652, -0.26750082, -0.12780671, 0.09570065, -0.03541132, 0.094820626, -0.13539355, -0.09468136, 0.18476579, -0.20970085, -0.20989786, -0.12084438, -0.04517079, -0.008074663, 0.02824076, 0.114496395, -0.20462593, 0.103516705, -0.101554185, -0.1374868, -0.24884155, -0.08101618, -0.016105993, 0.22608215, -0.007247754, -0.17246912, 0.058247145, -0.041018173, 0.19471274, -0.022576109, 0.032828204, -0.079321206, -0.09259324, 0.041115705, -0.25280195, -0.28517374, -0.19496292, 0.18070905, 0.06384923, -0.004056949, 0.1536253, 0.17861623, -0.033833142, 0.12039968, 0.04458716, 0.08793809, -0.15683243, -0.1087904, 0.1741014, 0.007256374, -0.20265253, 0.034111258, 0.03311363, -0.09449356, -0.13161612, -0.026084669, 0.07609202, 0.03452338, 0.08840356, -0.044566724, 0.1507175, 0.089273594, 0.18872644, 0.18333815, -0.023196407, 0.63831943, 0.20309874, 0.10217627, 0.11445079, 0.18965706, -0.16809432, -0.343172, -0.06439529, 0.08362327, 0.32746288, 0.38483366, 0.020372175, -0.25239283, 0.019468365, -0.016367752, 0.016749177, 0.024621855, 0.030529505, 0.20601188, -0.100692995, -0.16414656, -0.23193358, 0.26616478, 0.06166736, 0.14341855, 0.1294041, 0.045133967, 0.0014262896, -0.0194398, 0.040737696, 0.10099013, -0.10838136, -0.28768313, -0.073719576, -0.15836753, -0.10482511, -0.1349642, -0.107005455, 0.01957546, 0.13799994, 0.056444198, -0.38841644, -0.07585945, -0.018703599, -0.19934878, 0.15176265, 0.04133126, 0.063531734, 0.09720055, -0.29999572, 0.04765686, -0.23604262, 0.081500284, 0.056092553, -0.13664724, -0.37729686, 0.031137427, -0.052083906, 0.117984496, -0.14562207, -0.029609507, 0.13725121, 0.090367764, 0.12787215, 0.11026589, 0.25123242, 0.12911159, 0.055398554, 0.0032232201, 0.026706887, 0.14584258, 0.019900957, -0.12197998, -0.087177716, -0.24649806, -0.17869286, 0.07139921, -0.09633085, -0.16027117, 0.23617831, 0.05429949, -0.061085824, 0.040451035, 0.052443117, -0.14255014, 0.15598148, -0.2336374, 0.08394173, -0.34318882, 0.3419207, 0.18282516, -0.03709172, 0.10525048, -0.1871602, -0.22663523, 0.01635051, 0.16996534, -0.18056048, -0.169894, -0.18467705, -0.3641231, 0.060861763, -0.080082566, -0.08888943, 0.11629789, -0.00973362, 0.07452957, 0.25680214, 0.042024083, -0.024963235, 0.1743134, 0.10921186, 0.25191578, 0.028438354, 0.004781374, -0.08364819, 0.051807538, 0.1165724, 0.29184434, -0.21512283, 0.12515399, -0.08803969, 0.41930157, -0.10181762, 0.038189832, 0.085555896, -0.026453126, 0.04717047, 0.12667313, 0.023158737, -0.45877644, 0.18732828, 0.062374037, -0.21956007, -0.04449947, 0.19028638, 0.1359094, 0.26384917, 0.077602044, 0.35136092, 0.069637895, 0.048263475, -0.02498448, -0.09221205, -0.012142404, -0.124592446, 0.14599627, -0.050875153, -0.25454503, -0.069588415, -0.29793787, -0.13407284, 0.25388947, 0.35565627, -0.034204755, 0.0024766966, 0.086427726, -0.054318108, 0.063218184, -0.037823644, 0.108287826, 0.14440496, 0.025134278, 0.14978257, -0.03355889, 0.02980915, -0.13764386, 0.4167542, -0.03938922, 0.026970355, 0.24595529, 0.111741625, -0.074567944, -0.057232533}, 1896 expected: false, 1897 }, 1898 strfmt.UUID("0999d109-1d5f-465a-bd8b-e3fbd46f10aa"): { 1899 inputVec: []float32{-0.10486144, -0.07437922, 0.069469325, -0.1438278, 0.07740161, -0.18606456, -0.09991434, -0.020051572, 0.19863395, 0.4347328, 0.297606, 0.07853262, -0.16025662, 0.023596637, 0.16935731, 0.17052403, -0.29870638, -0.10309007, -0.20055692, 0.0027809117, -0.03928043, 0.21178603, -0.13793766, 0.08118157, 0.006693433, -0.13829204, 0.14778963, -0.13180175, -0.21128704, -0.0026104634, -0.076393716, 0.22200249, 0.32417125, 0.26045212, 0.1783609, -0.114116184, 0.0100981165, 0.07233143, -0.15913877, 0.4238603, -0.036907215, 0.0595873, 0.0807002, -0.07637312, -0.12889846, 0.111177936, 0.091114685, -0.018454906, -0.12132672, 0.056664582, -0.30461523, 0.020763714, -0.10992191, -0.14430659, -0.092879646, 0.13615008, 0.33039626, -0.115675874, 0.03607886, -0.027918883, 0.19531779, -0.7211654, -0.23073879, 0.011791817, 0.1315166, -0.22779183, -0.13773227, -0.1814997, -0.09008116, 0.021698939, -0.102921166, 0.090760864, 0.011856942, -0.25561005, 0.40769714, -0.21286584, -0.018059848, 0.13812906, 0.079457305, 0.12631191, 0.0024881593, -0.4282836, 0.0619608, 0.12207897, 0.39083096, -0.009502015, 0.19990632, -0.06503092, 0.0635979, 0.27579078, -0.020699967, 0.068474516, 0.0043831975, 0.10303624, -0.1885405, 0.22989234, -0.15952443, -0.29842895, 0.006752088, 0.22831629, -0.13150804, -0.13695218, 0.5357904, 0.050116863, -0.24064547, -0.01375713, -0.096647836, -0.24984525, -0.10429946, 0.002098812, -0.08113263, 0.05237009, -0.10246039, 0.05234802, -0.13899775, -0.3439524, 0.12522809, -0.18406768, -0.09022853, -0.19954625, 0.15810682, 0.039185096, -0.13576287, 0.045047805, 0.0035671506, 0.055920787, 1.1730403, 0.24019612, -0.13423051, -0.008052084, -0.00431602, -0.17079304, -0.09064658, -0.58728856, -0.1365065, 0.22919424, 0.22795208, 0.13396585, 0.018962797, -0.0075796233, -0.072394304, 0.10908417, -0.10881145, -0.16565171, 0.10378018, 0.27296618, -0.059810717, 0.03355443, -0.22429268, -0.12499127, -0.0441017, 0.20800696, -0.29992488, -0.003536096, 0.0026575085, 0.2427503, -0.007395092, 0.13233404, -0.5494433, -0.13144702, -0.2899963, -0.27367246, -0.05257514, -0.0939783, -0.267614, 0.16651331, 0.13891254, 0.08047202, -0.14046521, -0.19062972, -0.1433134, 0.0067776316, 0.00207368, 0.12986982, -0.35847133, -0.41852546, -0.15541135, -0.09865207, 0.14805861, 0.17072491, -0.22655731, -0.6473966, -0.007884447, -0.2060257, 0.035390265, 0.02781265, -0.09760371, 0.30535778, 0.047540557, 0.14565119, 0.21733035, 0.06558403, 0.13184759, 0.044231005, -0.22218557, 0.1897204, -0.1596938, 0.017510587, -0.030249557, -0.082377456, -0.39669412, -0.18365891, 0.34806964, -0.024830062, -0.06955674, 0.21521395, 0.1201222, -0.21855503, 0.23522708, 0.038058903, -0.019610198, -0.025448406, -0.18122384, 0.26068974, -0.055872105, 0.29595166, 0.11005987, -0.00841942, 0.006325112, -0.0013332894, -0.025598384, 0.17320716, 0.03480282, -0.1504056, -0.07133905, 0.08367911, -0.41866872, 0.062191408, -0.14972427, -0.18488628, -0.37027854, -0.14803104, 0.23587811, 0.33285886, 0.059688937, 0.030515533, 0.16795416, 0.3813925, 0.0755207, 0.15504116, -0.003507182, -0.08249321, 0.24292688, 0.13771294, 0.08057683, 0.016365156, -0.12878628, 0.1833687, 0.17496476, 0.050333332, 0.008188007, 0.32129762, 0.15476923, 0.2052587, -0.060781036, -0.1502798, -0.10187848, 0.11062117, -0.41137248, 0.016532877, 0.107270226, 0.08759128, 0.011842419, -0.17039144, -0.0139911, -0.13244899, -0.23845059, 0.075682834, -0.052250806, -0.30011725, -0.28581655, -0.00055503653, 0.022204043, -0.08598292, -0.24763824, 0.08245162, -0.39607832, 0.008443992, 0.16124122, 0.08812278, 0.0335653, -0.09692297, 0.07613783, 0.033542078, 0.11447116, -0.0069911424, 0.09004892, 0.09898015, 0.14595516, 0.24977732, -0.0018444546, 0.06290809, 0.013354713, -0.10336537, -0.1028908, 0.31109008, -0.110210516, 0.07165067, 0.050161615, -0.11413514}, 1900 expected: true, 1901 }, 1902 } 1903 1904 t.Run("insert test objects", func(t *testing.T) { 1905 for id, props := range tests { 1906 err := repo.PutObject(context.Background(), &models.Object{Class: className, ID: id}, props.inputVec, nil, nil) 1907 require.Nil(t, err) 1908 } 1909 }) 1910 1911 t.Run("perform nearVector search by distance", func(t *testing.T) { 1912 results, err := repo.VectorSearch(context.Background(), dto.GetParams{ 1913 ClassName: className, 1914 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 1915 NearVector: &searchparams.NearVector{ 1916 Certainty: 0.9, 1917 }, 1918 SearchVector: searchVector, 1919 AdditionalProperties: additional.Properties{Certainty: true}, 1920 }) 1921 require.Nil(t, err) 1922 require.NotEmpty(t, results) 1923 // ensure that we receive more results than 1924 // the `QueryMaximumResults`, as this should 1925 // only apply to limited vector searches 1926 require.Greater(t, len(results), 1) 1927 1928 for _, res := range results { 1929 if props, ok := tests[res.ID]; !ok { 1930 t.Fatalf("received unexpected result: %+v", res) 1931 } else { 1932 assert.True(t, props.expected, "result id was not intended to meet threshold %s", res.ID) 1933 } 1934 } 1935 }) 1936 1937 t.Run("perform nearObject search by distance", func(t *testing.T) { 1938 results, err := repo.VectorSearch(context.Background(), dto.GetParams{ 1939 ClassName: className, 1940 Pagination: &filters.Pagination{Limit: filters.LimitFlagSearchByDist}, 1941 NearObject: &searchparams.NearObject{ 1942 Certainty: 0.9, 1943 ID: searchObject.String(), 1944 }, 1945 SearchVector: searchVector, 1946 AdditionalProperties: additional.Properties{Certainty: true}, 1947 }) 1948 require.Nil(t, err) 1949 require.NotEmpty(t, results) 1950 // ensure that we receive more results than 1951 // the `QueryMaximumResults`, as this should 1952 // only apply to limited vector searches 1953 require.Greater(t, len(results), 1) 1954 1955 for _, res := range results { 1956 if props, ok := tests[res.ID]; !ok { 1957 t.Fatalf("received unexpected result: %+v", res) 1958 } else { 1959 assert.True(t, props.expected, "result id was not intended to meet threshold %s", res.ID) 1960 } 1961 } 1962 }) 1963 } 1964 1965 func Test_PutPatchRestart(t *testing.T) { 1966 dirName := t.TempDir() 1967 logger, _ := test.NewNullLogger() 1968 ctx, cancel := context.WithTimeout(context.Background(), time.Minute) 1969 defer cancel() 1970 1971 testClass := &models.Class{ 1972 VectorIndexConfig: enthnsw.NewDefaultUserConfig(), 1973 InvertedIndexConfig: invertedConfig(), 1974 Class: "PutPatchRestart", 1975 Properties: []*models.Property{ 1976 { 1977 Name: "description", 1978 DataType: schema.DataTypeText.PropString(), 1979 Tokenization: models.PropertyTokenizationWhitespace, 1980 }, 1981 }, 1982 } 1983 1984 schemaGetter := &fakeSchemaGetter{ 1985 schema: schema.Schema{Objects: &models.Schema{Classes: nil}}, 1986 shardState: singleShardState(), 1987 } 1988 repo, err := New(logger, Config{ 1989 MemtablesFlushDirtyAfter: 60, 1990 RootPath: dirName, 1991 QueryMaximumResults: 100, 1992 MaxImportGoroutinesFactor: 1, 1993 }, &fakeRemoteClient{}, &fakeNodeResolver{}, &fakeRemoteNodeClient{}, &fakeReplicationClient{}, nil) 1994 require.Nil(t, err) 1995 repo.SetSchemaGetter(schemaGetter) 1996 defer repo.Shutdown(context.Background()) 1997 require.Nil(t, repo.WaitForStartup(ctx)) 1998 migrator := NewMigrator(repo, logger) 1999 2000 require.Nil(t, 2001 migrator.AddClass(ctx, testClass, schemaGetter.shardState)) 2002 2003 // update schema getter so it's in sync with class 2004 schemaGetter.schema = schema.Schema{ 2005 Objects: &models.Schema{ 2006 Classes: []*models.Class{testClass}, 2007 }, 2008 } 2009 2010 testID := strfmt.UUID("93c31577-922e-4184-87a5-5ac6db12f73c") 2011 testVec := []float32{0.1, 0.2, 0.1, 0.3} 2012 2013 t.Run("create initial object", func(t *testing.T) { 2014 err = repo.PutObject(ctx, &models.Object{ 2015 ID: testID, 2016 Class: testClass.Class, 2017 Properties: map[string]interface{}{"description": "test object init"}, 2018 }, testVec, nil, nil) 2019 require.Nil(t, err) 2020 }) 2021 2022 t.Run("repeatedly put with nil vec, patch with vec, and restart", func(t *testing.T) { 2023 for i := 0; i < 10; i++ { 2024 err = repo.PutObject(ctx, &models.Object{ 2025 ID: testID, 2026 Class: testClass.Class, 2027 Properties: map[string]interface{}{ 2028 "description": fmt.Sprintf("test object, put #%d", i+1), 2029 }, 2030 }, nil, nil, nil) 2031 require.Nil(t, err) 2032 2033 err = repo.Merge(ctx, objects.MergeDocument{ 2034 ID: testID, 2035 Class: testClass.Class, 2036 PrimitiveSchema: map[string]interface{}{ 2037 "description": fmt.Sprintf("test object, patch #%d", i+1), 2038 }, 2039 Vector: testVec, 2040 UpdateTime: time.Now().UnixNano() / int64(time.Millisecond), 2041 }, nil, "") 2042 require.Nil(t, err) 2043 2044 require.Nil(t, repo.Shutdown(ctx)) 2045 require.Nil(t, repo.WaitForStartup(ctx)) 2046 } 2047 }) 2048 2049 t.Run("assert the final result is correct", func(t *testing.T) { 2050 findByIDFilter := &filters.LocalFilter{ 2051 Root: &filters.Clause{ 2052 Operator: filters.OperatorEqual, 2053 On: &filters.Path{ 2054 Class: schema.ClassName(testClass.Class), 2055 Property: filters.InternalPropID, 2056 }, 2057 Value: &filters.Value{ 2058 Value: testID.String(), 2059 Type: schema.DataTypeText, 2060 }, 2061 }, 2062 } 2063 res, err := repo.ObjectSearch(ctx, 0, 10, findByIDFilter, 2064 nil, additional.Properties{}, "") 2065 require.Nil(t, err) 2066 assert.Len(t, res, 1) 2067 2068 expectedDescription := "test object, patch #10" 2069 resultDescription := res[0].Schema.(map[string]interface{})["description"] 2070 assert.Equal(t, expectedDescription, resultDescription) 2071 }) 2072 } 2073 2074 func TestCRUDWithEmptyArrays(t *testing.T) { 2075 dirName := t.TempDir() 2076 2077 logger, _ := test.NewNullLogger() 2078 2079 class := &models.Class{ 2080 Class: "TestClass", 2081 VectorIndexConfig: enthnsw.NewDefaultUserConfig(), 2082 InvertedIndexConfig: invertedConfig(), 2083 Properties: []*models.Property{ 2084 { 2085 Name: "textArray", 2086 DataType: schema.DataTypeTextArray.PropString(), 2087 }, 2088 { 2089 Name: "numberArray", 2090 DataType: []string{string(schema.DataTypeNumberArray)}, 2091 }, 2092 { 2093 Name: "boolArray", 2094 DataType: []string{string(schema.DataTypeBooleanArray)}, 2095 }, 2096 }, 2097 } 2098 classRefName := "TestRefClass" 2099 classRef := &models.Class{ 2100 Class: classRefName, 2101 VectorIndexConfig: enthnsw.NewDefaultUserConfig(), 2102 InvertedIndexConfig: invertedConfig(), 2103 Properties: []*models.Property{ 2104 { 2105 Name: "stringProp", 2106 DataType: schema.DataTypeText.PropString(), 2107 Tokenization: models.PropertyTokenizationWhitespace, 2108 }, 2109 }, 2110 } 2111 classNameWithRefs := "TestClassWithRefs" 2112 classWithRefs := &models.Class{ 2113 Class: classNameWithRefs, 2114 VectorIndexConfig: enthnsw.NewDefaultUserConfig(), 2115 InvertedIndexConfig: invertedConfig(), 2116 Properties: []*models.Property{ 2117 { 2118 Name: "stringProp", 2119 DataType: schema.DataTypeText.PropString(), 2120 Tokenization: models.PropertyTokenizationWhitespace, 2121 }, 2122 { 2123 Name: "refProp", 2124 DataType: []string{classRefName}, 2125 }, 2126 }, 2127 } 2128 schemaGetter := &fakeSchemaGetter{ 2129 schema: schema.Schema{Objects: &models.Schema{Classes: nil}}, 2130 shardState: singleShardState(), 2131 } 2132 repo, err := New(logger, Config{ 2133 MemtablesFlushDirtyAfter: 60, 2134 RootPath: dirName, 2135 QueryMaximumResults: 100, 2136 MaxImportGoroutinesFactor: 1, 2137 }, &fakeRemoteClient{}, &fakeNodeResolver{}, &fakeRemoteNodeClient{}, &fakeReplicationClient{}, nil) 2138 require.Nil(t, err) 2139 repo.SetSchemaGetter(schemaGetter) 2140 require.Nil(t, repo.WaitForStartup(testCtx())) 2141 defer repo.Shutdown(context.Background()) 2142 migrator := NewMigrator(repo, logger) 2143 require.Nil(t, 2144 migrator.AddClass(context.Background(), class, schemaGetter.shardState)) 2145 require.Nil(t, 2146 migrator.AddClass(context.Background(), classRef, schemaGetter.shardState)) 2147 require.Nil(t, 2148 migrator.AddClass(context.Background(), classWithRefs, schemaGetter.shardState)) 2149 // update schema getter so it's in sync with class 2150 schemaGetter.schema = schema.Schema{ 2151 Objects: &models.Schema{ 2152 Classes: []*models.Class{class, classRef, classWithRefs}, 2153 }, 2154 } 2155 2156 t.Run("empty arrays", func(t *testing.T) { 2157 objID := strfmt.UUID("a0b55b05-bc5b-4cc9-b646-1452d1390a62") 2158 obj1 := &models.Object{ 2159 ID: objID, 2160 Class: "TestClass", 2161 Properties: map[string]interface{}{ 2162 "textArray": []string{}, 2163 "numberArray": []float64{}, 2164 "boolArray": []bool{}, 2165 }, 2166 } 2167 obj2 := &models.Object{ 2168 ID: objID, 2169 Class: "TestClass", 2170 Properties: map[string]interface{}{ 2171 "textArray": []string{"value"}, 2172 "numberArray": []float64{0.5}, 2173 "boolArray": []bool{true}, 2174 }, 2175 } 2176 2177 assert.Nil(t, repo.PutObject(context.Background(), obj1, []float32{1, 3, 5, 0.4}, nil, nil)) 2178 assert.Nil(t, repo.PutObject(context.Background(), obj2, []float32{1, 3, 5, 0.4}, nil, nil)) 2179 2180 res, err := repo.ObjectByID(context.Background(), objID, nil, additional.Properties{}, "") 2181 require.Nil(t, err) 2182 assert.Equal(t, obj2.Properties, res.ObjectWithVector(false).Properties) 2183 }) 2184 2185 t.Run("empty references", func(t *testing.T) { 2186 objRefID := strfmt.UUID("a0b55b05-bc5b-4cc9-b646-1452d1390000") 2187 objRef := &models.Object{ 2188 ID: objRefID, 2189 Class: classRefName, 2190 Properties: map[string]interface{}{ 2191 "stringProp": "string prop value", 2192 }, 2193 } 2194 assert.Nil(t, repo.PutObject(context.Background(), objRef, []float32{1, 3, 5, 0.4}, nil, nil)) 2195 2196 obj1ID := strfmt.UUID("a0b55b05-bc5b-4cc9-b646-1452d1390a62") 2197 obj1 := &models.Object{ 2198 ID: obj1ID, 2199 Class: classNameWithRefs, 2200 Properties: map[string]interface{}{ 2201 "stringProp": "some prop", 2202 // due to the fix introduced in https://github.com/weaviate/weaviate/pull/2320, 2203 // MultipleRef's can appear as empty []interface{} when no actual refs are provided for 2204 // an object's reference property. 2205 // 2206 // when obj1 is unmarshalled from storage, refProp will be represented as []interface{}, 2207 // because it is an empty reference property. so when comparing obj1 with the result of 2208 // repo.Object, we need this refProp here to be a []interface{}. Note that this is due 2209 // to our usage of storobj.Object.MarshallerVersion 1, and future MarshallerVersions may 2210 // not have this ambiguous property type limitation. 2211 "refProp": []interface{}{}, 2212 }, 2213 } 2214 obj2ID := strfmt.UUID("a0b55b05-bc5b-4cc9-b646-1452d1390a63") 2215 obj2 := &models.Object{ 2216 ID: obj2ID, 2217 Class: classNameWithRefs, 2218 Properties: map[string]interface{}{ 2219 "stringProp": "some second prop", 2220 "refProp": models.MultipleRef{ 2221 &models.SingleRef{ 2222 Beacon: strfmt.URI( 2223 crossref.NewLocalhost(classRefName, objRefID).String()), 2224 }, 2225 }, 2226 }, 2227 } 2228 2229 assert.Nil(t, repo.PutObject(context.Background(), obj1, []float32{1, 3, 5, 0.4}, nil, nil)) 2230 assert.Nil(t, repo.PutObject(context.Background(), obj2, []float32{1, 3, 5, 0.4}, nil, nil)) 2231 2232 res, err := repo.Object(context.Background(), classNameWithRefs, obj1ID, nil, 2233 additional.Properties{}, nil, "") 2234 require.Nil(t, err) 2235 assert.NotNil(t, res) 2236 assert.Equal(t, obj1.Properties, res.ObjectWithVector(false).Properties) 2237 2238 res, err = repo.Object(context.Background(), classNameWithRefs, obj2ID, nil, 2239 additional.Properties{}, nil, "") 2240 require.Nil(t, err) 2241 assert.NotNil(t, res) 2242 assert.Equal(t, obj2.Properties, res.ObjectWithVector(false).Properties) 2243 }) 2244 } 2245 2246 func TestOverwriteObjects(t *testing.T) { 2247 dirName := t.TempDir() 2248 logger, _ := test.NewNullLogger() 2249 class := &models.Class{ 2250 VectorIndexConfig: enthnsw.NewDefaultUserConfig(), 2251 InvertedIndexConfig: invertedConfig(), 2252 Class: "SomeClass", 2253 Properties: []*models.Property{ 2254 { 2255 Name: "stringProp", 2256 DataType: schema.DataTypeText.PropString(), 2257 Tokenization: models.PropertyTokenizationWhitespace, 2258 }, 2259 }, 2260 } 2261 schemaGetter := &fakeSchemaGetter{ 2262 schema: schema.Schema{Objects: &models.Schema{Classes: nil}}, 2263 shardState: singleShardState(), 2264 } 2265 repo, err := New(logger, Config{ 2266 MemtablesFlushDirtyAfter: 60, 2267 RootPath: dirName, 2268 QueryMaximumResults: 10, 2269 MaxImportGoroutinesFactor: 1, 2270 }, &fakeRemoteClient{}, &fakeNodeResolver{}, 2271 &fakeRemoteNodeClient{}, &fakeReplicationClient{}, nil) 2272 require.Nil(t, err) 2273 repo.SetSchemaGetter(schemaGetter) 2274 require.Nil(t, repo.WaitForStartup(testCtx())) 2275 defer repo.Shutdown(context.Background()) 2276 migrator := NewMigrator(repo, logger) 2277 t.Run("create the class", func(t *testing.T) { 2278 require.Nil(t, 2279 migrator.AddClass(context.Background(), class, schemaGetter.shardState)) 2280 }) 2281 // update schema getter so it's in sync with class 2282 schemaGetter.schema = schema.Schema{ 2283 Objects: &models.Schema{ 2284 Classes: []*models.Class{class}, 2285 }, 2286 } 2287 2288 now := time.Now() 2289 later := now.Add(time.Hour) // time-traveling ;) 2290 stale := &models.Object{ 2291 ID: "981c09f9-67f3-4e6e-a988-c53eaefbd58e", 2292 Class: class.Class, 2293 CreationTimeUnix: now.UnixMilli(), 2294 LastUpdateTimeUnix: now.UnixMilli(), 2295 Properties: map[string]interface{}{ 2296 "oldValue": "how things used to be", 2297 }, 2298 Vector: []float32{1, 2, 3}, 2299 VectorWeights: (map[string]string)(nil), 2300 Additional: models.AdditionalProperties{}, 2301 } 2302 2303 fresh := &models.Object{ 2304 ID: "981c09f9-67f3-4e6e-a988-c53eaefbd58e", 2305 Class: class.Class, 2306 CreationTimeUnix: now.UnixMilli(), 2307 LastUpdateTimeUnix: later.UnixMilli(), 2308 Properties: map[string]interface{}{ 2309 "oldValue": "how things used to be", 2310 "newValue": "how they are now", 2311 }, 2312 Vector: []float32{4, 5, 6}, 2313 VectorWeights: (map[string]string)(nil), 2314 Additional: models.AdditionalProperties{}, 2315 } 2316 2317 t.Run("insert stale object", func(t *testing.T) { 2318 err := repo.PutObject(context.Background(), stale, stale.Vector, nil, nil) 2319 require.Nil(t, err) 2320 }) 2321 2322 t.Run("overwrite with fresh object", func(t *testing.T) { 2323 input := []*objects.VObject{ 2324 { 2325 LatestObject: fresh, 2326 Vector: []float32{4, 5, 6}, 2327 StaleUpdateTime: stale.LastUpdateTimeUnix, 2328 }, 2329 } 2330 2331 idx := repo.GetIndex(schema.ClassName(class.Class)) 2332 shd, err := idx.determineObjectShard(fresh.ID, "") 2333 require.Nil(t, err) 2334 2335 received, err := idx.overwriteObjects(context.Background(), shd, input) 2336 assert.Nil(t, err) 2337 assert.ElementsMatch(t, nil, received) 2338 }) 2339 2340 t.Run("assert data was overwritten", func(t *testing.T) { 2341 found, err := repo.Object(context.Background(), stale.Class, 2342 stale.ID, nil, additional.Properties{}, nil, "") 2343 assert.Nil(t, err) 2344 assert.EqualValues(t, fresh, found.Object()) 2345 }) 2346 } 2347 2348 func TestIndexDigestObjects(t *testing.T) { 2349 dirName := t.TempDir() 2350 logger, _ := test.NewNullLogger() 2351 class := &models.Class{ 2352 VectorIndexConfig: enthnsw.NewDefaultUserConfig(), 2353 InvertedIndexConfig: invertedConfig(), 2354 Class: "SomeClass", 2355 Properties: []*models.Property{ 2356 { 2357 Name: "stringProp", 2358 DataType: schema.DataTypeText.PropString(), 2359 Tokenization: models.PropertyTokenizationWhitespace, 2360 }, 2361 }, 2362 } 2363 schemaGetter := &fakeSchemaGetter{ 2364 schema: schema.Schema{Objects: &models.Schema{Classes: nil}}, 2365 shardState: singleShardState(), 2366 } 2367 repo, err := New(logger, Config{ 2368 MemtablesFlushDirtyAfter: 60, 2369 RootPath: dirName, 2370 QueryMaximumResults: 10, 2371 MaxImportGoroutinesFactor: 1, 2372 }, &fakeRemoteClient{}, &fakeNodeResolver{}, 2373 &fakeRemoteNodeClient{}, &fakeReplicationClient{}, nil) 2374 require.Nil(t, err) 2375 repo.SetSchemaGetter(schemaGetter) 2376 require.Nil(t, repo.WaitForStartup(testCtx())) 2377 defer repo.Shutdown(context.Background()) 2378 migrator := NewMigrator(repo, logger) 2379 t.Run("create the class", func(t *testing.T) { 2380 require.Nil(t, 2381 migrator.AddClass(context.Background(), class, schemaGetter.shardState)) 2382 }) 2383 // update schema getter so it's in sync with class 2384 schemaGetter.schema = schema.Schema{ 2385 Objects: &models.Schema{ 2386 Classes: []*models.Class{class}, 2387 }, 2388 } 2389 2390 now := time.Now() 2391 later := now.Add(time.Hour) // time-traveling ;) 2392 obj1 := &models.Object{ 2393 ID: "ae48fda2-866a-4c90-94fc-fce40d5f3767", 2394 Class: class.Class, 2395 CreationTimeUnix: now.UnixMilli(), 2396 LastUpdateTimeUnix: now.UnixMilli(), 2397 Properties: map[string]interface{}{ 2398 "oldValue": "how things used to be", 2399 }, 2400 Vector: []float32{1, 2, 3}, 2401 VectorWeights: (map[string]string)(nil), 2402 Additional: models.AdditionalProperties{}, 2403 } 2404 2405 obj2 := &models.Object{ 2406 ID: "b71ffac8-6534-4368-9718-5410ca89ce16", 2407 Class: class.Class, 2408 CreationTimeUnix: later.UnixMilli(), 2409 LastUpdateTimeUnix: later.UnixMilli(), 2410 Properties: map[string]interface{}{ 2411 "oldValue": "how things used to be", 2412 }, 2413 Vector: []float32{1, 2, 3}, 2414 VectorWeights: (map[string]string)(nil), 2415 Additional: models.AdditionalProperties{}, 2416 } 2417 2418 t.Run("insert test objects", func(t *testing.T) { 2419 err := repo.PutObject(context.Background(), obj1, obj1.Vector, nil, nil) 2420 require.Nil(t, err) 2421 err = repo.PutObject(context.Background(), obj2, obj2.Vector, nil, nil) 2422 require.Nil(t, err) 2423 }) 2424 2425 t.Run("get digest object", func(t *testing.T) { 2426 idx := repo.GetIndex(schema.ClassName(class.Class)) 2427 shd, err := idx.determineObjectShard(obj1.ID, "") 2428 require.Nil(t, err) 2429 2430 input := []strfmt.UUID{obj1.ID, obj2.ID} 2431 2432 expected := []replica.RepairResponse{ 2433 { 2434 ID: obj1.ID.String(), 2435 UpdateTime: obj1.LastUpdateTimeUnix, 2436 }, 2437 { 2438 ID: obj2.ID.String(), 2439 UpdateTime: obj2.LastUpdateTimeUnix, 2440 }, 2441 } 2442 2443 res, err := idx.digestObjects(context.Background(), shd, input) 2444 require.Nil(t, err) 2445 assert.Equal(t, expected, res) 2446 }) 2447 } 2448 2449 func findID(list []search.Result, id strfmt.UUID) (search.Result, bool) { 2450 for _, item := range list { 2451 if item.ID == id { 2452 return item, true 2453 } 2454 } 2455 2456 return search.Result{}, false 2457 } 2458 2459 func ptFloat32(in float32) *float32 { 2460 return &in 2461 } 2462 2463 func ptFloat64(in float64) *float64 { 2464 return &in 2465 } 2466 2467 func randomVector(r *rand.Rand, dim int) []float32 { 2468 out := make([]float32, dim) 2469 for i := range out { 2470 out[i] = r.Float32() 2471 } 2472 2473 return out 2474 } 2475 2476 func TestIndexDifferentVectorLength(t *testing.T) { 2477 logger, _ := test.NewNullLogger() 2478 class := &models.Class{ 2479 VectorIndexConfig: enthnsw.NewDefaultUserConfig(), 2480 InvertedIndexConfig: invertedConfig(), 2481 Class: "SomeClass", 2482 Properties: []*models.Property{ 2483 { 2484 Name: "stringProp", 2485 DataType: schema.DataTypeText.PropString(), 2486 Tokenization: models.PropertyTokenizationWhitespace, 2487 }, 2488 }, 2489 } 2490 schemaGetter := &fakeSchemaGetter{ 2491 schema: schema.Schema{Objects: &models.Schema{Classes: nil}}, 2492 shardState: singleShardState(), 2493 } 2494 repo, err := New(logger, Config{ 2495 MemtablesFlushDirtyAfter: 60, 2496 RootPath: t.TempDir(), 2497 QueryMaximumResults: 10, 2498 MaxImportGoroutinesFactor: 1, 2499 }, &fakeRemoteClient{}, &fakeNodeResolver{}, 2500 &fakeRemoteNodeClient{}, &fakeReplicationClient{}, nil) 2501 require.Nil(t, err) 2502 repo.SetSchemaGetter(schemaGetter) 2503 require.Nil(t, repo.WaitForStartup(testCtx())) 2504 defer repo.Shutdown(context.Background()) 2505 migrator := NewMigrator(repo, logger) 2506 require.Nil(t, migrator.AddClass(context.Background(), class, schemaGetter.shardState)) 2507 // update schema getter so it's in sync with class 2508 schemaGetter.schema = schema.Schema{ 2509 Objects: &models.Schema{ 2510 Classes: []*models.Class{class}, 2511 }, 2512 } 2513 2514 obj1ID := strfmt.UUID("ae48fda2-866a-4c90-94fc-fce40d5f3767") 2515 objNilID := strfmt.UUID("b71ffac9-6534-4368-9718-5410ca89ce16") 2516 2517 t.Run("Add object with nil vector", func(t *testing.T) { 2518 objNil := &models.Object{ 2519 ID: objNilID, 2520 Class: class.Class, 2521 Vector: nil, 2522 } 2523 require.Nil(t, repo.PutObject(context.Background(), objNil, objNil.Vector, nil, nil)) 2524 found, err := repo.Object(context.Background(), class.Class, objNil.ID, nil, 2525 additional.Properties{}, nil, "") 2526 require.Nil(t, err) 2527 require.Equal(t, found.Vector, []float32{}) 2528 require.Equal(t, objNil.ID, found.ID) 2529 }) 2530 2531 t.Run("Add object with non-nil vector after nil vector", func(t *testing.T) { 2532 obj1 := &models.Object{ 2533 ID: obj1ID, 2534 Class: class.Class, 2535 Vector: []float32{1, 2, 3}, 2536 } 2537 require.Nil(t, repo.PutObject(context.Background(), obj1, obj1.Vector, nil, nil)) 2538 }) 2539 2540 t.Run("Add object with different vector length", func(t *testing.T) { 2541 obj2 := &models.Object{ 2542 ID: "b71ffac8-6534-4368-9718-5410ca89ce16", 2543 Class: class.Class, 2544 Vector: []float32{1, 2, 3, 4}, 2545 } 2546 require.NotNil(t, repo.PutObject(context.Background(), obj2, obj2.Vector, nil, nil)) 2547 found, err := repo.Object(context.Background(), class.Class, obj2.ID, nil, 2548 additional.Properties{}, nil, "") 2549 require.Nil(t, err) 2550 require.Nil(t, found) 2551 }) 2552 2553 t.Run("Update object with different vector length", func(t *testing.T) { 2554 err = repo.Merge(context.Background(), objects.MergeDocument{ 2555 ID: obj1ID, 2556 Class: class.Class, 2557 PrimitiveSchema: map[string]interface{}{}, 2558 Vector: []float32{1, 2, 3, 4}, 2559 UpdateTime: time.Now().UnixNano() / int64(time.Millisecond), 2560 }, nil, "") 2561 require.NotNil(t, err) 2562 found, err := repo.Object(context.Background(), class.Class, 2563 obj1ID, nil, additional.Properties{}, nil, "") 2564 require.Nil(t, err) 2565 require.Len(t, found.Vector, 3) 2566 }) 2567 2568 t.Run("Update nil object with fitting vector", func(t *testing.T) { 2569 err = repo.Merge(context.Background(), objects.MergeDocument{ 2570 ID: objNilID, 2571 Class: class.Class, 2572 PrimitiveSchema: map[string]interface{}{}, 2573 Vector: []float32{1, 2, 3}, 2574 UpdateTime: time.Now().UnixNano() / int64(time.Millisecond), 2575 }, nil, "") 2576 require.Nil(t, err) 2577 found, err := repo.Object(context.Background(), class.Class, objNilID, nil, 2578 additional.Properties{}, nil, "") 2579 require.Nil(t, err) 2580 require.Len(t, found.Vector, 3) 2581 }) 2582 2583 t.Run("Add nil object after objects with vector", func(t *testing.T) { 2584 obj2Nil := &models.Object{ 2585 ID: "b71ffac8-6534-4368-9718-5410ca89ce16", 2586 Class: class.Class, 2587 Vector: nil, 2588 } 2589 require.Nil(t, repo.PutObject(context.Background(), obj2Nil, obj2Nil.Vector, nil, nil)) 2590 found, err := repo.Object(context.Background(), class.Class, obj2Nil.ID, nil, 2591 additional.Properties{}, nil, "") 2592 require.Nil(t, err) 2593 require.Equal(t, obj2Nil.ID, found.ID) 2594 require.Equal(t, []float32{}, found.Vector) 2595 }) 2596 }