Skip to main content

qdrant_client/serverless/
models.rs

1//! Public models for the Qdrant Serverless collection management API.
2//!
3//! **In development — do not use yet.** Part of the experimental serverless client;
4//! the API may change without notice.
5//!
6//! These mirror the tenant-facing serverless config: unlike the regular client's
7//! collection models, they deliberately expose no storage internals (quantization,
8//! WAL, segments, on-disk placement, ...) — the serverless manager decides those.
9
10use std::collections::HashMap;
11
12/// Distance metric used to compare dense vectors.
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
16pub enum Distance {
17    Cosine,
18    Euclid,
19    Dot,
20    Manhattan,
21}
22
23/// How much of the original vector precision may be traded for cost.
24///
25/// The manager turns this into a concrete quantization / datatype choice.
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
29pub enum PrecisionTier {
30    Low,
31    Medium,
32    High,
33    Full,
34}
35
36/// Full-text tokenizer, mirrors Qdrant's `TokenizerType`.
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
38#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
39#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
40pub enum Tokenizer {
41    Prefix,
42    Whitespace,
43    Word,
44    Multilingual,
45}
46
47/// Configuration of a single dense (embedding) vector.
48#[derive(Debug, Clone, PartialEq, Eq)]
49#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
50pub struct DenseVectorConfig {
51    /// Dimensionality of the embedding, e.g. 512, 1536, 3072.
52    pub size: u64,
53    /// Distance metric used to compare vectors.
54    pub distance: Distance,
55    /// Store several sub-vectors per point and compare with max-sim.
56    pub multivector: bool,
57    /// Precision/cost trade-off for this vector. Unset: HIGH.
58    pub precision_tier: Option<PrecisionTier>,
59}
60
61impl DenseVectorConfig {
62    /// Create a dense vector config with the given size and distance.
63    pub fn new(size: u64, distance: Distance) -> Self {
64        Self {
65            size,
66            distance,
67            multivector: false,
68            precision_tier: None,
69        }
70    }
71
72    /// Enable multi-vector (late-interaction) storage.
73    pub fn multivector(mut self, multivector: bool) -> Self {
74        self.multivector = multivector;
75        self
76    }
77
78    /// Set the precision/cost trade-off.
79    pub fn precision_tier(mut self, tier: PrecisionTier) -> Self {
80        self.precision_tier = Some(tier);
81        self
82    }
83}
84
85/// Configuration of a single sparse vector.
86#[derive(Debug, Clone, PartialEq, Eq, Default)]
87#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
88pub struct SparseVectorConfig {
89    /// Apply the IDF modifier at query time.
90    pub use_idf: bool,
91    /// Precision/cost trade-off for this vector. Unset: HIGH.
92    pub precision_tier: Option<PrecisionTier>,
93}
94
95impl SparseVectorConfig {
96    /// Create a sparse vector config.
97    pub fn new() -> Self {
98        Self::default()
99    }
100
101    /// Enable IDF weighting (BM25-style).
102    pub fn use_idf(mut self, use_idf: bool) -> Self {
103        self.use_idf = use_idf;
104        self
105    }
106
107    /// Set the precision/cost trade-off.
108    pub fn precision_tier(mut self, tier: PrecisionTier) -> Self {
109        self.precision_tier = Some(tier);
110        self
111    }
112}
113
114/// Prefix matching options for a keyword index. Presence enables prefix matching.
115#[derive(Debug, Clone, PartialEq, Eq, Default)]
116#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
117pub struct KeywordPrefixParams;
118
119/// Exact match on string values, e.g. `color: "red"`.
120#[derive(Debug, Clone, PartialEq, Eq, Default)]
121#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
122pub struct KeywordIndex {
123    /// If set, enable prefix matching (`match: { "prefix": ... }`) on this field.
124    pub prefix: Option<KeywordPrefixParams>,
125}
126
127impl KeywordIndex {
128    pub fn new() -> Self {
129        Self::default()
130    }
131
132    /// Enable prefix matching on this keyword field.
133    pub fn with_prefix(mut self) -> Self {
134        self.prefix = Some(KeywordPrefixParams);
135        self
136    }
137}
138
139/// Exact match and/or range filters on integers. Both default to enabled.
140#[derive(Debug, Clone, PartialEq, Eq, Default)]
141#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
142pub struct IntegerIndex {
143    pub lookup: Option<bool>,
144    pub range: Option<bool>,
145}
146
147impl IntegerIndex {
148    pub fn new() -> Self {
149        Self::default()
150    }
151
152    pub fn lookup(mut self, lookup: bool) -> Self {
153        self.lookup = Some(lookup);
154        self
155    }
156
157    pub fn range(mut self, range: bool) -> Self {
158        self.range = Some(range);
159        self
160    }
161}
162
163/// Range filters on floating point (and integer) numbers.
164#[derive(Debug, Clone, PartialEq, Eq, Default)]
165#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
166pub struct FloatIndex;
167
168/// Exact match on UUID strings; like keyword but stored compactly.
169#[derive(Debug, Clone, PartialEq, Eq, Default)]
170#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
171pub struct UuidIndex;
172
173/// Range filters on RFC 3339 datetimes.
174#[derive(Debug, Clone, PartialEq, Eq, Default)]
175#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
176pub struct DatetimeIndex;
177
178/// Tokens ignored by a full-text index.
179#[derive(Debug, Clone, PartialEq, Eq, Default)]
180#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
181pub struct StopwordsSet {
182    /// Languages whose predefined stopword lists to apply (e.g. `"english"`).
183    pub languages: Vec<String>,
184    /// Extra stopwords to ignore, merged with the language lists.
185    pub custom: Vec<String>,
186}
187
188impl StopwordsSet {
189    pub fn new() -> Self {
190        Self::default()
191    }
192
193    pub fn languages(mut self, languages: impl IntoIterator<Item = impl Into<String>>) -> Self {
194        self.languages = languages.into_iter().map(Into::into).collect();
195        self
196    }
197
198    pub fn custom(mut self, custom: impl IntoIterator<Item = impl Into<String>>) -> Self {
199        self.custom = custom.into_iter().map(Into::into).collect();
200        self
201    }
202}
203
204/// Snowball stemming for a full-text index.
205#[derive(Debug, Clone, PartialEq, Eq)]
206#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
207pub struct SnowballParams {
208    /// Language for the snowball algorithm, e.g. `"english"`.
209    pub language: String,
210}
211
212impl SnowballParams {
213    pub fn new(language: impl Into<String>) -> Self {
214        Self {
215            language: language.into(),
216        }
217    }
218}
219
220/// Stemming algorithm for a full-text index. Unset: no stemming.
221#[derive(Debug, Clone, PartialEq, Eq)]
222#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
223#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
224pub enum StemmingAlgorithm {
225    /// Snowball stemmer for the given language.
226    Snowball(SnowballParams),
227    /// Explicitly disable stemming.
228    Disabled,
229}
230
231/// Full-text filtering on string values.
232#[derive(Debug, Clone, PartialEq, Eq, Default)]
233#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
234pub struct TextIndex {
235    pub tokenizer: Option<Tokenizer>,
236    pub lowercase: Option<bool>,
237    pub phrase_matching: Option<bool>,
238    pub min_token_len: Option<u64>,
239    pub max_token_len: Option<u64>,
240    pub ascii_folding: Option<bool>,
241    pub stopwords: Option<StopwordsSet>,
242    pub stemmer: Option<StemmingAlgorithm>,
243}
244
245impl TextIndex {
246    pub fn new() -> Self {
247        Self::default()
248    }
249
250    pub fn tokenizer(mut self, tokenizer: Tokenizer) -> Self {
251        self.tokenizer = Some(tokenizer);
252        self
253    }
254
255    pub fn lowercase(mut self, lowercase: bool) -> Self {
256        self.lowercase = Some(lowercase);
257        self
258    }
259
260    pub fn phrase_matching(mut self, phrase_matching: bool) -> Self {
261        self.phrase_matching = Some(phrase_matching);
262        self
263    }
264
265    pub fn min_token_len(mut self, min_token_len: u64) -> Self {
266        self.min_token_len = Some(min_token_len);
267        self
268    }
269
270    pub fn max_token_len(mut self, max_token_len: u64) -> Self {
271        self.max_token_len = Some(max_token_len);
272        self
273    }
274
275    pub fn ascii_folding(mut self, ascii_folding: bool) -> Self {
276        self.ascii_folding = Some(ascii_folding);
277        self
278    }
279
280    pub fn stopwords(mut self, stopwords: StopwordsSet) -> Self {
281        self.stopwords = Some(stopwords);
282        self
283    }
284
285    pub fn stemmer(mut self, stemmer: StemmingAlgorithm) -> Self {
286        self.stemmer = Some(stemmer);
287        self
288    }
289}
290
291/// Geo radius / bounding box / polygon filters on `{lon, lat}` values.
292#[derive(Debug, Clone, PartialEq, Eq, Default)]
293#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
294pub struct GeoIndex;
295
296/// Exact match on booleans.
297#[derive(Debug, Clone, PartialEq, Eq, Default)]
298#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
299pub struct BoolIndex;
300
301/// One payload index. Only the kind of filter the field supports is chosen;
302/// storage placement of the index is the manager's decision.
303#[derive(Debug, Clone, PartialEq, Eq)]
304#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
305#[cfg_attr(feature = "serde", serde(tag = "type", rename_all = "snake_case"))]
306pub enum PayloadIndex {
307    Keyword(KeywordIndex),
308    Integer(IntegerIndex),
309    Float(FloatIndex),
310    Uuid(UuidIndex),
311    Datetime(DatetimeIndex),
312    Text(TextIndex),
313    Geo(GeoIndex),
314    Bool(BoolIndex),
315}
316
317impl From<KeywordIndex> for PayloadIndex {
318    fn from(value: KeywordIndex) -> Self {
319        Self::Keyword(value)
320    }
321}
322
323impl From<IntegerIndex> for PayloadIndex {
324    fn from(value: IntegerIndex) -> Self {
325        Self::Integer(value)
326    }
327}
328
329impl From<FloatIndex> for PayloadIndex {
330    fn from(value: FloatIndex) -> Self {
331        Self::Float(value)
332    }
333}
334
335impl From<UuidIndex> for PayloadIndex {
336    fn from(value: UuidIndex) -> Self {
337        Self::Uuid(value)
338    }
339}
340
341impl From<DatetimeIndex> for PayloadIndex {
342    fn from(value: DatetimeIndex) -> Self {
343        Self::Datetime(value)
344    }
345}
346
347impl From<TextIndex> for PayloadIndex {
348    fn from(value: TextIndex) -> Self {
349        Self::Text(value)
350    }
351}
352
353impl From<GeoIndex> for PayloadIndex {
354    fn from(value: GeoIndex) -> Self {
355        Self::Geo(value)
356    }
357}
358
359impl From<BoolIndex> for PayloadIndex {
360    fn from(value: BoolIndex) -> Self {
361        Self::Bool(value)
362    }
363}
364
365/// The tenant-facing collection config.
366///
367/// Vector maps are keyed by vector name; the empty name `""` is the unnamed
368/// default vector. Payload indexes are keyed by payload field name
369/// (JSON path, e.g. `user_id` or `meta.tags`).
370#[derive(Debug, Clone, PartialEq, Eq, Default)]
371#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
372pub struct CollectionConfig {
373    pub dense_vectors: HashMap<String, DenseVectorConfig>,
374    pub sparse_vectors: HashMap<String, SparseVectorConfig>,
375    pub payload_indexes: HashMap<String, PayloadIndex>,
376}
377
378impl CollectionConfig {
379    pub fn new() -> Self {
380        Self::default()
381    }
382
383    /// Register a single unnamed dense vector (empty name `""`).
384    pub fn dense_vector(mut self, config: DenseVectorConfig) -> Self {
385        self.dense_vectors.insert(String::new(), config);
386        self
387    }
388
389    /// Register a named dense vector.
390    pub fn named_dense_vector(
391        mut self,
392        name: impl Into<String>,
393        config: DenseVectorConfig,
394    ) -> Self {
395        self.dense_vectors.insert(name.into(), config);
396        self
397    }
398
399    /// Register a single unnamed sparse vector (empty name `""`).
400    pub fn sparse_vector(mut self, config: SparseVectorConfig) -> Self {
401        self.sparse_vectors.insert(String::new(), config);
402        self
403    }
404
405    /// Register a named sparse vector.
406    pub fn named_sparse_vector(
407        mut self,
408        name: impl Into<String>,
409        config: SparseVectorConfig,
410    ) -> Self {
411        self.sparse_vectors.insert(name.into(), config);
412        self
413    }
414
415    /// Add a payload index for the given field path.
416    pub fn payload_index(
417        mut self,
418        field: impl Into<String>,
419        index: impl Into<PayloadIndex>,
420    ) -> Self {
421        self.payload_indexes.insert(field.into(), index.into());
422        self
423    }
424}
425
426/// Result of [`super::QdrantServerless::create_collection`].
427#[derive(Debug, Clone, PartialEq)]
428#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
429pub struct CreateCollectionResult {
430    /// Tenant-facing name of the collection.
431    pub collection_name: String,
432    /// Outcome, e.g. `"created"`.
433    pub result: String,
434    /// Time spent to process the request, in seconds.
435    pub time: f64,
436}
437
438/// Result of [`super::QdrantServerless::delete_collection`].
439#[derive(Debug, Clone, PartialEq)]
440#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
441pub struct DeleteCollectionResult {
442    /// Whether the collection existed and was deleted.
443    pub deleted: bool,
444    /// Time spent to process the request, in seconds.
445    pub time: f64,
446}
447
448/// A collection's configuration and stats, as returned by [`super::QdrantServerless::get_collection`].
449///
450/// `point_count` is eventually consistent and absent until stats have been
451/// written for the collection.
452#[derive(Debug, Clone, PartialEq)]
453#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
454pub struct CollectionInfo {
455    pub exists: bool,
456    pub config: Option<CollectionConfig>,
457    pub point_count: Option<u64>,
458    /// Time spent to process the request, in seconds.
459    pub time: f64,
460}
461
462/// Request for [`super::QdrantServerless::list_collections`].
463///
464/// Defaults to the server page size (20, max 100). Pass `offset_token` from a
465/// previous response's `next_offset_token` to fetch the next page.
466#[derive(Debug, Clone, PartialEq, Eq, Default)]
467#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
468pub struct ListCollections {
469    /// Maximum number of collections to return. Unset: server default (20).
470    pub limit: Option<u32>,
471    /// Opaque token from a previous page's `next_offset_token`.
472    pub offset_token: Option<String>,
473}
474
475impl ListCollections {
476    /// Create an empty request (server defaults).
477    pub fn new() -> Self {
478        Self::default()
479    }
480}
481
482/// Builder for [`ListCollections`].
483#[must_use]
484#[derive(Clone, Default)]
485pub struct ListCollectionsBuilder {
486    limit: Option<u32>,
487    offset_token: Option<String>,
488}
489
490impl ListCollectionsBuilder {
491    /// Create an empty builder (server defaults).
492    pub fn new() -> Self {
493        Self::default()
494    }
495
496    /// Maximum number of collections to return (must be 1..=100).
497    pub fn limit(mut self, limit: u32) -> Self {
498        self.limit = Some(limit);
499        self
500    }
501
502    /// Opaque token from a previous page's `next_offset_token`.
503    pub fn offset_token(mut self, offset_token: impl Into<String>) -> Self {
504        self.offset_token = Some(offset_token.into());
505        self
506    }
507
508    /// Build the [`ListCollections`] request.
509    pub fn build(self) -> ListCollections {
510        self.into()
511    }
512}
513
514impl From<ListCollectionsBuilder> for ListCollections {
515    fn from(builder: ListCollectionsBuilder) -> Self {
516        ListCollections {
517            limit: builder.limit,
518            offset_token: builder.offset_token,
519        }
520    }
521}
522
523/// One collection in a [`super::QdrantServerless::list_collections`] listing.
524#[derive(Debug, Clone, PartialEq, Eq)]
525#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
526pub struct CollectionSummary {
527    pub collection_name: String,
528    pub point_count: Option<u64>,
529}
530
531/// A page of collections returned by [`super::QdrantServerless::list_collections`].
532#[derive(Debug, Clone, PartialEq)]
533#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
534pub struct CollectionsList {
535    pub collections: Vec<CollectionSummary>,
536    /// Opaque token to pass as `offset_token` for the next page. Absent when done.
537    pub next_offset_token: Option<String>,
538    /// Time spent to process the request, in seconds.
539    pub time: f64,
540}