Skip to main content

vectorizer_sdk/client/
qdrant.rs

1//! Qdrant-compatible REST surface (`/qdrant/*` endpoints).
2//!
3//! 25 methods spanning core CRUD, snapshots, sharding, cluster
4//! management, metadata, and the Qdrant 1.7+ Query API.
5//!
6//! Every method returns `serde_json::Value` because the Qdrant
7//! response shapes evolve faster than we want to chase with typed
8//! structs — the server's `/qdrant/*` translation layer keeps us
9//! source-compatible with Qdrant clients without locking the SDK
10//! to a specific Qdrant minor version.
11
12use super::VectorizerClient;
13use crate::error::{Result, VectorizerError};
14
15/// Build the standard "parse-or-fail" wrapper used by every method
16/// in this module — keeps each method to one logical statement.
17macro_rules! parse_qdrant {
18    ($response:expr, $what:literal) => {{
19        serde_json::from_str(&$response).map_err(|e| {
20            VectorizerError::server(format!(
21                concat!("Failed to parse Qdrant ", $what, " response: {}"),
22                e
23            ))
24        })
25    }};
26}
27
28impl VectorizerClient {
29    /// List all collections (Qdrant-compatible).
30    pub async fn qdrant_list_collections(&self) -> Result<serde_json::Value> {
31        let response = self
32            .make_request("GET", "/qdrant/collections", None)
33            .await?;
34        parse_qdrant!(response, "collections")
35    }
36
37    /// Get one collection's metadata (Qdrant-compatible).
38    pub async fn qdrant_get_collection(&self, name: &str) -> Result<serde_json::Value> {
39        let url = format!("/qdrant/collections/{name}");
40        let response = self.make_request("GET", &url, None).await?;
41        parse_qdrant!(response, "collection")
42    }
43
44    /// Create a collection (Qdrant-compatible).
45    pub async fn qdrant_create_collection(
46        &self,
47        name: &str,
48        config: &serde_json::Value,
49    ) -> Result<serde_json::Value> {
50        let url = format!("/qdrant/collections/{name}");
51        let payload = serde_json::json!({ "config": config });
52        let response = self.make_request("PUT", &url, Some(payload)).await?;
53        parse_qdrant!(response, "create collection")
54    }
55
56    /// Upsert points into a collection (Qdrant-compatible).
57    pub async fn qdrant_upsert_points(
58        &self,
59        collection: &str,
60        points: &serde_json::Value,
61        wait: bool,
62    ) -> Result<serde_json::Value> {
63        let url = format!("/qdrant/collections/{collection}/points");
64        let payload = serde_json::json!({ "points": points, "wait": wait });
65        let response = self.make_request("PUT", &url, Some(payload)).await?;
66        parse_qdrant!(response, "upsert points")
67    }
68
69    /// Search points (Qdrant-compatible).
70    pub async fn qdrant_search_points(
71        &self,
72        collection: &str,
73        vector: &[f32],
74        limit: Option<usize>,
75        filter: Option<&serde_json::Value>,
76        with_payload: bool,
77        with_vector: bool,
78    ) -> Result<serde_json::Value> {
79        let url = format!("/qdrant/collections/{collection}/points/search");
80        let mut payload = serde_json::json!({
81            "vector": vector,
82            "limit": limit.unwrap_or(10),
83            "with_payload": with_payload,
84            "with_vector": with_vector,
85        });
86        if let Some(filter) = filter {
87            payload["filter"] = filter.clone();
88        }
89        let response = self.make_request("POST", &url, Some(payload)).await?;
90        parse_qdrant!(response, "search")
91    }
92
93    /// Delete points by id (Qdrant-compatible).
94    pub async fn qdrant_delete_points(
95        &self,
96        collection: &str,
97        point_ids: &[serde_json::Value],
98        wait: bool,
99    ) -> Result<serde_json::Value> {
100        let url = format!("/qdrant/collections/{collection}/points/delete");
101        let payload = serde_json::json!({ "points": point_ids, "wait": wait });
102        let response = self.make_request("POST", &url, Some(payload)).await?;
103        parse_qdrant!(response, "delete points")
104    }
105
106    /// Retrieve points by id (Qdrant-compatible).
107    pub async fn qdrant_retrieve_points(
108        &self,
109        collection: &str,
110        point_ids: &[serde_json::Value],
111        with_payload: bool,
112        with_vector: bool,
113    ) -> Result<serde_json::Value> {
114        let ids_str = point_ids
115            .iter()
116            .map(|id| match id {
117                serde_json::Value::String(s) => s.clone(),
118                serde_json::Value::Number(n) => n.to_string(),
119                _ => serde_json::to_string(id).unwrap_or_default(),
120            })
121            .collect::<Vec<_>>()
122            .join(",");
123        let url = format!(
124            "/qdrant/collections/{collection}/points?ids={ids_str}\
125             &with_payload={with_payload}&with_vector={with_vector}",
126        );
127        let response = self.make_request("GET", &url, None).await?;
128        parse_qdrant!(response, "retrieve points")
129    }
130
131    /// Count points (Qdrant-compatible).
132    pub async fn qdrant_count_points(
133        &self,
134        collection: &str,
135        filter: Option<&serde_json::Value>,
136    ) -> Result<serde_json::Value> {
137        let url = format!("/qdrant/collections/{collection}/points/count");
138        let payload = if let Some(filter) = filter {
139            serde_json::json!({ "filter": filter })
140        } else {
141            serde_json::json!({})
142        };
143        let response = self.make_request("POST", &url, Some(payload)).await?;
144        parse_qdrant!(response, "count points")
145    }
146
147    // ── Snapshots ───────────────────────────────────────────────
148
149    /// List snapshots for a collection (Qdrant-compatible).
150    pub async fn qdrant_list_collection_snapshots(
151        &self,
152        collection: &str,
153    ) -> Result<serde_json::Value> {
154        let url = format!("/qdrant/collections/{collection}/snapshots");
155        let response = self.make_request("GET", &url, None).await?;
156        parse_qdrant!(response, "list snapshots")
157    }
158
159    /// Create a snapshot for a collection (Qdrant-compatible).
160    pub async fn qdrant_create_collection_snapshot(
161        &self,
162        collection: &str,
163    ) -> Result<serde_json::Value> {
164        let url = format!("/qdrant/collections/{collection}/snapshots");
165        let response = self.make_request("POST", &url, None).await?;
166        parse_qdrant!(response, "create snapshot")
167    }
168
169    /// Delete a snapshot (Qdrant-compatible).
170    pub async fn qdrant_delete_collection_snapshot(
171        &self,
172        collection: &str,
173        snapshot_name: &str,
174    ) -> Result<serde_json::Value> {
175        let url = format!("/qdrant/collections/{collection}/snapshots/{snapshot_name}");
176        let response = self.make_request("DELETE", &url, None).await?;
177        parse_qdrant!(response, "delete snapshot")
178    }
179
180    /// Recover a collection from a snapshot location
181    /// (Qdrant-compatible).
182    pub async fn qdrant_recover_collection_snapshot(
183        &self,
184        collection: &str,
185        location: &str,
186    ) -> Result<serde_json::Value> {
187        let url = format!("/qdrant/collections/{collection}/snapshots/recover");
188        let payload = serde_json::json!({ "location": location });
189        let response = self.make_request("POST", &url, Some(payload)).await?;
190        parse_qdrant!(response, "recover snapshot")
191    }
192
193    /// List all snapshots across collections (Qdrant-compatible).
194    pub async fn qdrant_list_all_snapshots(&self) -> Result<serde_json::Value> {
195        let response = self.make_request("GET", "/qdrant/snapshots", None).await?;
196        parse_qdrant!(response, "list all snapshots")
197    }
198
199    /// Create a full-cluster snapshot (Qdrant-compatible).
200    pub async fn qdrant_create_full_snapshot(&self) -> Result<serde_json::Value> {
201        let response = self.make_request("POST", "/qdrant/snapshots", None).await?;
202        parse_qdrant!(response, "create full snapshot")
203    }
204
205    // ── Sharding ────────────────────────────────────────────────
206
207    /// List shard keys for a collection (Qdrant-compatible).
208    pub async fn qdrant_list_shard_keys(&self, collection: &str) -> Result<serde_json::Value> {
209        let url = format!("/qdrant/collections/{collection}/shards");
210        let response = self.make_request("GET", &url, None).await?;
211        parse_qdrant!(response, "list shard keys")
212    }
213
214    /// Create a shard key (Qdrant-compatible).
215    pub async fn qdrant_create_shard_key(
216        &self,
217        collection: &str,
218        shard_key: &serde_json::Value,
219    ) -> Result<serde_json::Value> {
220        let url = format!("/qdrant/collections/{collection}/shards");
221        let payload = serde_json::json!({ "shard_key": shard_key });
222        let response = self.make_request("PUT", &url, Some(payload)).await?;
223        parse_qdrant!(response, "create shard key")
224    }
225
226    /// Delete a shard key (Qdrant-compatible).
227    pub async fn qdrant_delete_shard_key(
228        &self,
229        collection: &str,
230        shard_key: &serde_json::Value,
231    ) -> Result<serde_json::Value> {
232        let url = format!("/qdrant/collections/{collection}/shards/delete");
233        let payload = serde_json::json!({ "shard_key": shard_key });
234        let response = self.make_request("POST", &url, Some(payload)).await?;
235        parse_qdrant!(response, "delete shard key")
236    }
237
238    // ── Cluster + metadata ──────────────────────────────────────
239
240    /// Get cluster status (Qdrant-compatible).
241    pub async fn qdrant_get_cluster_status(&self) -> Result<serde_json::Value> {
242        let response = self.make_request("GET", "/qdrant/cluster", None).await?;
243        parse_qdrant!(response, "cluster status")
244    }
245
246    /// Trigger a cluster recovery on the current peer
247    /// (Qdrant-compatible).
248    pub async fn qdrant_cluster_recover(&self) -> Result<serde_json::Value> {
249        let response = self
250            .make_request("POST", "/qdrant/cluster/recover", None)
251            .await?;
252        parse_qdrant!(response, "cluster recover")
253    }
254
255    /// Remove a peer from the cluster (Qdrant-compatible).
256    pub async fn qdrant_remove_peer(&self, peer_id: &str) -> Result<serde_json::Value> {
257        let url = format!("/qdrant/cluster/peer/{peer_id}");
258        let response = self.make_request("DELETE", &url, None).await?;
259        parse_qdrant!(response, "remove peer")
260    }
261
262    /// List metadata keys (Qdrant-compatible).
263    pub async fn qdrant_list_metadata_keys(&self) -> Result<serde_json::Value> {
264        let response = self
265            .make_request("GET", "/qdrant/cluster/metadata/keys", None)
266            .await?;
267        parse_qdrant!(response, "list metadata keys")
268    }
269
270    /// Get one metadata key (Qdrant-compatible).
271    pub async fn qdrant_get_metadata_key(&self, key: &str) -> Result<serde_json::Value> {
272        let url = format!("/qdrant/cluster/metadata/keys/{key}");
273        let response = self.make_request("GET", &url, None).await?;
274        parse_qdrant!(response, "get metadata key")
275    }
276
277    /// Update one metadata key (Qdrant-compatible).
278    pub async fn qdrant_update_metadata_key(
279        &self,
280        key: &str,
281        value: &serde_json::Value,
282    ) -> Result<serde_json::Value> {
283        let url = format!("/qdrant/cluster/metadata/keys/{key}");
284        let payload = serde_json::json!({ "value": value });
285        let response = self.make_request("PUT", &url, Some(payload)).await?;
286        parse_qdrant!(response, "update metadata key")
287    }
288
289    // ── Query API (Qdrant 1.7+) ─────────────────────────────────
290
291    /// Query points using the Qdrant 1.7+ Query API.
292    pub async fn qdrant_query_points(
293        &self,
294        collection: &str,
295        request: &serde_json::Value,
296    ) -> Result<serde_json::Value> {
297        let url = format!("/qdrant/collections/{collection}/points/query");
298        let response = self
299            .make_request("POST", &url, Some(request.clone()))
300            .await?;
301        parse_qdrant!(response, "query points")
302    }
303
304    /// Batch-query points using the Qdrant 1.7+ Query API.
305    pub async fn qdrant_batch_query_points(
306        &self,
307        collection: &str,
308        request: &serde_json::Value,
309    ) -> Result<serde_json::Value> {
310        let url = format!("/qdrant/collections/{collection}/points/query/batch");
311        let response = self
312            .make_request("POST", &url, Some(request.clone()))
313            .await?;
314        parse_qdrant!(response, "batch query points")
315    }
316
317    /// Query points with grouping (Qdrant 1.7+ Query API).
318    pub async fn qdrant_query_points_groups(
319        &self,
320        collection: &str,
321        request: &serde_json::Value,
322    ) -> Result<serde_json::Value> {
323        let url = format!("/qdrant/collections/{collection}/points/query/groups");
324        let response = self
325            .make_request("POST", &url, Some(request.clone()))
326            .await?;
327        parse_qdrant!(response, "query points groups")
328    }
329
330    /// Search points with grouping (Qdrant Search Groups API).
331    pub async fn qdrant_search_points_groups(
332        &self,
333        collection: &str,
334        request: &serde_json::Value,
335    ) -> Result<serde_json::Value> {
336        let url = format!("/qdrant/collections/{collection}/points/search/groups");
337        let response = self
338            .make_request("POST", &url, Some(request.clone()))
339            .await?;
340        parse_qdrant!(response, "search points groups")
341    }
342
343    /// Search matrix pairs (Qdrant Search Matrix API).
344    pub async fn qdrant_search_matrix_pairs(
345        &self,
346        collection: &str,
347        request: &serde_json::Value,
348    ) -> Result<serde_json::Value> {
349        let url = format!("/qdrant/collections/{collection}/points/search/matrix/pairs");
350        let response = self
351            .make_request("POST", &url, Some(request.clone()))
352            .await?;
353        parse_qdrant!(response, "search matrix pairs")
354    }
355
356    /// Search matrix offsets (Qdrant Search Matrix API).
357    pub async fn qdrant_search_matrix_offsets(
358        &self,
359        collection: &str,
360        request: &serde_json::Value,
361    ) -> Result<serde_json::Value> {
362        let url = format!("/qdrant/collections/{collection}/points/search/matrix/offsets");
363        let response = self
364            .make_request("POST", &url, Some(request.clone()))
365            .await?;
366        parse_qdrant!(response, "search matrix offsets")
367    }
368}