Skip to main content

uni_locy/
result.rs

1use std::collections::HashMap;
2use std::time::Duration;
3
4use uni_common::Value;
5
6/// A single row of bindings from a query result.
7pub type Row = HashMap<String, Value>;
8
9/// Opaque savepoint identifier for transactional rollback.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11pub struct SavepointId(pub u64);
12
13/// The result of evaluating a compiled Locy program.
14#[derive(Debug, Clone)]
15pub struct LocyResult {
16    /// Derived facts per rule name.
17    pub derived: HashMap<String, Vec<Row>>,
18    /// Execution statistics.
19    pub stats: LocyStats,
20    /// Results from Phase 4 commands.
21    pub command_results: Vec<CommandResult>,
22}
23
24/// Result of executing a single Phase 4 command.
25#[derive(Debug, Clone)]
26pub enum CommandResult {
27    Query(Vec<Row>),
28    Assume(Vec<Row>),
29    Explain(DerivationNode),
30    Abduce(AbductionResult),
31    Derive { affected: usize },
32    Cypher(Vec<Row>),
33}
34
35/// A node in a derivation tree, produced by EXPLAIN RULE.
36#[derive(Debug, Clone)]
37pub struct DerivationNode {
38    pub rule: String,
39    pub clause_index: usize,
40    pub priority: Option<i64>,
41    pub bindings: HashMap<String, Value>,
42    pub along_values: HashMap<String, Value>,
43    pub children: Vec<DerivationNode>,
44    pub graph_fact: Option<String>,
45}
46
47/// Result of an ABDUCE query.
48#[derive(Debug, Clone)]
49pub struct AbductionResult {
50    pub modifications: Vec<ValidatedModification>,
51}
52
53/// A modification with validation status and cost.
54#[derive(Debug, Clone)]
55pub struct ValidatedModification {
56    pub modification: Modification,
57    pub validated: bool,
58    pub cost: f64,
59}
60
61/// A proposed graph modification from ABDUCE.
62#[derive(Debug, Clone)]
63pub enum Modification {
64    RemoveEdge {
65        source_var: String,
66        target_var: String,
67        edge_var: String,
68        edge_type: String,
69        match_properties: HashMap<String, Value>,
70    },
71    ChangeProperty {
72        element_var: String,
73        property: String,
74        old_value: Box<Value>,
75        new_value: Box<Value>,
76    },
77    AddEdge {
78        source_var: String,
79        target_var: String,
80        edge_type: String,
81        properties: HashMap<String, Value>,
82    },
83}
84
85/// Statistics collected during Locy program evaluation.
86#[derive(Debug, Clone, Default)]
87pub struct LocyStats {
88    pub strata_evaluated: usize,
89    pub total_iterations: usize,
90    pub derived_nodes: usize,
91    pub derived_edges: usize,
92    pub evaluation_time: Duration,
93    pub queries_executed: usize,
94    pub mutations_executed: usize,
95    /// Peak memory used by derived relations (in bytes).
96    pub peak_memory_bytes: usize,
97}
98
99impl LocyResult {
100    /// Get derived facts for a specific rule.
101    pub fn derived_facts(&self, rule: &str) -> Option<&Vec<Row>> {
102        self.derived.get(rule)
103    }
104
105    /// Get rows from the first Query command result.
106    pub fn rows(&self) -> Option<&Vec<Row>> {
107        self.command_results.iter().find_map(|cr| cr.as_query())
108    }
109
110    /// Get column names from the first Query command result's first row.
111    pub fn columns(&self) -> Option<Vec<String>> {
112        self.rows()
113            .and_then(|rows| rows.first().map(|row| row.keys().cloned().collect()))
114    }
115
116    /// Get execution statistics.
117    pub fn stats(&self) -> &LocyStats {
118        &self.stats
119    }
120
121    /// Get the total number of fixpoint iterations.
122    pub fn iterations(&self) -> usize {
123        self.stats.total_iterations
124    }
125}
126
127impl CommandResult {
128    /// If this is an Explain result, return the derivation node.
129    pub fn as_explain(&self) -> Option<&DerivationNode> {
130        match self {
131            CommandResult::Explain(node) => Some(node),
132            _ => None,
133        }
134    }
135
136    /// If this is a Query result, return the rows.
137    pub fn as_query(&self) -> Option<&Vec<Row>> {
138        match self {
139            CommandResult::Query(rows) => Some(rows),
140            _ => None,
141        }
142    }
143
144    /// If this is an Abduce result, return it.
145    pub fn as_abduce(&self) -> Option<&AbductionResult> {
146        match self {
147            CommandResult::Abduce(result) => Some(result),
148            _ => None,
149        }
150    }
151}