From 9423770b07fa12a23b1a7ad6db77cca963e3febd Mon Sep 17 00:00:00 2001 From: Jorge Lapa <2780099+heyjorgedev@users.noreply.github.com> Date: Thu, 27 Feb 2025 16:52:01 +0700 Subject: [PATCH 1/5] feat: delete vectors with metadata filter and id prefix --- src/Contracts/IndexNamespaceInterface.php | 4 ++ src/Index.php | 10 ++++ src/IndexNamespace.php | 10 ++++ src/Operations/DeleteVectorsOperation.php | 45 +++++++++----- src/Transporter/Headers.php | 2 +- .../Operations/DeleteVectorsOperationTest.php | 58 +++++++++++++++++-- 6 files changed, 109 insertions(+), 20 deletions(-) diff --git a/src/Contracts/IndexNamespaceInterface.php b/src/Contracts/IndexNamespaceInterface.php index 78c508c..3525807 100644 --- a/src/Contracts/IndexNamespaceInterface.php +++ b/src/Contracts/IndexNamespaceInterface.php @@ -64,4 +64,8 @@ public function update(VectorUpdate $update): void; public function range(VectorRange $range): VectorRangeResult; public function rangeIterator(VectorRange $range): VectorRangeIterator; + + public function deleteUsingIdPrefix(string $prefix): VectorDeleteResult; + + public function deleteUsingMetadataFilter(string $filter): VectorDeleteResult; } diff --git a/src/Index.php b/src/Index.php index 11b9ec3..cfeba3f 100755 --- a/src/Index.php +++ b/src/Index.php @@ -152,4 +152,14 @@ public function rangeIterator(VectorRange $range): VectorRangeIterator { return $this->namespace('')->rangeIterator($range); } + + public function deleteUsingIdPrefix(string $prefix): VectorDeleteResult + { + return $this->namespace('')->deleteUsingIdPrefix($prefix); + } + + public function deleteUsingMetadataFilter(string $filter): VectorDeleteResult + { + return $this->namespace('')->deleteUsingMetadataFilter($filter); + } } diff --git a/src/IndexNamespace.php b/src/IndexNamespace.php index ea379c5..6bb6044 100644 --- a/src/IndexNamespace.php +++ b/src/IndexNamespace.php @@ -104,4 +104,14 @@ public function rangeIterator(VectorRange $range): VectorRangeIterator { return (new RangeVectorsOperation($this->namespace, $this->transporter))->rangeIterator($range); } + + public function deleteUsingIdPrefix(string $prefix): VectorDeleteResult + { + return (new DeleteVectorsOperation($this->namespace, $this->transporter))->deleteUsingIdPrefix($prefix); + } + + public function deleteUsingMetadataFilter(string $filter): VectorDeleteResult + { + return (new DeleteVectorsOperation($this->namespace, $this->transporter))->deleteUsingMetadataFilter($filter); + } } diff --git a/src/Operations/DeleteVectorsOperation.php b/src/Operations/DeleteVectorsOperation.php index 536de0f..a8264e8 100644 --- a/src/Operations/DeleteVectorsOperation.php +++ b/src/Operations/DeleteVectorsOperation.php @@ -27,15 +27,42 @@ public function __construct(private string $namespace, private TransporterInterf */ public function delete(array $ids): VectorDeleteResult { - $path = $this->getPath(); - $vectorIds = $this->mapIds($ids); + return $this->sendDeleteRequest([ + 'ids' => $this->mapIds($ids), + ]); + } + + public function deleteUsingIdPrefix(string $prefix): VectorDeleteResult + { + return $this->sendDeleteRequest(compact('prefix')); + } + + public function deleteUsingMetadataFilter(string $filter): VectorDeleteResult + { + return $this->sendDeleteRequest(compact('filter')); + } + + private function getPath(): string + { + $namespace = trim($this->namespace); + if ($namespace === '') { + return '/delete'; + } + + return "/delete/$namespace"; + } + /** + * @param array $payload + */ + private function sendDeleteRequest(array $payload): VectorDeleteResult + { try { $request = new TransporterRequest( contentType: ContentType::JSON, method: Method::DELETE, - path: $path, - data: $vectorIds, + path: $this->getPath(), + data: $payload, ); } catch (\JsonException $e) { throw new OperationFailedException('Invalid JSON'); @@ -48,16 +75,6 @@ public function delete(array $ids): VectorDeleteResult return $this->transformResponse($response); } - private function getPath(): string - { - $namespace = trim($this->namespace); - if ($namespace === '') { - return '/delete'; - } - - return "/delete/$namespace"; - } - private function transformResponse(TransporterResponse $response): VectorDeleteResult { $data = json_decode($response->data, true)['result'] ?? []; diff --git a/src/Transporter/Headers.php b/src/Transporter/Headers.php index cc424f5..8fcbd08 100644 --- a/src/Transporter/Headers.php +++ b/src/Transporter/Headers.php @@ -5,7 +5,7 @@ /** * @internal */ -class Headers +readonly class Headers { /** * @param array $headers diff --git a/tests/Dense/Operations/DeleteVectorsOperationTest.php b/tests/Dense/Operations/DeleteVectorsOperationTest.php index e07f096..e89a567 100644 --- a/tests/Dense/Operations/DeleteVectorsOperationTest.php +++ b/tests/Dense/Operations/DeleteVectorsOperationTest.php @@ -18,9 +18,9 @@ class DeleteVectorsOperationTest extends TestCase public function test_delete_vectors(): void { $this->namespace->upsertMany([ - new VectorUpsert('id-1', createRandomVector(2)), - new VectorUpsert('id-2', createRandomVector(2)), - new VectorUpsert('id-3', createRandomVector(2)), + new VectorUpsert('id-1', vector: createRandomVector(2)), + new VectorUpsert('id-2', vector: createRandomVector(2)), + new VectorUpsert('id-3', vector: createRandomVector(2)), ]); $this->waitForIndex($this->namespace); @@ -39,8 +39,8 @@ public function test_delete_vectors_from_a_query_result_results(): void $vector = createRandomVector(2); $this->namespace->upsertMany([ new VectorUpsert('id-1', $vector), - new VectorUpsert('id-2', createRandomVector(2)), - new VectorUpsert('id-3', createRandomVector(2)), + new VectorUpsert('id-2', vector: createRandomVector(2)), + new VectorUpsert('id-3', vector: createRandomVector(2)), ]); $this->waitForIndex($this->namespace); @@ -53,4 +53,52 @@ public function test_delete_vectors_from_a_query_result_results(): void $this->assertEquals(2, $result->deleted); } + + public function test_delete_vectors_using_an_id_prefix(): void + { + $this->namespace->upsertMany([ + new VectorUpsert('users:1', vector: createRandomVector(2)), + new VectorUpsert('users:2', vector: createRandomVector(2)), + new VectorUpsert('posts:1', vector: createRandomVector(2)), + ]); + $this->waitForIndex($this->namespace); + + $result = $this->namespace->deleteUsingIdPrefix('users:*'); + + $this->assertEquals(2, $result->deleted); + $this->assertEquals(1, $this->namespace->getNamespaceInfo()->vectorCount); + } + + public function test_delete_vectors_using_a_metadata_filter(): void + { + $this->namespace->upsertMany([ + new VectorUpsert( + id: 'users:1', + vector: createRandomVector(2), + metadata: [ + 'salary' => 1000, + ], + ), + new VectorUpsert( + id: 'users:2', + vector: createRandomVector(2), + metadata: [ + 'salary' => 2000, + ], + ), + new VectorUpsert( + id: 'users:3', + vector: createRandomVector(2), + metadata: [ + 'salary' => 3000, + ], + ), + ]); + $this->waitForIndex($this->namespace); + + $result = $this->namespace->deleteUsingMetadataFilter('salary < 3000'); + + $this->assertEquals(2, $result->deleted); + $this->assertEquals(1, $this->namespace->getNamespaceInfo()->vectorCount); + } } From 07f1a15e4e16e109f931f018a1a49f9b90cd0010 Mon Sep 17 00:00:00 2001 From: Jorge Lapa <2780099+heyjorgedev@users.noreply.github.com> Date: Thu, 27 Feb 2025 17:51:23 +0700 Subject: [PATCH 2/5] wip --- src/Contracts/IndexNamespaceInterface.php | 9 +-- .../ToDeletablePayloadInterface.php | 11 ++++ src/Index.php | 13 +---- src/IndexNamespace.php | 13 +---- src/Operations/DeleteVectorsOperation.php | 19 +++---- src/VectorDelete.php | 55 +++++++++++++++++++ src/VectorFilterDelete.php | 24 ++++++++ src/VectorPrefixDelete.php | 22 ++++++++ .../Operations/DeleteVectorsOperationTest.php | 5 +- 9 files changed, 129 insertions(+), 42 deletions(-) create mode 100644 src/Contracts/Transformers/ToDeletablePayloadInterface.php create mode 100644 src/VectorDelete.php create mode 100644 src/VectorFilterDelete.php create mode 100644 src/VectorPrefixDelete.php diff --git a/src/Contracts/IndexNamespaceInterface.php b/src/Contracts/IndexNamespaceInterface.php index 3525807..af047a1 100644 --- a/src/Contracts/IndexNamespaceInterface.php +++ b/src/Contracts/IndexNamespaceInterface.php @@ -2,6 +2,7 @@ namespace Upstash\Vector\Contracts; +use Upstash\Vector\Contracts\Transformers\ToDeletablePayloadInterface; use Upstash\Vector\DataQuery; use Upstash\Vector\DataQueryResult; use Upstash\Vector\DataUpsert; @@ -51,9 +52,9 @@ public function queryMany(array $queries): VectorQueryManyResult; public function queryData(DataQuery $query): DataQueryResult; /** - * @param array $ids + * @param array|ToDeletablePayloadInterface $ids */ - public function delete(array $ids): VectorDeleteResult; + public function delete(array|ToDeletablePayloadInterface $ids): VectorDeleteResult; public function fetch(VectorFetch $vectorFetch): VectorFetchResult; @@ -64,8 +65,4 @@ public function update(VectorUpdate $update): void; public function range(VectorRange $range): VectorRangeResult; public function rangeIterator(VectorRange $range): VectorRangeIterator; - - public function deleteUsingIdPrefix(string $prefix): VectorDeleteResult; - - public function deleteUsingMetadataFilter(string $filter): VectorDeleteResult; } diff --git a/src/Contracts/Transformers/ToDeletablePayloadInterface.php b/src/Contracts/Transformers/ToDeletablePayloadInterface.php new file mode 100644 index 0000000..fd6fa32 --- /dev/null +++ b/src/Contracts/Transformers/ToDeletablePayloadInterface.php @@ -0,0 +1,11 @@ + + */ + public function toDeletablePayload(): array; +} diff --git a/src/Index.php b/src/Index.php index cfeba3f..4cfc71f 100755 --- a/src/Index.php +++ b/src/Index.php @@ -5,6 +5,7 @@ use Http\Discovery\Psr18ClientDiscovery; use Upstash\Vector\Contracts\IndexInterface; use Upstash\Vector\Contracts\IndexNamespaceInterface; +use Upstash\Vector\Contracts\Transformers\ToDeletablePayloadInterface; use Upstash\Vector\Contracts\TransporterInterface; use Upstash\Vector\Exceptions\MissingEnvironmentVariableException; use Upstash\Vector\Iterators\VectorRangeIterator; @@ -113,7 +114,7 @@ public function queryData(DataQuery $query): DataQueryResult return $this->namespace('')->queryData($query); } - public function delete(array $ids): VectorDeleteResult + public function delete(array|ToDeletablePayloadInterface $ids): VectorDeleteResult { return $this->namespace('')->delete($ids); } @@ -152,14 +153,4 @@ public function rangeIterator(VectorRange $range): VectorRangeIterator { return $this->namespace('')->rangeIterator($range); } - - public function deleteUsingIdPrefix(string $prefix): VectorDeleteResult - { - return $this->namespace('')->deleteUsingIdPrefix($prefix); - } - - public function deleteUsingMetadataFilter(string $filter): VectorDeleteResult - { - return $this->namespace('')->deleteUsingMetadataFilter($filter); - } } diff --git a/src/IndexNamespace.php b/src/IndexNamespace.php index 6bb6044..8a3c93a 100644 --- a/src/IndexNamespace.php +++ b/src/IndexNamespace.php @@ -3,6 +3,7 @@ namespace Upstash\Vector; use Upstash\Vector\Contracts\IndexNamespaceInterface; +use Upstash\Vector\Contracts\Transformers\ToDeletablePayloadInterface; use Upstash\Vector\Contracts\TransporterInterface; use Upstash\Vector\Iterators\VectorRangeIterator; use Upstash\Vector\Operations\DeleteNamespaceOperation; @@ -73,7 +74,7 @@ public function queryData(DataQuery $query): DataQueryResult return (new QueryDataOperation($this->namespace, $this->transporter))->query($query); } - public function delete(array $ids): VectorDeleteResult + public function delete(array|ToDeletablePayloadInterface $ids): VectorDeleteResult { return (new DeleteVectorsOperation($this->namespace, $this->transporter)) ->delete($ids); @@ -104,14 +105,4 @@ public function rangeIterator(VectorRange $range): VectorRangeIterator { return (new RangeVectorsOperation($this->namespace, $this->transporter))->rangeIterator($range); } - - public function deleteUsingIdPrefix(string $prefix): VectorDeleteResult - { - return (new DeleteVectorsOperation($this->namespace, $this->transporter))->deleteUsingIdPrefix($prefix); - } - - public function deleteUsingMetadataFilter(string $filter): VectorDeleteResult - { - return (new DeleteVectorsOperation($this->namespace, $this->transporter))->deleteUsingMetadataFilter($filter); - } } diff --git a/src/Operations/DeleteVectorsOperation.php b/src/Operations/DeleteVectorsOperation.php index a8264e8..8fa13d3 100644 --- a/src/Operations/DeleteVectorsOperation.php +++ b/src/Operations/DeleteVectorsOperation.php @@ -3,6 +3,7 @@ namespace Upstash\Vector\Operations; use InvalidArgumentException; +use Upstash\Vector\Contracts\Transformers\ToDeletablePayloadInterface; use Upstash\Vector\Contracts\TransporterInterface; use Upstash\Vector\Contracts\VectorIdentifierInterface; use Upstash\Vector\Exceptions\OperationFailedException; @@ -23,25 +24,19 @@ public function __construct(private string $namespace, private TransporterInterface $transporter) {} /** - * @param array $ids + * @param array|ToDeletablePayloadInterface $ids */ - public function delete(array $ids): VectorDeleteResult + public function delete(array|ToDeletablePayloadInterface $ids): VectorDeleteResult { + if ($ids instanceof ToDeletablePayloadInterface) { + return $this->sendDeleteRequest($ids->toDeletablePayload()); + } + return $this->sendDeleteRequest([ 'ids' => $this->mapIds($ids), ]); } - public function deleteUsingIdPrefix(string $prefix): VectorDeleteResult - { - return $this->sendDeleteRequest(compact('prefix')); - } - - public function deleteUsingMetadataFilter(string $filter): VectorDeleteResult - { - return $this->sendDeleteRequest(compact('filter')); - } - private function getPath(): string { $namespace = trim($this->namespace); diff --git a/src/VectorDelete.php b/src/VectorDelete.php new file mode 100644 index 0000000..251c8c5 --- /dev/null +++ b/src/VectorDelete.php @@ -0,0 +1,55 @@ +ids = [$ids]; + } else { + $this->ids = array_values(array_unique($ids)); + } + } + + /** + * @param string[]|string $ids + */ + public static function fromIds(array|string $ids): VectorDelete + { + return new VectorDelete($ids); + } + + public static function fromPrefix(string $prefix): VectorPrefixDelete + { + return new VectorPrefixDelete($prefix); + } + + public static function fromMetadataFilter(string $filter): VectorFilterDelete + { + return new VectorFilterDelete($filter); + } + + /** + * @return array{ + * ids: string[] + * } + */ + public function toDeletablePayload(): array + { + return [ + 'ids' => $this->ids, + ]; + } +} diff --git a/src/VectorFilterDelete.php b/src/VectorFilterDelete.php new file mode 100644 index 0000000..bd40059 --- /dev/null +++ b/src/VectorFilterDelete.php @@ -0,0 +1,24 @@ + $this->filter, + ]; + } +} diff --git a/src/VectorPrefixDelete.php b/src/VectorPrefixDelete.php new file mode 100644 index 0000000..a516019 --- /dev/null +++ b/src/VectorPrefixDelete.php @@ -0,0 +1,22 @@ + $this->prefix, + ]; + } +} diff --git a/tests/Dense/Operations/DeleteVectorsOperationTest.php b/tests/Dense/Operations/DeleteVectorsOperationTest.php index e89a567..29a3920 100644 --- a/tests/Dense/Operations/DeleteVectorsOperationTest.php +++ b/tests/Dense/Operations/DeleteVectorsOperationTest.php @@ -5,6 +5,7 @@ use PHPUnit\Framework\TestCase; use Upstash\Vector\Tests\Concerns\UsesDenseIndex; use Upstash\Vector\Tests\Concerns\WaitsForIndex; +use Upstash\Vector\VectorDelete; use Upstash\Vector\VectorQuery; use Upstash\Vector\VectorUpsert; @@ -63,7 +64,7 @@ public function test_delete_vectors_using_an_id_prefix(): void ]); $this->waitForIndex($this->namespace); - $result = $this->namespace->deleteUsingIdPrefix('users:*'); + $result = $this->namespace->delete(VectorDelete::fromPrefix('users:*')); $this->assertEquals(2, $result->deleted); $this->assertEquals(1, $this->namespace->getNamespaceInfo()->vectorCount); @@ -96,7 +97,7 @@ public function test_delete_vectors_using_a_metadata_filter(): void ]); $this->waitForIndex($this->namespace); - $result = $this->namespace->deleteUsingMetadataFilter('salary < 3000'); + $result = $this->namespace->delete(VectorDelete::fromMetadataFilter('salary < 3000')); $this->assertEquals(2, $result->deleted); $this->assertEquals(1, $this->namespace->getNamespaceInfo()->vectorCount); From 63bbb5bf9958bd187575d74b1d811c46e292fb80 Mon Sep 17 00:00:00 2001 From: Jorge Lapa <2780099+heyjorgedev@users.noreply.github.com> Date: Thu, 27 Feb 2025 17:53:46 +0700 Subject: [PATCH 3/5] wip --- .../Operations/DeleteVectorsOperationTest.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/Dense/Operations/DeleteVectorsOperationTest.php b/tests/Dense/Operations/DeleteVectorsOperationTest.php index 29a3920..2e61c5b 100644 --- a/tests/Dense/Operations/DeleteVectorsOperationTest.php +++ b/tests/Dense/Operations/DeleteVectorsOperationTest.php @@ -55,6 +55,21 @@ public function test_delete_vectors_from_a_query_result_results(): void $this->assertEquals(2, $result->deleted); } + public function test_delete_vectors_using_builder_pattern(): void + { + $this->namespace->upsertMany([ + new VectorUpsert('users:1', vector: createRandomVector(2)), + new VectorUpsert('users:2', vector: createRandomVector(2)), + new VectorUpsert('posts:1', vector: createRandomVector(2)), + ]); + $this->waitForIndex($this->namespace); + + $result = $this->namespace->delete(VectorDelete::fromIds(['users:1', 'users:2'])); + + $this->assertEquals(2, $result->deleted); + $this->assertEquals(1, $this->namespace->getNamespaceInfo()->vectorCount); + } + public function test_delete_vectors_using_an_id_prefix(): void { $this->namespace->upsertMany([ From a8f5dd4bc4e881ba8ee9c5d537cdf72cd4b0586d Mon Sep 17 00:00:00 2001 From: Jorge Lapa <2780099+heyjorgedev@users.noreply.github.com> Date: Thu, 27 Feb 2025 18:58:46 +0700 Subject: [PATCH 4/5] wip --- tests/Dense/Operations/DeleteVectorsOperationTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Dense/Operations/DeleteVectorsOperationTest.php b/tests/Dense/Operations/DeleteVectorsOperationTest.php index 2e61c5b..15bbb12 100644 --- a/tests/Dense/Operations/DeleteVectorsOperationTest.php +++ b/tests/Dense/Operations/DeleteVectorsOperationTest.php @@ -79,7 +79,7 @@ public function test_delete_vectors_using_an_id_prefix(): void ]); $this->waitForIndex($this->namespace); - $result = $this->namespace->delete(VectorDelete::fromPrefix('users:*')); + $result = $this->namespace->delete(VectorDelete::fromPrefix('users:')); $this->assertEquals(2, $result->deleted); $this->assertEquals(1, $this->namespace->getNamespaceInfo()->vectorCount); From d724db9ea74aedfc46c6ec263cfaa57cb2601b51 Mon Sep 17 00:00:00 2001 From: Jorge Lapa <2780099+heyjorgedev@users.noreply.github.com> Date: Tue, 4 Mar 2025 19:01:32 +0800 Subject: [PATCH 5/5] wip --- src/Contracts/IndexNamespaceInterface.php | 7 ++- .../ToDeletablePayloadInterface.php | 11 ---- src/Index.php | 3 +- src/IndexNamespace.php | 6 +- src/Operations/DeleteVectorsOperation.php | 20 +++++-- src/VectorDelete.php | 55 ------------------- ...e.php => VectorDeleteByMetadataFilter.php} | 8 +-- src/VectorDeleteByPrefix.php | 24 ++++++++ src/VectorPrefixDelete.php | 22 -------- .../Operations/DeleteVectorsOperationTest.php | 42 ++++++++------ 10 files changed, 76 insertions(+), 122 deletions(-) delete mode 100644 src/Contracts/Transformers/ToDeletablePayloadInterface.php delete mode 100644 src/VectorDelete.php rename src/{VectorFilterDelete.php => VectorDeleteByMetadataFilter.php} (51%) create mode 100644 src/VectorDeleteByPrefix.php delete mode 100644 src/VectorPrefixDelete.php diff --git a/src/Contracts/IndexNamespaceInterface.php b/src/Contracts/IndexNamespaceInterface.php index af047a1..da74104 100644 --- a/src/Contracts/IndexNamespaceInterface.php +++ b/src/Contracts/IndexNamespaceInterface.php @@ -2,12 +2,13 @@ namespace Upstash\Vector\Contracts; -use Upstash\Vector\Contracts\Transformers\ToDeletablePayloadInterface; use Upstash\Vector\DataQuery; use Upstash\Vector\DataQueryResult; use Upstash\Vector\DataUpsert; use Upstash\Vector\Iterators\VectorRangeIterator; use Upstash\Vector\NamespaceInfo; +use Upstash\Vector\VectorDeleteByMetadataFilter; +use Upstash\Vector\VectorDeleteByPrefix; use Upstash\Vector\VectorDeleteResult; use Upstash\Vector\VectorFetch; use Upstash\Vector\VectorFetchResult; @@ -52,9 +53,9 @@ public function queryMany(array $queries): VectorQueryManyResult; public function queryData(DataQuery $query): DataQueryResult; /** - * @param array|ToDeletablePayloadInterface $ids + * @param array|string|VectorDeleteByPrefix|VectorDeleteByMetadataFilter $ids */ - public function delete(array|ToDeletablePayloadInterface $ids): VectorDeleteResult; + public function delete(array|string|VectorDeleteByPrefix|VectorDeleteByMetadataFilter $ids): VectorDeleteResult; public function fetch(VectorFetch $vectorFetch): VectorFetchResult; diff --git a/src/Contracts/Transformers/ToDeletablePayloadInterface.php b/src/Contracts/Transformers/ToDeletablePayloadInterface.php deleted file mode 100644 index fd6fa32..0000000 --- a/src/Contracts/Transformers/ToDeletablePayloadInterface.php +++ /dev/null @@ -1,11 +0,0 @@ - - */ - public function toDeletablePayload(): array; -} diff --git a/src/Index.php b/src/Index.php index 4cfc71f..e5245c2 100755 --- a/src/Index.php +++ b/src/Index.php @@ -5,7 +5,6 @@ use Http\Discovery\Psr18ClientDiscovery; use Upstash\Vector\Contracts\IndexInterface; use Upstash\Vector\Contracts\IndexNamespaceInterface; -use Upstash\Vector\Contracts\Transformers\ToDeletablePayloadInterface; use Upstash\Vector\Contracts\TransporterInterface; use Upstash\Vector\Exceptions\MissingEnvironmentVariableException; use Upstash\Vector\Iterators\VectorRangeIterator; @@ -114,7 +113,7 @@ public function queryData(DataQuery $query): DataQueryResult return $this->namespace('')->queryData($query); } - public function delete(array|ToDeletablePayloadInterface $ids): VectorDeleteResult + public function delete(array|string|VectorDeleteByPrefix|VectorDeleteByMetadataFilter $ids): VectorDeleteResult { return $this->namespace('')->delete($ids); } diff --git a/src/IndexNamespace.php b/src/IndexNamespace.php index 8a3c93a..2e4815e 100644 --- a/src/IndexNamespace.php +++ b/src/IndexNamespace.php @@ -3,7 +3,6 @@ namespace Upstash\Vector; use Upstash\Vector\Contracts\IndexNamespaceInterface; -use Upstash\Vector\Contracts\Transformers\ToDeletablePayloadInterface; use Upstash\Vector\Contracts\TransporterInterface; use Upstash\Vector\Iterators\VectorRangeIterator; use Upstash\Vector\Operations\DeleteNamespaceOperation; @@ -74,7 +73,10 @@ public function queryData(DataQuery $query): DataQueryResult return (new QueryDataOperation($this->namespace, $this->transporter))->query($query); } - public function delete(array|ToDeletablePayloadInterface $ids): VectorDeleteResult + /** + * @param string[]|string|VectorDeleteByPrefix|VectorDeleteByMetadataFilter $ids + */ + public function delete(array|string|VectorDeleteByPrefix|VectorDeleteByMetadataFilter $ids): VectorDeleteResult { return (new DeleteVectorsOperation($this->namespace, $this->transporter)) ->delete($ids); diff --git a/src/Operations/DeleteVectorsOperation.php b/src/Operations/DeleteVectorsOperation.php index 8fa13d3..ea0e70f 100644 --- a/src/Operations/DeleteVectorsOperation.php +++ b/src/Operations/DeleteVectorsOperation.php @@ -3,7 +3,7 @@ namespace Upstash\Vector\Operations; use InvalidArgumentException; -use Upstash\Vector\Contracts\Transformers\ToDeletablePayloadInterface; +use Upstash\Vector\Contracts\Arrayable; use Upstash\Vector\Contracts\TransporterInterface; use Upstash\Vector\Contracts\VectorIdentifierInterface; use Upstash\Vector\Exceptions\OperationFailedException; @@ -12,6 +12,8 @@ use Upstash\Vector\Transporter\Method; use Upstash\Vector\Transporter\TransporterRequest; use Upstash\Vector\Transporter\TransporterResponse; +use Upstash\Vector\VectorDeleteByMetadataFilter; +use Upstash\Vector\VectorDeleteByPrefix; use Upstash\Vector\VectorDeleteResult; /** @@ -24,12 +26,20 @@ public function __construct(private string $namespace, private TransporterInterface $transporter) {} /** - * @param array|ToDeletablePayloadInterface $ids + * @param string[]|string|VectorDeleteByPrefix|VectorDeleteByMetadataFilter $ids + * + * @throws OperationFailedException */ - public function delete(array|ToDeletablePayloadInterface $ids): VectorDeleteResult + public function delete(array|string|VectorDeleteByPrefix|VectorDeleteByMetadataFilter $ids): VectorDeleteResult { - if ($ids instanceof ToDeletablePayloadInterface) { - return $this->sendDeleteRequest($ids->toDeletablePayload()); + if ($ids instanceof Arrayable) { + return $this->sendDeleteRequest($ids->toArray()); + } + + if (is_string($ids)) { + return $this->sendDeleteRequest([ + 'ids' => [$ids], + ]); } return $this->sendDeleteRequest([ diff --git a/src/VectorDelete.php b/src/VectorDelete.php deleted file mode 100644 index 251c8c5..0000000 --- a/src/VectorDelete.php +++ /dev/null @@ -1,55 +0,0 @@ -ids = [$ids]; - } else { - $this->ids = array_values(array_unique($ids)); - } - } - - /** - * @param string[]|string $ids - */ - public static function fromIds(array|string $ids): VectorDelete - { - return new VectorDelete($ids); - } - - public static function fromPrefix(string $prefix): VectorPrefixDelete - { - return new VectorPrefixDelete($prefix); - } - - public static function fromMetadataFilter(string $filter): VectorFilterDelete - { - return new VectorFilterDelete($filter); - } - - /** - * @return array{ - * ids: string[] - * } - */ - public function toDeletablePayload(): array - { - return [ - 'ids' => $this->ids, - ]; - } -} diff --git a/src/VectorFilterDelete.php b/src/VectorDeleteByMetadataFilter.php similarity index 51% rename from src/VectorFilterDelete.php rename to src/VectorDeleteByMetadataFilter.php index bd40059..1c69ad8 100644 --- a/src/VectorFilterDelete.php +++ b/src/VectorDeleteByMetadataFilter.php @@ -2,9 +2,9 @@ namespace Upstash\Vector; -use Upstash\Vector\Contracts\Transformers\ToDeletablePayloadInterface; +use Upstash\Vector\Contracts\Arrayable; -final readonly class VectorFilterDelete implements ToDeletablePayloadInterface +final readonly class VectorDeleteByMetadataFilter implements Arrayable { public function __construct( public string $filter, @@ -12,10 +12,10 @@ public function __construct( /** * @return array{ - * filter: string + * filter: string, * } */ - public function toDeletablePayload(): array + public function toArray(): array { return [ 'filter' => $this->filter, diff --git a/src/VectorDeleteByPrefix.php b/src/VectorDeleteByPrefix.php new file mode 100644 index 0000000..b31c656 --- /dev/null +++ b/src/VectorDeleteByPrefix.php @@ -0,0 +1,24 @@ + $this->prefix, + ]; + } +} diff --git a/src/VectorPrefixDelete.php b/src/VectorPrefixDelete.php deleted file mode 100644 index a516019..0000000 --- a/src/VectorPrefixDelete.php +++ /dev/null @@ -1,22 +0,0 @@ - $this->prefix, - ]; - } -} diff --git a/tests/Dense/Operations/DeleteVectorsOperationTest.php b/tests/Dense/Operations/DeleteVectorsOperationTest.php index 15bbb12..6316947 100644 --- a/tests/Dense/Operations/DeleteVectorsOperationTest.php +++ b/tests/Dense/Operations/DeleteVectorsOperationTest.php @@ -5,7 +5,8 @@ use PHPUnit\Framework\TestCase; use Upstash\Vector\Tests\Concerns\UsesDenseIndex; use Upstash\Vector\Tests\Concerns\WaitsForIndex; -use Upstash\Vector\VectorDelete; +use Upstash\Vector\VectorDeleteByMetadataFilter; +use Upstash\Vector\VectorDeleteByPrefix; use Upstash\Vector\VectorQuery; use Upstash\Vector\VectorUpsert; @@ -35,6 +36,22 @@ public function test_delete_vectors(): void $this->assertSame(1, $info->vectorCount); } + public function test_delete_single_vector(): void + { + $this->namespace->upsertMany([ + new VectorUpsert('id-1', vector: createRandomVector(2)), + new VectorUpsert('id-2', vector: createRandomVector(2)), + new VectorUpsert('id-3', vector: createRandomVector(2)), + ]); + $this->waitForIndex($this->namespace); + + $result = $this->namespace->delete('id-1'); + + $this->assertEquals(1, $result->deleted); + $info = $this->namespace->getNamespaceInfo(); + $this->assertSame(2, $info->vectorCount); + } + public function test_delete_vectors_from_a_query_result_results(): void { $vector = createRandomVector(2); @@ -55,21 +72,6 @@ public function test_delete_vectors_from_a_query_result_results(): void $this->assertEquals(2, $result->deleted); } - public function test_delete_vectors_using_builder_pattern(): void - { - $this->namespace->upsertMany([ - new VectorUpsert('users:1', vector: createRandomVector(2)), - new VectorUpsert('users:2', vector: createRandomVector(2)), - new VectorUpsert('posts:1', vector: createRandomVector(2)), - ]); - $this->waitForIndex($this->namespace); - - $result = $this->namespace->delete(VectorDelete::fromIds(['users:1', 'users:2'])); - - $this->assertEquals(2, $result->deleted); - $this->assertEquals(1, $this->namespace->getNamespaceInfo()->vectorCount); - } - public function test_delete_vectors_using_an_id_prefix(): void { $this->namespace->upsertMany([ @@ -79,7 +81,9 @@ public function test_delete_vectors_using_an_id_prefix(): void ]); $this->waitForIndex($this->namespace); - $result = $this->namespace->delete(VectorDelete::fromPrefix('users:')); + $result = $this->namespace->delete(new VectorDeleteByPrefix( + prefix: 'users:', + )); $this->assertEquals(2, $result->deleted); $this->assertEquals(1, $this->namespace->getNamespaceInfo()->vectorCount); @@ -112,7 +116,9 @@ public function test_delete_vectors_using_a_metadata_filter(): void ]); $this->waitForIndex($this->namespace); - $result = $this->namespace->delete(VectorDelete::fromMetadataFilter('salary < 3000')); + $result = $this->namespace->delete(new VectorDeleteByMetadataFilter( + filter: 'salary > 1000', + )); $this->assertEquals(2, $result->deleted); $this->assertEquals(1, $this->namespace->getNamespaceInfo()->vectorCount);