Skip to main content

qql_core/ast/
statement.rs

1use super::{FilterExpr, FormulaExpr, Value};
2use alloc::boxed::Box;
3use alloc::string::String;
4use alloc::vec::Vec;
5
6/// Memory placement of a storage component (`cold` / `cached` / `pinned`).
7///
8/// Mirrors Qdrant 1.19 `Memory`. Data is always persisted on disk; this only
9/// controls how the component is held in RAM. `Pinned` is not valid for
10/// payload storage — parsers reject it for `payload_memory`.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
14pub enum MemoryPlacement {
15    Cold,
16    Cached,
17    Pinned,
18}
19
20impl MemoryPlacement {
21    pub const fn as_str(self) -> &'static str {
22        match self {
23            Self::Cold => "cold",
24            Self::Cached => "cached",
25            Self::Pinned => "pinned",
26        }
27    }
28
29    /// Parse a placement string (case-insensitive). Unknown values → `None`.
30    pub fn parse(s: &str) -> Option<Self> {
31        match s.to_ascii_lowercase().as_str() {
32            "cold" => Some(Self::Cold),
33            "cached" => Some(Self::Cached),
34            "pinned" => Some(Self::Pinned),
35            _ => None,
36        }
37    }
38}
39
40impl core::fmt::Display for MemoryPlacement {
41    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
42        f.write_str(self.as_str())
43    }
44}
45
46/// Dense / sparse vector storage datatype (OpenAPI `Datatype`).
47///
48/// Aliases accepted at parse: `f32`→`Float32`, `f16`→`Float16`, `u8`→`Uint8`,
49/// `t4`→`Turbo4`. Sparse indexes reject `Turbo4` (unsupported upstream).
50#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
51#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
52#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
53pub enum VectorDatatype {
54    Float32,
55    Float16,
56    Uint8,
57    Turbo4,
58}
59
60impl VectorDatatype {
61    pub const fn as_str(self) -> &'static str {
62        match self {
63            Self::Float32 => "float32",
64            Self::Float16 => "float16",
65            Self::Uint8 => "uint8",
66            Self::Turbo4 => "turbo4",
67        }
68    }
69
70    /// Parse a datatype string including short aliases. Unknown → `None`.
71    pub fn parse(s: &str) -> Option<Self> {
72        match s.to_ascii_lowercase().as_str() {
73            "float32" | "f32" => Some(Self::Float32),
74            "float16" | "f16" => Some(Self::Float16),
75            "uint8" | "u8" => Some(Self::Uint8),
76            "turbo4" | "t4" => Some(Self::Turbo4),
77            _ => None,
78        }
79    }
80
81    /// Sparse indexes support float32 / float16 / uint8 only (not turbo4).
82    pub fn parse_sparse(s: &str) -> Option<Self> {
83        match Self::parse(s) {
84            Some(Self::Turbo4) => None,
85            other => other,
86        }
87    }
88}
89
90impl core::fmt::Display for VectorDatatype {
91    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
92        f.write_str(self.as_str())
93    }
94}
95
96#[derive(Debug, Clone, PartialEq, Eq)]
97#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
98pub enum PointId {
99    Number(u64),
100    String(String),
101}
102
103#[derive(Debug, Clone, PartialEq)]
104#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
105pub enum VectorValue {
106    Dense(Vec<f32>),
107    Sparse { indices: Vec<u32>, values: Vec<f32> },
108    MultiDense(Vec<Vec<f32>>),
109}
110
111#[derive(Debug, Clone, PartialEq)]
112#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
113pub enum PointVectors {
114    Unnamed(VectorValue),
115    Named(Vec<(String, VectorValue)>),
116}
117
118#[derive(Debug, Clone, PartialEq)]
119#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
120pub enum QueryInput {
121    Text {
122        text: String,
123        model: Option<String>,
124    },
125    /// Image path or URL for dense embedding (CLIP vision, etc.).
126    /// Resolved to [`VectorValue::Dense`] before plan/dispatch.
127    Image {
128        source: String,
129        model: Option<String>,
130    },
131    Vector(VectorValue),
132    Point(PointId),
133}
134
135#[derive(Debug, Clone, Copy, PartialEq, Eq)]
136#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
137pub enum VectorKind {
138    Dense,
139    Sparse,
140}
141
142#[derive(Debug, Clone, PartialEq, Eq)]
143#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
144pub struct VectorTarget {
145    pub name: String,
146    pub kind: Option<VectorKind>,
147    /// Multivector (ColBERT-style) dense target. Filled at parse only via
148    /// `AS MULTI`, or at execution prep from collection schema
149    /// (`multivector_config`). Not a third `VectorKind` — still dense.
150    #[cfg_attr(feature = "serde", serde(default))]
151    pub multi: bool,
152}
153
154#[derive(Debug, Clone, PartialEq)]
155#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
156pub struct MmrConfig {
157    pub diversity: f64,
158    pub candidates: u64,
159}
160
161#[derive(Debug, Clone, PartialEq)]
162#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
163pub struct ContextPair {
164    pub positive: QueryInput,
165    pub negative: QueryInput,
166}
167
168#[derive(Debug, Clone, Copy, PartialEq, Eq)]
169#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
170pub enum RecommendStrategy {
171    AverageVector,
172    BestScore,
173    SumScores,
174}
175
176#[derive(Debug, Clone, PartialEq)]
177#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
178pub struct FeedbackItem {
179    pub example: QueryInput,
180    pub score: f64,
181}
182
183#[derive(Debug, Clone, Copy, PartialEq)]
184#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
185pub struct FeedbackStrategy {
186    pub a: f64,
187    pub b: f64,
188    pub c: f64,
189}
190
191#[derive(Debug, Clone, Copy, PartialEq, Eq)]
192#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
193pub enum OrderDirection {
194    Asc,
195    Desc,
196}
197
198#[derive(Debug, Clone, Copy, PartialEq, Eq)]
199#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
200pub enum FusionMethod {
201    Rrf,
202    Dbsf,
203}
204
205#[derive(Debug, Clone, PartialEq)]
206#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
207pub enum QueryCollection {
208    Explicit(String),
209    Inherited,
210}
211
212#[derive(Debug, Clone, PartialEq)]
213#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
214pub enum PrefetchSource {
215    Cte(String),
216    Query(Box<QueryStmt>),
217}
218
219#[derive(Debug, Clone, PartialEq)]
220#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
221pub struct LookupSpec {
222    pub collection: String,
223    pub vector: Option<String>,
224}
225
226#[derive(Debug, Clone, PartialEq)]
227#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
228pub struct Prefetch {
229    pub source: PrefetchSource,
230    pub filter: Option<Box<FilterExpr>>,
231    pub score_threshold: Option<f64>,
232    pub lookup: Option<LookupSpec>,
233}
234
235#[derive(Debug, Clone, PartialEq)]
236#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
237pub enum QueryExpr {
238    Points {
239        ids: Vec<PointId>,
240    },
241    Nearest {
242        input: QueryInput,
243        using: Option<VectorTarget>,
244        prefetch: Vec<Prefetch>,
245        mmr: Option<Box<MmrConfig>>,
246    },
247    Recommend {
248        positive: Vec<QueryInput>,
249        negative: Vec<QueryInput>,
250        strategy: Option<RecommendStrategy>,
251        using: Option<VectorTarget>,
252        prefetch: Vec<Prefetch>,
253    },
254    Context {
255        pairs: Vec<ContextPair>,
256        using: Option<VectorTarget>,
257        prefetch: Vec<Prefetch>,
258    },
259    Discover {
260        target: QueryInput,
261        context: Vec<ContextPair>,
262        using: Option<VectorTarget>,
263        prefetch: Vec<Prefetch>,
264    },
265    OrderBy {
266        field: String,
267        direction: OrderDirection,
268    },
269    SampleRandom,
270    Fusion {
271        method: FusionMethod,
272        prefetch: Vec<Prefetch>,
273    },
274    Formula {
275        expression: Box<FormulaExpr>,
276        defaults: Vec<(String, Value)>,
277        prefetch: Vec<Prefetch>,
278    },
279    RelevanceFeedback {
280        target: QueryInput,
281        feedback: Vec<FeedbackItem>,
282        strategy: FeedbackStrategy,
283        using: Option<VectorTarget>,
284        prefetch: Vec<Prefetch>,
285    },
286    Hybrid {
287        text: String,
288        model: Option<String>,
289        dense_vector: Option<String>,
290        sparse_vector: Option<String>,
291        fusion: FusionMethod,
292    },
293    Rerank {
294        input: QueryInput,
295        model: String,
296        using: Option<VectorTarget>,
297        prefetch: Vec<Prefetch>,
298    },
299    /// Cross-encoder pair rerank: score query against PREFETCH document texts.
300    /// Not sent to Qdrant as MaxSim — executor scores client-side then reorders.
301    CrossRerank {
302        /// Query string scored against each document.
303        query: String,
304        /// Cross-encoder model id (e.g. bge-reranker-base).
305        model: String,
306        /// Payload field holding document text (default `"text"` at resolve time).
307        field: Option<String>,
308        prefetch: Vec<Prefetch>,
309    },
310}
311
312#[derive(Debug, Clone, PartialEq, Default)]
313#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
314pub struct QuantizationSearchParams {
315    pub ignore: Option<bool>,
316    pub rescore: Option<bool>,
317    pub oversampling: Option<f64>,
318}
319
320/// Read consistency for Qdrant point reads.
321///
322/// OpenAPI `ReadConsistency` / proto `ReadConsistency`: either a replica
323/// **factor** `N`, or a named mode (`majority` / `quorum` / `all`).
324/// REST: query param on `/points/query` etc. gRPC: `read_consistency` field.
325#[derive(Debug, Clone, PartialEq)]
326#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
327pub enum ReadConsistency {
328    /// Send requests to N nodes; keep points present on all of them.
329    Factor(u64),
330    /// N/2+1 random requests; points present on all of them.
331    Majority,
332    /// All nodes; points present on a majority.
333    Quorum,
334    /// All nodes; points present on all of them.
335    All,
336}
337
338#[derive(Debug, Clone, PartialEq, Default)]
339#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
340pub struct SearchParams {
341    pub hnsw_ef: Option<u64>,
342    pub exact: Option<bool>,
343    pub acorn: Option<bool>,
344    /// ACORN selectivity ceiling in (0, 1]. Only valid with `acorn = true`.
345    pub max_selectivity: Option<f64>,
346    pub indexed_only: Option<bool>,
347    pub quantization: Option<QuantizationSearchParams>,
348    pub rrf_k: Option<u64>,
349    pub rrf_weights: Option<Vec<f64>>,
350    /// Per-query IDF corpus for sparse vectors. `None` = collection-wide (global).
351    pub idf: Option<IdfParams>,
352    /// Request-level timeout in **seconds** (OpenAPI query param / proto field).
353    /// Not part of body `SearchParams`.
354    pub timeout: Option<u64>,
355    /// Request-level read consistency (OpenAPI query param / proto field).
356    pub consistency: Option<ReadConsistency>,
357}
358
359/// Sparse-vector IDF scope.
360///
361/// `corpus = None` is collection-wide (`PARAMS (idf = 'global')`). Otherwise
362/// IDF statistics are computed over points matching the QQL filter
363/// (`PARAMS (idf = WHERE tenant_id = 'acme')`). The planner lowers the filter
364/// to a Qdrant `Filter`; the language never takes a JSON corpus object.
365#[derive(Debug, Clone, PartialEq)]
366#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
367pub struct IdfParams {
368    /// Corpus as a QQL filter. `None` = global collection statistics.
369    pub corpus: Option<FilterExpr>,
370}
371
372#[derive(Debug, Clone, PartialEq)]
373#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
374pub enum PayloadSelector {
375    All,
376    None,
377    Include(Vec<String>),
378    Exclude(Vec<String>),
379}
380
381#[derive(Debug, Clone, PartialEq)]
382#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
383pub enum VectorSelector {
384    All,
385    None,
386    Names(Vec<String>),
387}
388
389#[derive(Debug, Clone, PartialEq, Default)]
390#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
391pub struct QueryOutput {
392    pub payload: Option<PayloadSelector>,
393    pub vectors: Option<VectorSelector>,
394}
395
396#[derive(Debug, Clone, PartialEq)]
397#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
398pub struct GroupSpec {
399    pub field: String,
400    pub size: Option<u64>,
401    pub lookup: Option<String>,
402}
403
404#[derive(Debug, Clone, PartialEq, Default)]
405#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
406pub struct PageSpec {
407    pub limit: Option<u64>,
408    pub offset: Option<u64>,
409}
410
411#[derive(Debug, Clone, PartialEq)]
412#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
413pub struct Cte {
414    pub name: String,
415    pub query: Box<QueryStmt>,
416}
417
418#[derive(Debug, Clone, PartialEq)]
419#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
420pub struct QueryStmt {
421    pub ctes: Vec<Cte>,
422    pub collection: QueryCollection,
423    pub expression: QueryExpr,
424    pub filter: Option<Box<FilterExpr>>,
425    pub params: Option<SearchParams>,
426    pub score_threshold: Option<f64>,
427    pub group: Option<GroupSpec>,
428    pub output: QueryOutput,
429    pub page: PageSpec,
430    pub shard_key: Option<String>,
431}
432
433#[derive(Debug, Clone, PartialEq)]
434#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
435pub struct ScrollStmt {
436    pub collection: String,
437    pub limit: u64,
438    pub filter: Option<Box<FilterExpr>>,
439    pub after: Option<PointId>,
440    pub shard_key: Option<String>,
441    /// Optional `WITH VECTOR` selector. Defaults to no vectors when `None`.
442    pub with_vector: Option<VectorSelector>,
443}
444
445#[derive(Debug, Clone, PartialEq)]
446#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
447pub enum EmbedKind {
448    Dense {
449        model: Option<String>,
450    },
451    Sparse {
452        model: Option<String>,
453    },
454    /// Multivector / ColBERT bag (`embed_multi` → MultiDense).
455    Multi {
456        model: Option<String>,
457    },
458    /// Image / CLIP vision path or URL → dense vector (`embed_image`).
459    Image {
460        model: Option<String>,
461    },
462}
463
464#[derive(Debug, Clone, PartialEq)]
465#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
466pub struct EmbedDirective {
467    pub source_field: String,
468    pub target_vector: String,
469    pub kind: EmbedKind,
470}
471
472#[derive(Debug, Clone, PartialEq)]
473#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
474pub enum EmbeddingSpec {
475    Dense {
476        model: Option<String>,
477        vector: Option<String>,
478        field: Option<String>,
479    },
480    Sparse {
481        model: Option<String>,
482        vector: Option<String>,
483        field: Option<String>,
484    },
485    Hybrid {
486        dense_model: Option<String>,
487        dense_vector: Option<String>,
488        dense_field: Option<String>,
489        sparse_model: Option<String>,
490        sparse_vector: Option<String>,
491        sparse_field: Option<String>,
492    },
493    /// Multivector / ColBERT: text → bag of token vectors for a named multi slot.
494    MultiVector {
495        model: Option<String>,
496        vector: Option<String>,
497        field: Option<String>,
498    },
499    /// Image / CLIP vision: payload field holds a path or URL → dense vector.
500    Image {
501        model: Option<String>,
502        vector: Option<String>,
503        field: Option<String>,
504    },
505    /// Combined specs (e.g. DENSE + SPARSE + MULTI VECTOR colbert).
506    Multi(Vec<EmbeddingSpec>),
507}
508
509#[derive(Debug, Clone, PartialEq)]
510#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
511pub struct UpsertPoint {
512    pub id: PointId,
513    pub vectors: Option<PointVectors>,
514    pub payload: Vec<(String, Value)>,
515}
516
517#[derive(Debug, Clone, PartialEq)]
518#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
519pub struct UpsertStmt {
520    pub collection: String,
521    pub points: Vec<UpsertPoint>,
522    pub embedding: Option<EmbeddingSpec>,
523    pub embed: Vec<EmbedDirective>,
524    pub shard_key: Option<String>,
525}
526
527#[derive(Debug, Clone, Copy, PartialEq, Eq)]
528#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
529pub enum VectorDistance {
530    Cosine,
531    Dot,
532    Euclid,
533    Manhattan,
534}
535
536#[derive(Debug, Clone, Copy, PartialEq, Eq)]
537#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
538pub enum MultivectorComparator {
539    MaxSim,
540}
541
542#[derive(Debug, Clone, PartialEq)]
543#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
544pub struct MultivectorConfig {
545    pub comparator: MultivectorComparator,
546}
547
548#[derive(Debug, Clone, PartialEq)]
549#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
550pub struct VectorDef {
551    pub name: String,
552    pub size: u64,
553    pub distance: VectorDistance,
554    pub hnsw: Option<Box<HnswRuntimeConfig>>,
555    pub quantization: Option<Box<QuantizationConfig>>,
556    pub multivector: Option<MultivectorConfig>,
557    pub vectors: Option<Box<VectorsConfig>>,
558}
559
560#[derive(Debug, Clone, PartialEq)]
561#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
562pub struct SparseIndexConfig {
563    pub full_scan_threshold: Option<u64>,
564    pub on_disk: Option<bool>,
565    /// Storage datatype (`float32` / `float16` / `uint8`). Turbo4 is rejected.
566    pub datatype: Option<VectorDatatype>,
567    /// Memory placement of the index.
568    pub memory: Option<MemoryPlacement>,
569}
570
571#[derive(Debug, Clone, PartialEq)]
572#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
573pub struct SparseVectorDef {
574    pub name: String,
575    pub index: Option<Box<SparseIndexConfig>>,
576    pub modifier: Option<String>,
577}
578
579#[derive(Debug, Clone, Copy, PartialEq, Eq)]
580#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
581pub enum QuantizationType {
582    Scalar,
583    Binary,
584    Product,
585    Turbo,
586}
587
588#[derive(Debug, Clone, PartialEq)]
589#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
590pub struct QuantizationConfig {
591    pub qtype: QuantizationType,
592    pub always_ram: bool,
593    pub quantile: Option<f64>,
594    pub bits: Option<f64>,
595    pub compression: Option<String>,
596    pub encoding: Option<String>,
597    pub query_encoding: Option<String>,
598    /// Memory placement of quantized vectors.
599    pub memory: Option<MemoryPlacement>,
600}
601
602#[derive(Debug, Clone, PartialEq)]
603#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
604pub struct QuantizationUpdate {
605    pub disabled: bool,
606    pub config: Option<Box<QuantizationConfig>>,
607}
608
609#[derive(Debug, Clone, PartialEq)]
610#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
611pub struct HnswRuntimeConfig {
612    pub m: Option<u64>,
613    pub ef_construct: Option<u64>,
614    pub full_scan_threshold: Option<u64>,
615    pub max_indexing_threads: Option<u64>,
616    pub on_disk: Option<bool>,
617    pub payload_m: Option<u64>,
618    pub inline_storage: Option<bool>,
619    /// Memory placement of the HNSW graph.
620    pub memory: Option<MemoryPlacement>,
621}
622
623#[derive(Debug, Clone, PartialEq)]
624#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
625pub struct VectorsConfig {
626    pub on_disk: Option<bool>,
627    /// Memory placement of the original vector storage.
628    pub memory: Option<MemoryPlacement>,
629    /// Storage datatype for dense vectors.
630    pub datatype: Option<VectorDatatype>,
631}
632
633#[derive(Debug, Clone, Copy, PartialEq)]
634#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
635pub struct OptimizationThreads {
636    pub auto_: bool,
637    pub value: u64,
638}
639
640#[derive(Debug, Clone, PartialEq)]
641#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
642pub struct OptimizersRuntimeConfig {
643    pub deleted_threshold: Option<f64>,
644    pub vacuum_min_vector_number: Option<u64>,
645    pub default_segment_number: Option<u64>,
646    pub max_segment_size: Option<u64>,
647    pub memmap_threshold: Option<u64>,
648    pub indexing_threshold: Option<u64>,
649    pub flush_interval_sec: Option<u64>,
650    pub max_optimization_threads: Option<OptimizationThreads>,
651    pub prevent_unoptimized: Option<bool>,
652}
653
654#[derive(Debug, Clone, PartialEq)]
655#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
656pub struct CollectionParamsConfig {
657    pub replication_factor: Option<u64>,
658    pub write_consistency_factor: Option<u64>,
659    pub read_fan_out_factor: Option<u64>,
660    pub read_fan_out_delay_ms: Option<u64>,
661    pub on_disk_payload: Option<bool>,
662    /// Memory placement of the payload storage (`Cold` / `Cached` only; never `Pinned`).
663    pub payload_memory: Option<MemoryPlacement>,
664    pub shard_number: Option<u64>,
665    pub sharding_method: Option<String>,
666    pub shard_keys: Option<Vec<String>>,
667}
668
669#[derive(Debug, Clone, PartialEq)]
670#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
671pub struct CollectionConfig {
672    pub vectors: Option<Box<VectorsConfig>>,
673    pub hnsw: Option<Box<HnswRuntimeConfig>>,
674    pub optimizers: Option<Box<OptimizersRuntimeConfig>>,
675    pub params: Option<Box<CollectionParamsConfig>>,
676    pub quantization: Option<Box<QuantizationConfig>>,
677    pub quantization_update: Option<Box<QuantizationUpdate>>,
678}
679
680#[derive(Debug, Clone, PartialEq)]
681#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
682pub enum CollectionMode {
683    Dense {
684        model: Option<String>,
685    },
686    Hybrid {
687        dense_vector: Option<String>,
688        sparse_vector: Option<String>,
689    },
690    Rerank,
691}
692
693#[derive(Debug, Clone, PartialEq)]
694#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
695pub struct ClearPayloadStmt {
696    pub collection: String,
697    pub selector: PointSelector,
698    pub shard_key: Option<String>,
699}
700
701#[derive(Debug, Clone, PartialEq)]
702#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
703pub struct DeleteVectorStmt {
704    pub collection: String,
705    pub selector: PointSelector,
706    pub vector_names: Vec<String>,
707    pub shard_key: Option<String>,
708}
709
710#[derive(Debug, Clone, PartialEq)]
711#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
712pub struct CreateCollectionStmt {
713    pub collection: String,
714    pub mode: CollectionMode,
715    pub vectors: Vec<VectorDef>,
716    pub sparse_vectors: Vec<SparseVectorDef>,
717    pub config: Option<Box<CollectionConfig>>,
718}
719
720#[derive(Debug, Clone, PartialEq)]
721#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
722pub struct AlterCollectionStmt {
723    pub collection: String,
724    pub config: Option<Box<CollectionConfig>>,
725}
726
727#[derive(Debug, Clone, PartialEq)]
728#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
729pub struct DropCollectionStmt {
730    pub collection: String,
731}
732
733#[derive(Debug, Clone, PartialEq)]
734#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
735pub struct CreateIndexStmt {
736    pub collection: String,
737    pub field: String,
738    pub field_type: String,
739    pub options: Vec<(String, Value)>,
740}
741
742#[derive(Debug, Clone, PartialEq)]
743#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
744pub struct DropIndexStmt {
745    pub collection: String,
746    pub field: String,
747}
748
749#[derive(Debug, Clone, PartialEq)]
750#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
751pub struct CountStmt {
752    pub collection: QueryCollection,
753    pub filter: Option<Box<FilterExpr>>,
754    pub shard_key: Option<String>,
755    pub exact: Option<bool>,
756}
757
758#[derive(Debug, Clone, PartialEq)]
759#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
760pub struct CreateShardKeyStmt {
761    pub collection: String,
762    pub shard_key: String,
763    pub shards_number: Option<u64>,
764    pub replication_factor: Option<u64>,
765}
766
767#[derive(Debug, Clone, PartialEq)]
768#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
769pub struct DropShardKeyStmt {
770    pub collection: String,
771    pub shard_key: String,
772}
773
774/// Global quota configuration statement (`SET QUOTA (…) [WAIT bool]`).
775///
776/// `config` keeps the raw `key = value` pairs (enabled,
777/// max_resident_memory_percent, max_disk_usage_percent,
778/// release_margin_percent); the plan layer validates and serializes them.
779#[derive(Debug, Clone, PartialEq)]
780#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
781pub struct SetQuotaStmt {
782    pub config: Vec<(String, Value)>,
783    pub wait: Option<bool>,
784}
785
786#[derive(Debug, Clone, PartialEq)]
787#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
788pub enum PointSelector {
789    Id(PointId),
790    Ids(Vec<PointId>),
791    Filter(Box<FilterExpr>),
792}
793
794#[derive(Debug, Clone, PartialEq)]
795#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
796pub struct DeleteStmt {
797    pub collection: String,
798    pub selector: PointSelector,
799    pub shard_key: Option<String>,
800}
801
802#[derive(Debug, Clone, PartialEq)]
803#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
804pub struct UpdateVectorStmt {
805    pub collection: String,
806    pub point_id: PointId,
807    pub vector: VectorValue,
808    pub vector_name: Option<String>,
809    pub shard_key: Option<String>,
810}
811
812#[derive(Debug, Clone, PartialEq)]
813#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
814pub struct DeletePayloadStmt {
815    pub collection: String,
816    pub keys: Vec<String>,
817    pub selector: PointSelector,
818    pub shard_key: Option<String>,
819}
820
821#[derive(Debug, Clone, PartialEq)]
822#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
823pub struct UpdatePayloadStmt {
824    pub collection: String,
825    pub selector: PointSelector,
826    pub payload: Vec<(String, Value)>,
827    pub shard_key: Option<String>,
828}
829
830#[derive(Debug, Clone, PartialEq)]
831pub enum Stmt {
832    Query(Box<QueryStmt>),
833    Scroll(Box<ScrollStmt>),
834    Upsert(Box<UpsertStmt>),
835    CreateCollection(Box<CreateCollectionStmt>),
836    CreateIndex(Box<CreateIndexStmt>),
837    DropIndex(Box<DropIndexStmt>),
838    CreateShardKey(Box<CreateShardKeyStmt>),
839    DropShardKey(Box<DropShardKeyStmt>),
840    AlterCollection(Box<AlterCollectionStmt>),
841    DropCollection(Box<DropCollectionStmt>),
842    ShowCollections,
843    ShowCollection(String),
844    ShowShardKeys(String),
845    Delete(Box<DeleteStmt>),
846    ClearPayload(Box<ClearPayloadStmt>),
847    DeletePayload(Box<DeletePayloadStmt>),
848    DeleteVector(Box<DeleteVectorStmt>),
849    UpdateVector(Box<UpdateVectorStmt>),
850    UpdatePayload(Box<UpdatePayloadStmt>),
851    Count(Box<CountStmt>),
852    ShowQuotas,
853    SetQuota(Box<SetQuotaStmt>),
854}
855
856#[cfg(feature = "serde")]
857impl serde::Serialize for Stmt {
858    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
859    where
860        S: serde::Serializer,
861    {
862        use serde::ser::SerializeMap;
863        match self {
864            Stmt::Query(s) => serializer.serialize_newtype_variant("Stmt", 0, "Query", s),
865            Stmt::Scroll(s) => serializer.serialize_newtype_variant("Stmt", 1, "Scroll", s),
866            Stmt::Upsert(s) => serializer.serialize_newtype_variant("Stmt", 2, "Upsert", s),
867            Stmt::CreateCollection(s) => {
868                serializer.serialize_newtype_variant("Stmt", 3, "CreateCollection", s)
869            }
870            Stmt::CreateIndex(s) => {
871                serializer.serialize_newtype_variant("Stmt", 4, "CreateIndex", s)
872            }
873            Stmt::DropIndex(s) => serializer.serialize_newtype_variant("Stmt", 5, "DropIndex", s),
874            Stmt::CreateShardKey(s) => {
875                serializer.serialize_newtype_variant("Stmt", 6, "CreateShardKey", s)
876            }
877            Stmt::DropShardKey(s) => {
878                serializer.serialize_newtype_variant("Stmt", 7, "DropShardKey", s)
879            }
880            Stmt::AlterCollection(s) => {
881                serializer.serialize_newtype_variant("Stmt", 8, "AlterCollection", s)
882            }
883            Stmt::DropCollection(s) => {
884                serializer.serialize_newtype_variant("Stmt", 9, "DropCollection", s)
885            }
886            // Unit variant. The serialized form is the empty-object tag
887            // `{"ShowCollections": {}}` (kept for backward compatibility with
888            // consumers that already emit that shape). The manual
889            // `Deserialize` accepts both this form and the derived string
890            // form `"ShowCollections"`, so serde round-trips.
891            Stmt::ShowCollections => {
892                let mut map = serializer.serialize_map(Some(1))?;
893                let empty = std::collections::BTreeMap::<String, String>::new();
894                map.serialize_entry("ShowCollections", &empty)?;
895                map.end()
896            }
897            Stmt::ShowCollection(s) => {
898                serializer.serialize_newtype_variant("Stmt", 11, "ShowCollection", s)
899            }
900            Stmt::ShowShardKeys(s) => {
901                serializer.serialize_newtype_variant("Stmt", 12, "ShowShardKeys", s)
902            }
903            Stmt::Delete(s) => serializer.serialize_newtype_variant("Stmt", 13, "Delete", s),
904            Stmt::ClearPayload(s) => {
905                serializer.serialize_newtype_variant("Stmt", 14, "ClearPayload", s)
906            }
907            Stmt::DeletePayload(s) => {
908                serializer.serialize_newtype_variant("Stmt", 15, "DeletePayload", s)
909            }
910            Stmt::DeleteVector(s) => {
911                serializer.serialize_newtype_variant("Stmt", 16, "DeleteVector", s)
912            }
913            Stmt::UpdateVector(s) => {
914                serializer.serialize_newtype_variant("Stmt", 17, "UpdateVector", s)
915            }
916            Stmt::UpdatePayload(s) => {
917                serializer.serialize_newtype_variant("Stmt", 18, "UpdatePayload", s)
918            }
919            Stmt::Count(s) => serializer.serialize_newtype_variant("Stmt", 19, "Count", s),
920            Stmt::ShowQuotas => {
921                let mut map = serializer.serialize_map(Some(1))?;
922                let empty = std::collections::BTreeMap::<String, String>::new();
923                map.serialize_entry("ShowQuotas", &empty)?;
924                map.end()
925            }
926            Stmt::SetQuota(s) => serializer.serialize_newtype_variant("Stmt", 21, "SetQuota", s),
927        }
928    }
929}
930
931#[cfg(feature = "serde")]
932impl<'de> serde::Deserialize<'de> for Stmt {
933    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
934    where
935        D: serde::Deserializer<'de>,
936    {
937        use core::fmt;
938        use serde::de::{Error as _, IgnoredAny, MapAccess, Visitor};
939
940        struct StmtVisitor;
941
942        impl<'de> Visitor<'de> for StmtVisitor {
943            type Value = Stmt;
944
945            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
946                formatter.write_str("an externally tagged QQL statement")
947            }
948
949            /// Derived externally-tagged form of the unit variant.
950            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
951            where
952                E: serde::de::Error,
953            {
954                if value == "ShowCollections" {
955                    Ok(Stmt::ShowCollections)
956                } else {
957                    Err(E::unknown_variant(value, &["ShowCollections"]))
958                }
959            }
960
961            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
962            where
963                A: MapAccess<'de>,
964            {
965                let key = map
966                    .next_key::<alloc::string::String>()?
967                    .ok_or_else(|| A::Error::custom("expected a statement tag"))?;
968                let stmt = match key.as_str() {
969                    "Query" => Stmt::Query(map.next_value()?),
970                    "Scroll" => Stmt::Scroll(map.next_value()?),
971                    "Upsert" => Stmt::Upsert(map.next_value()?),
972                    "CreateCollection" => Stmt::CreateCollection(map.next_value()?),
973                    "CreateIndex" => Stmt::CreateIndex(map.next_value()?),
974                    "DropIndex" => Stmt::DropIndex(map.next_value()?),
975                    "CreateShardKey" => Stmt::CreateShardKey(map.next_value()?),
976                    "DropShardKey" => Stmt::DropShardKey(map.next_value()?),
977                    "AlterCollection" => Stmt::AlterCollection(map.next_value()?),
978                    "DropCollection" => Stmt::DropCollection(map.next_value()?),
979                    // Canonical serialized form (`{"ShowCollections": {}}`);
980                    // the payload is ignored, mirroring the derived impl's
981                    // permissive unit-variant handling.
982                    "ShowCollections" => {
983                        map.next_value::<IgnoredAny>()?;
984                        Stmt::ShowCollections
985                    }
986                    "ShowQuotas" => {
987                        map.next_value::<IgnoredAny>()?;
988                        Stmt::ShowQuotas
989                    }
990                    "ShowCollection" => Stmt::ShowCollection(map.next_value()?),
991                    "ShowShardKeys" => Stmt::ShowShardKeys(map.next_value()?),
992                    "Delete" => Stmt::Delete(map.next_value()?),
993                    "ClearPayload" => Stmt::ClearPayload(map.next_value()?),
994                    "DeletePayload" => Stmt::DeletePayload(map.next_value()?),
995                    "DeleteVector" => Stmt::DeleteVector(map.next_value()?),
996                    "UpdateVector" => Stmt::UpdateVector(map.next_value()?),
997                    "UpdatePayload" => Stmt::UpdatePayload(map.next_value()?),
998                    "Count" => Stmt::Count(map.next_value()?),
999                    "SetQuota" => Stmt::SetQuota(map.next_value()?),
1000                    _ => {
1001                        return Err(A::Error::unknown_variant(
1002                            &key,
1003                            &[
1004                                "Query",
1005                                "Scroll",
1006                                "Upsert",
1007                                "CreateCollection",
1008                                "CreateIndex",
1009                                "DropIndex",
1010                                "CreateShardKey",
1011                                "DropShardKey",
1012                                "AlterCollection",
1013                                "DropCollection",
1014                                "ShowCollections",
1015                                "ShowCollection",
1016                                "ShowShardKeys",
1017                                "Delete",
1018                                "ClearPayload",
1019                                "DeletePayload",
1020                                "DeleteVector",
1021                                "UpdateVector",
1022                                "UpdatePayload",
1023                                "Count",
1024                                "ShowQuotas",
1025                                "SetQuota",
1026                            ],
1027                        ));
1028                    }
1029                };
1030                if map.next_key::<IgnoredAny>()?.is_some() {
1031                    return Err(A::Error::custom("duplicate statement tag"));
1032                }
1033                Ok(stmt)
1034            }
1035        }
1036
1037        deserializer.deserialize_any(StmtVisitor)
1038    }
1039}