github.com/weaviate/weaviate@v1.24.6/adapters/repos/db/shard_skip_vector_reindex_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 // +build integrationTest 14 15 package db 16 17 import ( 18 "context" 19 "os" 20 "testing" 21 "time" 22 23 "github.com/go-openapi/strfmt" 24 "github.com/google/uuid" 25 "github.com/stretchr/testify/require" 26 "github.com/weaviate/weaviate/entities/additional" 27 "github.com/weaviate/weaviate/entities/filters" 28 "github.com/weaviate/weaviate/entities/models" 29 "github.com/weaviate/weaviate/entities/schema" 30 "github.com/weaviate/weaviate/entities/storobj" 31 "github.com/weaviate/weaviate/entities/vectorindex/common" 32 "github.com/weaviate/weaviate/entities/vectorindex/hnsw" 33 "github.com/weaviate/weaviate/usecases/objects" 34 ) 35 36 func TestShard_SkipVectorReindex(t *testing.T) { 37 ctx := context.Background() 38 39 uuid_ := strfmt.UUID(uuid.NewString()) 40 origCreateTimeUnix := int64(1704161045) 41 origUpdateTimeUnix := int64(1704161046) 42 updCreateTimeUnix := int64(1704161047) 43 updUpdateTimeUnix := int64(1704161048) 44 vector := []float32{1, 2, 3} 45 altVector := []float32{10, 0, -20} 46 47 class := &models.Class{ 48 Class: "TestClass", 49 InvertedIndexConfig: &models.InvertedIndexConfig{ 50 IndexTimestamps: true, 51 IndexNullState: true, 52 IndexPropertyLength: true, 53 }, 54 Properties: []*models.Property{ 55 { 56 Name: "texts", 57 DataType: schema.DataTypeTextArray.PropString(), 58 Tokenization: models.PropertyTokenizationWord, 59 }, 60 { 61 Name: "numbers", 62 DataType: schema.DataTypeNumberArray.PropString(), 63 }, 64 { 65 Name: "ints", 66 DataType: schema.DataTypeIntArray.PropString(), 67 }, 68 { 69 Name: "booleans", 70 DataType: schema.DataTypeBooleanArray.PropString(), 71 }, 72 { 73 Name: "dates", 74 DataType: schema.DataTypeDateArray.PropString(), 75 }, 76 { 77 Name: "uuids", 78 DataType: schema.DataTypeUUIDArray.PropString(), 79 }, 80 { 81 Name: "text", 82 DataType: schema.DataTypeText.PropString(), 83 Tokenization: models.PropertyTokenizationWord, 84 }, 85 { 86 Name: "number", 87 DataType: schema.DataTypeNumber.PropString(), 88 }, 89 { 90 Name: "int", 91 DataType: schema.DataTypeInt.PropString(), 92 }, 93 { 94 Name: "boolean", 95 DataType: schema.DataTypeBoolean.PropString(), 96 }, 97 { 98 Name: "date", 99 DataType: schema.DataTypeDate.PropString(), 100 }, 101 { 102 Name: "uuid", 103 DataType: schema.DataTypeUUID.PropString(), 104 }, 105 { 106 Name: "geo", 107 DataType: schema.DataTypeGeoCoordinates.PropString(), 108 }, 109 }, 110 } 111 112 createOrigObj := func() *storobj.Object { 113 return &storobj.Object{ 114 MarshallerVersion: 1, 115 Object: models.Object{ 116 ID: uuid_, 117 Class: class.Class, 118 Properties: map[string]interface{}{ 119 "texts": []string{ 120 "aaa", 121 "bbb", 122 "ccc", 123 }, 124 "numbers": []interface{}{}, 125 "ints": []float64{ 126 101, 101, 101, 101, 101, 101, 127 102, 128 103, 129 104, 130 }, 131 "booleans": []bool{ 132 true, true, true, 133 false, 134 }, 135 "dates": []time.Time{ 136 mustParseTime("2001-06-01T12:00:00.000000Z"), 137 mustParseTime("2002-06-02T12:00:00.000000Z"), 138 }, 139 // no uuids 140 "text": "ddd", 141 // no number 142 "int": float64(201), 143 "boolean": false, 144 "date": mustParseTime("2003-06-01T12:00:00.000000Z"), 145 // no uuid 146 "geo": &models.GeoCoordinates{ 147 Latitude: ptFloat32(1.1), 148 Longitude: ptFloat32(2.2), 149 }, 150 }, 151 CreationTimeUnix: origCreateTimeUnix, 152 LastUpdateTimeUnix: origUpdateTimeUnix, 153 }, 154 Vector: vector, 155 } 156 } 157 createUpdObj := func() *storobj.Object { 158 return &storobj.Object{ 159 MarshallerVersion: 1, 160 Object: models.Object{ 161 ID: uuid_, 162 Class: class.Class, 163 Properties: map[string]interface{}{ 164 "texts": []interface{}{}, 165 // no numbers 166 "ints": []float64{ 167 101, 101, 101, 101, 168 103, 169 104, 170 105, 171 }, 172 "booleans": []bool{ 173 true, true, true, 174 false, 175 }, 176 // no dates 177 "uuids": []uuid.UUID{ 178 uuid.MustParse("d726c960-aede-411c-85d3-2c77e9290a6e"), 179 }, 180 "text": "", 181 // no number 182 "int": float64(202), 183 "boolean": true, 184 // no date 185 "uuid": uuid.MustParse("7fabaf01-9e10-458a-acea-cc627376c506"), 186 "geo": &models.GeoCoordinates{ 187 Latitude: ptFloat32(1.1), 188 Longitude: ptFloat32(2.2), 189 }, 190 }, 191 CreationTimeUnix: updCreateTimeUnix, 192 LastUpdateTimeUnix: updUpdateTimeUnix, 193 }, 194 Vector: vector, 195 } 196 } 197 createMergeDoc := func() objects.MergeDocument { 198 return objects.MergeDocument{ 199 ID: uuid_, 200 Class: class.Class, 201 PrimitiveSchema: map[string]interface{}{ 202 "texts": []interface{}{}, 203 "ints": []interface{}{ 204 float64(101), float64(101), float64(101), float64(101), 205 float64(103), 206 float64(104), 207 float64(105), 208 }, 209 "uuids": []interface{}{ 210 uuid.MustParse("d726c960-aede-411c-85d3-2c77e9290a6e"), 211 }, 212 "text": "", 213 "int": float64(202), 214 "boolean": true, 215 "uuid": uuid.MustParse("7fabaf01-9e10-458a-acea-cc627376c506"), 216 }, 217 UpdateTime: updUpdateTimeUnix, 218 PropertiesToDelete: []string{"numbers", "dates", "date"}, 219 Vector: vector, 220 } 221 } 222 223 filterId := filterEqual[string](string(uuid_), schema.DataTypeText, class.Class, "_id") 224 225 filterTextsEqAAA := filterEqual[string]("aaa", schema.DataTypeText, class.Class, "texts") 226 filterTextsLen3 := filterEqual[int](3, schema.DataTypeInt, class.Class, "len(texts)") 227 filterTextsLen0 := filterEqual[int](0, schema.DataTypeInt, class.Class, "len(texts)") 228 filterTextsNotNil := filterNil(false, class.Class, "texts") 229 filterTextsNil := filterNil(true, class.Class, "texts") 230 231 filterNumbersEq123 := filterEqual[float64](1.23, schema.DataTypeNumber, class.Class, "numbers") 232 filterNumbersLen1 := filterEqual[int](1, schema.DataTypeInt, class.Class, "len(numbers)") 233 filterNumbersLen0 := filterEqual[int](0, schema.DataTypeInt, class.Class, "len(numbers)") 234 filterNumbersNotNil := filterNil(false, class.Class, "numbers") 235 filterNumbersNil := filterNil(true, class.Class, "numbers") 236 237 filterIntsEq102 := filterEqual[int](102, schema.DataTypeInt, class.Class, "ints") 238 filterIntsEq105 := filterEqual[int](105, schema.DataTypeInt, class.Class, "ints") 239 filterIntsLen9 := filterEqual[int](9, schema.DataTypeInt, class.Class, "len(ints)") 240 filterIntsLen7 := filterEqual[int](7, schema.DataTypeInt, class.Class, "len(ints)") 241 filterIntsNotNil := filterNil(false, class.Class, "ints") 242 filterIntsNil := filterNil(true, class.Class, "ints") 243 244 filterBoolsEqTrue := filterEqual[bool](true, schema.DataTypeBoolean, class.Class, "booleans") 245 filterBoolsEqFalse := filterEqual[bool](false, schema.DataTypeBoolean, class.Class, "booleans") 246 filterBoolsLen4 := filterEqual[int](4, schema.DataTypeInt, class.Class, "len(booleans)") 247 filterBoolsLen0 := filterEqual[int](0, schema.DataTypeInt, class.Class, "len(booleans)") 248 filterBoolsNotNil := filterNil(false, class.Class, "booleans") 249 filterBoolsNil := filterNil(true, class.Class, "booleans") 250 251 filterDatesEq2001 := filterEqual[string]("2001-06-01T12:00:00.000000Z", schema.DataTypeDate, class.Class, "dates") 252 filterDatesLen2 := filterEqual[int](2, schema.DataTypeInt, class.Class, "len(dates)") 253 filterDatesLen0 := filterEqual[int](0, schema.DataTypeInt, class.Class, "len(dates)") 254 filterDatesNotNil := filterNil(false, class.Class, "dates") 255 filterDatesNil := filterNil(true, class.Class, "dates") 256 257 filterUuidsEqD726 := filterEqual[string]("d726c960-aede-411c-85d3-2c77e9290a6e", schema.DataTypeText, class.Class, "uuids") 258 filterUuidsLen1 := filterEqual[int](1, schema.DataTypeInt, class.Class, "len(uuids)") 259 filterUuidsLen0 := filterEqual[int](0, schema.DataTypeInt, class.Class, "len(uuids)") 260 filterUuidsNotNil := filterNil(false, class.Class, "uuids") 261 filterUuidsNil := filterNil(true, class.Class, "uuids") 262 263 filterTextEqDDD := filterEqual[string]("ddd", schema.DataTypeText, class.Class, "text") 264 filterTextLen3 := filterEqual[int](3, schema.DataTypeInt, class.Class, "len(text)") 265 filterTextLen0 := filterEqual[int](0, schema.DataTypeInt, class.Class, "len(text)") 266 filterTextNotNil := filterNil(false, class.Class, "text") 267 filterTextNil := filterNil(true, class.Class, "text") 268 269 filterNumberEq123 := filterEqual[float64](1.23, schema.DataTypeNumber, class.Class, "number") 270 filterNumberNotNil := filterNil(false, class.Class, "number") 271 filterNumberNil := filterNil(true, class.Class, "number") 272 273 filterIntEq201 := filterEqual[int](201, schema.DataTypeInt, class.Class, "int") 274 filterIntEq202 := filterEqual[int](202, schema.DataTypeInt, class.Class, "int") 275 filterIntNotNil := filterNil(false, class.Class, "int") 276 filterIntNil := filterNil(true, class.Class, "int") 277 278 filterBoolEqFalse := filterEqual[bool](false, schema.DataTypeBoolean, class.Class, "boolean") 279 filterBoolEqTrue := filterEqual[bool](true, schema.DataTypeBoolean, class.Class, "boolean") 280 filterBoolNotNil := filterNil(false, class.Class, "boolean") 281 filterBoolNil := filterNil(true, class.Class, "boolean") 282 283 filterDateEq2003 := filterEqual[string]("2003-06-01T12:00:00.000000Z", schema.DataTypeDate, class.Class, "date") 284 filterDateNotNil := filterNil(false, class.Class, "date") 285 filterDateNil := filterNil(true, class.Class, "date") 286 287 filterUuidEq7FAB := filterEqual[string]("7fabaf01-9e10-458a-acea-cc627376c506", schema.DataTypeText, class.Class, "uuid") 288 filterUuidNotNil := filterNil(false, class.Class, "uuid") 289 filterUuidNil := filterNil(true, class.Class, "uuid") 290 291 search := func(t *testing.T, shard ShardLike, filter *filters.LocalFilter) []*storobj.Object { 292 searchLimit := 10 293 found, _, err := shard.ObjectSearch(ctx, searchLimit, filter, 294 nil, nil, nil, additional.Properties{}) 295 require.NoError(t, err) 296 return found 297 } 298 299 verifySearchAfterAdd := func(shard ShardLike) func(t *testing.T) { 300 return func(t *testing.T) { 301 t.Run("to be found", func(t *testing.T) { 302 for name, filter := range map[string]*filters.LocalFilter{ 303 "id": filterId, 304 305 "textsEqAAA": filterTextsEqAAA, 306 "textsLen3": filterTextsLen3, 307 "textsNotNil": filterTextsNotNil, 308 309 "numbersLen0": filterNumbersLen0, 310 "numbersNil": filterNumbersNil, 311 312 "intsEq102": filterIntsEq102, 313 "intsLen9": filterIntsLen9, 314 "intsNotNil": filterIntsNotNil, 315 316 "boolsEqTrue": filterBoolsEqTrue, 317 "boolsEqFalse": filterBoolsEqFalse, 318 "boolsLen4": filterBoolsLen4, 319 "boolsNotNil": filterBoolsNotNil, 320 321 "datesEq2001": filterDatesEq2001, 322 "datesLen2": filterDatesLen2, 323 "datesNotNil": filterDatesNotNil, 324 325 "uuidsLen0": filterUuidsLen0, 326 "uuidsNil": filterUuidsNil, 327 328 "textEqDDD": filterTextEqDDD, 329 "textLen3": filterTextLen3, 330 "textNotNil": filterTextNotNil, 331 332 "numberNil": filterNumberNil, 333 334 "intEq201": filterIntEq201, 335 "intNotNil": filterIntNotNil, 336 337 "boolEqFalse": filterBoolEqFalse, 338 "boolNotNil": filterBoolNotNil, 339 340 "dateEq2003": filterDateEq2003, 341 "dateNotNil": filterDateNotNil, 342 343 "uuidNil": filterUuidNil, 344 } { 345 t.Run(name, func(t *testing.T) { 346 found := search(t, shard, filter) 347 require.Len(t, found, 1) 348 require.Equal(t, uuid_, found[0].Object.ID) 349 }) 350 } 351 }) 352 353 t.Run("not to be found", func(t *testing.T) { 354 for name, filter := range map[string]*filters.LocalFilter{ 355 "textsLen0": filterTextsLen0, 356 "textsNil": filterTextsNil, 357 358 "numbersEq123": filterNumbersEq123, 359 "numbersLen1": filterNumbersLen1, 360 "numbersNotNil": filterNumbersNotNil, 361 362 "intsEq105": filterIntsEq105, 363 "intsLen7": filterIntsLen7, 364 "intsNil": filterIntsNil, 365 366 "boolsLen0": filterBoolsLen0, 367 "boolsNil": filterBoolsNil, 368 369 "datesLen0": filterDatesLen0, 370 "datesNil": filterDatesNil, 371 372 "uuidsEqD726": filterUuidsEqD726, 373 "uuidsLen1": filterUuidsLen1, 374 "uuidsNotNil": filterUuidsNotNil, 375 376 "textLen0": filterTextLen0, 377 "textNil": filterTextNil, 378 379 "numberEq123": filterNumberEq123, 380 "numberNotNil": filterNumberNotNil, 381 382 "intEq202": filterIntEq202, 383 "intNil": filterIntNil, 384 385 "boolEqTrue": filterBoolEqTrue, 386 "boolNil": filterBoolNil, 387 388 "dateNil": filterDateNil, 389 390 "uuidEq7FAB": filterUuidEq7FAB, 391 "uuidNotNil": filterUuidNotNil, 392 } { 393 t.Run(name, func(t *testing.T) { 394 found := search(t, shard, filter) 395 require.Len(t, found, 0) 396 }) 397 } 398 }) 399 } 400 } 401 verifySearchAfterUpdate := func(shard ShardLike) func(t *testing.T) { 402 return func(t *testing.T) { 403 t.Run("to be found", func(t *testing.T) { 404 for name, filter := range map[string]*filters.LocalFilter{ 405 "id": filterId, 406 407 "textsLen0": filterTextsLen0, 408 "textsNil": filterTextsNil, 409 410 "numbersLen0": filterNumbersLen0, 411 "numbersNil": filterNumbersNil, 412 413 "intsEq105": filterIntsEq105, 414 "intsLen7": filterIntsLen7, 415 "intsNotNil": filterIntsNotNil, 416 417 "boolsEqTrue": filterBoolsEqTrue, 418 "boolsEqFalse": filterBoolsEqFalse, 419 "boolsLen4": filterBoolsLen4, 420 "boolsNotNil": filterBoolsNotNil, 421 422 "datesLen0": filterDatesLen0, 423 "datesNil": filterDatesNil, 424 425 "uuidsEqD726": filterUuidsEqD726, 426 "uuidsLen1": filterUuidsLen1, 427 "uuidsNotNil": filterUuidsNotNil, 428 429 "textLen0": filterTextLen0, 430 "textNil": filterTextNil, 431 432 "numberNil": filterNumberNil, 433 434 "intEq202": filterIntEq202, 435 "intNotNil": filterIntNotNil, 436 437 "boolEqTrue": filterBoolEqTrue, 438 "boolNotNil": filterBoolNotNil, 439 440 "dateNil": filterDateNil, 441 442 "uuidEq7FAB": filterUuidEq7FAB, 443 "uuidNotNil": filterUuidNotNil, 444 } { 445 t.Run(name, func(t *testing.T) { 446 found := search(t, shard, filter) 447 require.Len(t, found, 1) 448 require.Equal(t, uuid_, found[0].Object.ID) 449 }) 450 } 451 }) 452 453 t.Run("not to be found", func(t *testing.T) { 454 for name, filter := range map[string]*filters.LocalFilter{ 455 "textsEqAAA": filterTextsEqAAA, 456 "textsLen3": filterTextsLen3, 457 "textsNotNil": filterTextsNotNil, 458 459 "numbersEq123": filterNumbersEq123, 460 "numbersLen1": filterNumbersLen1, 461 "numbersNotNil": filterNumbersNotNil, 462 463 "intsEq102": filterIntsEq102, 464 "intsLen9": filterIntsLen9, 465 "intsNil": filterIntsNil, 466 467 "boolsLen0": filterBoolsLen0, 468 "boolsNil": filterBoolsNil, 469 470 "datesEq2001": filterDatesEq2001, 471 "datesLen2": filterDatesLen2, 472 "datesNotNil": filterDatesNotNil, 473 474 "uuidsLen0": filterUuidsLen0, 475 "uuidsNil": filterUuidsNil, 476 477 "textEqDDD": filterTextEqDDD, 478 "textLen3": filterTextLen3, 479 "textNotNil": filterTextNotNil, 480 481 "numberEq123": filterNumberEq123, 482 "numberNotNil": filterNumberNotNil, 483 484 "intEq201": filterIntEq201, 485 "intNil": filterIntNil, 486 487 "boolEqFalse": filterBoolEqFalse, 488 "boolNil": filterBoolNil, 489 490 "dateEq2003": filterDateEq2003, 491 "dateNotNil": filterDateNotNil, 492 493 "uuidNil": filterUuidNil, 494 } { 495 t.Run(name, func(t *testing.T) { 496 found := search(t, shard, filter) 497 require.Len(t, found, 0) 498 }) 499 } 500 }) 501 } 502 } 503 verifyVectorSearch := func(shard ShardLike, vectorToBeFound, vectorNotToBeFound []float32) func(t *testing.T) { 504 vectorSearchLimit := -1 // negative to limit results by distance 505 vectorSearchDist := float32(1) 506 targetVector := "" 507 508 return func(t *testing.T) { 509 t.Run("to be found", func(t *testing.T) { 510 found, _, err := shard.ObjectVectorSearch(ctx, vectorToBeFound, targetVector, 511 vectorSearchDist, vectorSearchLimit, nil, nil, nil, additional.Properties{}) 512 require.NoError(t, err) 513 require.Len(t, found, 1) 514 require.Equal(t, uuid_, found[0].Object.ID) 515 }) 516 517 t.Run("not to be found", func(t *testing.T) { 518 found, _, err := shard.ObjectVectorSearch(ctx, vectorNotToBeFound, targetVector, 519 vectorSearchDist, vectorSearchLimit, nil, nil, nil, additional.Properties{}) 520 require.NoError(t, err) 521 require.Len(t, found, 0) 522 }) 523 } 524 } 525 526 createShard := func(t *testing.T) ShardLike { 527 vectorIndexConfig := hnsw.UserConfig{Distance: common.DefaultDistanceMetric} 528 shard, _ := testShardWithSettings(t, ctx, class, vectorIndexConfig, true, true) 529 return shard 530 } 531 532 t.Run("single object", func(t *testing.T) { 533 t.Run("sanity check - search after add", func(t *testing.T) { 534 shard := createShard(t) 535 536 t.Run("add object", func(t *testing.T) { 537 err := shard.PutObject(ctx, createOrigObj()) 538 require.NoError(t, err) 539 }) 540 541 t.Run("verify initial docID and timestamps", func(t *testing.T) { 542 expectedNextDocID := uint64(1) 543 require.Equal(t, expectedNextDocID, shard.Counter().Get()) 544 545 found := search(t, shard, filterId) 546 require.Len(t, found, 1) 547 require.Equal(t, origCreateTimeUnix, found[0].CreationTimeUnix()) 548 require.Equal(t, origUpdateTimeUnix, found[0].LastUpdateTimeUnix()) 549 }) 550 551 t.Run("verify search after add", verifySearchAfterAdd(shard)) 552 t.Run("verify vector search after add", verifyVectorSearch(shard, vector, altVector)) 553 }) 554 555 t.Run("replace with different object, same vector", func(t *testing.T) { 556 shard := createShard(t) 557 558 t.Run("add object", func(t *testing.T) { 559 err := shard.PutObject(ctx, createOrigObj()) 560 require.NoError(t, err) 561 }) 562 563 t.Run("put object", func(t *testing.T) { 564 updObj := createUpdObj() 565 566 err := shard.PutObject(ctx, updObj) 567 require.NoError(t, err) 568 }) 569 570 t.Run("verify same docID, changed create & update timestamps", func(t *testing.T) { 571 expectedNextDocID := uint64(1) 572 require.Equal(t, expectedNextDocID, shard.Counter().Get()) 573 574 found := search(t, shard, filterId) 575 require.Len(t, found, 1) 576 require.Equal(t, updCreateTimeUnix, found[0].CreationTimeUnix()) 577 require.Equal(t, updUpdateTimeUnix, found[0].LastUpdateTimeUnix()) 578 }) 579 580 t.Run("verify search after put", verifySearchAfterUpdate(shard)) 581 t.Run("verify vector search after put", verifyVectorSearch(shard, vector, altVector)) 582 }) 583 584 t.Run("replace with different object, different vector", func(t *testing.T) { 585 shard := createShard(t) 586 587 t.Run("add object", func(t *testing.T) { 588 err := shard.PutObject(ctx, createOrigObj()) 589 require.NoError(t, err) 590 }) 591 592 t.Run("put object", func(t *testing.T) { 593 // overwrite vector in updated object 594 altUpdObj := createUpdObj() 595 altUpdObj.Vector = altVector 596 597 err := shard.PutObject(ctx, altUpdObj) 598 require.NoError(t, err) 599 }) 600 601 t.Run("verify changed docID, changed create & update timestamps", func(t *testing.T) { 602 expectedNextDocID := uint64(2) 603 require.Equal(t, expectedNextDocID, shard.Counter().Get()) 604 605 found := search(t, shard, filterId) 606 require.Len(t, found, 1) 607 require.Equal(t, updCreateTimeUnix, found[0].CreationTimeUnix()) 608 require.Equal(t, updUpdateTimeUnix, found[0].LastUpdateTimeUnix()) 609 }) 610 611 t.Run("verify search after put", verifySearchAfterUpdate(shard)) 612 t.Run("verify vector search after put", verifyVectorSearch(shard, altVector, vector)) 613 }) 614 615 t.Run("replace with different object, different geo", func(t *testing.T) { 616 shard := createShard(t) 617 618 t.Run("add object", func(t *testing.T) { 619 err := shard.PutObject(ctx, createOrigObj()) 620 require.NoError(t, err) 621 }) 622 623 t.Run("put object", func(t *testing.T) { 624 // overwrite geo in updated object 625 altUpdObj := createUpdObj() 626 altUpdObj.Object.Properties.(map[string]interface{})["geo"] = &models.GeoCoordinates{ 627 Latitude: ptFloat32(3.3), 628 Longitude: ptFloat32(4.4), 629 } 630 631 err := shard.PutObject(ctx, altUpdObj) 632 require.NoError(t, err) 633 }) 634 635 t.Run("verify changed docID, changed create & update timestamps", func(t *testing.T) { 636 expectedNextDocID := uint64(2) 637 require.Equal(t, expectedNextDocID, shard.Counter().Get()) 638 639 found := search(t, shard, filterId) 640 require.Len(t, found, 1) 641 require.Equal(t, updCreateTimeUnix, found[0].CreationTimeUnix()) 642 require.Equal(t, updUpdateTimeUnix, found[0].LastUpdateTimeUnix()) 643 }) 644 645 t.Run("verify search after put", verifySearchAfterUpdate(shard)) 646 t.Run("verify vector search after put", verifyVectorSearch(shard, vector, altVector)) 647 }) 648 649 t.Run("merge with different object, same vector", func(t *testing.T) { 650 shard := createShard(t) 651 652 t.Run("add object", func(t *testing.T) { 653 err := shard.PutObject(ctx, createOrigObj()) 654 require.NoError(t, err) 655 }) 656 657 t.Run("merge object", func(t *testing.T) { 658 mergeDoc := createMergeDoc() 659 660 err := shard.MergeObject(ctx, mergeDoc) 661 require.NoError(t, err) 662 }) 663 664 t.Run("verify same docID, changed update timestamp", func(t *testing.T) { 665 expectedNextDocID := uint64(1) 666 require.Equal(t, expectedNextDocID, shard.Counter().Get()) 667 668 found := search(t, shard, filterId) 669 require.Len(t, found, 1) 670 require.Equal(t, origCreateTimeUnix, found[0].CreationTimeUnix()) 671 require.Equal(t, updUpdateTimeUnix, found[0].LastUpdateTimeUnix()) 672 }) 673 674 t.Run("verify search after merge", verifySearchAfterUpdate(shard)) 675 t.Run("verify vector search after merge", verifyVectorSearch(shard, vector, altVector)) 676 }) 677 678 t.Run("merge with different object, different vector", func(t *testing.T) { 679 shard := createShard(t) 680 681 t.Run("add object", func(t *testing.T) { 682 err := shard.PutObject(ctx, createOrigObj()) 683 require.NoError(t, err) 684 }) 685 686 t.Run("merge object", func(t *testing.T) { 687 // overwrite vector in merge doc 688 altMergeDoc := createMergeDoc() 689 altMergeDoc.Vector = altVector 690 691 err := shard.MergeObject(ctx, altMergeDoc) 692 require.NoError(t, err) 693 }) 694 695 t.Run("verify changed docID, changed update timestamp", func(t *testing.T) { 696 expectedNextDocID := uint64(2) 697 require.Equal(t, expectedNextDocID, shard.Counter().Get()) 698 699 found := search(t, shard, filterId) 700 require.Len(t, found, 1) 701 require.Equal(t, origCreateTimeUnix, found[0].CreationTimeUnix()) 702 require.Equal(t, updUpdateTimeUnix, found[0].LastUpdateTimeUnix()) 703 }) 704 705 t.Run("verify search after merge", verifySearchAfterUpdate(shard)) 706 t.Run("verify vector search after merge", verifyVectorSearch(shard, altVector, vector)) 707 }) 708 709 t.Run("merge with different object, different geo", func(t *testing.T) { 710 shard := createShard(t) 711 712 t.Run("add object", func(t *testing.T) { 713 err := shard.PutObject(ctx, createOrigObj()) 714 require.NoError(t, err) 715 }) 716 717 t.Run("merge object", func(t *testing.T) { 718 // overwrite geo in merge doc 719 mergeDoc := createMergeDoc() 720 mergeDoc.PrimitiveSchema["geo"] = &models.GeoCoordinates{ 721 Latitude: ptFloat32(3.3), 722 Longitude: ptFloat32(4.4), 723 } 724 725 err := shard.MergeObject(ctx, mergeDoc) 726 require.NoError(t, err) 727 }) 728 729 t.Run("verify changed docID, changed update timestamp", func(t *testing.T) { 730 expectedNextDocID := uint64(2) 731 require.Equal(t, expectedNextDocID, shard.Counter().Get()) 732 733 found := search(t, shard, filterId) 734 require.Len(t, found, 1) 735 require.Equal(t, origCreateTimeUnix, found[0].CreationTimeUnix()) 736 require.Equal(t, updUpdateTimeUnix, found[0].LastUpdateTimeUnix()) 737 }) 738 739 t.Run("verify search after merge", verifySearchAfterUpdate(shard)) 740 t.Run("verify vector search after merge", verifyVectorSearch(shard, vector, altVector)) 741 }) 742 743 t.Run("replace with same object, same vector", func(t *testing.T) { 744 shard := createShard(t) 745 746 t.Run("add object", func(t *testing.T) { 747 err := shard.PutObject(ctx, createOrigObj()) 748 require.NoError(t, err) 749 }) 750 751 t.Run("put object", func(t *testing.T) { 752 // overwrite timestamps in original object 753 updObj := createOrigObj() 754 updObj.Object.CreationTimeUnix = updCreateTimeUnix 755 updObj.Object.LastUpdateTimeUnix = updUpdateTimeUnix 756 757 err := shard.PutObject(ctx, updObj) 758 require.NoError(t, err) 759 }) 760 761 t.Run("verify same docID, same timestamps", func(t *testing.T) { 762 expectedNextDocID := uint64(1) 763 require.Equal(t, expectedNextDocID, shard.Counter().Get()) 764 765 found := search(t, shard, filterId) 766 require.Len(t, found, 1) 767 require.Equal(t, origCreateTimeUnix, found[0].CreationTimeUnix()) 768 require.Equal(t, origUpdateTimeUnix, found[0].LastUpdateTimeUnix()) 769 }) 770 771 t.Run("verify search after put same as add", verifySearchAfterAdd(shard)) 772 t.Run("verify vector search after put", verifyVectorSearch(shard, vector, altVector)) 773 }) 774 775 t.Run("replace with same object, different vector", func(t *testing.T) { 776 shard := createShard(t) 777 778 t.Run("add object", func(t *testing.T) { 779 err := shard.PutObject(ctx, createOrigObj()) 780 require.NoError(t, err) 781 }) 782 783 t.Run("put object", func(t *testing.T) { 784 // overwrite timestamps and vector in original object 785 altUpdObj := createOrigObj() 786 altUpdObj.Object.CreationTimeUnix = updCreateTimeUnix 787 altUpdObj.Object.LastUpdateTimeUnix = updUpdateTimeUnix 788 altUpdObj.Vector = altVector 789 790 err := shard.PutObject(ctx, altUpdObj) 791 require.NoError(t, err) 792 }) 793 794 t.Run("verify changed docID, changed create & update timestamps", func(t *testing.T) { 795 expectedNextDocID := uint64(2) 796 require.Equal(t, expectedNextDocID, shard.Counter().Get()) 797 798 found := search(t, shard, filterId) 799 require.Len(t, found, 1) 800 require.Equal(t, updCreateTimeUnix, found[0].CreationTimeUnix()) 801 require.Equal(t, updUpdateTimeUnix, found[0].LastUpdateTimeUnix()) 802 }) 803 804 t.Run("verify search after put same as add", verifySearchAfterAdd(shard)) 805 t.Run("verify vector search after put", verifyVectorSearch(shard, altVector, vector)) 806 }) 807 808 t.Run("replace with same object, different geo", func(t *testing.T) { 809 shard := createShard(t) 810 811 t.Run("add object", func(t *testing.T) { 812 err := shard.PutObject(ctx, createOrigObj()) 813 require.NoError(t, err) 814 }) 815 816 t.Run("put object", func(t *testing.T) { 817 // overwrite timestamps and geo in original object 818 updObj := createOrigObj() 819 updObj.Object.CreationTimeUnix = updCreateTimeUnix 820 updObj.Object.LastUpdateTimeUnix = updUpdateTimeUnix 821 updObj.Object.Properties.(map[string]interface{})["geo"] = &models.GeoCoordinates{ 822 Latitude: ptFloat32(3.3), 823 Longitude: ptFloat32(4.4), 824 } 825 826 err := shard.PutObject(ctx, updObj) 827 require.NoError(t, err) 828 }) 829 830 t.Run("verify changed docID, changed create & update timestamps", func(t *testing.T) { 831 expectedNextDocID := uint64(2) 832 require.Equal(t, expectedNextDocID, shard.Counter().Get()) 833 834 found := search(t, shard, filterId) 835 require.Len(t, found, 1) 836 require.Equal(t, updCreateTimeUnix, found[0].CreationTimeUnix()) 837 require.Equal(t, updUpdateTimeUnix, found[0].LastUpdateTimeUnix()) 838 }) 839 840 t.Run("verify search after put same as add", verifySearchAfterAdd(shard)) 841 t.Run("verify vector search after put", verifyVectorSearch(shard, vector, altVector)) 842 }) 843 844 t.Run("merge with same object, same vector", func(t *testing.T) { 845 shard := createShard(t) 846 847 t.Run("add object", func(t *testing.T) { 848 err := shard.PutObject(ctx, createOrigObj()) 849 require.NoError(t, err) 850 }) 851 852 t.Run("merge object", func(t *testing.T) { 853 // same values as in original object 854 mergeDoc := objects.MergeDocument{ 855 ID: uuid_, 856 Class: class.Class, 857 PrimitiveSchema: map[string]interface{}{ 858 "int": float64(201), 859 "text": "ddd", 860 }, 861 UpdateTime: updUpdateTimeUnix, 862 Vector: vector, 863 } 864 865 err := shard.MergeObject(ctx, mergeDoc) 866 require.NoError(t, err) 867 }) 868 869 t.Run("verify same docID, same timestamps", func(t *testing.T) { 870 expectedNextDocID := uint64(1) 871 require.Equal(t, expectedNextDocID, shard.Counter().Get()) 872 873 found := search(t, shard, filterId) 874 require.Len(t, found, 1) 875 require.Equal(t, origCreateTimeUnix, found[0].CreationTimeUnix()) 876 require.Equal(t, origUpdateTimeUnix, found[0].LastUpdateTimeUnix()) 877 }) 878 879 t.Run("verify search after merge same as add", verifySearchAfterAdd(shard)) 880 t.Run("verify vector search after merge", verifyVectorSearch(shard, vector, altVector)) 881 }) 882 883 t.Run("merge with same object, different vector", func(t *testing.T) { 884 shard := createShard(t) 885 886 t.Run("add object", func(t *testing.T) { 887 err := shard.PutObject(ctx, createOrigObj()) 888 require.NoError(t, err) 889 }) 890 891 t.Run("merge object", func(t *testing.T) { 892 // same props as in original object, overwrite timestamp and vector 893 altMergeDoc := objects.MergeDocument{ 894 ID: uuid_, 895 Class: class.Class, 896 PrimitiveSchema: map[string]interface{}{ 897 "int": float64(201), 898 "text": "ddd", 899 }, 900 UpdateTime: updUpdateTimeUnix, 901 Vector: altVector, 902 } 903 904 err := shard.MergeObject(ctx, altMergeDoc) 905 require.NoError(t, err) 906 }) 907 908 t.Run("verify changed docID, changed update timestamp", func(t *testing.T) { 909 expectedNextDocID := uint64(2) 910 require.Equal(t, expectedNextDocID, shard.Counter().Get()) 911 912 found := search(t, shard, filterId) 913 require.Len(t, found, 1) 914 require.Equal(t, origCreateTimeUnix, found[0].CreationTimeUnix()) 915 require.Equal(t, updUpdateTimeUnix, found[0].LastUpdateTimeUnix()) 916 }) 917 918 t.Run("verify search after merge same as add", verifySearchAfterAdd(shard)) 919 t.Run("verify vector search after merge", verifyVectorSearch(shard, altVector, vector)) 920 }) 921 922 t.Run("merge with same object, different geo", func(t *testing.T) { 923 shard := createShard(t) 924 925 t.Run("add object", func(t *testing.T) { 926 err := shard.PutObject(ctx, createOrigObj()) 927 require.NoError(t, err) 928 }) 929 930 t.Run("merge object", func(t *testing.T) { 931 // overwrite geo and timestamp 932 mergeDoc := objects.MergeDocument{ 933 ID: uuid_, 934 Class: class.Class, 935 PrimitiveSchema: map[string]interface{}{ 936 "geo": &models.GeoCoordinates{ 937 Latitude: ptFloat32(3.3), 938 Longitude: ptFloat32(4.4), 939 }, 940 }, 941 UpdateTime: updUpdateTimeUnix, 942 } 943 944 err := shard.MergeObject(ctx, mergeDoc) 945 require.NoError(t, err) 946 }) 947 948 t.Run("verify changed docID, changed update timestamp", func(t *testing.T) { 949 expectedNextDocID := uint64(2) 950 require.Equal(t, expectedNextDocID, shard.Counter().Get()) 951 952 found := search(t, shard, filterId) 953 require.Len(t, found, 1) 954 require.Equal(t, origCreateTimeUnix, found[0].CreationTimeUnix()) 955 require.Equal(t, updUpdateTimeUnix, found[0].LastUpdateTimeUnix()) 956 }) 957 958 t.Run("verify search after merge same as add", verifySearchAfterAdd(shard)) 959 t.Run("verify vector search after merge", verifyVectorSearch(shard, vector, altVector)) 960 }) 961 }) 962 963 t.Run("batch", func(t *testing.T) { 964 runBatch := func(t *testing.T) { 965 t.Run("sanity check - search after add", func(t *testing.T) { 966 shard := createShard(t) 967 968 t.Run("add batch", func(t *testing.T) { 969 errs := shard.PutObjectBatch(ctx, []*storobj.Object{createOrigObj()}) 970 for i := range errs { 971 require.NoError(t, errs[i]) 972 } 973 }) 974 975 t.Run("verify initial docID and timestamps", func(t *testing.T) { 976 expectedNextDocID := uint64(1) 977 require.Equal(t, expectedNextDocID, shard.Counter().Get()) 978 979 found := search(t, shard, filterId) 980 require.Len(t, found, 1) 981 require.Equal(t, origCreateTimeUnix, found[0].CreationTimeUnix()) 982 require.Equal(t, origUpdateTimeUnix, found[0].LastUpdateTimeUnix()) 983 }) 984 985 t.Run("verify search after batch", verifySearchAfterAdd(shard)) 986 t.Run("verify vector search after batch", verifyVectorSearch(shard, vector, altVector)) 987 }) 988 989 t.Run("replace with different object, same vector", func(t *testing.T) { 990 shard := createShard(t) 991 992 t.Run("add batch", func(t *testing.T) { 993 errs := shard.PutObjectBatch(ctx, []*storobj.Object{createOrigObj()}) 994 for i := range errs { 995 require.NoError(t, errs[i]) 996 } 997 }) 998 999 t.Run("add 2nd batch", func(t *testing.T) { 1000 updObj := createUpdObj() 1001 1002 errs := shard.PutObjectBatch(ctx, []*storobj.Object{updObj}) 1003 for i := range errs { 1004 require.NoError(t, errs[i]) 1005 } 1006 }) 1007 1008 t.Run("verify same docID, changed create & update timestamps", func(t *testing.T) { 1009 expectedNextDocID := uint64(1) 1010 require.Equal(t, expectedNextDocID, shard.Counter().Get()) 1011 1012 found := search(t, shard, filterId) 1013 require.Len(t, found, 1) 1014 require.Equal(t, updCreateTimeUnix, found[0].CreationTimeUnix()) 1015 require.Equal(t, updUpdateTimeUnix, found[0].LastUpdateTimeUnix()) 1016 }) 1017 1018 t.Run("verify search after 2nd batch", verifySearchAfterUpdate(shard)) 1019 t.Run("verify vector search after 2nd batch", verifyVectorSearch(shard, vector, altVector)) 1020 }) 1021 1022 t.Run("replace with different object, different vector", func(t *testing.T) { 1023 shard := createShard(t) 1024 1025 t.Run("add batch", func(t *testing.T) { 1026 errs := shard.PutObjectBatch(ctx, []*storobj.Object{createOrigObj()}) 1027 for i := range errs { 1028 require.NoError(t, errs[i]) 1029 } 1030 }) 1031 1032 t.Run("add 2nd batch", func(t *testing.T) { 1033 // overwrite vector in updated object 1034 altUpdObj := createUpdObj() 1035 altUpdObj.Vector = altVector 1036 1037 errs := shard.PutObjectBatch(ctx, []*storobj.Object{altUpdObj}) 1038 for i := range errs { 1039 require.NoError(t, errs[i]) 1040 } 1041 }) 1042 1043 t.Run("verify changed docID, changed create & update timestamps", func(t *testing.T) { 1044 expectedNextDocID := uint64(2) 1045 require.Equal(t, expectedNextDocID, shard.Counter().Get()) 1046 1047 found := search(t, shard, filterId) 1048 require.Len(t, found, 1) 1049 require.Equal(t, updCreateTimeUnix, found[0].CreationTimeUnix()) 1050 require.Equal(t, updUpdateTimeUnix, found[0].LastUpdateTimeUnix()) 1051 }) 1052 1053 t.Run("verify search after 2nd batch", verifySearchAfterUpdate(shard)) 1054 t.Run("verify vector search after 2nd batch", verifyVectorSearch(shard, altVector, vector)) 1055 }) 1056 1057 t.Run("replace with different object, different geo", func(t *testing.T) { 1058 shard := createShard(t) 1059 1060 t.Run("add batch", func(t *testing.T) { 1061 errs := shard.PutObjectBatch(ctx, []*storobj.Object{createOrigObj()}) 1062 for i := range errs { 1063 require.NoError(t, errs[i]) 1064 } 1065 }) 1066 1067 t.Run("add 2nd batch", func(t *testing.T) { 1068 // overwrite geo in updated object 1069 altUpdObj := createUpdObj() 1070 altUpdObj.Object.Properties.(map[string]interface{})["geo"] = &models.GeoCoordinates{ 1071 Latitude: ptFloat32(3.3), 1072 Longitude: ptFloat32(4.4), 1073 } 1074 1075 errs := shard.PutObjectBatch(ctx, []*storobj.Object{altUpdObj}) 1076 for i := range errs { 1077 require.NoError(t, errs[i]) 1078 } 1079 }) 1080 1081 t.Run("verify changed docID, changed create & update timestamps", func(t *testing.T) { 1082 expectedNextDocID := uint64(2) 1083 require.Equal(t, expectedNextDocID, shard.Counter().Get()) 1084 1085 found := search(t, shard, filterId) 1086 require.Len(t, found, 1) 1087 require.Equal(t, updCreateTimeUnix, found[0].CreationTimeUnix()) 1088 require.Equal(t, updUpdateTimeUnix, found[0].LastUpdateTimeUnix()) 1089 }) 1090 1091 t.Run("verify search after 2nd batch", verifySearchAfterUpdate(shard)) 1092 t.Run("verify vector search after 2nd batch", verifyVectorSearch(shard, vector, altVector)) 1093 }) 1094 1095 t.Run("replace with same object, same vector", func(t *testing.T) { 1096 shard := createShard(t) 1097 1098 t.Run("add batch", func(t *testing.T) { 1099 errs := shard.PutObjectBatch(ctx, []*storobj.Object{createOrigObj()}) 1100 for i := range errs { 1101 require.NoError(t, errs[i]) 1102 } 1103 }) 1104 1105 t.Run("add 2nd batch", func(t *testing.T) { 1106 // overwrite timestamps in original object 1107 updObj := createOrigObj() 1108 updObj.Object.CreationTimeUnix = updCreateTimeUnix 1109 updObj.Object.LastUpdateTimeUnix = updUpdateTimeUnix 1110 1111 errs := shard.PutObjectBatch(ctx, []*storobj.Object{updObj}) 1112 for i := range errs { 1113 require.NoError(t, errs[i]) 1114 } 1115 }) 1116 1117 t.Run("verify same docID, same timestamps", func(t *testing.T) { 1118 expectedNextDocID := uint64(1) 1119 require.Equal(t, expectedNextDocID, shard.Counter().Get()) 1120 1121 found := search(t, shard, filterId) 1122 require.Len(t, found, 1) 1123 require.Equal(t, origCreateTimeUnix, found[0].CreationTimeUnix()) 1124 require.Equal(t, origUpdateTimeUnix, found[0].LastUpdateTimeUnix()) 1125 }) 1126 1127 t.Run("verify search after 2nd batch same as 1st", verifySearchAfterAdd(shard)) 1128 t.Run("verify vector search after 2nd batch", verifyVectorSearch(shard, vector, altVector)) 1129 }) 1130 1131 t.Run("replace with same object, different vector", func(t *testing.T) { 1132 shard := createShard(t) 1133 1134 t.Run("add batch", func(t *testing.T) { 1135 errs := shard.PutObjectBatch(ctx, []*storobj.Object{createOrigObj()}) 1136 for i := range errs { 1137 require.NoError(t, errs[i]) 1138 } 1139 }) 1140 1141 t.Run("add 2nd batch", func(t *testing.T) { 1142 // overwrite timestamps and vector in original object 1143 altUpdObj := createOrigObj() 1144 altUpdObj.Object.CreationTimeUnix = updCreateTimeUnix 1145 altUpdObj.Object.LastUpdateTimeUnix = updUpdateTimeUnix 1146 altUpdObj.Vector = altVector 1147 1148 errs := shard.PutObjectBatch(ctx, []*storobj.Object{altUpdObj}) 1149 for i := range errs { 1150 require.NoError(t, errs[i]) 1151 } 1152 }) 1153 1154 t.Run("verify changed docID, changed create & update timestamps", func(t *testing.T) { 1155 expectedNextDocID := uint64(2) 1156 require.Equal(t, expectedNextDocID, shard.Counter().Get()) 1157 1158 found := search(t, shard, filterId) 1159 require.Len(t, found, 1) 1160 require.Equal(t, updCreateTimeUnix, found[0].CreationTimeUnix()) 1161 require.Equal(t, updUpdateTimeUnix, found[0].LastUpdateTimeUnix()) 1162 }) 1163 1164 t.Run("verify search after 2nd batch same as 1st", verifySearchAfterAdd(shard)) 1165 t.Run("verify vector search after 2nd batch", verifyVectorSearch(shard, altVector, vector)) 1166 }) 1167 1168 t.Run("replace with same object, different geo", func(t *testing.T) { 1169 shard := createShard(t) 1170 1171 t.Run("add batch", func(t *testing.T) { 1172 errs := shard.PutObjectBatch(ctx, []*storobj.Object{createOrigObj()}) 1173 for i := range errs { 1174 require.NoError(t, errs[i]) 1175 } 1176 }) 1177 1178 t.Run("add 2nd batch", func(t *testing.T) { 1179 // overwrite geo and timestamp 1180 updObj := createOrigObj() 1181 updObj.Object.CreationTimeUnix = updCreateTimeUnix 1182 updObj.Object.LastUpdateTimeUnix = updUpdateTimeUnix 1183 updObj.Object.Properties.(map[string]interface{})["geo"] = &models.GeoCoordinates{ 1184 Latitude: ptFloat32(3.3), 1185 Longitude: ptFloat32(4.4), 1186 } 1187 1188 errs := shard.PutObjectBatch(ctx, []*storobj.Object{updObj}) 1189 for i := range errs { 1190 require.NoError(t, errs[i]) 1191 } 1192 }) 1193 1194 t.Run("verify changed docID, changed create & update timestamps", func(t *testing.T) { 1195 expectedNextDocID := uint64(2) 1196 require.Equal(t, expectedNextDocID, shard.Counter().Get()) 1197 1198 found := search(t, shard, filterId) 1199 require.Len(t, found, 1) 1200 require.Equal(t, updCreateTimeUnix, found[0].CreationTimeUnix()) 1201 require.Equal(t, updUpdateTimeUnix, found[0].LastUpdateTimeUnix()) 1202 }) 1203 1204 t.Run("verify search after 2nd batch same as 1st", verifySearchAfterAdd(shard)) 1205 t.Run("verify vector search after 2nd batch", verifyVectorSearch(shard, vector, altVector)) 1206 }) 1207 } 1208 1209 t.Run("sync", func(t *testing.T) { 1210 currentIndexing := os.Getenv("ASYNC_INDEXING") 1211 t.Setenv("ASYNC_INDEXING", "") 1212 defer t.Setenv("ASYNC_INDEXING", currentIndexing) 1213 1214 runBatch(t) 1215 }) 1216 1217 t.Run("async", func(t *testing.T) { 1218 currentIndexing := os.Getenv("ASYNC_INDEXING") 1219 t.Setenv("ASYNC_INDEXING", "true") 1220 defer t.Setenv("ASYNC_INDEXING", currentIndexing) 1221 1222 runBatch(t) 1223 }) 1224 }) 1225 } 1226 1227 func filterEqual[T any](value T, dataType schema.DataType, className, propName string) *filters.LocalFilter { 1228 return &filters.LocalFilter{ 1229 Root: &filters.Clause{ 1230 Operator: filters.OperatorEqual, 1231 Value: &filters.Value{ 1232 Value: value, 1233 Type: dataType, 1234 }, 1235 On: &filters.Path{ 1236 Class: schema.ClassName(className), 1237 Property: schema.PropertyName(propName), 1238 }, 1239 }, 1240 } 1241 } 1242 1243 func filterNil(value bool, className, propName string) *filters.LocalFilter { 1244 return &filters.LocalFilter{ 1245 Root: &filters.Clause{ 1246 Operator: filters.OperatorIsNull, 1247 Value: &filters.Value{ 1248 Value: value, 1249 Type: schema.DataTypeBoolean, 1250 }, 1251 On: &filters.Path{ 1252 Class: schema.ClassName(className), 1253 Property: schema.PropertyName(propName), 1254 }, 1255 }, 1256 } 1257 }