Skip to main content

ant_types/
graph.rs

1//! Graph data model: Vertex, Edge, PropertyValue, SubGraph.
2
3use std::collections::BTreeMap;
4
5use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7
8use crate::evidence::EvidenceId;
9use crate::ids::{EdgeId, TypeName, VertexId};
10
11pub use crate::property::PropertyValue;
12
13/// A graph node: business id, display name, qualified type label,
14/// and typed properties. The payload of a `vertex` record.
15#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
16pub struct Vertex {
17    /// Business id, unique within the type (e.g. `deal_1`).
18    pub id: VertexId,
19    /// Human-readable display name.
20    pub name: String,
21    /// Namespace-qualified, e.g. "Antares.Deal".
22    pub label: TypeName,
23    /// Typed properties, keyed by property name.
24    pub properties: BTreeMap<String, PropertyValue>,
25}
26
27/// A directed, labeled edge between two vertices, with optional
28/// bitemporal validity. The payload of an `edge` record.
29#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
30pub struct Edge {
31    /// Edge id, unique within the scope.
32    pub id: EdgeId,
33    /// Source vertex id.
34    pub src: VertexId,
35    /// Source vertex type.
36    pub src_type: TypeName,
37    /// Destination vertex id.
38    pub dst: VertexId,
39    /// Destination vertex type.
40    pub dst_type: TypeName,
41    /// Relation name (e.g. "hasStakeholder"), NOT namespace-qualified.
42    pub label: String,
43    /// Typed properties carried on the edge, keyed by property name.
44    pub properties: BTreeMap<String, PropertyValue>,
45
46    // --- Antares-native bitemporal annotations ---
47    //
48    // None on valid_from = valid from -infinity (always was).
49    // None on valid_to   = still holds (no end).
50    // observed_at        = wall-clock time the fact was recorded.
51    // extracted_at       = wall-clock time the extractor produced it.
52    /// Start of real-world validity; `None` = always was.
53    #[serde(default, skip_serializing_if = "Option::is_none")]
54    pub valid_from: Option<DateTime<Utc>>,
55    /// End of real-world validity (exclusive); `None` = still holds.
56    #[serde(default, skip_serializing_if = "Option::is_none")]
57    pub valid_to: Option<DateTime<Utc>>,
58    /// Wall-clock time the fact was recorded.
59    #[serde(default, skip_serializing_if = "Option::is_none")]
60    pub observed_at: Option<DateTime<Utc>>,
61    /// Wall-clock time the extractor produced it.
62    #[serde(default, skip_serializing_if = "Option::is_none")]
63    pub extracted_at: Option<DateTime<Utc>>,
64
65    /// Confidence in `[0,1]` for the fact. `None` is treated as 1.0 for
66    /// matching purposes.
67    #[serde(default, skip_serializing_if = "Option::is_none")]
68    pub confidence: Option<f32>,
69
70    /// First-class evidence references. Empty by default
71    /// (backwards-compatible with older payloads).
72    #[serde(default, skip_serializing_if = "Vec::is_empty")]
73    pub evidenced_by: Vec<EvidenceId>,
74}
75
76impl Edge {
77    /// Returns true iff this edge is valid at the given instant.
78    ///
79    /// Edges with no `valid_from` are treated as valid from -infinity.
80    /// Edges with no `valid_to` are treated as still-holding.
81    pub fn valid_at(&self, t: DateTime<Utc>) -> bool {
82        match (self.valid_from, self.valid_to) {
83            (None, None) => true,
84            (Some(f), None) => t >= f,
85            (None, Some(u)) => t < u,
86            (Some(f), Some(u)) => t >= f && t < u,
87        }
88    }
89
90    /// Returns true iff this edge's validity interval overlaps `[t1, t2)`.
91    pub fn valid_between(&self, t1: DateTime<Utc>, t2: DateTime<Utc>) -> bool {
92        if t2 <= t1 {
93            return false;
94        }
95        let lo = self.valid_from.unwrap_or(DateTime::<Utc>::MIN_UTC);
96        let hi = self.valid_to.unwrap_or(DateTime::<Utc>::MAX_UTC);
97        lo < t2 && t1 < hi
98    }
99
100    /// True if the edge is currently active (valid_to is None).
101    pub fn still_holds(&self) -> bool {
102        self.valid_to.is_none()
103    }
104}
105
106/// A set of vertices and edges, as returned by queries or carried in
107/// bulk payloads.
108#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
109pub struct SubGraph {
110    /// The vertices.
111    pub nodes: Vec<Vertex>,
112    /// The edges.
113    pub edges: Vec<Edge>,
114}