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 Field {
281 key: String,
283 #[serde(rename = "match", skip_serializing_if = "Option::is_none")]
285 matches: Option<MatchValue>,
286 #[serde(skip_serializing_if = "Option::is_none")]
288 range: Option<Range>,
289 },
290 HasId {
292 has_id: Vec<PointId>,
294 },
295 Nested(Filter),
297}
298
299impl Condition {
300 pub fn matches(key: impl Into<String>, value: impl Into<Value>) -> Self {
302 Self::Field {
303 key: key.into(),
304 matches: Some(MatchValue {
305 value: value.into(),
306 }),
307 range: None,
308 }
309 }
310
311 pub fn range(key: impl Into<String>, range: Range) -> Self {
313 Self::Field {
314 key: key.into(),
315 matches: None,
316 range: Some(range),
317 }
318 }
319
320 pub fn has_id(ids: impl IntoIterator<Item = PointId>) -> Self {
322 Self::HasId {
323 has_id: ids.into_iter().collect(),
324 }
325 }
326}
327
328#[derive(Clone, Debug, Default, Serialize, Deserialize)]
333#[serde(default)]
334pub struct Filter {
335 pub must: Vec<Condition>,
337 pub should: Vec<Condition>,
339 pub must_not: Vec<Condition>,
341}
342
343impl Filter {
344 pub fn must(conditions: impl IntoIterator<Item = Condition>) -> Self {
346 Self {
347 must: conditions.into_iter().collect(),
348 ..Self::default()
349 }
350 }
351}
352
353#[derive(Clone, Debug, Default, Serialize, Deserialize)]
355#[serde(default)]
356pub struct QueryParams {
357 pub exact: Option<bool>,
359 pub probes: usize,
361 pub candidate_limit: usize,
363}
364
365#[derive(Clone, Debug, Serialize, Deserialize)]
367#[serde(default)]
368pub struct Query {
369 pub vector: Vec<f32>,
371 pub limit: usize,
373 pub filter: Option<Filter>,
375 pub with_payload: bool,
377 pub with_vector: bool,
379 pub expected_vector_space: Option<String>,
381 pub params: QueryParams,
383}
384
385impl Query {
386 pub fn new(vector: impl IntoIterator<Item = f32>, limit: usize) -> Self {
388 Self {
389 vector: vector.into_iter().collect(),
390 limit,
391 ..Self::default()
392 }
393 }
394
395 pub fn exact(vector: impl IntoIterator<Item = f32>, limit: usize) -> Self {
397 let mut query = Self::new(vector, limit);
398 query.params.exact = Some(true);
399 query
400 }
401
402 pub fn approximate(vector: impl IntoIterator<Item = f32>, limit: usize) -> Self {
404 let mut query = Self::new(vector, limit);
405 query.params.exact = Some(false);
406 query
407 }
408
409 #[must_use]
411 pub fn with_filter(mut self, filter: Filter) -> Self {
412 self.filter = Some(filter);
413 self
414 }
415
416 #[must_use]
418 pub fn with_payload(mut self) -> Self {
419 self.with_payload = true;
420 self
421 }
422
423 #[must_use]
425 pub fn with_vector(mut self) -> Self {
426 self.with_vector = true;
427 self
428 }
429
430 #[must_use]
432 pub fn in_vector_space(mut self, vector_space: impl Into<String>) -> Self {
433 self.expected_vector_space = Some(vector_space.into());
434 self
435 }
436
437 #[must_use]
439 pub fn with_params(mut self, params: QueryParams) -> Self {
440 self.params = params;
441 self
442 }
443}
444
445impl Default for Query {
446 fn default() -> Self {
447 Self {
448 vector: Vec::new(),
449 limit: 10,
450 filter: None,
451 with_payload: false,
452 with_vector: false,
453 expected_vector_space: None,
454 params: QueryParams::default(),
455 }
456 }
457}
458
459#[derive(Clone, Debug, Serialize, Deserialize)]
461pub struct ScoredPoint {
462 pub id: PointId,
464 pub score: f32,
466 #[serde(skip_serializing_if = "Option::is_none")]
468 pub payload: Option<JsonObject>,
469 #[serde(skip_serializing_if = "Option::is_none")]
471 pub vector: Option<Vec<f32>>,
472}
473
474#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
476#[serde(rename_all = "snake_case")]
477#[non_exhaustive]
478pub enum QueryMode {
479 Exact,
481 Approximate,
483}
484
485#[derive(Clone, Debug, Serialize, Deserialize)]
487pub struct QueryStats {
488 pub mode: QueryMode,
490 pub collection_points: usize,
492 pub buckets_probed: usize,
494 pub candidates_discovered: usize,
496 pub vectors_scored: usize,
498 pub probe_limit_exhausted: bool,
500 pub candidate_limit_exhausted: bool,
502}
503
504#[derive(Clone, Debug, Serialize, Deserialize)]
506pub struct QueryResult {
507 pub root: ObjectId,
509 pub points: Vec<ScoredPoint>,
511 pub stats: QueryStats,
513}
514
515#[derive(Clone, Debug, Default, Serialize, Deserialize)]
517#[serde(default)]
518pub struct GetRequest {
519 pub ids: Vec<PointId>,
521 pub filter: Option<Filter>,
523 pub offset: usize,
525 pub limit: Option<usize>,
527 pub with_payload: bool,
529 pub with_vector: bool,
531}
532
533#[derive(Clone, Debug, Serialize, Deserialize)]
535pub struct Record {
536 pub id: PointId,
538 #[serde(skip_serializing_if = "Option::is_none")]
540 pub payload: Option<JsonObject>,
541 #[serde(skip_serializing_if = "Option::is_none")]
543 pub vector: Option<Vec<f32>>,
544}
545
546#[derive(Clone, Debug, Serialize, Deserialize)]
548pub struct GetResult {
549 pub root: ObjectId,
551 pub points: Vec<Record>,
553}
554
555#[derive(Clone, Debug, Default, Serialize, Deserialize)]
557#[serde(default)]
558pub struct DeleteSelector {
559 pub ids: Vec<PointId>,
561 pub filter: Option<Filter>,
563}
564
565#[derive(Clone, Debug, Serialize, Deserialize)]
567pub struct WriteResult {
568 pub root: ObjectId,
570 pub affected_points: usize,
572}
573
574#[derive(Clone, Debug, Serialize, Deserialize)]
576pub struct CountResult {
577 pub root: ObjectId,
579 pub count: usize,
581}
582
583#[derive(Clone, Debug, Serialize, Deserialize)]
585pub struct CollectionInfo {
586 pub root: ObjectId,
588 pub name: String,
590 pub format_version: u32,
592 pub point_count: usize,
594 pub config: CollectionConfig,
596 pub read_only: bool,
598}
599
600#[derive(Clone, Debug, Serialize, Deserialize)]
602pub struct SnapshotInfo {
603 pub root: ObjectId,
605 pub format_version: u32,
607 pub point_count: usize,
609 pub config: CollectionConfig,
611}
612
613#[derive(Clone, Debug, Serialize, Deserialize)]
615#[serde(tag = "operation", rename_all = "snake_case")]
616#[non_exhaustive]
617pub enum SnapshotMutation {
618 Upsert {
620 point: Point,
622 },
623 DeleteIds {
625 ids: Vec<PointId>,
627 },
628 DeleteFilter {
630 filter: Filter,
632 },
633}
634
635impl SnapshotMutation {
636 pub fn upsert(point: Point) -> Self {
638 Self::Upsert { point }
639 }
640
641 pub fn delete_ids(ids: impl IntoIterator<Item = PointId>) -> Self {
643 Self::DeleteIds {
644 ids: ids.into_iter().collect(),
645 }
646 }
647
648 pub fn delete_filter(filter: Filter) -> Self {
650 Self::DeleteFilter { filter }
651 }
652}
653
654#[derive(Clone, Debug, Serialize, Deserialize)]
656pub struct HistoryEntry {
657 pub commit: ObjectId,
659 pub root: ObjectId,
661 pub parent: Option<ObjectId>,
663 pub message: String,
665 pub time_seconds: i64,
667}
668
669#[derive(Clone, Debug, Default, Serialize, Deserialize)]
671pub struct ObjectStats {
672 pub objects: usize,
674 pub bytes: usize,
676}
677
678#[derive(Clone, Debug, Serialize, Deserialize)]
680pub struct DiffResult {
681 pub left_root: ObjectId,
683 pub right_root: ObjectId,
685 pub added: Vec<PointId>,
687 pub removed: Vec<PointId>,
689 pub changed: Vec<PointId>,
691 pub configuration_changed: bool,
693 pub buckets_changed: bool,
695 pub shared: ObjectStats,
697 pub left_unique: ObjectStats,
699 pub right_unique: ObjectStats,
701}
702
703#[derive(Clone, Debug, Serialize, Deserialize)]
705pub struct ValidationReport {
706 pub root: ObjectId,
708 pub full: bool,
710 pub point_count: usize,
712 pub checked_buckets: usize,
714 pub valid: bool,
716}