use std::path::Path;
use anyhow::Result;
use colored::Colorize;
use crate::collection_helpers;
pub fn handle_info(path: &Path) -> Result<()> {
let db = crate::helpers::open_database(path)?;
println!("VelesDB Database: {}", path.display());
println!("Collections:");
for name in db.list_collections() {
print_collection_summary(&db, &name);
}
Ok(())
}
fn print_collection_summary(db: &velesdb_core::Database, name: &str) {
let type_label = collection_helpers::collection_type_label(db, name);
match collection_helpers::resolve_collection(db, name) {
Some(collection_helpers::TypedCollection::Vector(col)) => {
let config = col.config();
println!(
" - {} [{}] ({} dims, {:?}, {} points)",
config.name, type_label, config.dimension, config.metric, config.point_count
);
}
Some(collection_helpers::TypedCollection::Graph(col)) => {
let edge_count = col.get_edges(None).len();
println!(" - {} [{}] ({} edges)", col.name(), type_label, edge_count);
}
Some(collection_helpers::TypedCollection::Metadata(col)) => {
println!(" - {} [{}] ({} items)", col.name(), type_label, col.len());
}
None => {
println!(" - {} [{}]", name, type_label);
}
}
}
pub fn handle_list(path: &Path, format: &str) -> Result<()> {
let db = crate::helpers::open_database(path)?;
let collections = db.list_collections();
if format == "json" {
print_list_json(&db, &collections)?;
} else {
print_list_table(&db, &collections);
}
Ok(())
}
fn print_list_json(db: &velesdb_core::Database, collections: &[String]) -> Result<()> {
let data: Vec<_> = collections
.iter()
.map(|name| collection_to_json(db, name))
.collect();
println!("{}", serde_json::to_string_pretty(&data)?);
Ok(())
}
fn collection_to_json(db: &velesdb_core::Database, name: &str) -> serde_json::Value {
let type_label = collection_helpers::collection_type_label(db, name);
match collection_helpers::resolve_collection(db, name) {
Some(collection_helpers::TypedCollection::Vector(col)) => {
let cfg = col.config();
serde_json::json!({
"name": cfg.name,
"type": type_label,
"dimension": cfg.dimension,
"metric": format!("{:?}", cfg.metric),
"point_count": cfg.point_count,
"storage_mode": format!("{:?}", cfg.storage_mode)
})
}
Some(collection_helpers::TypedCollection::Graph(col)) => {
serde_json::json!({
"name": col.name(),
"type": type_label,
"edge_count": col.get_edges(None).len(),
"has_embeddings": col.has_embeddings()
})
}
Some(collection_helpers::TypedCollection::Metadata(col)) => {
serde_json::json!({
"name": col.name(),
"type": type_label,
"item_count": col.len()
})
}
None => {
serde_json::json!({
"name": name,
"type": type_label
})
}
}
}
fn print_list_table(db: &velesdb_core::Database, collections: &[String]) {
println!("\n{}", "Collections".bold().underline());
if collections.is_empty() {
println!(" No collections found.\n");
return;
}
for name in collections {
print_collection_row(db, name);
}
println!("\n Total: {} collection(s)\n", collections.len());
}
fn print_collection_row(db: &velesdb_core::Database, name: &str) {
let type_label = collection_helpers::collection_type_label(db, name);
match collection_helpers::resolve_collection(db, name) {
Some(collection_helpers::TypedCollection::Vector(col)) => {
let cfg = col.config();
println!(
" {} {} [{}] ({} dims, {:?}, {} points)",
"\u{2022}".cyan(),
cfg.name.green(),
type_label.cyan(),
cfg.dimension,
cfg.metric,
cfg.point_count
);
}
Some(collection_helpers::TypedCollection::Graph(col)) => {
println!(
" {} {} [{}] ({} edges)",
"\u{2022}".cyan(),
col.name().green(),
type_label.cyan(),
col.get_edges(None).len()
);
}
Some(collection_helpers::TypedCollection::Metadata(col)) => {
println!(
" {} {} [{}] ({} items)",
"\u{2022}".cyan(),
col.name().green(),
type_label.cyan(),
col.len()
);
}
None => {
println!(
" {} {} [{}]",
"\u{2022}".cyan(),
name.green(),
type_label.cyan()
);
}
}
}
pub fn handle_show(path: &Path, collection: &str, samples: usize, format: &str) -> Result<()> {
let db = crate::helpers::open_database(path)?;
let typed = collection_helpers::resolve_collection(&db, collection)
.ok_or_else(|| anyhow::anyhow!("Collection '{}' not found", collection))?;
let type_label = collection_helpers::collection_type_label(&db, collection);
match typed {
collection_helpers::TypedCollection::Vector(col) => {
show_vector(&col, type_label, samples, format)?;
}
collection_helpers::TypedCollection::Graph(col) => {
show_graph(&col, type_label, format)?;
}
collection_helpers::TypedCollection::Metadata(col) => {
show_metadata(&col, type_label, format)?;
}
}
Ok(())
}
fn show_vector(
col: &velesdb_core::VectorCollection,
type_label: &str,
samples: usize,
format: &str,
) -> Result<()> {
let cfg = col.config();
if format == "json" {
let data = serde_json::json!({
"name": cfg.name,
"type": type_label,
"dimension": cfg.dimension,
"metric": format!("{:?}", cfg.metric),
"point_count": cfg.point_count,
"storage_mode": format!("{:?}", cfg.storage_mode),
"estimated_memory_mb": (cfg.point_count * cfg.dimension * 4) as f64 / 1_000_000.0
});
println!("{}", serde_json::to_string_pretty(&data)?);
} else {
print_vector_table(&cfg, type_label);
if samples > 0 {
print_vector_samples(col, samples);
}
println!();
}
Ok(())
}
fn print_vector_table(cfg: &velesdb_core::collection::CollectionConfig, type_label: &str) {
println!("\n{}", "Collection Details".bold().underline());
println!(" {} {}", "Name:".cyan(), cfg.name.green());
println!(" {} {}", "Type:".cyan(), type_label.green());
println!(" {} {}", "Dimension:".cyan(), cfg.dimension);
println!(" {} {:?}", "Metric:".cyan(), cfg.metric);
println!(" {} {}", "Point Count:".cyan(), cfg.point_count);
println!(" {} {:?}", "Storage Mode:".cyan(), cfg.storage_mode);
let estimated_mb = (cfg.point_count * cfg.dimension * 4) as f64 / 1_000_000.0;
println!(" {} {:.2} MB", "Est. Memory:".cyan(), estimated_mb);
}
fn print_vector_samples(col: &velesdb_core::VectorCollection, samples: usize) {
println!("\n{}", "Sample Records".bold().underline());
let ids: Vec<u64> = col.all_point_ids().into_iter().take(samples).collect();
let points = col.get(&ids);
for point in points.into_iter().flatten().take(samples) {
println!(" ID: {}", point.id.to_string().green());
if let Some(payload) = &point.payload {
println!(" Payload: {}", payload);
}
}
}
fn show_graph(col: &velesdb_core::GraphCollection, type_label: &str, format: &str) -> Result<()> {
let edge_count = col.get_edges(None).len();
if format == "json" {
let data = serde_json::json!({
"name": col.name(),
"type": type_label,
"edge_count": edge_count,
"has_embeddings": col.has_embeddings(),
"schema": format!("{:?}", col.schema())
});
println!("{}", serde_json::to_string_pretty(&data)?);
} else {
println!("\n{}", "Collection Details".bold().underline());
println!(" {} {}", "Name:".cyan(), col.name().green());
println!(" {} {}", "Type:".cyan(), type_label.green());
println!(" {} {}", "Edges:".cyan(), edge_count);
println!(" {} {}", "Embeddings:".cyan(), col.has_embeddings());
println!(" {} {:?}", "Schema:".cyan(), col.schema());
println!();
}
Ok(())
}
fn show_metadata(
col: &velesdb_core::MetadataCollection,
type_label: &str,
format: &str,
) -> Result<()> {
if format == "json" {
let data = serde_json::json!({
"name": col.name(),
"type": type_label,
"item_count": col.len()
});
println!("{}", serde_json::to_string_pretty(&data)?);
} else {
println!("\n{}", "Collection Details".bold().underline());
println!(" {} {}", "Name:".cyan(), col.name().green());
println!(" {} {}", "Type:".cyan(), type_label.green());
println!(" {} {}", "Item Count:".cyan(), col.len());
println!();
}
Ok(())
}
pub fn handle_analyze(path: &Path, collection: &str, format: &str) -> Result<()> {
let db = crate::helpers::open_database(path)?;
let stats = db
.analyze_collection(collection)
.map_err(|e| anyhow::anyhow!("Analyze error: {}", e))?;
if format == "json" {
println!("{}", serde_json::to_string_pretty(&stats)?);
} else {
print_analyze_table(collection, &stats);
}
Ok(())
}
fn print_analyze_table(collection: &str, stats: &velesdb_core::collection::stats::CollectionStats) {
println!("\n{}", "Collection Statistics".bold().underline());
println!(" {} {}", "Collection:".cyan(), collection.green());
println!(" {} {}", "Total points:".cyan(), stats.total_points);
println!(" {} {}", "Row count:".cyan(), stats.row_count);
println!(" {} {}", "Deleted count:".cyan(), stats.deleted_count);
println!(" {} {}", "Live rows:".cyan(), stats.live_row_count());
println!(
" {} {:.2}%",
"Deletion ratio:".cyan(),
stats.deletion_ratio() * 100.0
);
println!(
" {} {} bytes",
"Payload size:".cyan(),
stats.payload_size_bytes
);
println!(
" {} {} bytes",
"Avg row size:".cyan(),
stats.avg_row_size_bytes
);
println!(
" {} {} bytes",
"Total size:".cyan(),
stats.total_size_bytes
);
print_analyze_indexes(stats);
print_analyze_columns(stats);
if let Some(ts) = stats.last_analyzed_epoch_ms {
println!("\n {} epoch {} ms", "Last analyzed:".cyan(), ts);
}
println!();
}
fn print_analyze_indexes(stats: &velesdb_core::collection::stats::CollectionStats) {
if stats.index_stats.is_empty() {
return;
}
println!("\n {}", "Indexes:".cyan().bold());
for (name, idx) in &stats.index_stats {
println!(
" {} ({}) \u{2014} {} entries, depth {}, {} bytes",
name.green(),
idx.index_type,
idx.entry_count,
idx.depth,
idx.size_bytes
);
}
}
fn print_analyze_columns(stats: &velesdb_core::collection::stats::CollectionStats) {
if stats.column_stats.is_empty() {
return;
}
println!("\n {}", "Columns:".cyan().bold());
for (name, col) in &stats.column_stats {
println!(
" {} \u{2014} {} distinct, {} nulls",
name.green(),
col.distinct_count,
col.null_count
);
}
}