Skip to main content

agentic_memory/v3/indexes/
temporal.rs

1//! B-tree index for temporal queries. O(log n) lookup by timestamp.
2
3use super::{Index, IndexResult};
4use crate::v3::block::{Block, BlockHash};
5use chrono::{DateTime, Utc};
6use std::collections::BTreeMap;
7
8/// B-tree index for temporal queries.
9pub struct TemporalIndex {
10    /// Timestamp (millis) -> (sequence, hash)
11    by_time: BTreeMap<i64, Vec<(u64, BlockHash)>>,
12
13    /// Sequence -> timestamp (for reverse lookup)
14    by_sequence: Vec<i64>,
15}
16
17impl TemporalIndex {
18    pub fn new() -> Self {
19        Self {
20            by_time: BTreeMap::new(),
21            by_sequence: Vec::new(),
22        }
23    }
24
25    /// Query blocks in time range
26    pub fn query_range(&self, start: DateTime<Utc>, end: DateTime<Utc>) -> Vec<IndexResult> {
27        let start_ts = start.timestamp_millis();
28        let end_ts = end.timestamp_millis();
29
30        self.by_time
31            .range(start_ts..=end_ts)
32            .flat_map(|(_, blocks)| blocks.iter())
33            .map(|(seq, hash)| IndexResult {
34                block_sequence: *seq,
35                block_hash: *hash,
36                score: 1.0,
37            })
38            .collect()
39    }
40
41    /// Get blocks from last N seconds
42    pub fn query_recent(&self, seconds: i64) -> Vec<IndexResult> {
43        let now = Utc::now().timestamp_millis();
44        let start = now - (seconds * 1000);
45
46        self.by_time
47            .range(start..=now)
48            .flat_map(|(_, blocks)| blocks.iter())
49            .map(|(seq, hash)| IndexResult {
50                block_sequence: *seq,
51                block_hash: *hash,
52                score: 1.0,
53            })
54            .collect()
55    }
56
57    /// Get total indexed block count
58    pub fn len(&self) -> usize {
59        self.by_sequence.len()
60    }
61
62    /// Check if empty
63    pub fn is_empty(&self) -> bool {
64        self.by_sequence.is_empty()
65    }
66}
67
68impl Default for TemporalIndex {
69    fn default() -> Self {
70        Self::new()
71    }
72}
73
74impl Index for TemporalIndex {
75    fn index(&mut self, block: &Block) {
76        let ts = block.timestamp.timestamp_millis();
77
78        self.by_time
79            .entry(ts)
80            .or_default()
81            .push((block.sequence, block.hash));
82
83        // Ensure by_sequence is large enough
84        while self.by_sequence.len() <= block.sequence as usize {
85            self.by_sequence.push(0);
86        }
87        self.by_sequence[block.sequence as usize] = ts;
88    }
89
90    fn remove(&mut self, sequence: u64) {
91        if let Some(&ts) = self.by_sequence.get(sequence as usize) {
92            if let Some(blocks) = self.by_time.get_mut(&ts) {
93                blocks.retain(|(seq, _)| *seq != sequence);
94            }
95        }
96    }
97
98    fn rebuild(&mut self, blocks: impl Iterator<Item = Block>) {
99        self.by_time.clear();
100        self.by_sequence.clear();
101        for block in blocks {
102            self.index(&block);
103        }
104    }
105}