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 pub fn with_metadata(mut self, metadata: impl Serialize) -> crate::Result<Self> {
146 match serde_json::to_value(metadata)? {
147 Value::Object(payload) => {
148 self.payload = payload;
149 Ok(self)
150 }
151 _ => Err(crate::Error::Invalid(
152 "point metadata must serialize to a JSON object".into(),
153 )),
154 }
155 }
156}
157
158#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
160#[serde(rename_all = "snake_case")]
161#[non_exhaustive]
162pub enum Distance {
163 #[default]
165 Cosine,
166}
167
168#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
174#[serde(default)]
175pub struct IndexConfig {
176 pub tables: usize,
178 pub signature_bits: usize,
180 pub projection_seed: u64,
182 pub full_scan_threshold: usize,
184 pub default_probes: usize,
186 pub default_candidate_limit: usize,
188}
189
190impl Default for IndexConfig {
191 fn default() -> Self {
192 Self {
193 tables: 12,
194 signature_bits: 12,
195 projection_seed: 0x6769_742d_7664_6231,
196 full_scan_threshold: 1_000,
197 default_probes: 96,
198 default_candidate_limit: 10_000,
199 }
200 }
201}
202
203#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
205#[serde(default)]
206pub struct CollectionConfig {
207 pub dimension: usize,
209 pub distance: Distance,
211 pub vector_space: Option<String>,
213 pub index: IndexConfig,
215}
216
217impl CollectionConfig {
218 pub fn new(dimension: usize) -> Self {
220 Self {
221 dimension,
222 ..Self::default()
223 }
224 }
225
226 #[must_use]
228 pub fn with_vector_space(mut self, vector_space: impl Into<String>) -> Self {
229 self.vector_space = Some(vector_space.into());
230 self
231 }
232
233 #[must_use]
237 pub fn with_index(mut self, index: IndexConfig) -> Self {
238 self.index = index;
239 self
240 }
241}
242
243impl Default for CollectionConfig {
244 fn default() -> Self {
245 Self {
246 dimension: 0,
247 distance: Distance::Cosine,
248 vector_space: None,
249 index: IndexConfig::default(),
250 }
251 }
252}
253
254#[derive(Clone, Debug, Serialize, Deserialize)]
256pub struct MatchValue {
257 pub value: Value,
259}
260
261#[derive(Clone, Debug, Default, Serialize, Deserialize)]
263pub struct Range {
264 pub gt: Option<f64>,
266 pub gte: Option<f64>,
268 pub lt: Option<f64>,
270 pub lte: Option<f64>,
272}
273
274#[derive(Clone, Debug, Serialize, Deserialize)]
276#[serde(untagged)]
277#[non_exhaustive]
278pub enum Condition {
279 HasField {
281 has_field: String,
283 },
284 FieldIn {
286 key: String,
288 any: Vec<Value>,
290 },
291 FieldNotIn {
293 key: String,
295 none: Vec<Value>,
297 },
298 FieldContains {
300 key: String,
302 contains: Value,
304 },
305 DocumentContains {
307 document_contains: String,
309 },
310 DocumentRegex {
312 document_regex: String,
314 },
315 Field {
317 key: String,
319 #[serde(rename = "match", skip_serializing_if = "Option::is_none")]
321 matches: Option<MatchValue>,
322 #[serde(skip_serializing_if = "Option::is_none")]
324 range: Option<Range>,
325 },
326 HasId {
328 has_id: Vec<PointId>,
330 },
331 Nested(Filter),
333}
334
335impl Condition {
336 pub fn matches(key: impl Into<String>, value: impl Into<Value>) -> Self {
338 Self::Field {
339 key: key.into(),
340 matches: Some(MatchValue {
341 value: value.into(),
342 }),
343 range: None,
344 }
345 }
346
347 pub fn range(key: impl Into<String>, range: Range) -> Self {
349 Self::Field {
350 key: key.into(),
351 matches: None,
352 range: Some(range),
353 }
354 }
355
356 pub fn has_id(ids: impl IntoIterator<Item = PointId>) -> Self {
358 Self::HasId {
359 has_id: ids.into_iter().collect(),
360 }
361 }
362
363 pub fn exists(key: impl Into<String>) -> Self {
365 Self::HasField {
366 has_field: key.into(),
367 }
368 }
369
370 pub fn is_in(key: impl Into<String>, values: impl IntoIterator<Item = Value>) -> Self {
372 Self::FieldIn {
373 key: key.into(),
374 any: values.into_iter().collect(),
375 }
376 }
377
378 pub fn not_in(key: impl Into<String>, values: impl IntoIterator<Item = Value>) -> Self {
380 Self::FieldNotIn {
381 key: key.into(),
382 none: values.into_iter().collect(),
383 }
384 }
385
386 pub fn contains(key: impl Into<String>, value: impl Into<Value>) -> Self {
388 Self::FieldContains {
389 key: key.into(),
390 contains: value.into(),
391 }
392 }
393
394 pub fn document_contains(value: impl Into<String>) -> Self {
396 Self::DocumentContains {
397 document_contains: value.into(),
398 }
399 }
400
401 pub fn document_regex(value: impl Into<String>) -> Self {
403 Self::DocumentRegex {
404 document_regex: value.into(),
405 }
406 }
407}
408
409#[derive(Clone, Debug, Default, Serialize, Deserialize)]
414#[serde(default)]
415pub struct Filter {
416 pub must: Vec<Condition>,
418 pub should: Vec<Condition>,
420 pub must_not: Vec<Condition>,
422}
423
424impl Filter {
425 pub fn must(conditions: impl IntoIterator<Item = Condition>) -> Self {
427 Self {
428 must: conditions.into_iter().collect(),
429 ..Self::default()
430 }
431 }
432}
433
434#[derive(Clone, Debug, Default, Serialize, Deserialize)]
436#[serde(default)]
437pub struct QueryParams {
438 pub exact: Option<bool>,
440 pub probes: usize,
442 pub candidate_limit: usize,
444}
445
446#[derive(Clone, Debug, Serialize, Deserialize)]
448#[serde(default)]
449pub struct Query {
450 pub vector: Vec<f32>,
452 pub limit: usize,
454 pub filter: Option<Filter>,
456 pub with_payload: bool,
458 pub with_vector: bool,
460 pub expected_vector_space: Option<String>,
462 pub params: QueryParams,
464}
465
466impl Query {
467 pub fn new(vector: impl IntoIterator<Item = f32>, limit: usize) -> Self {
469 Self {
470 vector: vector.into_iter().collect(),
471 limit,
472 ..Self::default()
473 }
474 }
475
476 pub fn exact(vector: impl IntoIterator<Item = f32>, limit: usize) -> Self {
478 let mut query = Self::new(vector, limit);
479 query.params.exact = Some(true);
480 query
481 }
482
483 pub fn approximate(vector: impl IntoIterator<Item = f32>, limit: usize) -> Self {
485 let mut query = Self::new(vector, limit);
486 query.params.exact = Some(false);
487 query
488 }
489
490 #[must_use]
492 pub fn with_filter(mut self, filter: Filter) -> Self {
493 self.filter = Some(filter);
494 self
495 }
496
497 #[must_use]
499 pub fn with_payload(mut self) -> Self {
500 self.with_payload = true;
501 self
502 }
503
504 #[must_use]
506 pub fn with_vector(mut self) -> Self {
507 self.with_vector = true;
508 self
509 }
510
511 #[must_use]
513 pub fn in_vector_space(mut self, vector_space: impl Into<String>) -> Self {
514 self.expected_vector_space = Some(vector_space.into());
515 self
516 }
517
518 #[must_use]
520 pub fn with_params(mut self, params: QueryParams) -> Self {
521 self.params = params;
522 self
523 }
524}
525
526impl Default for Query {
527 fn default() -> Self {
528 Self {
529 vector: Vec::new(),
530 limit: 10,
531 filter: None,
532 with_payload: false,
533 with_vector: false,
534 expected_vector_space: None,
535 params: QueryParams::default(),
536 }
537 }
538}
539
540#[derive(Clone, Debug, Serialize, Deserialize)]
542pub struct ScoredPoint {
543 pub id: PointId,
545 pub score: f32,
547 #[serde(skip_serializing_if = "Option::is_none")]
549 pub payload: Option<JsonObject>,
550 #[serde(skip_serializing_if = "Option::is_none")]
552 pub vector: Option<Vec<f32>>,
553}
554
555#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
557#[serde(rename_all = "snake_case")]
558#[non_exhaustive]
559pub enum QueryMode {
560 Exact,
562 Approximate,
564}
565
566#[derive(Clone, Debug, Serialize, Deserialize)]
568pub struct QueryStats {
569 pub mode: QueryMode,
571 pub collection_points: usize,
573 pub buckets_probed: usize,
575 pub candidates_discovered: usize,
577 pub vectors_scored: usize,
579 pub probe_limit_exhausted: bool,
581 pub candidate_limit_exhausted: bool,
583}
584
585#[derive(Clone, Debug, Serialize, Deserialize)]
587pub struct QueryResult {
588 pub root: ObjectId,
590 pub points: Vec<ScoredPoint>,
592 pub stats: QueryStats,
594}
595
596#[derive(Clone, Debug, Default, Serialize, Deserialize)]
598#[serde(default)]
599pub struct GetRequest {
600 pub ids: Vec<PointId>,
602 pub filter: Option<Filter>,
604 pub offset: usize,
606 pub limit: Option<usize>,
608 pub with_payload: bool,
610 pub with_vector: bool,
612}
613
614#[derive(Clone, Debug, Serialize, Deserialize)]
616pub struct Record {
617 pub id: PointId,
619 #[serde(skip_serializing_if = "Option::is_none")]
621 pub payload: Option<JsonObject>,
622 #[serde(skip_serializing_if = "Option::is_none")]
624 pub vector: Option<Vec<f32>>,
625}
626
627#[derive(Clone, Debug, Serialize, Deserialize)]
629pub struct GetResult {
630 pub root: ObjectId,
632 pub points: Vec<Record>,
634}
635
636#[derive(Clone, Debug, Default, Serialize, Deserialize)]
638#[serde(default)]
639pub struct DeleteSelector {
640 pub ids: Vec<PointId>,
642 pub filter: Option<Filter>,
644}
645
646#[derive(Clone, Debug, Serialize, Deserialize)]
648pub struct WriteResult {
649 pub root: ObjectId,
651 pub affected_points: usize,
653}
654
655#[derive(Clone, Debug, Serialize, Deserialize)]
657pub struct MutationResult {
658 pub root: ObjectId,
660 pub points_before: usize,
662 pub points_after: usize,
664 pub operations: usize,
666}
667
668#[derive(Clone, Debug, Serialize, Deserialize)]
670pub struct CountResult {
671 pub root: ObjectId,
673 pub count: usize,
675}
676
677#[derive(Clone, Debug, Serialize, Deserialize)]
679pub struct CollectionInfo {
680 pub root: ObjectId,
682 pub name: String,
684 pub format_version: u32,
686 pub point_count: usize,
688 pub config: CollectionConfig,
690 pub read_only: bool,
692}
693
694#[derive(Clone, Debug, Serialize, Deserialize)]
696pub struct SnapshotInfo {
697 pub root: ObjectId,
699 pub format_version: u32,
701 pub point_count: usize,
703 pub config: CollectionConfig,
705}
706
707#[derive(Clone, Debug, Serialize, Deserialize)]
709#[serde(tag = "operation", rename_all = "snake_case")]
710#[non_exhaustive]
711pub enum SnapshotMutation {
712 Upsert {
714 point: Point,
716 },
717 DeleteIds {
719 ids: Vec<PointId>,
721 },
722 DeleteFilter {
724 filter: Filter,
726 },
727}
728
729impl SnapshotMutation {
730 pub fn upsert(point: Point) -> Self {
732 Self::Upsert { point }
733 }
734
735 pub fn delete_ids(ids: impl IntoIterator<Item = PointId>) -> Self {
737 Self::DeleteIds {
738 ids: ids.into_iter().collect(),
739 }
740 }
741
742 pub fn delete_filter(filter: Filter) -> Self {
744 Self::DeleteFilter { filter }
745 }
746}
747
748#[derive(Clone, Debug, Serialize, Deserialize)]
750pub struct HistoryEntry {
751 pub commit: ObjectId,
753 pub root: ObjectId,
755 pub parent: Option<ObjectId>,
757 pub message: String,
759 pub time_seconds: i64,
761}
762
763#[derive(Clone, Debug, Default, Serialize, Deserialize)]
765pub struct ObjectStats {
766 pub objects: usize,
768 pub bytes: usize,
770}
771
772#[derive(Clone, Debug, Serialize, Deserialize)]
774pub struct DiffResult {
775 pub left_root: ObjectId,
777 pub right_root: ObjectId,
779 pub added: Vec<PointId>,
781 pub removed: Vec<PointId>,
783 pub changed: Vec<PointId>,
785 pub configuration_changed: bool,
787 pub buckets_changed: bool,
789 pub shared: ObjectStats,
791 pub left_unique: ObjectStats,
793 pub right_unique: ObjectStats,
795}
796
797#[derive(Clone, Debug, Serialize, Deserialize)]
799pub struct ValidationReport {
800 pub root: ObjectId,
802 pub full: bool,
804 pub point_count: usize,
806 pub checked_buckets: usize,
808 pub valid: bool,
810}