#![allow(clippy::pedantic)]
#![allow(clippy::nursery)]
#![allow(clippy::needless_pass_by_value)]
#![allow(clippy::missing_errors_doc)]
#![allow(clippy::missing_panics_doc)]
#![allow(clippy::must_use_candidate)]
#![allow(clippy::uninlined_format_args)]
#![allow(clippy::similar_names)]
#![allow(clippy::module_name_repetitions)]
#![allow(clippy::doc_markdown)]
#![allow(clippy::wildcard_imports)]
#![allow(clippy::redundant_closure_for_method_calls)]
uniffi::setup_scaffolding!();
mod agent;
mod collection;
mod collection_sparse;
mod graph;
mod observer;
mod query;
mod streaming_runtime;
mod types;
pub use agent::{SemanticResult, VelesSemanticMemory};
pub use collection::VelesCollection;
pub use graph::{MobileGraphEdge, MobileGraphNode, MobileGraphStore, TraversalResult};
pub use observer::{
MobileAccessDecision, MobileObserver, MobileQueryContext, MobileQueryOperationKind,
};
pub use query::{QueryResult, QueryResultKind, QueryResultRow};
pub use types::{
DistanceMetric, FusionStrategy, IndividualSearchRequest, MobileAdvancedConfig,
MobileAsyncIndexBuilderConfig, MobileCollectionDiagnostics, MobileCollectionStats,
MobileDeferredIndexerConfig, MobileIndexInfo, MobileQueryLimits, MobileStreamingConfig,
PqTrainConfig, SearchQuality, SearchResult, StorageMode, VelesError, VelesPoint,
VelesSparseVector,
};
use std::sync::Arc;
use velesdb_core::{Database as CoreDatabase, DatabaseObserver};
use crate::observer::ForeignObserver;
#[cfg(test)]
use velesdb_core::DistanceMetric as CoreDistanceMetric;
#[cfg(test)]
use velesdb_core::FusionStrategy as CoreFusionStrategy;
#[cfg(test)]
use velesdb_core::SearchQuality as CoreSearchQuality;
fn config_error(err: velesdb_core::config::ConfigError) -> VelesError {
velesdb_core::Error::Config(err.to_string()).into()
}
fn load_engine_config_from_path(
config_path: &str,
) -> Result<velesdb_core::config::VelesConfig, VelesError> {
velesdb_core::config::VelesConfig::load_from_path_engine_only(config_path).map_err(config_error)
}
fn load_engine_config_from_toml(
config_toml: &str,
) -> Result<velesdb_core::config::VelesConfig, VelesError> {
velesdb_core::config::VelesConfig::from_toml_engine_only(config_toml).map_err(config_error)
}
#[derive(uniffi::Object)]
pub struct VelesDatabase {
inner: Arc<CoreDatabase>,
}
#[uniffi::export]
impl VelesDatabase {
#[uniffi::constructor]
pub fn open(path: String) -> Result<Arc<Self>, VelesError> {
let db = CoreDatabase::open(&path)?;
Ok(Arc::new(Self {
inner: Arc::new(db),
}))
}
#[uniffi::constructor]
pub fn open_with_observer(
path: String,
observer: Arc<dyn MobileObserver>,
) -> Result<Arc<Self>, VelesError> {
let core_observer: Arc<dyn DatabaseObserver> = Arc::new(ForeignObserver::new(observer));
let db = CoreDatabase::open_with_observer(&path, core_observer)?;
Ok(Arc::new(Self {
inner: Arc::new(db),
}))
}
#[uniffi::constructor]
pub fn open_with_config(path: String, config_path: String) -> Result<Arc<Self>, VelesError> {
let config = load_engine_config_from_path(&config_path)?;
let db = CoreDatabase::open_with_config(&path, config)?;
Ok(Arc::new(Self {
inner: Arc::new(db),
}))
}
#[uniffi::constructor]
pub fn open_with_config_toml(
path: String,
config_toml: String,
) -> Result<Arc<Self>, VelesError> {
let config = load_engine_config_from_toml(&config_toml)?;
let db = CoreDatabase::open_with_config(&path, config)?;
Ok(Arc::new(Self {
inner: Arc::new(db),
}))
}
#[uniffi::constructor]
pub fn open_with_observer_and_config(
path: String,
observer: Arc<dyn MobileObserver>,
config_path: String,
) -> Result<Arc<Self>, VelesError> {
let config = load_engine_config_from_path(&config_path)?;
let core_observer: Arc<dyn DatabaseObserver> = Arc::new(ForeignObserver::new(observer));
let db = CoreDatabase::open_with_observer_and_config(&path, core_observer, config)?;
Ok(Arc::new(Self {
inner: Arc::new(db),
}))
}
#[uniffi::constructor]
pub fn open_with_observer_and_config_toml(
path: String,
observer: Arc<dyn MobileObserver>,
config_toml: String,
) -> Result<Arc<Self>, VelesError> {
let config = load_engine_config_from_toml(&config_toml)?;
let core_observer: Arc<dyn DatabaseObserver> = Arc::new(ForeignObserver::new(observer));
let db = CoreDatabase::open_with_observer_and_config(&path, core_observer, config)?;
Ok(Arc::new(Self {
inner: Arc::new(db),
}))
}
pub fn update_guardrails(&self, limits: MobileQueryLimits) {
self.inner.update_guardrails(&limits.into());
}
pub fn create_collection(
&self,
name: String,
dimension: u32,
metric: DistanceMetric,
) -> Result<(), VelesError> {
self.inner.create_collection(
&name,
usize::try_from(dimension).unwrap_or(usize::MAX),
metric.into(),
)?;
Ok(())
}
pub fn create_collection_with_storage(
&self,
name: String,
dimension: u32,
metric: DistanceMetric,
storage_mode: StorageMode,
) -> Result<(), VelesError> {
self.inner.create_vector_collection_with_options(
&name,
usize::try_from(dimension).unwrap_or(usize::MAX),
metric.into(),
storage_mode.into(),
)?;
Ok(())
}
pub fn create_metadata_collection(&self, name: String) -> Result<(), VelesError> {
self.inner.create_metadata_collection(&name)?;
Ok(())
}
pub fn create_graph_collection(&self, name: String) -> Result<(), VelesError> {
self.inner
.create_graph_collection(&name, velesdb_core::GraphSchema::schemaless())?;
Ok(())
}
pub fn create_graph_collection_with_embeddings(
&self,
name: String,
dimension: u32,
metric: DistanceMetric,
) -> Result<(), VelesError> {
self.inner.create_graph_collection_with_embeddings(
&name,
velesdb_core::GraphSchema::schemaless(),
usize::try_from(dimension).unwrap_or(usize::MAX),
metric.into(),
)?;
Ok(())
}
pub fn get_collection(&self, name: String) -> Result<Option<Arc<VelesCollection>>, VelesError> {
match self.inner.get_any_collection(&name) {
Some(any_coll) => match any_coll.into_vector() {
Ok(vc) => Ok(Some(Arc::new(VelesCollection {
inner: vc,
db: self.inner.clone(),
name,
}))),
Err(_other_variant) => Err(VelesError::Collection {
message: format!(
"Collection '{name}' is not a vector collection. \
Query graph collections through execute_query() (VelesQL)."
),
}),
},
None => Ok(None),
}
}
pub fn list_collections(&self) -> Vec<String> {
self.inner.list_collections()
}
pub fn delete_collection(&self, name: String) -> Result<(), VelesError> {
self.inner.delete_collection(&name)?;
Ok(())
}
pub fn train_pq(
&self,
collection_name: String,
config: PqTrainConfig,
) -> Result<String, VelesError> {
use std::collections::HashMap;
use velesdb_core::velesql::{Query, TrainStatement, WithValue};
let mut params = HashMap::new();
params.insert("m".to_string(), WithValue::Integer(i64::from(config.m)));
params.insert("k".to_string(), WithValue::Integer(i64::from(config.k)));
if config.opq {
params.insert("type".to_string(), WithValue::Identifier("opq".to_string()));
}
let query = Query::new_train(TrainStatement {
collection: collection_name,
params,
});
let empty_params = HashMap::new();
self.inner
.execute_query(&query, &empty_params)
.map_err(|e| VelesError::database(format!("PQ training failed: {e}")))?;
Ok("PQ training complete".to_string())
}
pub fn execute_query(
&self,
sql: String,
params_json: Option<String>,
) -> Result<QueryResult, VelesError> {
let parsed = velesdb_core::velesql::Parser::parse(&sql)
.map_err(|e| VelesError::database(format!("VelesQL parse error: {}", e.message)))?;
let params = query::parse_params(params_json)?;
let kind = query::classify_query(&parsed);
let core_results = self
.inner
.execute_query(&parsed, ¶ms)
.map_err(|e| VelesError::database(format!("Query execution failed: {e}")))?;
let rows: Result<Vec<QueryResultRow>, VelesError> =
core_results.iter().map(query::to_result_row).collect();
let rows = rows?;
#[allow(clippy::cast_possible_truncation)]
let row_count = rows.len() as u32;
let message = query::build_message(&kind, row_count);
Ok(QueryResult {
kind,
rows,
row_count,
message,
})
}
}
#[cfg(test)]
#[path = "lib_tests.rs"]
mod tests;