1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
//! `CALL refresh_stats() YIELD src_type, edge_type, tgt_type, count`
//!
//! Operator-callable recomputation of the label-pair edge-count
//! cardinality cache (0.9.35). Forces a fresh O(E) walk of all edges
//! and yields one row per `(src_type, edge_type, tgt_type)` triple
//! with its current count. Useful after bulk loads that bypass the
//! mutation paths the cache invalidator listens on, and as a manual
//! "what does the planner think the schema looks like right now?"
//! diagnostic.
//!
//! Pattern follows `affected_tests` (0.9.34) — same yield validation,
//! same per-column-optional model, same dispatch shape from
//! `call_clause.rs`.
//!
//! @procedure: refresh_stats
use super::helpers::yield_alias;
use std::collections::HashMap;
use super::super::ast::YieldItem;
use super::super::result::ResultRow;
use crate::datatypes::values::Value;
use crate::graph::dir_graph::DirGraph;
const PROC: &str = "refresh_stats";
pub(super) fn execute_refresh_stats(
graph: &DirGraph,
_params: &HashMap<String, Value>,
yield_items: &[YieldItem],
) -> Result<Vec<ResultRow>, String> {
// Each YIELD column is optional individually — caller can ask for
// just `count` (totals view), just the triple, or both.
let src_var = yield_alias(yield_items, "src_type");
let edge_var = yield_alias(yield_items, "edge_type");
let tgt_var = yield_alias(yield_items, "tgt_type");
let count_var = yield_alias(yield_items, "count");
if src_var.is_none() && edge_var.is_none() && tgt_var.is_none() && count_var.is_none() {
return Err(format!(
"CALL {PROC}: must YIELD at least one of \
'src_type', 'edge_type', 'tgt_type', 'count'."
));
}
// Force fresh recompute: invalidate then read. The invalidate +
// lazy-recompute combo guarantees the next read walks all edges,
// even if the cache was warm from a prior call.
graph.invalidate_edge_type_counts_cache();
let mut triples = graph.get_or_compute_type_connectivity();
// Sort for deterministic output — agents calling this expect
// stable row order so they can diff between calls.
triples.sort_by(|a, b| {
a.src
.cmp(&b.src)
.then(a.conn.cmp(&b.conn))
.then(a.tgt.cmp(&b.tgt))
});
let mut rows = Vec::with_capacity(triples.len());
for t in triples {
let mut row = ResultRow::new();
if let Some(name) = &src_var {
row.projected.insert(name.clone(), Value::String(t.src));
}
if let Some(name) = &edge_var {
row.projected.insert(name.clone(), Value::String(t.conn));
}
if let Some(name) = &tgt_var {
row.projected.insert(name.clone(), Value::String(t.tgt));
}
if let Some(name) = &count_var {
row.projected
.insert(name.clone(), Value::Int64(t.count as i64));
}
rows.push(row);
}
Ok(rows)
}