Skip to main content

agentic_memory/index/
temporal_index.rs

1//! Temporal index — sorted (timestamp, node_id) pairs for time range queries.
2
3use crate::types::CognitiveEvent;
4
5/// Sorted list of (created_at, node_id) pairs for efficient time range queries.
6pub struct TemporalIndex {
7    /// Sorted by timestamp ascending.
8    entries: Vec<(u64, u64)>,
9}
10
11impl TemporalIndex {
12    /// Create a new, empty temporal index.
13    pub fn new() -> Self {
14        Self {
15            entries: Vec::new(),
16        }
17    }
18
19    /// Get all node IDs created within a time range (inclusive).
20    pub fn range(&self, start: u64, end: u64) -> Vec<u64> {
21        let lo = self.entries.partition_point(|(ts, _)| *ts < start);
22        let hi = self.entries.partition_point(|(ts, _)| *ts <= end);
23        self.entries[lo..hi].iter().map(|(_, id)| *id).collect()
24    }
25
26    /// Get all node IDs created after a timestamp (exclusive).
27    pub fn after(&self, timestamp: u64) -> Vec<u64> {
28        let lo = self.entries.partition_point(|(ts, _)| *ts <= timestamp);
29        self.entries[lo..].iter().map(|(_, id)| *id).collect()
30    }
31
32    /// Get all node IDs created before a timestamp (exclusive).
33    pub fn before(&self, timestamp: u64) -> Vec<u64> {
34        let hi = self.entries.partition_point(|(ts, _)| *ts < timestamp);
35        self.entries[..hi].iter().map(|(_, id)| *id).collect()
36    }
37
38    /// Get the most recent N node IDs.
39    pub fn most_recent(&self, n: usize) -> Vec<u64> {
40        let start = self.entries.len().saturating_sub(n);
41        self.entries[start..]
42            .iter()
43            .rev()
44            .map(|(_, id)| *id)
45            .collect()
46    }
47
48    /// Get the oldest N node IDs.
49    pub fn oldest(&self, n: usize) -> Vec<u64> {
50        let end = n.min(self.entries.len());
51        self.entries[..end].iter().map(|(_, id)| *id).collect()
52    }
53
54    /// Rebuild the entire index from a slice of nodes.
55    pub fn rebuild(&mut self, nodes: &[CognitiveEvent]) {
56        self.entries.clear();
57        self.entries.reserve(nodes.len());
58        for node in nodes {
59            self.entries.push((node.created_at, node.id));
60        }
61        self.entries.sort_unstable();
62    }
63
64    /// Incrementally add a new node.
65    pub fn add_node(&mut self, event: &CognitiveEvent) {
66        let entry = (event.created_at, event.id);
67        let pos = self.entries.partition_point(|e| *e < entry);
68        self.entries.insert(pos, entry);
69    }
70
71    /// Remove a node from the index.
72    pub fn remove_node(&mut self, id: u64, created_at: u64) {
73        let entry = (created_at, id);
74        if let Ok(pos) = self.entries.binary_search(&entry) {
75            self.entries.remove(pos);
76        }
77    }
78
79    /// Clear the index.
80    pub fn clear(&mut self) {
81        self.entries.clear();
82    }
83
84    /// Number of entries.
85    pub fn len(&self) -> usize {
86        self.entries.len()
87    }
88
89    /// Whether the index is empty.
90    pub fn is_empty(&self) -> bool {
91        self.entries.is_empty()
92    }
93
94    /// Get a reference to the underlying entries (for serialization).
95    pub fn entries(&self) -> &[(u64, u64)] {
96        &self.entries
97    }
98}
99
100impl Default for TemporalIndex {
101    fn default() -> Self {
102        Self::new()
103    }
104}