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)]
154#[serde(default)]
155pub struct IndexConfig {
156 pub tables: usize,
158 pub signature_bits: usize,
160 pub projection_seed: u64,
162 pub full_scan_threshold: usize,
164 pub default_probes: usize,
166 pub default_candidate_limit: usize,
168}
169
170impl Default for IndexConfig {
171 fn default() -> Self {
172 Self {
173 tables: 12,
174 signature_bits: 12,
175 projection_seed: 0x6769_742d_7664_6231,
176 full_scan_threshold: 1_000,
177 default_probes: 96,
178 default_candidate_limit: 10_000,
179 }
180 }
181}
182
183#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
185#[serde(default)]
186pub struct CollectionConfig {
187 pub dimension: usize,
189 pub distance: Distance,
191 pub vector_space: Option<String>,
193 pub index: IndexConfig,
195}
196
197impl CollectionConfig {
198 pub fn new(dimension: usize) -> Self {
200 Self {
201 dimension,
202 ..Self::default()
203 }
204 }
205
206 #[must_use]
208 pub fn with_vector_space(mut self, vector_space: impl Into<String>) -> Self {
209 self.vector_space = Some(vector_space.into());
210 self
211 }
212
213 #[must_use]
215 pub fn with_index(mut self, index: IndexConfig) -> Self {
216 self.index = index;
217 self
218 }
219}
220
221impl Default for CollectionConfig {
222 fn default() -> Self {
223 Self {
224 dimension: 0,
225 distance: Distance::Cosine,
226 vector_space: None,
227 index: IndexConfig::default(),
228 }
229 }
230}
231
232#[derive(Clone, Debug, Serialize, Deserialize)]
234pub struct MatchValue {
235 pub value: Value,
237}
238
239#[derive(Clone, Debug, Default, Serialize, Deserialize)]
241pub struct Range {
242 pub gt: Option<f64>,
244 pub gte: Option<f64>,
246 pub lt: Option<f64>,
248 pub lte: Option<f64>,
250}
251
252#[derive(Clone, Debug, Serialize, Deserialize)]
254#[serde(untagged)]
255#[non_exhaustive]
256pub enum Condition {
257 Field {
259 key: String,
261 #[serde(rename = "match", skip_serializing_if = "Option::is_none")]
263 matches: Option<MatchValue>,
264 #[serde(skip_serializing_if = "Option::is_none")]
266 range: Option<Range>,
267 },
268 HasId {
270 has_id: Vec<PointId>,
272 },
273 Nested(Filter),
275}
276
277impl Condition {
278 pub fn matches(key: impl Into<String>, value: impl Into<Value>) -> Self {
280 Self::Field {
281 key: key.into(),
282 matches: Some(MatchValue {
283 value: value.into(),
284 }),
285 range: None,
286 }
287 }
288
289 pub fn range(key: impl Into<String>, range: Range) -> Self {
291 Self::Field {
292 key: key.into(),
293 matches: None,
294 range: Some(range),
295 }
296 }
297
298 pub fn has_id(ids: impl IntoIterator<Item = PointId>) -> Self {
300 Self::HasId {
301 has_id: ids.into_iter().collect(),
302 }
303 }
304}
305
306#[derive(Clone, Debug, Default, Serialize, Deserialize)]
311#[serde(default)]
312pub struct Filter {
313 pub must: Vec<Condition>,
315 pub should: Vec<Condition>,
317 pub must_not: Vec<Condition>,
319}
320
321impl Filter {
322 pub fn must(conditions: impl IntoIterator<Item = Condition>) -> Self {
324 Self {
325 must: conditions.into_iter().collect(),
326 ..Self::default()
327 }
328 }
329}
330
331#[derive(Clone, Debug, Default, Serialize, Deserialize)]
333#[serde(default)]
334pub struct QueryParams {
335 pub exact: Option<bool>,
337 pub probes: usize,
339 pub candidate_limit: usize,
341}
342
343#[derive(Clone, Debug, Serialize, Deserialize)]
345#[serde(default)]
346pub struct Query {
347 pub vector: Vec<f32>,
349 pub limit: usize,
351 pub filter: Option<Filter>,
353 pub with_payload: bool,
355 pub with_vector: bool,
357 pub expected_vector_space: Option<String>,
359 pub params: QueryParams,
361}
362
363impl Query {
364 pub fn new(vector: impl IntoIterator<Item = f32>, limit: usize) -> Self {
366 Self {
367 vector: vector.into_iter().collect(),
368 limit,
369 ..Self::default()
370 }
371 }
372
373 pub fn exact(vector: impl IntoIterator<Item = f32>, limit: usize) -> Self {
375 let mut query = Self::new(vector, limit);
376 query.params.exact = Some(true);
377 query
378 }
379
380 pub fn approximate(vector: impl IntoIterator<Item = f32>, limit: usize) -> Self {
382 let mut query = Self::new(vector, limit);
383 query.params.exact = Some(false);
384 query
385 }
386
387 #[must_use]
389 pub fn with_filter(mut self, filter: Filter) -> Self {
390 self.filter = Some(filter);
391 self
392 }
393
394 #[must_use]
396 pub fn with_payload(mut self) -> Self {
397 self.with_payload = true;
398 self
399 }
400
401 #[must_use]
403 pub fn with_vector(mut self) -> Self {
404 self.with_vector = true;
405 self
406 }
407
408 #[must_use]
410 pub fn in_vector_space(mut self, vector_space: impl Into<String>) -> Self {
411 self.expected_vector_space = Some(vector_space.into());
412 self
413 }
414
415 #[must_use]
417 pub fn with_params(mut self, params: QueryParams) -> Self {
418 self.params = params;
419 self
420 }
421}
422
423impl Default for Query {
424 fn default() -> Self {
425 Self {
426 vector: Vec::new(),
427 limit: 10,
428 filter: None,
429 with_payload: false,
430 with_vector: false,
431 expected_vector_space: None,
432 params: QueryParams::default(),
433 }
434 }
435}
436
437#[derive(Clone, Debug, Serialize, Deserialize)]
439pub struct ScoredPoint {
440 pub id: PointId,
442 pub score: f32,
444 #[serde(skip_serializing_if = "Option::is_none")]
446 pub payload: Option<JsonObject>,
447 #[serde(skip_serializing_if = "Option::is_none")]
449 pub vector: Option<Vec<f32>>,
450}
451
452#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
454#[serde(rename_all = "snake_case")]
455#[non_exhaustive]
456pub enum QueryMode {
457 Exact,
459 Approximate,
461}
462
463#[derive(Clone, Debug, Serialize, Deserialize)]
465pub struct QueryStats {
466 pub mode: QueryMode,
468 pub collection_points: usize,
470 pub buckets_probed: usize,
472 pub candidates_discovered: usize,
474 pub vectors_scored: usize,
476 pub probe_limit_exhausted: bool,
478 pub candidate_limit_exhausted: bool,
480}
481
482#[derive(Clone, Debug, Serialize, Deserialize)]
484pub struct QueryResult {
485 pub root: ObjectId,
487 pub points: Vec<ScoredPoint>,
489 pub stats: QueryStats,
491}
492
493#[derive(Clone, Debug, Default, Serialize, Deserialize)]
495#[serde(default)]
496pub struct GetRequest {
497 pub ids: Vec<PointId>,
499 pub filter: Option<Filter>,
501 pub offset: usize,
503 pub limit: Option<usize>,
505 pub with_payload: bool,
507 pub with_vector: bool,
509}
510
511#[derive(Clone, Debug, Serialize, Deserialize)]
513pub struct Record {
514 pub id: PointId,
516 #[serde(skip_serializing_if = "Option::is_none")]
518 pub payload: Option<JsonObject>,
519 #[serde(skip_serializing_if = "Option::is_none")]
521 pub vector: Option<Vec<f32>>,
522}
523
524#[derive(Clone, Debug, Serialize, Deserialize)]
526pub struct GetResult {
527 pub root: ObjectId,
529 pub points: Vec<Record>,
531}
532
533#[derive(Clone, Debug, Default, Serialize, Deserialize)]
535#[serde(default)]
536pub struct DeleteSelector {
537 pub ids: Vec<PointId>,
539 pub filter: Option<Filter>,
541}
542
543#[derive(Clone, Debug, Serialize, Deserialize)]
545pub struct WriteResult {
546 pub root: ObjectId,
548 pub affected_points: usize,
550}
551
552#[derive(Clone, Debug, Serialize, Deserialize)]
554pub struct CountResult {
555 pub root: ObjectId,
557 pub count: usize,
559}
560
561#[derive(Clone, Debug, Serialize, Deserialize)]
563pub struct CollectionInfo {
564 pub root: ObjectId,
566 pub name: String,
568 pub point_count: usize,
570 pub config: CollectionConfig,
572 pub read_only: bool,
574}
575
576#[derive(Clone, Debug, Serialize, Deserialize)]
578pub struct SnapshotInfo {
579 pub root: ObjectId,
581 pub point_count: usize,
583 pub config: CollectionConfig,
585}
586
587#[derive(Clone, Debug, Serialize, Deserialize)]
589#[serde(tag = "operation", rename_all = "snake_case")]
590#[non_exhaustive]
591pub enum SnapshotMutation {
592 Upsert {
594 point: Point,
596 },
597 DeleteIds {
599 ids: Vec<PointId>,
601 },
602 DeleteFilter {
604 filter: Filter,
606 },
607}
608
609impl SnapshotMutation {
610 pub fn upsert(point: Point) -> Self {
612 Self::Upsert { point }
613 }
614
615 pub fn delete_ids(ids: impl IntoIterator<Item = PointId>) -> Self {
617 Self::DeleteIds {
618 ids: ids.into_iter().collect(),
619 }
620 }
621
622 pub fn delete_filter(filter: Filter) -> Self {
624 Self::DeleteFilter { filter }
625 }
626}
627
628#[derive(Clone, Debug, Serialize, Deserialize)]
630pub struct HistoryEntry {
631 pub commit: ObjectId,
633 pub root: ObjectId,
635 pub parent: Option<ObjectId>,
637 pub message: String,
639 pub time_seconds: i64,
641}
642
643#[derive(Clone, Debug, Default, Serialize, Deserialize)]
645pub struct ObjectStats {
646 pub objects: usize,
648 pub bytes: usize,
650}
651
652#[derive(Clone, Debug, Serialize, Deserialize)]
654pub struct DiffResult {
655 pub left_root: ObjectId,
657 pub right_root: ObjectId,
659 pub added: Vec<PointId>,
661 pub removed: Vec<PointId>,
663 pub changed: Vec<PointId>,
665 pub configuration_changed: bool,
667 pub buckets_changed: bool,
669 pub shared: ObjectStats,
671 pub left_unique: ObjectStats,
673 pub right_unique: ObjectStats,
675}
676
677#[derive(Clone, Debug, Serialize, Deserialize)]
679pub struct ValidationReport {
680 pub root: ObjectId,
682 pub full: bool,
684 pub point_count: usize,
686 pub checked_buckets: usize,
688 pub valid: bool,
690}