1- use defs:: { DbError , Dimension , IndexedVector , Similarity } ;
1+ use defs:: { Dimension , IndexedVector , Similarity } ;
22
33use defs:: { DenseVector , Payload , Point , PointId } ;
44use index:: hnsw:: HnswIndex ;
@@ -13,6 +13,9 @@ use storage::{StorageEngine, StorageType, VectorPage};
1313
1414use uuid:: Uuid ;
1515
16+ pub mod error;
17+ pub use error:: { ApiError , Result } ;
18+
1619// static NEXT_ID: AtomicU64 = AtomicU64::new(1);
1720
1821// fn generate_point_id() -> u64 {
@@ -43,17 +46,20 @@ impl VectorDb {
4346 }
4447
4548 //TODO: Make this an atomic operation
46- pub fn insert ( & self , vector : DenseVector , payload : Payload ) -> Result < PointId , DbError > {
49+ pub fn insert ( & self , vector : DenseVector , payload : Payload ) -> Result < PointId > {
4750 if vector. len ( ) != self . dimension {
48- return Err ( DbError :: DimensionMismatch ) ;
51+ return Err ( ApiError :: DimensionMismatch {
52+ expected : self . dimension ,
53+ got : vector. len ( ) ,
54+ } ) ;
4955 }
5056 // Generate a new point id
5157 let point_id = generate_point_id ( ) ;
5258 self . storage
5359 . insert_point ( point_id, Some ( vector. clone ( ) ) , Some ( payload) ) ?;
5460
5561 // Get write lock on the index
56- let mut index = self . index . write ( ) . map_err ( |_| DbError :: LockError ) ?;
62+ let mut index = self . index . write ( ) . map_err ( |_| ApiError :: LockError ) ?;
5763 index. insert ( IndexedVector {
5864 vector,
5965 id : point_id,
@@ -63,16 +69,16 @@ impl VectorDb {
6369 }
6470
6571 //TODO: Make this an atomic operation
66- pub fn delete ( & self , id : PointId ) -> Result < bool , DbError > {
72+ pub fn delete ( & self , id : PointId ) -> Result < bool > {
6773 // Remove from storage
6874 self . storage . delete_point ( id) ?;
6975 // Remove from index
70- let mut index = self . index . write ( ) . map_err ( |_| DbError :: LockError ) ?;
76+ let mut index = self . index . write ( ) . map_err ( |_| ApiError :: LockError ) ?;
7177 let point_found = index. delete ( id) ?;
7278 Ok ( point_found)
7379 }
7480
75- pub fn get ( & self , id : PointId ) -> Result < Option < Point > , DbError > {
81+ pub fn get ( & self , id : PointId ) -> Result < Option < Point > > {
7682 // Search for the Point with given id in storage
7783 let payload = self . storage . get_payload ( id) ?;
7884 let vector = self . storage . get_vector ( id) ?;
@@ -92,28 +98,42 @@ impl VectorDb {
9298 query : DenseVector ,
9399 similarity : Similarity ,
94100 limit : usize ,
95- ) -> Result < Vec < PointId > , DbError > {
101+ ) -> Result < Vec < PointId > > {
102+ // Validate search limit
103+ if limit == 0 {
104+ return Err ( ApiError :: InvalidSearchLimit { limit } ) ;
105+ }
106+
107+ // Validate query dimension
108+ if query. len ( ) != self . dimension {
109+ return Err ( ApiError :: DimensionMismatch {
110+ expected : self . dimension ,
111+ got : query. len ( ) ,
112+ } ) ;
113+ }
114+
96115 // Use vector index to find similar vectors
97- let index = self . index . read ( ) . map_err ( |_| DbError :: LockError ) ?;
116+ let index = self . index . read ( ) . map_err ( |_| ApiError :: LockError ) ?;
98117
99118 //TODO: Add feat of returning similarity scores in the search
100119 let vectors = index. search ( query, similarity, limit) ?;
101120
102121 Ok ( vectors)
103122 }
104123
105- pub fn list ( & self , offset : PointId , limit : usize ) -> Result < Option < VectorPage > , DbError > {
106- self . storage . list_vectors ( offset, limit)
124+ pub fn list ( & self , offset : PointId , limit : usize ) -> Result < Option < VectorPage > > {
125+ let page = self . storage . list_vectors ( offset, limit) ?;
126+ Ok ( page)
107127 }
108128
109129 // populates the current index with vectors from the storage
110- pub fn build_index ( & self ) -> Result < usize , DbError > {
130+ pub fn build_index ( & self ) -> Result < usize > {
111131 // start from the minimal UUID and fetch in bounded batches and insert
112132 let mut offset = Uuid :: nil ( ) ;
113133 let page_size: usize = 1000 ;
114134 let mut inserted: usize = 0 ;
115135
116- let mut index = self . index . write ( ) . map_err ( |_| DbError :: LockError ) ?;
136+ let mut index = self . index . write ( ) . map_err ( |_| ApiError :: LockError ) ?;
117137
118138 while let Some ( ( batch, next_offset) ) = self . storage . list_vectors ( offset, page_size) ? {
119139 if batch. is_empty ( ) || next_offset == offset {
@@ -141,7 +161,7 @@ pub struct DbConfig {
141161 pub similarity : Similarity ,
142162}
143163
144- pub fn init_api ( config : DbConfig ) -> Result < VectorDb , DbError > {
164+ pub fn init_api ( config : DbConfig ) -> Result < VectorDb > {
145165 // Initialize the storage engine
146166 let storage = match config. storage_type {
147167 StorageType :: RocksDb => Arc :: new ( RocksDbStorage :: new ( config. data_path ) ?) ,
@@ -230,7 +250,13 @@ mod tests {
230250 // Insert vector of dimension 2 != 3
231251 let res2 = db. insert ( v2, payload) ;
232252 assert ! ( res2. is_err( ) ) ;
233- assert_eq ! ( res2. unwrap_err( ) , DbError :: DimensionMismatch ) ;
253+ match res2. unwrap_err ( ) {
254+ ApiError :: DimensionMismatch { expected, got } => {
255+ assert_eq ! ( expected, 3 ) ;
256+ assert_eq ! ( got, 2 ) ;
257+ }
258+ other => panic ! ( "Expected DimensionMismatch, got: {:?}" , other) ,
259+ }
234260 }
235261
236262 #[ test]
@@ -312,6 +338,22 @@ mod tests {
312338 assert_eq ! ( results. len( ) , 3 ) ;
313339 }
314340
341+ #[ test]
342+ fn test_search_zero_limit ( ) {
343+ let ( db, _temp_dir) = create_test_db ( ) ;
344+
345+ let query = vec ! [ 1.0 , 2.0 , 3.0 ] ;
346+ let result = db. search ( query, Similarity :: Cosine , 0 ) ;
347+
348+ assert ! ( result. is_err( ) ) ;
349+ match result. unwrap_err ( ) {
350+ ApiError :: InvalidSearchLimit { limit } => {
351+ assert_eq ! ( limit, 0 ) ;
352+ }
353+ other => panic ! ( "Expected InvalidSearchLimit, got: {:?}" , other) ,
354+ }
355+ }
356+
315357 #[ test]
316358 fn test_empty_database ( ) {
317359 let ( db, _temp_dir) = create_test_db ( ) ;
0 commit comments