Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
84 / 84
100.00% covered (success)
100.00%
7 / 7
CRAP
100.00% covered (success)
100.00%
1 / 1
DataPlane
100.00% covered (success)
100.00%
84 / 84
100.00% covered (success)
100.00%
7 / 7
34
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 upsert
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
3
 query
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
1 / 1
7
 fetch
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
4
 delete
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
6
 update
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
6
 listVectorIds
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
7
1<?php
2
3declare(strict_types=1);
4
5namespace Mbvb1223\Pinecone\Data;
6
7use GuzzleHttp\Client;
8use GuzzleHttp\Exception\GuzzleException;
9use Mbvb1223\Pinecone\Errors\PineconeException;
10use Mbvb1223\Pinecone\Errors\PineconeValidationException;
11use Mbvb1223\Pinecone\Utils\HandlesApiResponse;
12
13class DataPlane
14{
15    use HandlesApiResponse;
16
17    public function __construct(private readonly Client $httpClient)
18    {
19    }
20
21    /**
22     * @param array<int, array{id: string, values: array<float>, sparseValues?: array{indices: array<int>, values: array<float>}, metadata?: array<string, mixed>}> $vectors
23     * @return array<string, mixed>
24     */
25    public function upsert(array $vectors, ?string $namespace = null): array
26    {
27        try {
28            $payload = ['vectors' => $vectors];
29            if ($namespace !== null) {
30                $payload['namespace'] = $namespace;
31            }
32
33            $response = $this->httpClient->post('/vectors/upsert', [
34                'json' => $payload,
35            ]);
36
37            return $this->handleResponse($response);
38        } catch (GuzzleException $e) {
39            throw new PineconeException('Failed to upsert vectors: ' . $e->getMessage(), 0, $e);
40        }
41    }
42
43    /**
44     * @param array<float> $vector
45     * @param array<string, mixed>|null $filter Metadata filter expression
46     * @param array{indices: array<int>, values: array<float>}|null $sparseVector
47     * @return array<string, mixed>
48     */
49    public function query(
50        array $vector = [],
51        ?string $id = null,
52        int $topK = 10,
53        ?array $filter = null,
54        ?string $namespace = null,
55        bool $includeValues = false,
56        bool $includeMetadata = true,
57        ?array $sparseVector = null,
58    ): array {
59        try {
60            $payload = [
61                'topK' => $topK,
62                'includeValues' => $includeValues,
63                'includeMetadata' => $includeMetadata,
64            ];
65
66            if (!empty($vector)) {
67                $payload['vector'] = $vector;
68            }
69
70            if ($id !== null) {
71                $payload['id'] = $id;
72            }
73
74            if ($filter !== null) {
75                $payload['filter'] = $filter;
76            }
77
78            if ($namespace !== null) {
79                $payload['namespace'] = $namespace;
80            }
81
82            if ($sparseVector !== null) {
83                $payload['sparseVector'] = $sparseVector;
84            }
85
86            $response = $this->httpClient->post('/query', [
87                'json' => $payload,
88            ]);
89
90            return $this->handleResponse($response);
91        } catch (GuzzleException $e) {
92            throw new PineconeException('Failed to query vectors: ' . $e->getMessage(), 0, $e);
93        }
94    }
95
96    /**
97     * @param array<int, string> $ids
98     * @return array<string, array<string, mixed>>
99     */
100    public function fetch(array $ids, ?string $namespace = null): array
101    {
102        if (empty($ids)) {
103            throw new PineconeValidationException('At least one vector ID is required for fetch.');
104        }
105
106        try {
107            // Pinecone expects repeated query params (ids=vec1&ids=vec2),
108            // not PHP-style array params (ids[0]=vec1&ids[1]=vec2),
109            // so we build the query string manually.
110            $query = implode('&', array_map(fn ($id) => 'ids=' . urlencode((string) $id), $ids));
111            if ($namespace !== null) {
112                $query .= '&namespace=' . urlencode($namespace);
113            }
114
115            $response = $this->httpClient->get('/vectors/fetch?' . $query);
116
117            return $this->handleResponse($response)['vectors'] ?? [];
118        } catch (GuzzleException $e) {
119            throw new PineconeException('Failed to fetch vectors: ' . $e->getMessage(), 0, $e);
120        }
121    }
122
123    /**
124     * @param array<int, string> $ids
125     * @param array<string, mixed>|null $filter Metadata filter expression
126     * @return array<string, mixed>
127     */
128    public function delete(array $ids = [], ?array $filter = null, ?string $namespace = null, bool $deleteAll = false): array
129    {
130        try {
131            $payload = [];
132
133            if ($deleteAll) {
134                $payload['deleteAll'] = true;
135            } else {
136                if (!empty($ids)) {
137                    $payload['ids'] = $ids;
138                }
139                if ($filter !== null) {
140                    $payload['filter'] = $filter;
141                }
142            }
143
144            if ($namespace !== null) {
145                $payload['namespace'] = $namespace;
146            }
147
148            $response = $this->httpClient->post('/vectors/delete', [
149                'json' => $payload,
150            ]);
151
152            return $this->handleResponse($response);
153        } catch (GuzzleException $e) {
154            throw new PineconeException('Failed to delete vectors: ' . $e->getMessage(), 0, $e);
155        }
156    }
157
158    /**
159     * @param array<float> $values
160     * @param array<string, mixed>|null $setMetadata
161     * @param array{indices: array<int>, values: array<float>}|null $sparseValues
162     * @return array<string, mixed>
163     */
164    public function update(string $id, array $values = [], ?array $setMetadata = null, ?string $namespace = null, ?array $sparseValues = null): array
165    {
166        try {
167            $payload = ['id' => $id];
168
169            if (!empty($values)) {
170                $payload['values'] = $values;
171            }
172
173            if ($setMetadata !== null) {
174                $payload['setMetadata'] = $setMetadata;
175            }
176
177            if ($namespace !== null) {
178                $payload['namespace'] = $namespace;
179            }
180
181            if ($sparseValues !== null) {
182                $payload['sparseValues'] = $sparseValues;
183            }
184
185            $response = $this->httpClient->post('/vectors/update', [
186                'json' => $payload,
187            ]);
188
189            return $this->handleResponse($response);
190        } catch (GuzzleException $e) {
191            throw new PineconeException('Failed to update vector: ' . $e->getMessage(), 0, $e);
192        }
193    }
194
195    /** @return array<string, mixed> */
196    public function listVectorIds(?string $prefix = null, ?int $limit = null, ?string $paginationToken = null, ?string $namespace = null): array
197    {
198        try {
199            $params = [];
200            if ($prefix !== null) {
201                $params['prefix'] = $prefix;
202            }
203            if ($limit !== null) {
204                $params['limit'] = $limit;
205            }
206            if ($paginationToken !== null) {
207                $params['paginationToken'] = $paginationToken;
208            }
209            if ($namespace !== null) {
210                $params['namespace'] = $namespace;
211            }
212
213            $options = !empty($params) ? ['query' => $params] : [];
214            $response = $this->httpClient->get('/vectors/list', $options);
215
216            return $this->handleResponse($response);
217        } catch (GuzzleException $e) {
218            throw new PineconeException('Failed to list vector IDs: ' . $e->getMessage(), 0, $e);
219        }
220    }
221}