Skip to content

Commit bc5b7f8

Browse files
authored
feat: delete vectors with metadata filter and id prefix (#17)
* feat: delete vectors with metadata filter and id prefix * wip * wip * wip * wip
1 parent e45d130 commit bc5b7f8

File tree

8 files changed

+171
-26
lines changed

8 files changed

+171
-26
lines changed

src/Contracts/IndexNamespaceInterface.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
use Upstash\Vector\DataUpsert;
88
use Upstash\Vector\Iterators\VectorRangeIterator;
99
use Upstash\Vector\NamespaceInfo;
10+
use Upstash\Vector\VectorDeleteByMetadataFilter;
11+
use Upstash\Vector\VectorDeleteByPrefix;
1012
use Upstash\Vector\VectorDeleteResult;
1113
use Upstash\Vector\VectorFetch;
1214
use Upstash\Vector\VectorFetchByPrefix;
@@ -52,9 +54,9 @@ public function queryMany(array $queries): VectorQueryManyResult;
5254
public function queryData(DataQuery $query): DataQueryResult;
5355

5456
/**
55-
* @param array<string|VectorIdentifierInterface> $ids
57+
* @param array<string|VectorIdentifierInterface>|string|VectorDeleteByPrefix|VectorDeleteByMetadataFilter $ids
5658
*/
57-
public function delete(array $ids): VectorDeleteResult;
59+
public function delete(array|string|VectorDeleteByPrefix|VectorDeleteByMetadataFilter $ids): VectorDeleteResult;
5860

5961
public function fetch(VectorFetch|VectorFetchByPrefix $vectorFetch): VectorFetchResult;
6062

src/Index.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public function queryData(DataQuery $query): DataQueryResult
113113
return $this->namespace('')->queryData($query);
114114
}
115115

116-
public function delete(array $ids): VectorDeleteResult
116+
public function delete(array|string|VectorDeleteByPrefix|VectorDeleteByMetadataFilter $ids): VectorDeleteResult
117117
{
118118
return $this->namespace('')->delete($ids);
119119
}

src/IndexNamespace.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,10 @@ public function queryData(DataQuery $query): DataQueryResult
7373
return (new QueryDataOperation($this->namespace, $this->transporter))->query($query);
7474
}
7575

76-
public function delete(array $ids): VectorDeleteResult
76+
/**
77+
* @param string[]|string|VectorDeleteByPrefix|VectorDeleteByMetadataFilter $ids
78+
*/
79+
public function delete(array|string|VectorDeleteByPrefix|VectorDeleteByMetadataFilter $ids): VectorDeleteResult
7780
{
7881
return (new DeleteVectorsOperation($this->namespace, $this->transporter))
7982
->delete($ids);

src/Operations/DeleteVectorsOperation.php

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Upstash\Vector\Operations;
44

55
use InvalidArgumentException;
6+
use Upstash\Vector\Contracts\Arrayable;
67
use Upstash\Vector\Contracts\TransporterInterface;
78
use Upstash\Vector\Contracts\VectorIdentifierInterface;
89
use Upstash\Vector\Exceptions\OperationFailedException;
@@ -11,6 +12,8 @@
1112
use Upstash\Vector\Transporter\Method;
1213
use Upstash\Vector\Transporter\TransporterRequest;
1314
use Upstash\Vector\Transporter\TransporterResponse;
15+
use Upstash\Vector\VectorDeleteByMetadataFilter;
16+
use Upstash\Vector\VectorDeleteByPrefix;
1417
use Upstash\Vector\VectorDeleteResult;
1518

1619
/**
@@ -23,19 +26,48 @@
2326
public function __construct(private string $namespace, private TransporterInterface $transporter) {}
2427

2528
/**
26-
* @param array<string|VectorIdentifierInterface> $ids
29+
* @param string[]|string|VectorDeleteByPrefix|VectorDeleteByMetadataFilter $ids
30+
*
31+
* @throws OperationFailedException
2732
*/
28-
public function delete(array $ids): VectorDeleteResult
33+
public function delete(array|string|VectorDeleteByPrefix|VectorDeleteByMetadataFilter $ids): VectorDeleteResult
2934
{
30-
$path = $this->getPath();
31-
$vectorIds = $this->mapIds($ids);
35+
if ($ids instanceof Arrayable) {
36+
return $this->sendDeleteRequest($ids->toArray());
37+
}
38+
39+
if (is_string($ids)) {
40+
return $this->sendDeleteRequest([
41+
'ids' => [$ids],
42+
]);
43+
}
44+
45+
return $this->sendDeleteRequest([
46+
'ids' => $this->mapIds($ids),
47+
]);
48+
}
49+
50+
private function getPath(): string
51+
{
52+
$namespace = trim($this->namespace);
53+
if ($namespace === '') {
54+
return '/delete';
55+
}
56+
57+
return "/delete/$namespace";
58+
}
3259

60+
/**
61+
* @param array<string, mixed> $payload
62+
*/
63+
private function sendDeleteRequest(array $payload): VectorDeleteResult
64+
{
3365
try {
3466
$request = new TransporterRequest(
3567
contentType: ContentType::JSON,
3668
method: Method::DELETE,
37-
path: $path,
38-
data: $vectorIds,
69+
path: $this->getPath(),
70+
data: $payload,
3971
);
4072
} catch (\JsonException $e) {
4173
throw new OperationFailedException('Invalid JSON');
@@ -48,16 +80,6 @@ public function delete(array $ids): VectorDeleteResult
4880
return $this->transformResponse($response);
4981
}
5082

51-
private function getPath(): string
52-
{
53-
$namespace = trim($this->namespace);
54-
if ($namespace === '') {
55-
return '/delete';
56-
}
57-
58-
return "/delete/$namespace";
59-
}
60-
6183
private function transformResponse(TransporterResponse $response): VectorDeleteResult
6284
{
6385
$data = json_decode($response->data, true)['result'] ?? [];

src/Transporter/Headers.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
/**
66
* @internal
77
*/
8-
class Headers
8+
readonly class Headers
99
{
1010
/**
1111
* @param array<string, string> $headers
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace Upstash\Vector;
4+
5+
use Upstash\Vector\Contracts\Arrayable;
6+
7+
final readonly class VectorDeleteByMetadataFilter implements Arrayable
8+
{
9+
public function __construct(
10+
public string $filter,
11+
) {}
12+
13+
/**
14+
* @return array{
15+
* filter: string,
16+
* }
17+
*/
18+
public function toArray(): array
19+
{
20+
return [
21+
'filter' => $this->filter,
22+
];
23+
}
24+
}

src/VectorDeleteByPrefix.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace Upstash\Vector;
4+
5+
use Upstash\Vector\Contracts\Arrayable;
6+
7+
final readonly class VectorDeleteByPrefix implements Arrayable
8+
{
9+
public function __construct(
10+
public string $prefix,
11+
) {}
12+
13+
/**
14+
* @return array{
15+
* prefix: string,
16+
* }
17+
*/
18+
public function toArray(): array
19+
{
20+
return [
21+
'prefix' => $this->prefix,
22+
];
23+
}
24+
}

tests/Dense/Operations/DeleteVectorsOperationTest.php

Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
use PHPUnit\Framework\TestCase;
66
use Upstash\Vector\Tests\Concerns\UsesDenseIndex;
77
use Upstash\Vector\Tests\Concerns\WaitsForIndex;
8+
use Upstash\Vector\VectorDeleteByMetadataFilter;
9+
use Upstash\Vector\VectorDeleteByPrefix;
810
use Upstash\Vector\VectorQuery;
911
use Upstash\Vector\VectorUpsert;
1012

@@ -18,9 +20,9 @@ class DeleteVectorsOperationTest extends TestCase
1820
public function test_delete_vectors(): void
1921
{
2022
$this->namespace->upsertMany([
21-
new VectorUpsert('id-1', createRandomVector(2)),
22-
new VectorUpsert('id-2', createRandomVector(2)),
23-
new VectorUpsert('id-3', createRandomVector(2)),
23+
new VectorUpsert('id-1', vector: createRandomVector(2)),
24+
new VectorUpsert('id-2', vector: createRandomVector(2)),
25+
new VectorUpsert('id-3', vector: createRandomVector(2)),
2426
]);
2527
$this->waitForIndex($this->namespace);
2628

@@ -34,13 +36,29 @@ public function test_delete_vectors(): void
3436
$this->assertSame(1, $info->vectorCount);
3537
}
3638

39+
public function test_delete_single_vector(): void
40+
{
41+
$this->namespace->upsertMany([
42+
new VectorUpsert('id-1', vector: createRandomVector(2)),
43+
new VectorUpsert('id-2', vector: createRandomVector(2)),
44+
new VectorUpsert('id-3', vector: createRandomVector(2)),
45+
]);
46+
$this->waitForIndex($this->namespace);
47+
48+
$result = $this->namespace->delete('id-1');
49+
50+
$this->assertEquals(1, $result->deleted);
51+
$info = $this->namespace->getNamespaceInfo();
52+
$this->assertSame(2, $info->vectorCount);
53+
}
54+
3755
public function test_delete_vectors_from_a_query_result_results(): void
3856
{
3957
$vector = createRandomVector(2);
4058
$this->namespace->upsertMany([
4159
new VectorUpsert('id-1', $vector),
42-
new VectorUpsert('id-2', createRandomVector(2)),
43-
new VectorUpsert('id-3', createRandomVector(2)),
60+
new VectorUpsert('id-2', vector: createRandomVector(2)),
61+
new VectorUpsert('id-3', vector: createRandomVector(2)),
4462
]);
4563
$this->waitForIndex($this->namespace);
4664

@@ -53,4 +71,56 @@ public function test_delete_vectors_from_a_query_result_results(): void
5371

5472
$this->assertEquals(2, $result->deleted);
5573
}
74+
75+
public function test_delete_vectors_using_an_id_prefix(): void
76+
{
77+
$this->namespace->upsertMany([
78+
new VectorUpsert('users:1', vector: createRandomVector(2)),
79+
new VectorUpsert('users:2', vector: createRandomVector(2)),
80+
new VectorUpsert('posts:1', vector: createRandomVector(2)),
81+
]);
82+
$this->waitForIndex($this->namespace);
83+
84+
$result = $this->namespace->delete(new VectorDeleteByPrefix(
85+
prefix: 'users:',
86+
));
87+
88+
$this->assertEquals(2, $result->deleted);
89+
$this->assertEquals(1, $this->namespace->getNamespaceInfo()->vectorCount);
90+
}
91+
92+
public function test_delete_vectors_using_a_metadata_filter(): void
93+
{
94+
$this->namespace->upsertMany([
95+
new VectorUpsert(
96+
id: 'users:1',
97+
vector: createRandomVector(2),
98+
metadata: [
99+
'salary' => 1000,
100+
],
101+
),
102+
new VectorUpsert(
103+
id: 'users:2',
104+
vector: createRandomVector(2),
105+
metadata: [
106+
'salary' => 2000,
107+
],
108+
),
109+
new VectorUpsert(
110+
id: 'users:3',
111+
vector: createRandomVector(2),
112+
metadata: [
113+
'salary' => 3000,
114+
],
115+
),
116+
]);
117+
$this->waitForIndex($this->namespace);
118+
119+
$result = $this->namespace->delete(new VectorDeleteByMetadataFilter(
120+
filter: 'salary > 1000',
121+
));
122+
123+
$this->assertEquals(2, $result->deleted);
124+
$this->assertEquals(1, $this->namespace->getNamespaceInfo()->vectorCount);
125+
}
56126
}

0 commit comments

Comments
 (0)