use std::any::Any;
use std::collections::BTreeMap;
use lora_compiler::{plan_tree_from_compiled, PlanTree, PlanTreeNode};
use lora_executor::{classify_stream, plan_result_columns, LoraValue};
use lora_store::{GraphStats, GraphStorage, GraphStorageMut};
use crate::database::Database;
use crate::error::LoraError;
use crate::explain::{PlanShape, QueryPlan};
impl<S> Database<S>
where
S: GraphStorage + GraphStorageMut + Any + Clone + Send + Sync + 'static,
{
pub fn explain(
&self,
query: &str,
_params: Option<BTreeMap<String, LoraValue>>,
) -> Result<QueryPlan, LoraError> {
let (store, store_epoch) = self.read_store_with_epoch_deadline(None)?;
let compiled = self
.compile_query_cached(query, &*store, store_epoch)
.map_err(LoraError::from_anyhow)?;
let mut tree = plan_tree_from_compiled(&compiled);
let stats = store.graph_stats();
annotate_estimated_rows(&mut tree, &stats);
let shape: PlanShape = classify_stream(&compiled).into();
let result_columns = plan_result_columns(&compiled.physical);
Ok(QueryPlan {
query: query.to_string(),
tree,
shape,
result_columns,
})
}
}
pub(crate) fn annotate_estimated_rows(tree: &mut PlanTree, stats: &GraphStats) {
annotate_node(&mut tree.root, stats);
}
fn annotate_node(node: &mut PlanTreeNode, stats: &GraphStats) {
node.estimated_rows = match node.operator.as_str() {
"NodeScan" => Some(stats.node_count as u64),
"NodeByLabelScan" => labels_estimate(node, stats),
"NodeByPropertyScan" => property_equality_estimate(node, stats),
_ => None,
};
for child in &mut node.children {
annotate_node(child, stats);
}
}
fn labels_estimate(node: &PlanTreeNode, stats: &GraphStats) -> Option<u64> {
let labels = node.details.get("labels")?;
let trimmed = labels.trim_start_matches(':');
let bare = trimmed.split('|').next()?.trim();
stats.label_count(bare)
}
fn property_equality_estimate(node: &PlanTreeNode, stats: &GraphStats) -> Option<u64> {
let property = node.details.get("key")?;
let labels = node.details.get("labels")?;
let trimmed = labels.trim_start_matches(':');
let bare = trimmed.split('|').next()?.trim();
stats.estimate_node_property_equality(bare, property)
}