Skip to main content

velesdb_mobile/
types.rs

1//! Mobile types and enums module (EPIC-061/US-005 refactoring).
2//!
3//! Extracted from lib.rs to improve modularity.
4
5use velesdb_core::DistanceMetric as CoreDistanceMetric;
6use velesdb_core::FusionStrategy as CoreFusionStrategy;
7
8// ============================================================================
9// Error Types
10// ============================================================================
11
12/// Errors that can occur when using VelesDB on mobile.
13#[derive(Debug, thiserror::Error, uniffi::Error)]
14pub enum VelesError {
15    /// Database operation failed.
16    #[error("Database error: {message}")]
17    Database { message: String },
18
19    /// Collection operation failed.
20    #[error("Collection error: {message}")]
21    Collection { message: String },
22
23    /// Vector dimension mismatch.
24    #[error("Dimension mismatch: expected {expected}, got {actual}")]
25    DimensionMismatch { expected: u32, actual: u32 },
26}
27
28impl From<velesdb_core::Error> for VelesError {
29    fn from(err: velesdb_core::Error) -> Self {
30        match err {
31            velesdb_core::Error::DimensionMismatch { expected, actual } =>
32            {
33                #[allow(clippy::cast_possible_truncation)]
34                VelesError::DimensionMismatch {
35                    expected: expected as u32,
36                    actual: actual as u32,
37                }
38            }
39            velesdb_core::Error::CollectionNotFound(name) => VelesError::Collection {
40                message: format!("Collection not found: {name}"),
41            },
42            velesdb_core::Error::CollectionExists(name) => VelesError::Collection {
43                message: format!("Collection already exists: {name}"),
44            },
45            other => VelesError::Database {
46                message: other.to_string(),
47            },
48        }
49    }
50}
51
52// ============================================================================
53// Enums
54// ============================================================================
55
56/// Distance metric for vector similarity.
57#[derive(Debug, Clone, Copy, uniffi::Enum)]
58pub enum DistanceMetric {
59    /// Cosine similarity (1 - cosine_distance). Higher is more similar.
60    Cosine,
61    /// Euclidean (L2) distance. Lower is more similar.
62    Euclidean,
63    /// Dot product. Higher is more similar (for normalized vectors).
64    DotProduct,
65    /// Hamming distance for binary vectors. Lower is more similar.
66    Hamming,
67    /// Jaccard similarity for set-like vectors. Higher is more similar.
68    Jaccard,
69}
70
71impl From<DistanceMetric> for CoreDistanceMetric {
72    fn from(metric: DistanceMetric) -> Self {
73        match metric {
74            DistanceMetric::Cosine => CoreDistanceMetric::Cosine,
75            DistanceMetric::Euclidean => CoreDistanceMetric::Euclidean,
76            DistanceMetric::DotProduct => CoreDistanceMetric::DotProduct,
77            DistanceMetric::Hamming => CoreDistanceMetric::Hamming,
78            DistanceMetric::Jaccard => CoreDistanceMetric::Jaccard,
79        }
80    }
81}
82
83/// Storage mode for vector quantization (IoT/Edge optimization).
84#[derive(Debug, Clone, Copy, uniffi::Enum)]
85pub enum StorageMode {
86    /// Full f32 precision (4 bytes/dimension). Best recall.
87    Full,
88    /// SQ8: 8-bit scalar quantization (1 byte/dimension). 4x compression, ~1% recall loss.
89    Sq8,
90    /// Binary: 1-bit quantization (1 bit/dimension). 32x compression, ~5-10% recall loss.
91    Binary,
92}
93
94impl From<StorageMode> for velesdb_core::StorageMode {
95    fn from(mode: StorageMode) -> Self {
96        match mode {
97            StorageMode::Full => velesdb_core::StorageMode::Full,
98            StorageMode::Sq8 => velesdb_core::StorageMode::SQ8,
99            StorageMode::Binary => velesdb_core::StorageMode::Binary,
100        }
101    }
102}
103
104/// Fusion strategy for combining results from multiple vector searches.
105#[derive(Debug, Clone, uniffi::Enum)]
106pub enum FusionStrategy {
107    /// Average scores across all queries.
108    Average,
109    /// Take the maximum score for each document.
110    Maximum,
111    /// Reciprocal Rank Fusion with configurable k parameter.
112    Rrf {
113        /// RRF k parameter (default: 60). Lower k emphasizes top ranks more.
114        k: u32,
115    },
116    /// Weighted combination of average, maximum, and hit ratio.
117    Weighted {
118        /// Weight for average score (0.0-1.0).
119        avg_weight: f32,
120        /// Weight for maximum score (0.0-1.0).
121        max_weight: f32,
122        /// Weight for hit ratio (0.0-1.0).
123        hit_weight: f32,
124    },
125}
126
127impl From<FusionStrategy> for CoreFusionStrategy {
128    fn from(strategy: FusionStrategy) -> Self {
129        match strategy {
130            FusionStrategy::Average => CoreFusionStrategy::Average,
131            FusionStrategy::Maximum => CoreFusionStrategy::Maximum,
132            FusionStrategy::Rrf { k } => CoreFusionStrategy::RRF { k },
133            FusionStrategy::Weighted {
134                avg_weight,
135                max_weight,
136                hit_weight,
137            } => CoreFusionStrategy::Weighted {
138                avg_weight,
139                max_weight,
140                hit_weight,
141            },
142        }
143    }
144}
145
146impl Default for FusionStrategy {
147    fn default() -> Self {
148        Self::Rrf { k: 60 }
149    }
150}
151
152// ============================================================================
153// Data Types
154// ============================================================================
155
156/// A sparse vector represented as parallel arrays of indices and values.
157///
158/// Uses parallel `Vec<u32>` / `Vec<f32>` instead of `HashMap` for safe FFI
159/// mapping to all mobile targets (Swift arrays, Kotlin IntArray/FloatArray).
160#[derive(Debug, Clone, uniffi::Record)]
161pub struct VelesSparseVector {
162    /// Dimension indices (must be sorted, unique).
163    pub indices: Vec<u32>,
164    /// Weights corresponding to each index.
165    pub values: Vec<f32>,
166}
167
168/// Configuration for Product Quantization training.
169#[derive(Debug, Clone, uniffi::Record)]
170pub struct PqTrainConfig {
171    /// Number of sub-quantizers (subspaces).
172    pub m: u32,
173    /// Number of centroids per sub-quantizer.
174    pub k: u32,
175    /// Whether to use Optimized Product Quantization.
176    pub opq: bool,
177}
178
179/// A search result containing an ID and similarity score.
180#[derive(Debug, Clone, uniffi::Record)]
181pub struct SearchResult {
182    /// Vector ID.
183    pub id: u64,
184    /// Similarity score.
185    pub score: f32,
186}
187
188/// A point to insert into the database.
189#[derive(Debug, Clone, uniffi::Record)]
190pub struct VelesPoint {
191    /// Unique identifier.
192    pub id: u64,
193    /// Vector embedding.
194    pub vector: Vec<f32>,
195    /// Optional JSON payload as string.
196    pub payload: Option<String>,
197}
198
199/// Individual search request within a batch.
200#[derive(Debug, Clone, uniffi::Record)]
201pub struct IndividualSearchRequest {
202    /// Query vector.
203    pub vector: Vec<f32>,
204    /// Number of results.
205    pub top_k: u32,
206    /// Optional metadata filter as JSON string.
207    pub filter: Option<String>,
208}
209
210/// Public statistics snapshot for a collection.
211#[derive(Debug, Clone, uniffi::Record)]
212pub struct MobileCollectionStats {
213    /// Total number of points currently stored.
214    pub total_points: u64,
215    /// Total payload footprint in bytes.
216    pub payload_size_bytes: u64,
217    /// Number of rows in storage.
218    pub row_count: u64,
219    /// Number of deleted/tombstoned rows.
220    pub deleted_count: u64,
221    /// Mean row size estimate in bytes.
222    pub avg_row_size_bytes: u64,
223    /// Total collection size estimate in bytes.
224    pub total_size_bytes: u64,
225    /// Number of tracked fields.
226    pub field_stats_count: u32,
227    /// Number of tracked columns.
228    pub column_stats_count: u32,
229    /// Number of tracked indexes.
230    pub index_stats_count: u32,
231}
232
233impl From<velesdb_core::collection::stats::CollectionStats> for MobileCollectionStats {
234    fn from(stats: velesdb_core::collection::stats::CollectionStats) -> Self {
235        Self {
236            total_points: stats.total_points,
237            payload_size_bytes: stats.payload_size_bytes,
238            row_count: stats.row_count,
239            deleted_count: stats.deleted_count,
240            avg_row_size_bytes: stats.avg_row_size_bytes,
241            total_size_bytes: stats.total_size_bytes,
242            field_stats_count: u32::try_from(stats.field_stats.len()).unwrap_or(u32::MAX),
243            column_stats_count: u32::try_from(stats.column_stats.len()).unwrap_or(u32::MAX),
244            index_stats_count: u32::try_from(stats.index_stats.len()).unwrap_or(u32::MAX),
245        }
246    }
247}
248
249/// Metadata and graph index details.
250#[derive(Debug, Clone, uniffi::Record)]
251pub struct MobileIndexInfo {
252    /// Node label.
253    pub label: String,
254    /// Property name.
255    pub property: String,
256    /// Index type name.
257    pub index_type: String,
258    /// Number of distinct values.
259    pub cardinality: u64,
260    /// Approximate memory usage in bytes.
261    pub memory_bytes: u64,
262}
263
264impl From<velesdb_core::IndexInfo> for MobileIndexInfo {
265    fn from(value: velesdb_core::IndexInfo) -> Self {
266        Self {
267            label: value.label,
268            property: value.property,
269            index_type: value.index_type,
270            cardinality: u64::try_from(value.cardinality).unwrap_or(u64::MAX),
271            memory_bytes: u64::try_from(value.memory_bytes).unwrap_or(u64::MAX),
272        }
273    }
274}