1use crate::collection::CollectionType;
12use crate::columnar::{ColumnarProfile, DocumentMode};
13use crate::vector_ann::VectorQuantization;
14use crate::vector_distance::DistanceMetric;
15use crate::vector_dtype::VectorStorageDtype;
16
17#[repr(u8)]
24#[derive(
25 Debug,
26 Clone,
27 Copy,
28 Default,
29 PartialEq,
30 Eq,
31 Hash,
32 serde::Serialize,
33 serde::Deserialize,
34 zerompk::ToMessagePack,
35 zerompk::FromMessagePack,
36)]
37#[non_exhaustive]
38pub enum PrimaryEngine {
39 #[default]
41 Document = 0,
42 Strict = 1,
44 KeyValue = 2,
46 Columnar = 3,
48 Spatial = 4,
50 Vector = 10,
52}
53
54impl PrimaryEngine {
55 pub fn infer_from_collection_type(ct: &CollectionType) -> Self {
60 match ct {
61 CollectionType::Document(DocumentMode::Schemaless) => Self::Document,
62 CollectionType::Document(DocumentMode::Strict(_)) => Self::Strict,
63 CollectionType::Columnar(ColumnarProfile::Plain) => Self::Columnar,
64 CollectionType::Columnar(ColumnarProfile::Timeseries { .. }) => Self::Columnar,
65 CollectionType::Columnar(ColumnarProfile::Spatial { .. }) => Self::Spatial,
66 CollectionType::KeyValue(_) => Self::KeyValue,
67 }
68 }
69}
70
71#[derive(
77 Debug,
78 Clone,
79 PartialEq,
80 serde::Serialize,
81 serde::Deserialize,
82 zerompk::ToMessagePack,
83 zerompk::FromMessagePack,
84)]
85pub struct VectorPrimaryConfig {
86 pub vector_field: String,
88 pub dim: u32,
90 pub quantization: VectorQuantization,
92 pub m: u8,
94 pub ef_construction: u16,
96 pub metric: DistanceMetric,
98 pub storage_dtype: VectorStorageDtype,
104 pub payload_indexes: Vec<(String, PayloadIndexKind)>,
110}
111
112impl Default for VectorPrimaryConfig {
113 fn default() -> Self {
114 Self {
115 vector_field: String::new(),
116 dim: 0,
117 quantization: VectorQuantization::default(),
118 m: 16,
119 ef_construction: 200,
120 metric: DistanceMetric::Cosine,
121 storage_dtype: VectorStorageDtype::F32,
122 payload_indexes: Vec::new(),
123 }
124 }
125}
126
127#[derive(
131 Debug,
132 Clone,
133 Copy,
134 Default,
135 PartialEq,
136 Eq,
137 serde::Serialize,
138 serde::Deserialize,
139 zerompk::ToMessagePack,
140 zerompk::FromMessagePack,
141)]
142#[non_exhaustive]
143pub enum PayloadIndexKind {
144 #[default]
145 Equality,
146 Range,
147 Boolean,
148}
149
150#[derive(
155 Debug,
156 Clone,
157 PartialEq,
158 serde::Serialize,
159 serde::Deserialize,
160 zerompk::ToMessagePack,
161 zerompk::FromMessagePack,
162)]
163#[non_exhaustive]
164pub enum PayloadAtom {
165 Eq(String, crate::Value),
167 In(String, Vec<crate::Value>),
169 Range {
173 field: String,
174 low: Option<crate::Value>,
175 low_inclusive: bool,
176 high: Option<crate::Value>,
177 high_inclusive: bool,
178 },
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184
185 #[test]
186 fn primary_engine_default_is_document() {
187 assert_eq!(PrimaryEngine::default(), PrimaryEngine::Document);
188 }
189
190 #[test]
191 fn infer_from_collection_type_document_schemaless() {
192 let ct = CollectionType::document();
193 assert_eq!(
194 PrimaryEngine::infer_from_collection_type(&ct),
195 PrimaryEngine::Document
196 );
197 }
198
199 #[test]
200 fn infer_from_collection_type_document_strict() {
201 use crate::columnar::{ColumnDef, ColumnType, StrictSchema};
202 let schema = StrictSchema::new(vec![
203 ColumnDef::required("id", ColumnType::Int64).with_primary_key(),
204 ])
205 .unwrap();
206 let ct = CollectionType::strict(schema);
207 assert_eq!(
208 PrimaryEngine::infer_from_collection_type(&ct),
209 PrimaryEngine::Strict
210 );
211 }
212
213 #[test]
214 fn infer_from_collection_type_columnar_plain() {
215 let ct = CollectionType::columnar();
216 assert_eq!(
217 PrimaryEngine::infer_from_collection_type(&ct),
218 PrimaryEngine::Columnar
219 );
220 }
221
222 #[test]
223 fn infer_from_collection_type_columnar_timeseries() {
224 let ct = CollectionType::timeseries("ts", "1h");
225 assert_eq!(
226 PrimaryEngine::infer_from_collection_type(&ct),
227 PrimaryEngine::Columnar
228 );
229 }
230
231 #[test]
232 fn infer_from_collection_type_columnar_spatial() {
233 let ct = CollectionType::spatial("geom");
234 assert_eq!(
235 PrimaryEngine::infer_from_collection_type(&ct),
236 PrimaryEngine::Spatial
237 );
238 }
239
240 #[test]
241 fn infer_from_collection_type_kv() {
242 use crate::columnar::{ColumnDef, ColumnType, StrictSchema};
243 let schema = StrictSchema::new(vec![
244 ColumnDef::required("k", ColumnType::String).with_primary_key(),
245 ])
246 .unwrap();
247 let ct = CollectionType::kv(schema);
248 assert_eq!(
249 PrimaryEngine::infer_from_collection_type(&ct),
250 PrimaryEngine::KeyValue
251 );
252 }
253
254 #[test]
255 fn primary_engine_serde_roundtrip() {
256 for variant in [
257 PrimaryEngine::Document,
258 PrimaryEngine::Strict,
259 PrimaryEngine::KeyValue,
260 PrimaryEngine::Columnar,
261 PrimaryEngine::Spatial,
262 PrimaryEngine::Vector,
263 ] {
264 let json = sonic_rs::to_string(&variant).unwrap();
265 let back: PrimaryEngine = sonic_rs::from_str(&json).unwrap();
266 assert_eq!(back, variant);
267 }
268 }
269
270 #[test]
271 fn primary_engine_msgpack_roundtrip() {
272 for variant in [
273 PrimaryEngine::Document,
274 PrimaryEngine::Strict,
275 PrimaryEngine::KeyValue,
276 PrimaryEngine::Columnar,
277 PrimaryEngine::Spatial,
278 PrimaryEngine::Vector,
279 ] {
280 let bytes = zerompk::to_msgpack_vec(&variant).unwrap();
281 let back: PrimaryEngine = zerompk::from_msgpack(&bytes).unwrap();
282 assert_eq!(back, variant);
283 }
284 }
285
286 #[test]
287 fn vector_primary_config_serde_roundtrip() {
288 let cfg = VectorPrimaryConfig {
289 vector_field: "embedding".to_string(),
290 dim: 1024,
291 quantization: VectorQuantization::RaBitQ,
292 m: 32,
293 ef_construction: 200,
294 metric: DistanceMetric::Cosine,
295 storage_dtype: VectorStorageDtype::F32,
296 payload_indexes: vec![
297 ("category".to_string(), PayloadIndexKind::Equality),
298 ("timestamp".to_string(), PayloadIndexKind::Range),
299 ],
300 };
301 let json = sonic_rs::to_string(&cfg).unwrap();
302 let back: VectorPrimaryConfig = sonic_rs::from_str(&json).unwrap();
303 assert_eq!(back, cfg);
304 }
305
306 #[test]
307 fn vector_primary_config_msgpack_roundtrip() {
308 let cfg = VectorPrimaryConfig {
309 vector_field: "vec".to_string(),
310 dim: 512,
311 quantization: VectorQuantization::Bbq,
312 m: 16,
313 ef_construction: 100,
314 metric: DistanceMetric::L2,
315 storage_dtype: VectorStorageDtype::F32,
316 payload_indexes: vec![],
317 };
318 let bytes = zerompk::to_msgpack_vec(&cfg).unwrap();
319 let back: VectorPrimaryConfig = zerompk::from_msgpack(&bytes).unwrap();
320 assert_eq!(back, cfg);
321 }
322}