use super::import_export::{format_csv_value, import_space_impl};
use super::{CatalogStore, GraphStore, StorageAdmin, StorageAuthOps, StoragePersistenceOps};
use crate::stats_reader::ColumnStatsReader;
use crate::{AutoCommitBatchOps, AutoCommitGroupOps, SnapshotHandle};
use linkrs_core::StorageError;
use std::io::Write;
pub trait QueryStorage:
GraphStore
+ CatalogStore
+ StorageAuthOps
+ StorageAdmin
+ StoragePersistenceOps
+ ColumnStatsReader
+ AutoCommitBatchOps
+ AutoCommitGroupOps
{
fn snapshot_handle(&self) -> Option<SnapshotHandle> {
let context = self.operation_context()?;
Some(SnapshotHandle::new(context.read_timestamp, 0))
}
fn export_space(&self, space: &str, path: &std::path::Path) -> Result<(), StorageError> {
let base = path.join(space);
std::fs::create_dir_all(&base)
.map_err(|e| StorageError::io_error(format!("Failed to create export dir: {e}")))?;
let tags = self.list_tags(space)?;
let edge_types = self.list_edge_types(space)?;
let schema_meta = serde_json::json!({
"space": space,
"tags": tags.iter().map(|t| serde_json::json!({
"name": t.tag_name,
"properties": t.properties.iter().map(|p| serde_json::json!({
"name": p.name,
"type": p.data_type.to_string(),
})).collect::<Vec<_>>(),
})).collect::<Vec<_>>(),
"edge_types": edge_types.iter().map(|e| serde_json::json!({
"name": e.edge_type_name,
"src_tag": e.src_tag_name,
"dst_tag": e.dst_tag_name,
"properties": e.properties.iter().map(|p| serde_json::json!({
"name": p.name,
"type": p.data_type.to_string(),
})).collect::<Vec<_>>(),
})).collect::<Vec<_>>(),
});
let schema_path = base.join("schema.json");
let mut schema_file = std::fs::File::create(&schema_path)
.map_err(|e| StorageError::io_error(format!("Failed to create schema.json: {e}")))?;
schema_file
.write_all(
serde_json::to_string_pretty(&schema_meta)
.unwrap_or_default()
.as_bytes(),
)
.map_err(|e| StorageError::io_error(format!("Failed to write schema.json: {e}")))?;
for tag_info in &tags {
let vertices = self.scan_vertices_by_tag(space, &tag_info.tag_name)?;
if vertices.is_empty() {
continue;
}
let mut prop_keys: Vec<std::sync::Arc<str>> = vertices
.iter()
.flat_map(|v| v.tag.properties.keys())
.cloned()
.collect::<std::collections::HashSet<_>>()
.into_iter()
.collect();
prop_keys.sort();
let csv_path = base.join(format!("{}.csv", tag_info.tag_name));
let mut file = std::fs::File::create(&csv_path).map_err(|e| {
StorageError::io_error(format!("Failed to create {}.csv: {e}", tag_info.tag_name))
})?;
let mut header = vec!["vid".to_string()];
header.extend(prop_keys.iter().map(|k| k.to_string()));
writeln!(file, "{}", header.join(","))
.map_err(|e| StorageError::io_error(format!("CSV write error: {e}")))?;
for vertex in &vertices {
let mut row = vec![vertex.vid.to_string()];
for key in &prop_keys {
let val = vertex
.property_value(key)
.map(|value| format_csv_value(&value))
.unwrap_or_default();
row.push(val);
}
writeln!(file, "{}", row.join(","))
.map_err(|e| StorageError::io_error(format!("CSV write error: {e}")))?;
}
}
for edge_info in &edge_types {
let edges = self.scan_edges_by_type(space, &edge_info.edge_type_name)?;
if edges.is_empty() {
continue;
}
let mut prop_keys: Vec<std::sync::Arc<str>> = edges
.iter()
.flat_map(|e| e.props.keys())
.cloned()
.collect::<std::collections::HashSet<_>>()
.into_iter()
.collect();
prop_keys.sort();
let csv_path = base.join(format!("{}.csv", edge_info.edge_type_name));
let mut file = std::fs::File::create(&csv_path).map_err(|e| {
StorageError::io_error(format!(
"Failed to create {}.csv: {e}",
edge_info.edge_type_name
))
})?;
let mut header = vec!["src".to_string(), "dst".to_string(), "ranking".to_string()];
header.extend(prop_keys.iter().map(|k| k.to_string()));
writeln!(file, "{}", header.join(","))
.map_err(|e| StorageError::io_error(format!("CSV write error: {e}")))?;
for edge in &edges {
let mut row = vec![
edge.src.to_string(),
edge.dst.to_string(),
edge.ranking.to_string(),
];
for key in &prop_keys {
let val = edge
.props
.get(key.as_ref())
.map(format_csv_value)
.unwrap_or_default();
row.push(val);
}
writeln!(file, "{}", row.join(","))
.map_err(|e| StorageError::io_error(format!("CSV write error: {e}")))?;
}
}
Ok(())
}
fn import_space(&mut self, space: &str, path: &std::path::Path) -> Result<(), StorageError> {
import_space_impl(self, space, path)
}
}
impl<T> QueryStorage for T where
T: GraphStore
+ CatalogStore
+ StorageAuthOps
+ StorageAdmin
+ StoragePersistenceOps
+ ColumnStatsReader
+ AutoCommitBatchOps
+ AutoCommitGroupOps
{
}