1use velesdb_core::{Filter, FusionStrategy as CoreFusionStrategy, QueryOperationKind};
6
7use crate::collection::{and_scope, deny_if_scoped};
8use crate::types::{FusionStrategy, SearchResult, VelesError, VelesPoint, VelesSparseVector};
9use crate::VelesCollection;
10
11#[uniffi::export]
12impl VelesCollection {
13 pub fn sparse_search(
25 &self,
26 sparse_vector: VelesSparseVector,
27 limit: u32,
28 index_name: Option<String>,
29 ) -> Result<Vec<SearchResult>, VelesError> {
30 let core_sv = Self::to_core_sparse_vector(&sparse_vector);
31 let idx_name = index_name.unwrap_or_default();
32
33 let scope =
37 self.db
38 .authorize_read(&self.name, QueryOperationKind::VectorSearch, None, None)?;
39 deny_if_scoped(scope, "sparse_search")?;
40
41 let results = self
42 .inner
43 .sparse_search(
44 &core_sv,
45 usize::try_from(limit).unwrap_or(usize::MAX),
46 &idx_name,
47 )
48 .map_err(|e| VelesError::database(format!("Sparse search failed: {e}")))?;
49
50 Ok(results
51 .into_iter()
52 .map(|r| SearchResult {
53 id: r.point.id,
54 score: r.score,
55 payload: None,
56 })
57 .collect())
58 }
59
60 pub fn hybrid_sparse_search(
76 &self,
77 vector: Vec<f32>,
78 sparse_vector: VelesSparseVector,
79 limit: u32,
80 index_name: Option<String>,
81 ) -> Result<Vec<SearchResult>, VelesError> {
82 let core_sv = Self::to_core_sparse_vector(&sparse_vector);
83 let strategy = velesdb_core::fusion::FusionStrategy::RRF { k: 60 };
84 let idx_name = index_name.unwrap_or_default();
85
86 let scope =
89 self.db
90 .authorize_read(&self.name, QueryOperationKind::HybridSearch, None, None)?;
91 deny_if_scoped(scope, "hybrid_sparse_search")?;
92
93 let results = self
94 .inner
95 .hybrid_sparse_search(
96 &vector,
97 &core_sv,
98 usize::try_from(limit).unwrap_or(usize::MAX),
99 &idx_name,
100 &strategy,
101 )
102 .map_err(|e| VelesError::database(format!("Hybrid sparse search failed: {e}")))?;
103
104 Ok(results
105 .into_iter()
106 .map(|r| SearchResult {
107 id: r.point.id,
108 score: r.score,
109 payload: None,
110 })
111 .collect())
112 }
113
114 pub fn multi_query_search(
116 &self,
117 vectors: Vec<Vec<f32>>,
118 limit: u32,
119 strategy: FusionStrategy,
120 ) -> Result<Vec<SearchResult>, VelesError> {
121 if vectors.is_empty() {
122 return Err(VelesError::database(
123 "multi_query_search requires at least one vector".to_string(),
124 ));
125 }
126
127 let query_refs: Vec<&[f32]> = vectors.iter().map(|v| v.as_slice()).collect();
128 let core_strategy: CoreFusionStrategy = strategy.into();
129
130 let scope =
134 self.db
135 .authorize_read(&self.name, QueryOperationKind::VectorSearch, None, None)?;
136 let effective: Option<Filter> = and_scope(None, scope);
137
138 let results = self
139 .inner
140 .multi_query_search(
141 &query_refs,
142 usize::try_from(limit).unwrap_or(usize::MAX),
143 core_strategy,
144 effective.as_ref(),
145 )
146 .map_err(|e| VelesError::database(format!("Multi-query search failed: {e}")))?;
147
148 Ok(results
149 .into_iter()
150 .map(|r| SearchResult {
151 id: r.point.id,
152 score: r.score,
153 payload: None,
154 })
155 .collect())
156 }
157
158 pub fn multi_query_search_ids(
163 &self,
164 vectors: Vec<Vec<f32>>,
165 limit: u32,
166 strategy: FusionStrategy,
167 ) -> Result<Vec<SearchResult>, VelesError> {
168 if vectors.is_empty() {
169 return Err(VelesError::database(
170 "multi_query_search requires at least one vector".to_string(),
171 ));
172 }
173
174 let query_refs: Vec<&[f32]> = vectors.iter().map(|v| v.as_slice()).collect();
175 let core_strategy: CoreFusionStrategy = strategy.into();
176
177 let scope =
181 self.db
182 .authorize_read(&self.name, QueryOperationKind::VectorSearch, None, None)?;
183 deny_if_scoped(scope, "multi_query_search_ids")?;
184
185 let results = self
186 .inner
187 .multi_query_search_ids(
188 &query_refs,
189 usize::try_from(limit).unwrap_or(usize::MAX),
190 core_strategy,
191 )
192 .map_err(|e| VelesError::database(format!("Multi-query search failed: {e}")))?;
193
194 Ok(results
195 .into_iter()
196 .map(|(id, score)| SearchResult {
197 id,
198 score,
199 payload: None,
200 })
201 .collect())
202 }
203
204 pub fn multi_query_search_with_filter(
206 &self,
207 vectors: Vec<Vec<f32>>,
208 limit: u32,
209 strategy: FusionStrategy,
210 filter_json: String,
211 ) -> Result<Vec<SearchResult>, VelesError> {
212 if vectors.is_empty() {
213 return Err(VelesError::database(
214 "multi_query_search requires at least one vector".to_string(),
215 ));
216 }
217
218 let filter: Filter = serde_json::from_str(&filter_json)
219 .map_err(|e| VelesError::database(format!("Invalid filter JSON: {e}")))?;
220
221 let query_refs: Vec<&[f32]> = vectors.iter().map(|v| v.as_slice()).collect();
222 let core_strategy: CoreFusionStrategy = strategy.into();
223
224 let scope =
227 self.db
228 .authorize_read(&self.name, QueryOperationKind::VectorSearch, None, None)?;
229 let effective = and_scope(Some(filter), scope);
230
231 let results = self
232 .inner
233 .multi_query_search(
234 &query_refs,
235 usize::try_from(limit).unwrap_or(usize::MAX),
236 core_strategy,
237 effective.as_ref(),
238 )
239 .map_err(|e| VelesError::database(format!("Multi-query search failed: {e}")))?;
240
241 Ok(results
242 .into_iter()
243 .map(|r| SearchResult {
244 id: r.point.id,
245 score: r.score,
246 payload: None,
247 })
248 .collect())
249 }
250
251 pub fn upsert_with_sparse(
258 &self,
259 point: VelesPoint,
260 sparse_vector: VelesSparseVector,
261 ) -> Result<(), VelesError> {
262 let payload = point
263 .payload
264 .map(|s| serde_json::from_str(&s))
265 .transpose()
266 .map_err(|e| VelesError::database(format!("Invalid JSON payload: {e}")))?;
267
268 let core_sv = Self::to_core_sparse_vector(&sparse_vector);
269 let mut sparse_map = std::collections::BTreeMap::new();
270 sparse_map.insert(String::new(), core_sv);
271
272 let core_point =
273 velesdb_core::Point::with_sparse(point.id, point.vector, payload, Some(sparse_map));
274 self.inner.upsert(vec![core_point])?;
275 Ok(())
276 }
277}
278
279impl VelesCollection {
280 pub(crate) fn to_core_sparse_vector(
283 sv: &VelesSparseVector,
284 ) -> velesdb_core::sparse_index::SparseVector {
285 let pairs: Vec<(u32, f32)> = sv
286 .indices
287 .iter()
288 .copied()
289 .zip(sv.values.iter().copied())
290 .collect();
291 velesdb_core::sparse_index::SparseVector::new(pairs)
292 }
293}