From 6dfcc5814404788b27d27f27d600abe7408d190e Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Fri, 1 Aug 2025 16:55:02 +0200 Subject: [PATCH] Add exact search to vectorDB --- .../photos/lib/src/rust/api/usearch_api.dart | 8 +- .../photos/lib/src/rust/frb_generated.dart | 34 ++-- mobile/apps/photos/rust/Cargo.lock | 157 +++++++++++++++--- mobile/apps/photos/rust/Cargo.toml | 2 +- .../apps/photos/rust/src/api/usearch_api.rs | 23 ++- mobile/apps/photos/rust/src/frb_generated.rs | 4 + 6 files changed, 184 insertions(+), 44 deletions(-) diff --git a/mobile/apps/photos/lib/src/rust/api/usearch_api.dart b/mobile/apps/photos/lib/src/rust/api/usearch_api.dart index e24c3e1188..7d370723cd 100644 --- a/mobile/apps/photos/lib/src/rust/api/usearch_api.dart +++ b/mobile/apps/photos/lib/src/rust/api/usearch_api.dart @@ -22,7 +22,9 @@ abstract class VectorDb implements RustOpaqueInterface { Future bulkRemoveVectors({required Uint64List keys}); Future<(List, List)> bulkSearchVectors( - {required List queries, required BigInt count}); + {required List queries, + required BigInt count, + required bool exact}); /// Check if a vector with the given key exists in the index. /// `true` if the index contains the vector with the given key, `false` otherwise. @@ -44,5 +46,7 @@ abstract class VectorDb implements RustOpaqueInterface { Future resetIndex(); Future<(Uint64List, Float32List)> searchVectors( - {required List query, required BigInt count}); + {required List query, + required BigInt count, + required bool exact}); } diff --git a/mobile/apps/photos/lib/src/rust/frb_generated.dart b/mobile/apps/photos/lib/src/rust/frb_generated.dart index 69c4023808..c9496ecf9e 100644 --- a/mobile/apps/photos/lib/src/rust/frb_generated.dart +++ b/mobile/apps/photos/lib/src/rust/frb_generated.dart @@ -105,7 +105,8 @@ abstract class RustLibApi extends BaseApi { crateApiUsearchApiVectorDbBulkSearchVectors( {required VectorDb that, required List queries, - required BigInt count}); + required BigInt count, + required bool exact}); Future crateApiUsearchApiVectorDbContainsVector( {required VectorDb that, required BigInt key}); @@ -129,7 +130,8 @@ abstract class RustLibApi extends BaseApi { Future<(Uint64List, Float32List)> crateApiUsearchApiVectorDbSearchVectors( {required VectorDb that, required List query, - required BigInt count}); + required BigInt count, + required bool exact}); String crateApiSimpleGreet({required String name}); @@ -275,7 +277,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { crateApiUsearchApiVectorDbBulkSearchVectors( {required VectorDb that, required List queries, - required BigInt count}) { + required BigInt count, + required bool exact}) { return handler.executeNormal(NormalTask( callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); @@ -283,6 +286,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { that, serializer); sse_encode_list_list_prim_f_32_strict(queries, serializer); sse_encode_usize(count, serializer); + sse_encode_bool(exact, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 5, port: port_); }, @@ -292,7 +296,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { decodeErrorData: null, ), constMeta: kCrateApiUsearchApiVectorDbBulkSearchVectorsConstMeta, - argValues: [that, queries, count], + argValues: [that, queries, count, exact], apiImpl: this, )); } @@ -300,7 +304,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { TaskConstMeta get kCrateApiUsearchApiVectorDbBulkSearchVectorsConstMeta => const TaskConstMeta( debugName: "VectorDb_bulk_search_vectors", - argNames: ["that", "queries", "count"], + argNames: ["that", "queries", "count", "exact"], ); @override @@ -498,7 +502,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { Future<(Uint64List, Float32List)> crateApiUsearchApiVectorDbSearchVectors( {required VectorDb that, required List query, - required BigInt count}) { + required BigInt count, + required bool exact}) { return handler.executeNormal(NormalTask( callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); @@ -506,6 +511,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { that, serializer); sse_encode_list_prim_f_32_loose(query, serializer); sse_encode_usize(count, serializer); + sse_encode_bool(exact, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 13, port: port_); }, @@ -515,7 +521,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { decodeErrorData: null, ), constMeta: kCrateApiUsearchApiVectorDbSearchVectorsConstMeta, - argValues: [that, query, count], + argValues: [that, query, count, exact], apiImpl: this, )); } @@ -523,7 +529,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { TaskConstMeta get kCrateApiUsearchApiVectorDbSearchVectorsConstMeta => const TaskConstMeta( debugName: "VectorDb_search_vectors", - argNames: ["that", "query", "count"], + argNames: ["that", "query", "count", "exact"], ); @override @@ -1103,9 +1109,11 @@ class VectorDbImpl extends RustOpaque implements VectorDb { .crateApiUsearchApiVectorDbBulkRemoveVectors(that: this, keys: keys); Future<(List, List)> bulkSearchVectors( - {required List queries, required BigInt count}) => + {required List queries, + required BigInt count, + required bool exact}) => RustLib.instance.api.crateApiUsearchApiVectorDbBulkSearchVectors( - that: this, queries: queries, count: count); + that: this, queries: queries, count: count, exact: exact); /// Check if a vector with the given key exists in the index. /// `true` if the index contains the vector with the given key, `false` otherwise. @@ -1135,7 +1143,9 @@ class VectorDbImpl extends RustOpaque implements VectorDb { ); Future<(Uint64List, Float32List)> searchVectors( - {required List query, required BigInt count}) => + {required List query, + required BigInt count, + required bool exact}) => RustLib.instance.api.crateApiUsearchApiVectorDbSearchVectors( - that: this, query: query, count: count); + that: this, query: query, count: count, exact: exact); } diff --git a/mobile/apps/photos/rust/Cargo.lock b/mobile/apps/photos/rust/Cargo.lock index ca61af7f02..e091b5defd 100644 --- a/mobile/apps/photos/rust/Cargo.lock +++ b/mobile/apps/photos/rust/Cargo.lock @@ -54,6 +54,12 @@ dependencies = [ "log", ] +[[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + [[package]] name = "anyhow" version = "1.0.75" @@ -142,11 +148,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "codespan-reporting" -version = "0.11.1" +name = "clap" +version = "4.5.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882" dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966" +dependencies = [ + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" + +[[package]] +name = "codespan-reporting" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" +dependencies = [ + "serde", "termcolor", "unicode-width", ] @@ -173,25 +206,27 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.112" +version = "1.0.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ab30434ea0ff6aa640a08dda5284026a366d47565496fd40b6cbfbdd7e31a2" +checksum = "a3523cc02ad831111491dd64b27ad999f1ae189986728e477604e61b81f828df" dependencies = [ "cc", + "cxxbridge-cmd", "cxxbridge-flags", "cxxbridge-macro", + "foldhash", "link-cplusplus", ] [[package]] name = "cxx-build" -version = "1.0.112" +version = "1.0.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b649d7dfae8268450d53d109388b337b9352c7cba1fc10db4a1bc23c3dc189fb" +checksum = "212b754247a6f07b10fa626628c157593f0abf640a3dd04cce2760eca970f909" dependencies = [ "cc", "codespan-reporting", - "once_cell", + "indexmap", "proc-macro2", "quote", "scratch", @@ -199,19 +234,35 @@ dependencies = [ ] [[package]] -name = "cxxbridge-flags" -version = "1.0.112" +name = "cxxbridge-cmd" +version = "1.0.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42281b20eba5218c539295c667c18e2f50211bb11902419194c6ed1ae808e547" +checksum = "f426a20413ec2e742520ba6837c9324b55ffac24ead47491a6e29f933c5b135a" +dependencies = [ + "clap", + "codespan-reporting", + "indexmap", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258b6069020b4e5da6415df94a50ee4f586a6c38b037a180e940a43d06a070d" [[package]] name = "cxxbridge-macro" -version = "1.0.112" +version = "1.0.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45506e3c66512b0a65d291a6b452128b7b1dd9841e20d1e151addbd2c00ea50" +checksum = "e8dec184b52be5008d6eaf7e62fc1802caf1ad1227d11b3b7df2c409c7ffc3f4" dependencies = [ + "indexmap", "proc-macro2", "quote", + "rustversion", "syn", ] @@ -231,7 +282,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown", + "hashbrown 0.14.5", "lock_api", "once_cell", "parking_lot_core", @@ -268,6 +319,12 @@ dependencies = [ "regex", ] +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "flutter_rust_bridge" version = "2.11.1" @@ -310,6 +367,12 @@ dependencies = [ "syn", ] +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "futures" version = "0.3.29" @@ -421,6 +484,12 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +[[package]] +name = "hashbrown" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" + [[package]] name = "hermit-abi" version = "0.3.3" @@ -433,6 +502,16 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "indexmap" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +dependencies = [ + "equivalent", + "hashbrown 0.15.4", +] + [[package]] name = "js-sys" version = "0.3.69" @@ -573,18 +652,18 @@ checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" [[package]] name = "proc-macro2" -version = "1.0.70" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] @@ -641,6 +720,12 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustversion" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" + [[package]] name = "scopeguard" version = "1.2.0" @@ -653,6 +738,26 @@ version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f6280af86e5f559536da57a45ebc84948833b3bee313a7dd25232e09c878a52" +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "slab" version = "0.4.9" @@ -669,10 +774,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] -name = "syn" -version = "2.0.39" +name = "strsim" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", @@ -728,9 +839,9 @@ checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "usearch" -version = "2.17.11" +version = "2.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "908331accde6ff6bfe83e1f2dfd4cc77343d107bfacecd2f19b7a14d87cdb2df" +checksum = "afa869be4baf62146b39700da30b235b06f58d2c03b7e073a8e2b57e0f511aa9" dependencies = [ "cxx", "cxx-build", diff --git a/mobile/apps/photos/rust/Cargo.toml b/mobile/apps/photos/rust/Cargo.toml index 791462ba55..2a861a57c5 100644 --- a/mobile/apps/photos/rust/Cargo.toml +++ b/mobile/apps/photos/rust/Cargo.toml @@ -8,7 +8,7 @@ crate-type = ["cdylib", "staticlib"] [dependencies] flutter_rust_bridge = "=2.11.1" -usearch = "2.17.11" +usearch = "2.19.1" [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ['cfg(frb_expand)'] } diff --git a/mobile/apps/photos/rust/src/api/usearch_api.rs b/mobile/apps/photos/rust/src/api/usearch_api.rs index 457b45f7ff..78503c9dd5 100644 --- a/mobile/apps/photos/rust/src/api/usearch_api.rs +++ b/mobile/apps/photos/rust/src/api/usearch_api.rs @@ -83,11 +83,21 @@ impl VectorDB { self.save_index(); } - pub fn search_vectors(&self, query: &Vec, count: usize) -> (Vec, Vec) { - let matches = self - .index - .search(query, count) - .expect("Failed to search vectors"); + pub fn search_vectors( + &self, + query: &Vec, + count: usize, + exact: bool, + ) -> (Vec, Vec) { + let matches = if exact { + self.index + .exact_search(query, count) + .expect("Failed to exact search vectors") + } else { + self.index + .search(query, count) + .expect("Failed to search vectors") + }; (matches.keys, matches.distances) } @@ -95,12 +105,13 @@ impl VectorDB { &self, queries: &Vec>, count: usize, + exact: bool, ) -> (Vec>, Vec>) { let mut keys = Vec::new(); let mut distances = Vec::new(); for query in queries { - let (keys_result, distances_result) = self.search_vectors(query, count); + let (keys_result, distances_result) = self.search_vectors(query, count, exact); keys.push(keys_result); distances.push(distances_result); } diff --git a/mobile/apps/photos/rust/src/frb_generated.rs b/mobile/apps/photos/rust/src/frb_generated.rs index 3fe26fe80f..6b114469a7 100644 --- a/mobile/apps/photos/rust/src/frb_generated.rs +++ b/mobile/apps/photos/rust/src/frb_generated.rs @@ -296,6 +296,7 @@ fn wire__crate__api__usearch_api__VectorDb_bulk_search_vectors_impl( >>::sse_decode(&mut deserializer); let api_queries = >>::sse_decode(&mut deserializer); let api_count = ::sse_decode(&mut deserializer); + let api_exact = ::sse_decode(&mut deserializer); deserializer.end(); move |context| { transform_result_sse::<_, ()>((move || { @@ -318,6 +319,7 @@ fn wire__crate__api__usearch_api__VectorDb_bulk_search_vectors_impl( &*api_that_guard, &api_queries, api_count, + api_exact, ), )?; Ok(output_ok) @@ -682,6 +684,7 @@ fn wire__crate__api__usearch_api__VectorDb_search_vectors_impl( >>::sse_decode(&mut deserializer); let api_query = >::sse_decode(&mut deserializer); let api_count = ::sse_decode(&mut deserializer); + let api_exact = ::sse_decode(&mut deserializer); deserializer.end(); move |context| { transform_result_sse::<_, ()>((move || { @@ -704,6 +707,7 @@ fn wire__crate__api__usearch_api__VectorDb_search_vectors_impl( &*api_that_guard, &api_query, api_count, + api_exact, ))?; Ok(output_ok) })())