1use serde::{Deserialize, Serialize};
4use serde_json::{Map, Value};
5use std::fmt;
6use std::str::FromStr;
7
8pub type JsonObject = Map<String, Value>;
10
11#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
16#[serde(untagged)]
17pub enum PointId {
18 String(String),
20 UInt(u64),
22}
23
24impl PointId {
25 pub(crate) fn canonical_bytes(&self) -> Vec<u8> {
26 match self {
27 Self::String(value) => {
28 let mut bytes = vec![b's', 0];
29 bytes.extend_from_slice(value.as_bytes());
30 bytes
31 }
32 Self::UInt(value) => {
33 let mut bytes = vec![b'u', 0];
34 bytes.extend_from_slice(&value.to_be_bytes());
35 bytes
36 }
37 }
38 }
39}
40
41impl From<&str> for PointId {
42 fn from(value: &str) -> Self {
43 Self::String(value.to_owned())
44 }
45}
46
47impl From<String> for PointId {
48 fn from(value: String) -> Self {
49 Self::String(value)
50 }
51}
52
53impl From<u64> for PointId {
54 fn from(value: u64) -> Self {
55 Self::UInt(value)
56 }
57}
58
59impl Ord for PointId {
60 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
61 self.canonical_bytes().cmp(&other.canonical_bytes())
62 }
63}
64
65impl PartialOrd for PointId {
66 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
67 Some(self.cmp(other))
68 }
69}
70
71impl fmt::Display for PointId {
72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73 match self {
74 Self::String(value) => write!(f, "{value}"),
75 Self::UInt(value) => write!(f, "{value}"),
76 }
77 }
78}
79
80#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
82#[serde(transparent)]
83pub struct ObjectId(pub String);
84
85impl fmt::Display for ObjectId {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 f.write_str(&self.0)
88 }
89}
90
91impl FromStr for ObjectId {
92 type Err = git2::Error;
93
94 fn from_str(value: &str) -> Result<Self, Self::Err> {
95 git2::Oid::from_str(value)?;
96 Ok(Self(value.to_owned()))
97 }
98}
99
100impl From<git2::Oid> for ObjectId {
101 fn from(value: git2::Oid) -> Self {
102 Self(value.to_string())
103 }
104}
105
106impl AsRef<str> for ObjectId {
107 fn as_ref(&self) -> &str {
108 &self.0
109 }
110}
111
112#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
114pub struct Point {
115 pub id: PointId,
117 pub vector: Vec<f32>,
119 #[serde(default)]
121 pub payload: JsonObject,
122}
123
124impl Point {
125 pub fn new(id: impl Into<PointId>, vector: impl IntoIterator<Item = f32>) -> Self {
127 Self {
128 id: id.into(),
129 vector: vector.into_iter().collect(),
130 payload: JsonObject::new(),
131 }
132 }
133
134 #[must_use]
136 pub fn with_payload(mut self, payload: JsonObject) -> Self {
137 self.payload = payload;
138 self
139 }
140}
141
142#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
144#[serde(rename_all = "snake_case")]
145#[non_exhaustive]
146pub enum Distance {
147 #[default]
149 Cosine,
150}
151
152#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
158#[serde(default)]
159pub struct IndexConfig {
160 pub tables: usize,
162 pub signature_bits: usize,
164 pub projection_seed: u64,
166 pub full_scan_threshold: usize,
168 pub default_probes: usize,
170 pub default_candidate_limit: usize,
172}
173
174impl Default for IndexConfig {
175 fn default() -> Self {
176 Self {
177 tables: 12,
178 signature_bits: 12,
179 projection_seed: 0x6769_742d_7664_6231,
180 full_scan_threshold: 1_000,
181 default_probes: 96,
182 default_candidate_limit: 10_000,
183 }
184 }
185}
186
187#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
189#[serde(default)]
190pub struct CollectionConfig {
191 pub dimension: usize,
193 pub distance: Distance,
195 pub vector_space: Option<String>,
197 pub index: IndexConfig,
199}
200
201impl CollectionConfig {
202 pub fn new(dimension: usize) -> Self {
204 Self {
205 dimension,
206 ..Self::default()
207 }
208 }
209
210 #[must_use]
212 pub fn with_vector_space(mut self, vector_space: impl Into<String>) -> Self {
213 self.vector_space = Some(vector_space.into());
214 self
215 }
216
217 #[must_use]
221 pub fn with_index(mut self, index: IndexConfig) -> Self {
222 self.index = index;
223 self
224 }
225}
226
227impl Default for CollectionConfig {
228 fn default() -> Self {
229 Self {
230 dimension: 0,
231 distance: Distance::Cosine,
232 vector_space: None,
233 index: IndexConfig::default(),
234 }
235 }
236}
237
238#[derive(Clone, Debug, Serialize, Deserialize)]
240pub struct MatchValue {
241 pub value: Value,
243}
244
245#[derive(Clone, Debug, Default, Serialize, Deserialize)]
247pub struct Range {
248 pub gt: Option<f64>,
250 pub gte: Option<f64>,
252 pub lt: Option<f64>,
254 pub lte: Option<f64>,
256}
257
258#[derive(Clone, Debug, Serialize, Deserialize)]
260#[serde(untagged)]
261#[non_exhaustive]
262pub enum Condition {
263 Field {
265 key: String,
267 #[serde(rename = "match", skip_serializing_if = "Option::is_none")]
269 matches: Option<MatchValue>,
270 #[serde(skip_serializing_if = "Option::is_none")]
272 range: Option<Range>,
273 },
274 HasId {
276 has_id: Vec<PointId>,
278 },
279 Nested(Filter),
281}
282
283impl Condition {
284 pub fn matches(key: impl Into<String>, value: impl Into<Value>) -> Self {
286 Self::Field {
287 key: key.into(),
288 matches: Some(MatchValue {
289 value: value.into(),
290 }),
291 range: None,
292 }
293 }
294
295 pub fn range(key: impl Into<String>, range: Range) -> Self {
297 Self::Field {
298 key: key.into(),
299 matches: None,
300 range: Some(range),
301 }
302 }
303
304 pub fn has_id(ids: impl IntoIterator<Item = PointId>) -> Self {
306 Self::HasId {
307 has_id: ids.into_iter().collect(),
308 }
309 }
310}
311
312#[derive(Clone, Debug, Default, Serialize, Deserialize)]
317#[serde(default)]
318pub struct Filter {
319 pub must: Vec<Condition>,
321 pub should: Vec<Condition>,
323 pub must_not: Vec<Condition>,
325}
326
327impl Filter {
328 pub fn must(conditions: impl IntoIterator<Item = Condition>) -> Self {
330 Self {
331 must: conditions.into_iter().collect(),
332 ..Self::default()
333 }
334 }
335}
336
337#[derive(Clone, Debug, Default, Serialize, Deserialize)]
339#[serde(default)]
340pub struct QueryParams {
341 pub exact: Option<bool>,
343 pub probes: usize,
345 pub candidate_limit: usize,
347}
348
349#[derive(Clone, Debug, Serialize, Deserialize)]
351#[serde(default)]
352pub struct Query {
353 pub vector: Vec<f32>,
355 pub limit: usize,
357 pub filter: Option<Filter>,
359 pub with_payload: bool,
361 pub with_vector: bool,
363 pub expected_vector_space: Option<String>,
365 pub params: QueryParams,
367}
368
369impl Query {
370 pub fn new(vector: impl IntoIterator<Item = f32>, limit: usize) -> Self {
372 Self {
373 vector: vector.into_iter().collect(),
374 limit,
375 ..Self::default()
376 }
377 }
378
379 pub fn exact(vector: impl IntoIterator<Item = f32>, limit: usize) -> Self {
381 let mut query = Self::new(vector, limit);
382 query.params.exact = Some(true);
383 query
384 }
385
386 pub fn approximate(vector: impl IntoIterator<Item = f32>, limit: usize) -> Self {
388 let mut query = Self::new(vector, limit);
389 query.params.exact = Some(false);
390 query
391 }
392
393 #[must_use]
395 pub fn with_filter(mut self, filter: Filter) -> Self {
396 self.filter = Some(filter);
397 self
398 }
399
400 #[must_use]
402 pub fn with_payload(mut self) -> Self {
403 self.with_payload = true;
404 self
405 }
406
407 #[must_use]
409 pub fn with_vector(mut self) -> Self {
410 self.with_vector = true;
411 self
412 }
413
414 #[must_use]
416 pub fn in_vector_space(mut self, vector_space: impl Into<String>) -> Self {
417 self.expected_vector_space = Some(vector_space.into());
418 self
419 }
420
421 #[must_use]
423 pub fn with_params(mut self, params: QueryParams) -> Self {
424 self.params = params;
425 self
426 }
427}
428
429impl Default for Query {
430 fn default() -> Self {
431 Self {
432 vector: Vec::new(),
433 limit: 10,
434 filter: None,
435 with_payload: false,
436 with_vector: false,
437 expected_vector_space: None,
438 params: QueryParams::default(),
439 }
440 }
441}
442
443#[derive(Clone, Debug, Serialize, Deserialize)]
445pub struct ScoredPoint {
446 pub id: PointId,
448 pub score: f32,
450 #[serde(skip_serializing_if = "Option::is_none")]
452 pub payload: Option<JsonObject>,
453 #[serde(skip_serializing_if = "Option::is_none")]
455 pub vector: Option<Vec<f32>>,
456}
457
458#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
460#[serde(rename_all = "snake_case")]
461#[non_exhaustive]
462pub enum QueryMode {
463 Exact,
465 Approximate,
467}
468
469#[derive(Clone, Debug, Serialize, Deserialize)]
471pub struct QueryStats {
472 pub mode: QueryMode,
474 pub collection_points: usize,
476 pub buckets_probed: usize,
478 pub candidates_discovered: usize,
480 pub vectors_scored: usize,
482 pub probe_limit_exhausted: bool,
484 pub candidate_limit_exhausted: bool,
486}
487
488#[derive(Clone, Debug, Serialize, Deserialize)]
490pub struct QueryResult {
491 pub root: ObjectId,
493 pub points: Vec<ScoredPoint>,
495 pub stats: QueryStats,
497}
498
499#[derive(Clone, Debug, Default, Serialize, Deserialize)]
501#[serde(default)]
502pub struct GetRequest {
503 pub ids: Vec<PointId>,
505 pub filter: Option<Filter>,
507 pub offset: usize,
509 pub limit: Option<usize>,
511 pub with_payload: bool,
513 pub with_vector: bool,
515}
516
517#[derive(Clone, Debug, Serialize, Deserialize)]
519pub struct Record {
520 pub id: PointId,
522 #[serde(skip_serializing_if = "Option::is_none")]
524 pub payload: Option<JsonObject>,
525 #[serde(skip_serializing_if = "Option::is_none")]
527 pub vector: Option<Vec<f32>>,
528}
529
530#[derive(Clone, Debug, Serialize, Deserialize)]
532pub struct GetResult {
533 pub root: ObjectId,
535 pub points: Vec<Record>,
537}
538
539#[derive(Clone, Debug, Default, Serialize, Deserialize)]
541#[serde(default)]
542pub struct DeleteSelector {
543 pub ids: Vec<PointId>,
545 pub filter: Option<Filter>,
547}
548
549#[derive(Clone, Debug, Serialize, Deserialize)]
551pub struct WriteResult {
552 pub root: ObjectId,
554 pub affected_points: usize,
556}
557
558#[derive(Clone, Debug, Serialize, Deserialize)]
560pub struct CountResult {
561 pub root: ObjectId,
563 pub count: usize,
565}
566
567#[derive(Clone, Debug, Serialize, Deserialize)]
569pub struct CollectionInfo {
570 pub root: ObjectId,
572 pub name: String,
574 pub format_version: u32,
576 pub point_count: usize,
578 pub config: CollectionConfig,
580 pub read_only: bool,
582}
583
584#[derive(Clone, Debug, Serialize, Deserialize)]
586pub struct SnapshotInfo {
587 pub root: ObjectId,
589 pub format_version: u32,
591 pub point_count: usize,
593 pub config: CollectionConfig,
595}
596
597#[derive(Clone, Debug, Serialize, Deserialize)]
599#[serde(tag = "operation", rename_all = "snake_case")]
600#[non_exhaustive]
601pub enum SnapshotMutation {
602 Upsert {
604 point: Point,
606 },
607 DeleteIds {
609 ids: Vec<PointId>,
611 },
612 DeleteFilter {
614 filter: Filter,
616 },
617}
618
619impl SnapshotMutation {
620 pub fn upsert(point: Point) -> Self {
622 Self::Upsert { point }
623 }
624
625 pub fn delete_ids(ids: impl IntoIterator<Item = PointId>) -> Self {
627 Self::DeleteIds {
628 ids: ids.into_iter().collect(),
629 }
630 }
631
632 pub fn delete_filter(filter: Filter) -> Self {
634 Self::DeleteFilter { filter }
635 }
636}
637
638#[derive(Clone, Debug, Serialize, Deserialize)]
640pub struct HistoryEntry {
641 pub commit: ObjectId,
643 pub root: ObjectId,
645 pub parent: Option<ObjectId>,
647 pub message: String,
649 pub time_seconds: i64,
651}
652
653#[derive(Clone, Debug, Default, Serialize, Deserialize)]
655pub struct ObjectStats {
656 pub objects: usize,
658 pub bytes: usize,
660}
661
662#[derive(Clone, Debug, Serialize, Deserialize)]
664pub struct DiffResult {
665 pub left_root: ObjectId,
667 pub right_root: ObjectId,
669 pub added: Vec<PointId>,
671 pub removed: Vec<PointId>,
673 pub changed: Vec<PointId>,
675 pub configuration_changed: bool,
677 pub buckets_changed: bool,
679 pub shared: ObjectStats,
681 pub left_unique: ObjectStats,
683 pub right_unique: ObjectStats,
685}
686
687#[derive(Clone, Debug, Serialize, Deserialize)]
689pub struct ValidationReport {
690 pub root: ObjectId,
692 pub full: bool,
694 pub point_count: usize,
696 pub checked_buckets: usize,
698 pub valid: bool,
700}