use std::time::Duration;
use nodedb_types::surrogate::Surrogate;
use crate::Error;
use crate::bridge::envelope::PhysicalPlan;
use crate::control::server::shared::ddl::sync_dispatch;
use crate::control::server::wal_dispatch::wal_append_if_write;
use crate::control::state::SharedState;
use crate::engine::vector::index_config::{IndexConfig, IndexType};
use crate::types::{DatabaseId, TenantId, VShardId};
use nodedb_physical::physical_plan::VectorOp;
use nodedb_types::vector_distance::DistanceMetric;
pub fn split_vector_coll_key(coll_key: &str) -> (&str, &str) {
coll_key.split_once(':').unwrap_or((coll_key, ""))
}
pub fn build_vector_insert_plan(
collection: &str,
field_name: &str,
vector: Vec<f32>,
surrogate: Surrogate,
) -> PhysicalPlan {
let dim = vector.len();
PhysicalPlan::Vector(VectorOp::Insert {
collection: collection.to_string(),
vector,
dim,
field_name: field_name.to_string(),
surrogate,
pk_bytes: None,
provenance: None,
})
}
fn metric_to_str(metric: DistanceMetric) -> &'static str {
match metric {
DistanceMetric::L2 => "l2",
DistanceMetric::Cosine => "cosine",
DistanceMetric::InnerProduct => "inner_product",
DistanceMetric::Manhattan => "manhattan",
DistanceMetric::Chebyshev => "chebyshev",
DistanceMetric::Hamming => "hamming",
DistanceMetric::Jaccard => "jaccard",
DistanceMetric::Pearson => "pearson",
_ => "cosine",
}
}
fn index_type_to_str(index_type: &IndexType) -> &'static str {
match index_type {
IndexType::Hnsw => "hnsw",
IndexType::HnswPq => "hnsw_pq",
IndexType::IvfPq => "ivf_pq",
_ => "hnsw",
}
}
pub fn build_vector_set_params_plan(
collection: &str,
field_name: &str,
config: &IndexConfig,
) -> PhysicalPlan {
PhysicalPlan::Vector(VectorOp::SetParams {
collection: collection.to_string(),
field_name: field_name.to_string(),
m: config.hnsw.m,
ef_construction: config.hnsw.ef_construction,
metric: metric_to_str(config.hnsw.metric).to_string(),
index_type: index_type_to_str(&config.index_type).to_string(),
pq_m: config.pq_m,
ivf_cells: config.ivf_cells,
ivf_nprobe: config.ivf_nprobe,
})
}
pub async fn reissue_vector_durably(
state: &SharedState,
tenant_id: TenantId,
database_id: DatabaseId,
collection: &str,
plan: PhysicalPlan,
) -> crate::Result<()> {
let vshard = VShardId::from_collection_in_database(database_id, collection);
if let Some(proposer) = state.async_raft_proposer.get() {
let entry = crate::control::wal_replication::to_replicated_entry(
tenant_id,
database_id,
vshard,
&plan,
)
.ok_or_else(|| Error::Internal {
detail: format!(
"restore reissue: vector plan for '{collection}' did not map to a \
replicated write"
),
})?;
crate::control::wal_replication::propose_replicated_entry(state, proposer, entry).await?;
return Ok(());
}
wal_append_if_write(&state.wal, tenant_id, vshard, database_id, &plan)?;
sync_dispatch::dispatch_async(
state,
tenant_id,
database_id,
collection,
plan,
REISSUE_TIMEOUT,
)
.await?;
Ok(())
}
const REISSUE_TIMEOUT: Duration = Duration::from_secs(120);