From bc61ec5bb99dd0c492d7dbaaf4ba19a644dc1368 Mon Sep 17 00:00:00 2001 From: Lucas Jeub Date: Tue, 21 Apr 2026 14:21:54 +0200 Subject: [PATCH 01/15] add fast path for last on properties --- db4-storage/src/generic_t_props.rs | 6 ++++ .../src/core/entities/properties/tprop.rs | 2 ++ .../src/entities/properties/tcell.rs | 19 +++++++++-- .../src/entities/properties/tprop.rs | 33 ++++++++++++++++++- .../src/graph/variants/storage_variants2.rs | 4 +++ .../src/graph/variants/storage_variants3.rs | 4 +++ .../time_semantics/base_time_semantics.rs | 10 ++++++ .../time_semantics/event_semantics.rs | 11 +++++++ .../internal/time_semantics/filtered_edge.rs | 7 ++++ .../time_semantics/persistent_semantics.rs | 11 +++++++ .../internal/time_semantics/time_semantics.rs | 9 +++++ .../time_semantics/time_semantics_ops.rs | 7 ++++ .../time_semantics/window_time_semantics.rs | 10 ++++++ raphtory/src/db/graph/node.rs | 8 ++--- 14 files changed, 133 insertions(+), 8 deletions(-) diff --git a/db4-storage/src/generic_t_props.rs b/db4-storage/src/generic_t_props.rs index e2d96955e9..d51e579d91 100644 --- a/db4-storage/src/generic_t_props.rs +++ b/db4-storage/src/generic_t_props.rs @@ -105,6 +105,12 @@ impl<'a, Ref: WithTProps<'a>> TPropOps<'a> for GenericTProps<'a, Ref> { .max_by_key(|(t, _)| *t) } + fn last(&self) -> Option<(EventTime, Prop)> { + self.tprops(self.prop_id) + .filter_map(|t_props| t_props.last()) + .max_by_key(|(t, _)| *t) + } + fn iter_inner( self, w: Option>, diff --git a/raphtory-api/src/core/entities/properties/tprop.rs b/raphtory-api/src/core/entities/properties/tprop.rs index bcf4c2e3c1..6a6bb747d4 100644 --- a/raphtory-api/src/core/entities/properties/tprop.rs +++ b/raphtory-api/src/core/entities/properties/tprop.rs @@ -18,6 +18,8 @@ pub trait TPropOps<'a>: Clone + Send + Sync + Sized + 'a { self.clone().iter_inner_rev(Some(EventTime::MIN..t)).next() } + fn last(&self) -> Option<(EventTime, Prop)>; + fn iter_inner( self, range: Option>, diff --git a/raphtory-core/src/entities/properties/tcell.rs b/raphtory-core/src/entities/properties/tcell.rs index dc6fd3abea..24bccace4a 100644 --- a/raphtory-core/src/entities/properties/tcell.rs +++ b/raphtory-core/src/entities/properties/tcell.rs @@ -118,8 +118,23 @@ impl TCell { match self { TCell::Empty => None, TCell::TCell1(t2, v) => (*t2 < t).then_some((*t2, v)), - TCell::TCellCap(map) => map.range(EventTime::MIN..t).last().map(|(ti, v)| (*ti, v)), - TCell::TCellN(map) => map.range(EventTime::MIN..t).last().map(|(ti, v)| (*ti, v)), + TCell::TCellCap(map) => map + .range(EventTime::MIN..t) + .next_back() + .map(|(ti, v)| (*ti, v)), + TCell::TCellN(map) => map + .range(EventTime::MIN..t) + .next_back() + .map(|(ti, v)| (*ti, v)), + } + } + + pub fn last_value(&self) -> Option<(EventTime, &A)> { + match self { + TCell::Empty => None, + TCell::TCell1(t, v) => Some((*t, v)), + TCell::TCellCap(map) => map.last_key_value().map(|(t, v)| (*t, v)), + TCell::TCellN(map) => map.last_key_value().map(|(t, v)| (*t, v)), } } diff --git a/raphtory-core/src/entities/properties/tprop.rs b/raphtory-core/src/entities/properties/tprop.rs index b6d426ba23..593b65eecc 100644 --- a/raphtory-core/src/entities/properties/tprop.rs +++ b/raphtory-core/src/entities/properties/tprop.rs @@ -12,7 +12,7 @@ use raphtory_api::core::{ prop::{Prop, PropArray, PropType}, tprop::TPropOps, }, - storage::arc_str::ArcStr, + storage::{arc_str::ArcStr, timeindex::TimeIndexOps}, }; use rustc_hash::FxHashMap; use serde::Serialize; @@ -119,6 +119,15 @@ impl<'a> TPropCell<'a> { } impl<'a> TPropOps<'a> for TPropCell<'a> { + fn last(&self) -> Option<(EventTime, Prop)> { + let i = self.t_cell?.last()?; + None + } + + fn last_before(&self, t: EventTime) -> Option<(EventTime, Prop)> { + todo!() + } + fn iter_inner( self, range: Option>, @@ -386,6 +395,28 @@ impl TProp { } impl<'a> TPropOps<'a> for &'a TProp { + fn last(&self) -> Option<(EventTime, Prop)> { + match self { + TProp::Empty => None, + TProp::Str(cell) => cell.last_value().map(|(t, v)| (t, Prop::Str(v.clone()))), + TProp::U8(cell) => cell.last_value().map(|(t, v)| (t, Prop::U8(v.clone()))), + TProp::U16(cell) => cell.last_value().map(|(t, v)| (t, Prop::U16(v.clone()))), + TProp::I32(cell) => cell.last_value().map(|(t, v)| (t, Prop::I32(v.clone()))), + TProp::I64(cell) => cell.last_value().map(|(t, v)| (t, Prop::I64(v.clone()))), + TProp::U32(cell) => cell.last_value().map(|(t, v)| (t, Prop::U32(v.clone()))), + TProp::U64(cell) => cell.last_value().map(|(t, v)| (t, Prop::U64(v.clone()))), + TProp::F32(cell) => cell.last_value().map(|(t, v)| (t, Prop::F32(v.clone()))), + TProp::F64(cell) => cell.last_value().map(|(t, v)| (t, Prop::F64(v.clone()))), + TProp::Bool(cell) => cell.last_value().map(|(t, v)| (t, Prop::Bool(v.clone()))), + TProp::DTime(cell) => cell.last_value().map(|(t, v)| (t, Prop::DTime(v.clone()))), + TProp::List(cell) => cell.last_value().map(|(t, v)| (t, Prop::List(v.clone()))), + TProp::NDTime(cell) => cell.last_value().map(|(t, v)| (t, Prop::NDTime(v.clone()))), + TProp::Map(cell) => cell.last_value().map(|(t, v)| (t, Prop::Map(v.clone()))), + TProp::Decimal(cell) => cell + .last_value() + .map(|(t, v)| (t, Prop::Decimal(v.clone()))), + } + } fn last_before(&self, t: EventTime) -> Option<(EventTime, Prop)> { match self { TProp::Empty => None, diff --git a/raphtory-storage/src/graph/variants/storage_variants2.rs b/raphtory-storage/src/graph/variants/storage_variants2.rs index 949a444a26..3320f412ba 100644 --- a/raphtory-storage/src/graph/variants/storage_variants2.rs +++ b/raphtory-storage/src/graph/variants/storage_variants2.rs @@ -51,6 +51,10 @@ impl<'a, Mem: TPropOps<'a> + 'a> TPropOps<'a> for SelfType!(Mem, Disk) { for_all!(self, props => props.last_before(t)) } + fn last(&self) -> Option<(EventTime, Prop)> { + for_all!(self, props => props.last()) + } + fn iter_inner( self, range: Option>, diff --git a/raphtory-storage/src/graph/variants/storage_variants3.rs b/raphtory-storage/src/graph/variants/storage_variants3.rs index f49a3e4224..9809b75e45 100644 --- a/raphtory-storage/src/graph/variants/storage_variants3.rs +++ b/raphtory-storage/src/graph/variants/storage_variants3.rs @@ -55,6 +55,10 @@ impl<'a, Mem: TPropOps<'a> + 'a, Unlocked: TPropOps<'a> + 'a> TPropOps<'a> for_all!(self, props => props.last_before(t)) } + fn last(&self) -> Option<(EventTime, Prop)> { + for_all!(self, props => props.last()) + } + fn iter_inner( self, range: Option>, diff --git a/raphtory/src/db/api/view/internal/time_semantics/base_time_semantics.rs b/raphtory/src/db/api/view/internal/time_semantics/base_time_semantics.rs index 7e395dcdcd..1acf7088f6 100644 --- a/raphtory/src/db/api/view/internal/time_semantics/base_time_semantics.rs +++ b/raphtory/src/db/api/view/internal/time_semantics/base_time_semantics.rs @@ -290,6 +290,16 @@ impl NodeTimeSemanticsOps for BaseTimeSemantics { ) -> Option<(EventTime, Prop)> { for_all!(self, semantics => semantics.node_tprop_last_at_window(node, view, prop_id, t, w)) } + + #[inline] + fn node_tprop_last<'graph, G: GraphView + 'graph>( + &self, + node: NodeStorageRef<'graph>, + view: G, + prop_id: usize, + ) -> Option<(EventTime, Prop)> { + for_all!(self, semantics => semantics.node_tprop_last(node, view, prop_id)) + } } impl EdgeTimeSemanticsOps for BaseTimeSemantics { diff --git a/raphtory/src/db/api/view/internal/time_semantics/event_semantics.rs b/raphtory/src/db/api/view/internal/time_semantics/event_semantics.rs index 81db1c874b..43a8db9afc 100644 --- a/raphtory/src/db/api/view/internal/time_semantics/event_semantics.rs +++ b/raphtory/src/db/api/view/internal/time_semantics/event_semantics.rs @@ -237,6 +237,17 @@ impl NodeTimeSemanticsOps for EventSemantics { .kmerge_by(|(a, _), (b, _)| a >= b) } + fn node_tprop_last<'graph, G: GraphView + 'graph>( + &self, + node: NodeStorageRef<'graph>, + view: G, + prop_id: usize, + ) -> Option<(EventTime, Prop)> { + node.tprop_iter_layers(view.layer_ids(), prop_id) + .filter_map(|prop| prop.last()) + .max_by_key(|(t, _)| *t) + } + fn node_tprop_last_at<'graph, G: GraphView + 'graph>( &self, node: NodeStorageRef<'graph>, diff --git a/raphtory/src/db/api/view/internal/time_semantics/filtered_edge.rs b/raphtory/src/db/api/view/internal/time_semantics/filtered_edge.rs index b411e0b85a..dccc65a547 100644 --- a/raphtory/src/db/api/view/internal/time_semantics/filtered_edge.rs +++ b/raphtory/src/db/api/view/internal/time_semantics/filtered_edge.rs @@ -203,6 +203,13 @@ pub struct FilteredEdgeTProp { impl<'graph, G: GraphViewOps<'graph>, P: TPropOps<'graph>> TPropOps<'graph> for FilteredEdgeTProp { + fn last(&self) -> Option<(EventTime, Prop)> { + if self.view.internal_exploded_edge_filtered() { + self.clone().iter_rev().next() + } else { + self.props.last() + } + } fn iter_inner( self, range: Option>, diff --git a/raphtory/src/db/api/view/internal/time_semantics/persistent_semantics.rs b/raphtory/src/db/api/view/internal/time_semantics/persistent_semantics.rs index c28e6aa961..54d8cbc938 100644 --- a/raphtory/src/db/api/view/internal/time_semantics/persistent_semantics.rs +++ b/raphtory/src/db/api/view/internal/time_semantics/persistent_semantics.rs @@ -504,6 +504,17 @@ impl NodeTimeSemanticsOps for PersistentSemantics { window_iter_rev.chain(first) } + fn node_tprop_last<'graph, G: GraphView + 'graph>( + &self, + node: NodeStorageRef<'graph>, + view: G, + prop_id: usize, + ) -> Option<(EventTime, Prop)> { + node.tprop_iter_layers(view.layer_ids(), prop_id) + .filter_map(|prop| prop.last()) + .max_by_key(|(t, _)| *t) + } + fn node_tprop_last_at<'graph, G: GraphViewOps<'graph>>( &self, node: NodeStorageRef<'graph>, diff --git a/raphtory/src/db/api/view/internal/time_semantics/time_semantics.rs b/raphtory/src/db/api/view/internal/time_semantics/time_semantics.rs index 5293e71898..bbc7cd79e5 100644 --- a/raphtory/src/db/api/view/internal/time_semantics/time_semantics.rs +++ b/raphtory/src/db/api/view/internal/time_semantics/time_semantics.rs @@ -265,6 +265,15 @@ impl NodeTimeSemanticsOps for TimeSemantics { ) -> impl Iterator + Send + Sync + 'graph { for_all_iter!(self, semantics => semantics.node_edge_history_rev_window(node, view, layer_ids, w)) } + + fn node_tprop_last<'graph, G: GraphView + 'graph>( + &self, + node: NodeStorageRef<'graph>, + view: G, + prop_id: usize, + ) -> Option<(EventTime, Prop)> { + for_all!(self, semantics => semantics.node_tprop_last(node, view, prop_id)) + } } impl EdgeTimeSemanticsOps for TimeSemantics { diff --git a/raphtory/src/db/api/view/internal/time_semantics/time_semantics_ops.rs b/raphtory/src/db/api/view/internal/time_semantics/time_semantics_ops.rs index 9899e752db..20596eb401 100644 --- a/raphtory/src/db/api/view/internal/time_semantics/time_semantics_ops.rs +++ b/raphtory/src/db/api/view/internal/time_semantics/time_semantics_ops.rs @@ -164,6 +164,13 @@ pub trait NodeTimeSemanticsOps { w: Range, ) -> impl Iterator + Send + Sync + 'graph; + fn node_tprop_last<'graph, G: GraphView + 'graph>( + &self, + node: NodeStorageRef<'graph>, + view: G, + prop_id: usize, + ) -> Option<(EventTime, Prop)>; + fn node_tprop_last_at<'graph, G: GraphView + 'graph>( &self, node: NodeStorageRef<'graph>, diff --git a/raphtory/src/db/api/view/internal/time_semantics/window_time_semantics.rs b/raphtory/src/db/api/view/internal/time_semantics/window_time_semantics.rs index 05b520559a..2f728de642 100644 --- a/raphtory/src/db/api/view/internal/time_semantics/window_time_semantics.rs +++ b/raphtory/src/db/api/view/internal/time_semantics/window_time_semantics.rs @@ -264,6 +264,16 @@ impl NodeTimeSemanticsOps for WindowTimeSemantics { .node_tprop_iter_window_rev(node, view, prop_id, w) } + fn node_tprop_last<'graph, G: GraphView + 'graph>( + &self, + node: NodeStorageRef<'graph>, + view: G, + prop_id: usize, + ) -> Option<(EventTime, Prop)> { + self.semantics + .node_tprop_last_at(node, view, prop_id, self.window.end.previous()) + } + #[inline] fn node_tprop_last_at<'graph, G: GraphView + 'graph>( &self, diff --git a/raphtory/src/db/graph/node.rs b/raphtory/src/db/graph/node.rs index 0636f3e72b..05fe9e567c 100644 --- a/raphtory/src/db/graph/node.rs +++ b/raphtory/src/db/graph/node.rs @@ -255,11 +255,9 @@ impl<'graph, G: GraphViewOps<'graph>> InternalTemporalPropertyViewOps for NodeVi fn temporal_value(&self, id: usize) -> Option { let semantics = self.graph.node_time_semantics(); let node = self.graph.core_node(self.node); - let res = semantics - .node_tprop_iter_rev(node.as_ref(), &self.graph, id) - .next() - .map(|(_, v)| v); - res + semantics + .node_tprop_last(node.as_ref(), &self.graph, id) + .map(|(_, v)| v) } fn temporal_iter(&self, id: usize) -> BoxedLIter<'_, (EventTime, Prop)> { From 88c1bfa002d9bda03370ba331d2cb6a95b9e88ab Mon Sep 17 00:00:00 2001 From: Fabian Murariu Date: Tue, 21 Apr 2026 18:00:20 +0100 Subject: [PATCH 02/15] some fixes for failing tests --- optd | 2 +- raphtory-core/src/entities/properties/tprop.rs | 15 ++++++++++++--- .../time_semantics/window_time_semantics.rs | 2 +- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/optd b/optd index c8a444e100..9e68f11a6d 160000 --- a/optd +++ b/optd @@ -1 +1 @@ -Subproject commit c8a444e1000ecd3b3f366f52a970b6495ec6a591 +Subproject commit 9e68f11a6d8698cd54a17d69bb22621b819d1f8e diff --git a/raphtory-core/src/entities/properties/tprop.rs b/raphtory-core/src/entities/properties/tprop.rs index 593b65eecc..7773f78615 100644 --- a/raphtory-core/src/entities/properties/tprop.rs +++ b/raphtory-core/src/entities/properties/tprop.rs @@ -120,12 +120,21 @@ impl<'a> TPropCell<'a> { impl<'a> TPropOps<'a> for TPropCell<'a> { fn last(&self) -> Option<(EventTime, Prop)> { - let i = self.t_cell?.last()?; - None + let (time, pos) = self.t_cell?.last_value()?; + let end_pos = pos.as_ref()?; + (0..=*end_pos) + .rev() + .find_map(|idx| self.log?.get(idx)) + .map(|prop| (time, prop)) } fn last_before(&self, t: EventTime) -> Option<(EventTime, Prop)> { - todo!() + let (time, pos) = self.t_cell?.last_before(t)?; + let end_pos = pos.as_ref()?; + (0..=*end_pos) + .rev() + .find_map(|idx| self.log?.get(idx)) + .map(|prop| (time, prop)) } fn iter_inner( diff --git a/raphtory/src/db/api/view/internal/time_semantics/window_time_semantics.rs b/raphtory/src/db/api/view/internal/time_semantics/window_time_semantics.rs index 2f728de642..1cdf2c8d02 100644 --- a/raphtory/src/db/api/view/internal/time_semantics/window_time_semantics.rs +++ b/raphtory/src/db/api/view/internal/time_semantics/window_time_semantics.rs @@ -271,7 +271,7 @@ impl NodeTimeSemanticsOps for WindowTimeSemantics { prop_id: usize, ) -> Option<(EventTime, Prop)> { self.semantics - .node_tprop_last_at(node, view, prop_id, self.window.end.previous()) + .node_tprop_last_at(node, view, prop_id, self.window.end) } #[inline] From 2cfe02622b88c8bad6802241fefe1146ab5bac2d Mon Sep 17 00:00:00 2001 From: Fabian Murariu Date: Wed, 22 Apr 2026 11:34:24 +0100 Subject: [PATCH 03/15] fix last and last_before for in memory segment --- raphtory-api/src/core/storage/timeindex.rs | 15 ++++++++++++++- raphtory-core/src/entities/properties/tprop.rs | 17 +++-------------- .../time_semantics/window_time_semantics.rs | 2 +- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/raphtory-api/src/core/storage/timeindex.rs b/raphtory-api/src/core/storage/timeindex.rs index 954ea4c16f..13defb156e 100644 --- a/raphtory-api/src/core/storage/timeindex.rs +++ b/raphtory-api/src/core/storage/timeindex.rs @@ -248,7 +248,7 @@ impl EventTime { if self.1 > 0 { Self(self.0, self.1 - 1) } else if self.0 > i64::MIN { - Self(self.0 - 1, 0) + Self(self.0 - 1, usize::MAX) } else { *self } @@ -289,3 +289,16 @@ impl AsTime for EventTime { Self(t, s) } } + +#[cfg(test)] +mod test { + use super::*; + #[test] + fn symetric_next_previous() { + proptest::proptest!(|(t in i64::MIN..=i64::MAX, s in 0..=usize::MAX)| { + let event_time = EventTime(t, s); + assert_eq!(event_time, event_time.next().previous()); + assert_eq!(event_time, event_time.previous().next()); + }); + } +} diff --git a/raphtory-core/src/entities/properties/tprop.rs b/raphtory-core/src/entities/properties/tprop.rs index 7773f78615..48a45c6833 100644 --- a/raphtory-core/src/entities/properties/tprop.rs +++ b/raphtory-core/src/entities/properties/tprop.rs @@ -120,21 +120,10 @@ impl<'a> TPropCell<'a> { impl<'a> TPropOps<'a> for TPropCell<'a> { fn last(&self) -> Option<(EventTime, Prop)> { - let (time, pos) = self.t_cell?.last_value()?; - let end_pos = pos.as_ref()?; - (0..=*end_pos) + self.t_cell? + .iter() .rev() - .find_map(|idx| self.log?.get(idx)) - .map(|prop| (time, prop)) - } - - fn last_before(&self, t: EventTime) -> Option<(EventTime, Prop)> { - let (time, pos) = self.t_cell?.last_before(t)?; - let end_pos = pos.as_ref()?; - (0..=*end_pos) - .rev() - .find_map(|idx| self.log?.get(idx)) - .map(|prop| (time, prop)) + .find_map(|(t, pos)| self.log?.get(pos.as_ref().copied()?).map(|prop| (*t, prop))) } fn iter_inner( diff --git a/raphtory/src/db/api/view/internal/time_semantics/window_time_semantics.rs b/raphtory/src/db/api/view/internal/time_semantics/window_time_semantics.rs index 1cdf2c8d02..2f728de642 100644 --- a/raphtory/src/db/api/view/internal/time_semantics/window_time_semantics.rs +++ b/raphtory/src/db/api/view/internal/time_semantics/window_time_semantics.rs @@ -271,7 +271,7 @@ impl NodeTimeSemanticsOps for WindowTimeSemantics { prop_id: usize, ) -> Option<(EventTime, Prop)> { self.semantics - .node_tprop_last_at(node, view, prop_id, self.window.end) + .node_tprop_last_at(node, view, prop_id, self.window.end.previous()) } #[inline] From 28c4b3f38326df65f8aec43f27a7527e5374f267 Mon Sep 17 00:00:00 2001 From: Fabian Murariu Date: Wed, 22 Apr 2026 13:49:38 +0100 Subject: [PATCH 04/15] remove TProp --- optd | 2 +- .../src/entities/properties/graph_meta.rs | 170 ----- raphtory-core/src/entities/properties/mod.rs | 1 - .../src/entities/properties/props.rs | 3 - .../src/entities/properties/tprop.rs | 684 +----------------- raphtory-graphql/schema.graphql | 1 - raphtory-storage/src/mutation/mod.rs | 2 - 7 files changed, 6 insertions(+), 857 deletions(-) delete mode 100644 raphtory-core/src/entities/properties/graph_meta.rs diff --git a/optd b/optd index 9e68f11a6d..c8a444e100 160000 --- a/optd +++ b/optd @@ -1 +1 @@ -Subproject commit 9e68f11a6d8698cd54a17d69bb22621b819d1f8e +Subproject commit c8a444e1000ecd3b3f366f52a970b6495ec6a591 diff --git a/raphtory-core/src/entities/properties/graph_meta.rs b/raphtory-core/src/entities/properties/graph_meta.rs deleted file mode 100644 index 70a222a186..0000000000 --- a/raphtory-core/src/entities/properties/graph_meta.rs +++ /dev/null @@ -1,170 +0,0 @@ -use crate::{ - entities::properties::{ - props::MetadataError, - tprop::{IllegalPropType, TProp}, - }, - storage::{locked_view::LockedView, timeindex::EventTime}, -}; -use raphtory_api::core::{ - entities::properties::{ - meta::PropMapper, - prop::{Prop, PropError, PropType}, - }, - storage::{ - arc_str::ArcStr, - dict_mapper::{MaybeNew, PublicKeys}, - FxDashMap, - }, -}; -use serde::Serialize; -use std::ops::{Deref, DerefMut}; - -#[derive(Serialize, Debug, Default)] -pub struct GraphMeta { - metadata_mapper: PropMapper, - temporal_mapper: PropMapper, - metadata: FxDashMap>, - temporal: FxDashMap, -} - -impl GraphMeta { - pub fn new() -> Self { - Self { - metadata_mapper: PropMapper::default(), - temporal_mapper: PropMapper::default(), - metadata: FxDashMap::default(), - temporal: FxDashMap::default(), - } - } - - pub fn deep_clone(&self) -> Self { - Self { - metadata_mapper: self.metadata_mapper.deep_clone(), - temporal_mapper: self.temporal_mapper.deep_clone(), - metadata: self.metadata.clone(), - temporal: self.temporal.clone(), - } - } - - #[inline] - pub fn metadata_mapper(&self) -> &PropMapper { - &self.metadata_mapper - } - - #[inline] - pub fn temporal_mapper(&self) -> &PropMapper { - &self.temporal_mapper - } - - #[inline] - pub fn resolve_property( - &self, - name: &str, - dtype: PropType, - is_metadata: bool, - ) -> Result, PropError> { - let mapper = if is_metadata { - &self.metadata_mapper - } else { - &self.temporal_mapper - }; - mapper.get_or_create_and_validate(name, dtype) - } - - pub fn add_metadata(&self, prop_id: usize, prop: Prop) -> Result<(), MetadataError> { - let mut prop_entry = self.metadata.entry(prop_id).or_insert(None); - match prop_entry.deref_mut() { - Some(old_value) => { - if !(old_value == &prop) { - return Err(MetadataError::IllegalUpdate { - old: old_value.clone(), - new: prop, - }); - } - } - None => { - *prop_entry = Some(prop); - } - } - Ok(()) - } - - pub fn update_metadata(&self, prop_id: usize, prop: Prop) { - let mut prop_entry = self.metadata.entry(prop_id).or_insert(None); - *prop_entry = Some(prop); - } - - pub fn add_prop( - &self, - t: EventTime, - prop_id: usize, - prop: Prop, - ) -> Result<(), IllegalPropType> { - let mut prop_entry = self.temporal.entry(prop_id).or_default(); - (*prop_entry).set(t, prop) - } - - pub fn get_metadata(&self, id: usize) -> Option { - let entry = self.metadata.get(&id)?; - entry.as_ref().cloned() - } - - pub fn get_temporal_prop(&self, prop_id: usize) -> Option> { - let entry = self.temporal.get(&prop_id)?; - Some(LockedView::DashMap(entry)) - } - - pub fn get_metadata_id(&self, name: &str) -> Option { - self.metadata_mapper.get_id(name) - } - - pub fn get_temporal_id(&self, name: &str) -> Option { - self.temporal_mapper.get_id(name) - } - - pub fn get_metadata_name(&self, prop_id: usize) -> ArcStr { - self.metadata_mapper.get_name(prop_id).clone() - } - - pub fn get_temporal_name(&self, prop_id: usize) -> ArcStr { - self.temporal_mapper.get_name(prop_id).clone() - } - - pub fn get_temporal_dtype(&self, prop_id: usize) -> Option { - self.temporal_mapper.get_dtype(prop_id) - } - - pub fn get_metadata_dtype(&self, prop_id: usize) -> Option { - self.metadata_mapper.get_dtype(prop_id) - } - - pub fn metadata_names(&self) -> PublicKeys { - self.metadata_mapper.keys() - } - - pub fn metadata_ids(&self) -> impl Iterator { - self.metadata_mapper.ids() - } - - pub fn temporal_names(&self) -> PublicKeys { - self.temporal_mapper.keys() - } - - pub fn temporal_ids(&self) -> impl Iterator { - self.temporal_mapper.ids() - } - - pub fn metadata(&self) -> impl Iterator + '_ { - self.metadata - .iter() - .filter_map(|kv| kv.value().as_ref().map(|v| (*kv.key(), v.clone()))) - } - - pub fn temporal_props( - &self, - ) -> impl Iterator + '_)> + '_ { - self.temporal_mapper - .ids() - .filter_map(|id| self.temporal.get(&id).map(|v| (id, v))) - } -} diff --git a/raphtory-core/src/entities/properties/mod.rs b/raphtory-core/src/entities/properties/mod.rs index 5c4f091ba4..11e9bb613d 100644 --- a/raphtory-core/src/entities/properties/mod.rs +++ b/raphtory-core/src/entities/properties/mod.rs @@ -1,4 +1,3 @@ -pub mod graph_meta; pub mod props; pub mod tcell; pub mod tprop; diff --git a/raphtory-core/src/entities/properties/props.rs b/raphtory-core/src/entities/properties/props.rs index 63f6331afe..c3243d24a0 100644 --- a/raphtory-core/src/entities/properties/props.rs +++ b/raphtory-core/src/entities/properties/props.rs @@ -17,9 +17,6 @@ pub enum MetadataError { #[error("Attempted to change value of metadata, old: {old}, new: {new}")] IllegalUpdate { old: Prop, new: Prop }, - #[error(transparent)] - IllegalPropType(#[from] IllegalPropType), - #[error(transparent)] ColumnError(#[from] TPropColumnError), } diff --git a/raphtory-core/src/entities/properties/tprop.rs b/raphtory-core/src/entities/properties/tprop.rs index 48a45c6833..554f32a6ea 100644 --- a/raphtory-core/src/entities/properties/tprop.rs +++ b/raphtory-core/src/entities/properties/tprop.rs @@ -2,44 +2,17 @@ use crate::{ entities::properties::tcell::TCell, storage::{timeindex::EventTime, PropColumn}, }; -use bigdecimal::BigDecimal; -use chrono::{DateTime, NaiveDateTime, Utc}; use either::Either; -use iter_enum::{DoubleEndedIterator, ExactSizeIterator, FusedIterator, Iterator}; -use raphtory_api::core::{ +use raphtory_api::core:: entities::properties::{ - prop::{Prop, PropArray, PropType}, + prop::{Prop, PropType}, tprop::TPropOps, - }, - storage::{arc_str::ArcStr, timeindex::TimeIndexOps}, -}; -use rustc_hash::FxHashMap; -use serde::Serialize; -use std::{collections::HashMap, iter, ops::Range, sync::Arc}; + } +; +use std::ops::Range; use thiserror::Error; -#[derive(Debug, Default, PartialEq, Clone, Serialize)] -pub enum TProp { - #[default] - Empty, - Str(TCell), - U8(TCell), - U16(TCell), - I32(TCell), - I64(TCell), - U32(TCell), - U64(TCell), - F32(TCell), - F64(TCell), - Bool(TCell), - DTime(TCell>), - List(TCell), - NDTime(TCell), - Map(TCell>>), - Decimal(TCell), -} - #[derive(Error, Debug)] #[error("Property type mismatch, expected {expected:?}, received {actual:?}")] pub struct IllegalPropType { @@ -47,43 +20,6 @@ pub struct IllegalPropType { pub(crate) actual: PropType, } -#[derive(Debug, Iterator, DoubleEndedIterator, ExactSizeIterator, FusedIterator)] -pub enum TPropVariants< - Empty, - Str, - U8, - U16, - I32, - I64, - U32, - U64, - F32, - F64, - Bool, - DTime, - NDTime, - List, - Map, - Decimal, -> { - Empty(Empty), - Str(Str), - U8(U8), - U16(U16), - I32(I32), - I64(I64), - U32(U32), - U64(U64), - F32(F32), - F64(F64), - Bool(Bool), - DTime(DTime), - NDTime(NDTime), - List(List), - Map(Map), - Decimal(Decimal), -} - #[derive(Copy, Clone, Debug, Default)] pub struct TPropCell<'a> { t_cell: Option<&'a TCell>>, @@ -162,613 +98,3 @@ impl<'a> TPropOps<'a> for TPropCell<'a> { self.t_cell?.at(ti).and_then(|&id| self.log?.get(id?)) } } - -impl TProp { - pub(crate) fn from(t: EventTime, prop: Prop) -> Self { - match prop { - Prop::Str(value) => TProp::Str(TCell::new(t, value)), - Prop::I32(value) => TProp::I32(TCell::new(t, value)), - Prop::I64(value) => TProp::I64(TCell::new(t, value)), - Prop::U8(value) => TProp::U8(TCell::new(t, value)), - Prop::U16(value) => TProp::U16(TCell::new(t, value)), - Prop::U32(value) => TProp::U32(TCell::new(t, value)), - Prop::U64(value) => TProp::U64(TCell::new(t, value)), - Prop::F32(value) => TProp::F32(TCell::new(t, value)), - Prop::F64(value) => TProp::F64(TCell::new(t, value)), - Prop::Bool(value) => TProp::Bool(TCell::new(t, value)), - Prop::DTime(value) => TProp::DTime(TCell::new(t, value)), - Prop::NDTime(value) => TProp::NDTime(TCell::new(t, value)), - Prop::List(value) => TProp::List(TCell::new(t, value)), - Prop::Map(value) => TProp::Map(TCell::new(t, value)), - Prop::Decimal(value) => TProp::Decimal(TCell::new(t, value)), - } - } - - pub fn dtype(&self) -> PropType { - match self { - TProp::Empty => PropType::Empty, - TProp::Str(_) => PropType::Str, - TProp::U8(_) => PropType::U8, - TProp::U16(_) => PropType::U16, - TProp::I32(_) => PropType::I32, - TProp::I64(_) => PropType::I64, - TProp::U32(_) => PropType::U32, - TProp::U64(_) => PropType::U64, - TProp::F32(_) => PropType::F32, - TProp::F64(_) => PropType::F64, - TProp::Bool(_) => PropType::Bool, - TProp::DTime(_) => PropType::DTime, - TProp::NDTime(_) => PropType::NDTime, - TProp::List(_) => PropType::List(Box::new(PropType::Empty)), - TProp::Map(_) => PropType::Map(HashMap::new().into()), - TProp::Decimal(_) => PropType::Decimal { scale: 0 }, - } - } - - pub(crate) fn set(&mut self, t: EventTime, prop: Prop) -> Result<(), IllegalPropType> { - if matches!(self, TProp::Empty) { - *self = TProp::from(t, prop); - } else { - match (self, prop) { - (TProp::Empty, _) => {} - - (TProp::Str(cell), Prop::Str(a)) => { - cell.set(t, a); - } - (TProp::I32(cell), Prop::I32(a)) => { - cell.set(t, a); - } - (TProp::I64(cell), Prop::I64(a)) => { - cell.set(t, a); - } - (TProp::U32(cell), Prop::U32(a)) => { - cell.set(t, a); - } - (TProp::U8(cell), Prop::U8(a)) => { - cell.set(t, a); - } - (TProp::U16(cell), Prop::U16(a)) => { - cell.set(t, a); - } - (TProp::U64(cell), Prop::U64(a)) => { - cell.set(t, a); - } - (TProp::F32(cell), Prop::F32(a)) => { - cell.set(t, a); - } - (TProp::F64(cell), Prop::F64(a)) => { - cell.set(t, a); - } - (TProp::Bool(cell), Prop::Bool(a)) => { - cell.set(t, a); - } - (TProp::DTime(cell), Prop::DTime(a)) => { - cell.set(t, a); - } - (TProp::NDTime(cell), Prop::NDTime(a)) => { - cell.set(t, a); - } - (TProp::List(cell), Prop::List(a)) => { - cell.set(t, a); - } - (TProp::Map(cell), Prop::Map(a)) => { - cell.set(t, a); - } - (TProp::Decimal(cell), Prop::Decimal(a)) => { - cell.set(t, a); - } - (cell, prop) => { - return Err(IllegalPropType { - expected: cell.dtype(), - actual: prop.dtype(), - }) - } - }; - } - Ok(()) - } - - pub(crate) fn iter_window_inner( - &self, - r: Range, - ) -> impl DoubleEndedIterator + Send + Sync + '_ { - match self { - TProp::Empty => TPropVariants::Empty(iter::empty()), - TProp::Str(cell) => TPropVariants::Str( - cell.iter_window(r) - .map(|(t, value)| (*t, Prop::Str(value.clone()))), - ), - TProp::I32(cell) => TPropVariants::I32( - cell.iter_window(r) - .map(|(t, value)| (*t, Prop::I32(*value))), - ), - TProp::I64(cell) => TPropVariants::I64( - cell.iter_window(r) - .map(|(t, value)| (*t, Prop::I64(*value))), - ), - TProp::U8(cell) => { - TPropVariants::U8(cell.iter_window(r).map(|(t, value)| (*t, Prop::U8(*value)))) - } - TProp::U16(cell) => TPropVariants::U16( - cell.iter_window(r) - .map(|(t, value)| (*t, Prop::U16(*value))), - ), - TProp::U32(cell) => TPropVariants::U32( - cell.iter_window(r) - .map(|(t, value)| (*t, Prop::U32(*value))), - ), - TProp::U64(cell) => TPropVariants::U64( - cell.iter_window(r) - .map(|(t, value)| (*t, Prop::U64(*value))), - ), - TProp::F32(cell) => TPropVariants::F32( - cell.iter_window(r) - .map(|(t, value)| (*t, Prop::F32(*value))), - ), - TProp::F64(cell) => TPropVariants::F64( - cell.iter_window(r) - .map(|(t, value)| (*t, Prop::F64(*value))), - ), - TProp::Bool(cell) => TPropVariants::Bool( - cell.iter_window(r) - .map(|(t, value)| (*t, Prop::Bool(*value))), - ), - TProp::DTime(cell) => TPropVariants::DTime( - cell.iter_window(r) - .map(|(t, value)| (*t, Prop::DTime(*value))), - ), - TProp::NDTime(cell) => TPropVariants::NDTime( - cell.iter_window(r) - .map(|(t, value)| (*t, Prop::NDTime(*value))), - ), - TProp::List(cell) => TPropVariants::List( - cell.iter_window(r) - .map(|(t, value)| (*t, Prop::List(value.clone()))), - ), - TProp::Map(cell) => TPropVariants::Map( - cell.iter_window(r) - .map(|(t, value)| (*t, Prop::Map(value.clone()))), - ), - TProp::Decimal(cell) => TPropVariants::Decimal( - cell.iter_window(r) - .map(|(t, value)| (*t, Prop::Decimal(value.clone()))), - ), - } - } - - pub(crate) fn iter_inner( - &self, - ) -> impl DoubleEndedIterator + Send + Sync + '_ { - match self { - TProp::Empty => TPropVariants::Empty(iter::empty()), - TProp::Str(cell) => { - TPropVariants::Str(cell.iter().map(|(t, value)| (*t, Prop::Str(value.clone())))) - } - TProp::I32(cell) => { - TPropVariants::I32(cell.iter().map(|(t, value)| (*t, Prop::I32(*value)))) - } - TProp::I64(cell) => { - TPropVariants::I64(cell.iter().map(|(t, value)| (*t, Prop::I64(*value)))) - } - TProp::U8(cell) => { - TPropVariants::U8(cell.iter().map(|(t, value)| (*t, Prop::U8(*value)))) - } - TProp::U16(cell) => { - TPropVariants::U16(cell.iter().map(|(t, value)| (*t, Prop::U16(*value)))) - } - TProp::U32(cell) => { - TPropVariants::U32(cell.iter().map(|(t, value)| (*t, Prop::U32(*value)))) - } - TProp::U64(cell) => { - TPropVariants::U64(cell.iter().map(|(t, value)| (*t, Prop::U64(*value)))) - } - TProp::F32(cell) => { - TPropVariants::F32(cell.iter().map(|(t, value)| (*t, Prop::F32(*value)))) - } - TProp::F64(cell) => { - TPropVariants::F64(cell.iter().map(|(t, value)| (*t, Prop::F64(*value)))) - } - TProp::Bool(cell) => { - TPropVariants::Bool(cell.iter().map(|(t, value)| (*t, Prop::Bool(*value)))) - } - TProp::DTime(cell) => { - TPropVariants::DTime(cell.iter().map(|(t, value)| (*t, Prop::DTime(*value)))) - } - TProp::NDTime(cell) => { - TPropVariants::NDTime(cell.iter().map(|(t, value)| (*t, Prop::NDTime(*value)))) - } - TProp::List(cell) => TPropVariants::List( - cell.iter() - .map(|(t, value)| (*t, Prop::List(value.clone()))), - ), - TProp::Map(cell) => { - TPropVariants::Map(cell.iter().map(|(t, value)| (*t, Prop::Map(value.clone())))) - } - TProp::Decimal(cell) => TPropVariants::Decimal( - cell.iter() - .map(|(t, value)| (*t, Prop::Decimal(value.clone()))), - ), - } - } -} - -impl<'a> TPropOps<'a> for &'a TProp { - fn last(&self) -> Option<(EventTime, Prop)> { - match self { - TProp::Empty => None, - TProp::Str(cell) => cell.last_value().map(|(t, v)| (t, Prop::Str(v.clone()))), - TProp::U8(cell) => cell.last_value().map(|(t, v)| (t, Prop::U8(v.clone()))), - TProp::U16(cell) => cell.last_value().map(|(t, v)| (t, Prop::U16(v.clone()))), - TProp::I32(cell) => cell.last_value().map(|(t, v)| (t, Prop::I32(v.clone()))), - TProp::I64(cell) => cell.last_value().map(|(t, v)| (t, Prop::I64(v.clone()))), - TProp::U32(cell) => cell.last_value().map(|(t, v)| (t, Prop::U32(v.clone()))), - TProp::U64(cell) => cell.last_value().map(|(t, v)| (t, Prop::U64(v.clone()))), - TProp::F32(cell) => cell.last_value().map(|(t, v)| (t, Prop::F32(v.clone()))), - TProp::F64(cell) => cell.last_value().map(|(t, v)| (t, Prop::F64(v.clone()))), - TProp::Bool(cell) => cell.last_value().map(|(t, v)| (t, Prop::Bool(v.clone()))), - TProp::DTime(cell) => cell.last_value().map(|(t, v)| (t, Prop::DTime(v.clone()))), - TProp::List(cell) => cell.last_value().map(|(t, v)| (t, Prop::List(v.clone()))), - TProp::NDTime(cell) => cell.last_value().map(|(t, v)| (t, Prop::NDTime(v.clone()))), - TProp::Map(cell) => cell.last_value().map(|(t, v)| (t, Prop::Map(v.clone()))), - TProp::Decimal(cell) => cell - .last_value() - .map(|(t, v)| (t, Prop::Decimal(v.clone()))), - } - } - fn last_before(&self, t: EventTime) -> Option<(EventTime, Prop)> { - match self { - TProp::Empty => None, - TProp::Str(cell) => cell.last_before(t).map(|(t, v)| (t, Prop::Str(v.clone()))), - TProp::I32(cell) => cell.last_before(t).map(|(t, v)| (t, Prop::I32(*v))), - TProp::I64(cell) => cell.last_before(t).map(|(t, v)| (t, Prop::I64(*v))), - TProp::U8(cell) => cell.last_before(t).map(|(t, v)| (t, Prop::U8(*v))), - TProp::U16(cell) => cell.last_before(t).map(|(t, v)| (t, Prop::U16(*v))), - TProp::U32(cell) => cell.last_before(t).map(|(t, v)| (t, Prop::U32(*v))), - TProp::U64(cell) => cell.last_before(t).map(|(t, v)| (t, Prop::U64(*v))), - TProp::F32(cell) => cell.last_before(t).map(|(t, v)| (t, Prop::F32(*v))), - TProp::F64(cell) => cell.last_before(t).map(|(t, v)| (t, Prop::F64(*v))), - TProp::Bool(cell) => cell.last_before(t).map(|(t, v)| (t, Prop::Bool(*v))), - TProp::DTime(cell) => cell.last_before(t).map(|(t, v)| (t, Prop::DTime(*v))), - TProp::NDTime(cell) => cell.last_before(t).map(|(t, v)| (t, Prop::NDTime(*v))), - TProp::List(cell) => cell.last_before(t).map(|(t, v)| (t, Prop::List(v.clone()))), - TProp::Map(cell) => cell.last_before(t).map(|(t, v)| (t, Prop::Map(v.clone()))), - TProp::Decimal(cell) => cell - .last_before(t) - .map(|(t, v)| (t, Prop::Decimal(v.clone()))), - } - } - - fn at(&self, ti: &EventTime) -> Option { - match self { - TProp::Empty => None, - TProp::Str(cell) => cell.at(ti).map(|v| Prop::Str(v.clone())), - TProp::I32(cell) => cell.at(ti).map(|v| Prop::I32(*v)), - TProp::I64(cell) => cell.at(ti).map(|v| Prop::I64(*v)), - TProp::U32(cell) => cell.at(ti).map(|v| Prop::U32(*v)), - TProp::U8(cell) => cell.at(ti).map(|v| Prop::U8(*v)), - TProp::U16(cell) => cell.at(ti).map(|v| Prop::U16(*v)), - TProp::U64(cell) => cell.at(ti).map(|v| Prop::U64(*v)), - TProp::F32(cell) => cell.at(ti).map(|v| Prop::F32(*v)), - TProp::F64(cell) => cell.at(ti).map(|v| Prop::F64(*v)), - TProp::Bool(cell) => cell.at(ti).map(|v| Prop::Bool(*v)), - TProp::DTime(cell) => cell.at(ti).map(|v| Prop::DTime(*v)), - TProp::NDTime(cell) => cell.at(ti).map(|v| Prop::NDTime(*v)), - TProp::List(cell) => cell.at(ti).map(|v| Prop::List(v.clone())), - TProp::Map(cell) => cell.at(ti).map(|v| Prop::Map(v.clone())), - TProp::Decimal(cell) => cell.at(ti).map(|v| Prop::Decimal(v.clone())), - } - } - - fn iter_inner( - self, - range: Option>, - ) -> impl Iterator + Send + Sync + 'a { - match range { - Some(w) => { - let iter = self.iter_window_inner(w); - Either::Right(iter) - } - None => { - let iter = self.iter_inner(); - Either::Left(iter) - } - } - } - - fn iter_inner_rev( - self, - range: Option>, - ) -> impl Iterator + Send + Sync + 'a { - match range { - Some(w) => { - let iter = self.iter_window_inner(w).rev(); - Either::Right(iter) - } - None => { - let iter = self.iter_inner().rev(); - Either::Left(iter) - } - } - } -} - -#[cfg(test)] -mod tprop_tests { - use crate::storage::lazy_vec::LazyVec; - - use super::*; - - #[test] - fn t_prop_cell() { - let col = PropColumn::Bool(LazyVec::from(0, true)); - assert_eq!(col.get(0), Some(Prop::Bool(true))); - - let t_prop = TPropCell::new(&TCell::TCell1(EventTime(0, 0), Some(0)), Some(&col)); - - let actual = t_prop.iter().collect::>(); - - assert_eq!(actual, vec![(EventTime(0, 0), Prop::Bool(true))]); - } - - #[test] - fn set_new_value_for_tprop_initialized_as_empty() { - let mut tprop = TProp::Empty; - tprop.set(1.into(), Prop::I32(10)).unwrap(); - - assert_eq!(tprop.iter_t().collect::>(), vec![(1, Prop::I32(10))]); - } - - #[test] - fn every_new_update_to_the_same_prop_is_recorded_as_history() { - let mut tprop = TProp::from(1.into(), "Pometry".into()); - tprop.set(2.into(), "Pometry Inc.".into()).unwrap(); - - assert_eq!( - tprop.iter_t().collect::>(), - vec![(1, "Pometry".into()), (2, "Pometry Inc.".into())] - ); - } - - #[test] - fn new_update_with_the_same_time_to_a_prop_is_ignored() { - let mut tprop = TProp::from(1.into(), "Pometry".into()); - tprop.set(1.into(), "Pometry Inc.".into()).unwrap(); - - assert_eq!( - tprop.iter_t().collect::>(), - vec![(1, "Pometry Inc.".into())] - ); - } - - #[test] - fn updates_to_prop_can_be_iterated() { - let tprop = TProp::default(); - - assert_eq!(tprop.iter_t().collect::>(), vec![]); - - let mut tprop = TProp::from(1.into(), "Pometry".into()); - tprop.set(2.into(), "Pometry Inc.".into()).unwrap(); - - assert_eq!( - tprop.iter_t().collect::>(), - vec![ - (1, Prop::Str("Pometry".into())), - (2, Prop::Str("Pometry Inc.".into())) - ] - ); - - let mut tprop = TProp::from(1.into(), Prop::I32(2022)); - tprop.set(2.into(), Prop::I32(2023)).unwrap(); - - assert_eq!( - tprop.iter_t().collect::>(), - vec![(1, Prop::I32(2022)), (2, Prop::I32(2023))] - ); - - let mut tprop = TProp::from(1.into(), Prop::I64(2022)); - tprop.set(2.into(), Prop::I64(2023)).unwrap(); - - assert_eq!( - tprop.iter_t().collect::>(), - vec![(1, Prop::I64(2022)), (2, Prop::I64(2023))] - ); - - let mut tprop = TProp::from(1.into(), Prop::F32(10.0)); - tprop.set(2.into(), Prop::F32(11.0)).unwrap(); - - assert_eq!( - tprop.iter_t().collect::>(), - vec![(1, Prop::F32(10.0)), (2, Prop::F32(11.0))] - ); - - let mut tprop = TProp::from(1.into(), Prop::F64(10.0)); - tprop.set(2.into(), Prop::F64(11.0)).unwrap(); - - assert_eq!( - tprop.iter_t().collect::>(), - vec![(1, Prop::F64(10.0)), (2, Prop::F64(11.0))] - ); - - let mut tprop = TProp::from(1.into(), Prop::U32(1)); - tprop.set(2.into(), Prop::U32(2)).unwrap(); - - assert_eq!( - tprop.iter_t().collect::>(), - vec![(1, Prop::U32(1)), (2, Prop::U32(2))] - ); - - let mut tprop = TProp::from(1.into(), Prop::U64(1)); - tprop.set(2.into(), Prop::U64(2)).unwrap(); - - assert_eq!( - tprop.iter_t().collect::>(), - vec![(1, Prop::U64(1)), (2, Prop::U64(2))] - ); - - let mut tprop = TProp::from(1.into(), Prop::U8(1)); - tprop.set(2.into(), Prop::U8(2)).unwrap(); - - assert_eq!( - tprop.iter_t().collect::>(), - vec![(1, Prop::U8(1)), (2, Prop::U8(2))] - ); - - let mut tprop = TProp::from(1.into(), Prop::U16(1)); - tprop.set(2.into(), Prop::U16(2)).unwrap(); - - assert_eq!( - tprop.iter_t().collect::>(), - vec![(1, Prop::U16(1)), (2, Prop::U16(2))] - ); - - let mut tprop = TProp::from(1.into(), Prop::Bool(true)); - tprop.set(2.into(), Prop::Bool(true)).unwrap(); - - assert_eq!( - tprop.iter_t().collect::>(), - vec![(1, Prop::Bool(true)), (2, Prop::Bool(true))] - ); - } - - #[test] - fn updates_to_prop_can_be_window_iterated() { - let tprop = &TProp::default(); - - assert_eq!( - tprop.iter_window_t(i64::MIN..i64::MAX).collect::>(), - vec![] - ); - - let mut tprop = TProp::from(3.into(), Prop::Str("Pometry".into())); - tprop - .set(1.into(), Prop::Str("Pometry Inc.".into())) - .unwrap(); - tprop.set(2.into(), Prop::Str("Raphtory".into())).unwrap(); - - let tprop = &tprop; - assert_eq!( - tprop.iter_window_t(2..3).collect::>(), - vec![(2, Prop::Str("Raphtory".into()))] - ); - - assert_eq!(tprop.iter_window_t(4..5).collect::>(), vec![]); - - assert_eq!( - // Results are ordered by time - tprop.iter_window_t(1..i64::MAX).collect::>(), - vec![ - (1, Prop::Str("Pometry Inc.".into())), - (2, Prop::Str("Raphtory".into())), - (3, Prop::Str("Pometry".into())) - ] - ); - - assert_eq!( - tprop.iter_window_t(3..i64::MAX).collect::>(), - vec![(3, Prop::Str("Pometry".into()))] - ); - - assert_eq!( - tprop.iter_window_t(2..i64::MAX).collect::>(), - vec![ - (2, Prop::Str("Raphtory".into())), - (3, Prop::Str("Pometry".into())) - ] - ); - - assert_eq!(tprop.iter_window_t(5..i64::MAX).collect::>(), vec![]); - - assert_eq!( - tprop.iter_window_t(i64::MIN..4).collect::>(), - // Results are ordered by time - vec![ - (1, Prop::Str("Pometry Inc.".into())), - (2, Prop::Str("Raphtory".into())), - (3, Prop::Str("Pometry".into())) - ] - ); - - assert_eq!(tprop.iter_window_t(i64::MIN..1).collect::>(), vec![]); - - let mut tprop = TProp::from(1.into(), Prop::I32(2022)); - tprop.set(2.into(), Prop::I32(2023)).unwrap(); - - let tprop = &tprop; - assert_eq!( - tprop.iter_window_t(i64::MIN..i64::MAX).collect::>(), - vec![(1, Prop::I32(2022)), (2, Prop::I32(2023))] - ); - - let mut tprop = TProp::from(1.into(), Prop::I64(2022)); - tprop.set(2.into(), Prop::I64(2023)).unwrap(); - - let tprop = &tprop; - assert_eq!( - tprop.iter_window_t(i64::MIN..i64::MAX).collect::>(), - vec![(1, Prop::I64(2022)), (2, Prop::I64(2023))] - ); - - let mut tprop = TProp::from(1.into(), Prop::F32(10.0)); - tprop.set(2.into(), Prop::F32(11.0)).unwrap(); - - let tprop = &tprop; - assert_eq!( - tprop.iter_window_t(i64::MIN..i64::MAX).collect::>(), - vec![(1, Prop::F32(10.0)), (2, Prop::F32(11.0))] - ); - - let mut tprop = TProp::from(1.into(), Prop::F64(10.0)); - tprop.set(2.into(), Prop::F64(11.0)).unwrap(); - - let tprop = &tprop; - assert_eq!( - tprop.iter_window_t(i64::MIN..i64::MAX).collect::>(), - vec![(1, Prop::F64(10.0)), (2, Prop::F64(11.0))] - ); - - let mut tprop = TProp::from(1.into(), Prop::U32(1)); - tprop.set(2.into(), Prop::U32(2)).unwrap(); - - let tprop = &tprop; - assert_eq!( - tprop.iter_window_t(i64::MIN..i64::MAX).collect::>(), - vec![(1, Prop::U32(1)), (2, Prop::U32(2))] - ); - - let mut tprop = TProp::from(1.into(), Prop::U64(1)); - tprop.set(2.into(), Prop::U64(2)).unwrap(); - - let tprop = &tprop; - assert_eq!( - tprop.iter_window_t(i64::MIN..i64::MAX).collect::>(), - vec![(1, Prop::U64(1)), (2, Prop::U64(2))] - ); - - let mut tprop = TProp::from(1.into(), Prop::U8(1)); - tprop.set(2.into(), Prop::U8(2)).unwrap(); - - let tprop = &tprop; - assert_eq!( - tprop.iter_window_t(i64::MIN..i64::MAX).collect::>(), - vec![(1, Prop::U8(1)), (2, Prop::U8(2))] - ); - - let mut tprop = TProp::from(1.into(), Prop::U16(1)); - tprop.set(2.into(), Prop::U16(2)).unwrap(); - - let tprop = &tprop; - assert_eq!( - tprop.iter_window_t(i64::MIN..i64::MAX).collect::>(), - vec![(1, Prop::U16(1)), (2, Prop::U16(2))] - ); - - let mut tprop = TProp::from(1.into(), Prop::Bool(true)); - tprop.set(2.into(), Prop::Bool(true)).unwrap(); - - let tprop = &tprop; - assert_eq!( - tprop.iter_window_t(i64::MIN..i64::MAX).collect::>(), - vec![(1, Prop::Bool(true)), (2, Prop::Bool(true))] - ); - } -} diff --git a/raphtory-graphql/schema.graphql b/raphtory-graphql/schema.graphql index dbe88f7daa..28956786d7 100644 --- a/raphtory-graphql/schema.graphql +++ b/raphtory-graphql/schema.graphql @@ -3491,4 +3491,3 @@ schema { query: QueryRoot mutation: MutRoot } - diff --git a/raphtory-storage/src/mutation/mod.rs b/raphtory-storage/src/mutation/mod.rs index 57bb85bcb3..30ff7fcc61 100644 --- a/raphtory-storage/src/mutation/mod.rs +++ b/raphtory-storage/src/mutation/mod.rs @@ -56,8 +56,6 @@ pub enum MutationError { #[error(transparent)] InvalidBigDecimal(#[from] InvalidBigDecimal), #[error(transparent)] - IllegalPropType(#[from] IllegalPropType), - #[error(transparent)] MetadataError(#[from] MetadataError), #[error("Layer {layer} does not exist for edge ({src}, {dst})")] InvalidEdgeLayer { From a03ec250c70874b29b1aff0dd1c72a0bdb2b5f5a Mon Sep 17 00:00:00 2001 From: Fabian Murariu Date: Thu, 23 Apr 2026 14:47:39 +0100 Subject: [PATCH 05/15] fixes for remaining failing tests --- db4-storage/src/generic_t_props.rs | 6 ++++++ raphtory-api/src/core/entities/properties/tprop.rs | 4 ++++ raphtory-core/src/entities/properties/tprop.rs | 10 ++++------ .../view/internal/time_semantics/event_semantics.rs | 2 +- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/db4-storage/src/generic_t_props.rs b/db4-storage/src/generic_t_props.rs index d51e579d91..3d153dc13b 100644 --- a/db4-storage/src/generic_t_props.rs +++ b/db4-storage/src/generic_t_props.rs @@ -105,6 +105,12 @@ impl<'a, Ref: WithTProps<'a>> TPropOps<'a> for GenericTProps<'a, Ref> { .max_by_key(|(t, _)| *t) } + fn last_window(&self, w: Range) -> Option<(EventTime, Prop)> { + self.tprops(self.prop_id) + .filter_map(|t_props| t_props.last_window(w.clone())) + .max_by_key(|(t, _)| *t) + } + fn last(&self) -> Option<(EventTime, Prop)> { self.tprops(self.prop_id) .filter_map(|t_props| t_props.last()) diff --git a/raphtory-api/src/core/entities/properties/tprop.rs b/raphtory-api/src/core/entities/properties/tprop.rs index 6a6bb747d4..b2387a02e1 100644 --- a/raphtory-api/src/core/entities/properties/tprop.rs +++ b/raphtory-api/src/core/entities/properties/tprop.rs @@ -18,6 +18,10 @@ pub trait TPropOps<'a>: Clone + Send + Sync + Sized + 'a { self.clone().iter_inner_rev(Some(EventTime::MIN..t)).next() } + fn last_window(&self, w: Range) -> Option<(EventTime, Prop)> { + self.clone().iter_window_rev(w).next() + } + fn last(&self) -> Option<(EventTime, Prop)>; fn iter_inner( diff --git a/raphtory-core/src/entities/properties/tprop.rs b/raphtory-core/src/entities/properties/tprop.rs index 554f32a6ea..728381659d 100644 --- a/raphtory-core/src/entities/properties/tprop.rs +++ b/raphtory-core/src/entities/properties/tprop.rs @@ -4,12 +4,10 @@ use crate::{ }; use either::Either; -use raphtory_api::core:: - entities::properties::{ - prop::{Prop, PropType}, - tprop::TPropOps, - } -; +use raphtory_api::core::entities::properties::{ + prop::{Prop, PropType}, + tprop::TPropOps, +}; use std::ops::Range; use thiserror::Error; diff --git a/raphtory/src/db/api/view/internal/time_semantics/event_semantics.rs b/raphtory/src/db/api/view/internal/time_semantics/event_semantics.rs index 43a8db9afc..c836c49260 100644 --- a/raphtory/src/db/api/view/internal/time_semantics/event_semantics.rs +++ b/raphtory/src/db/api/view/internal/time_semantics/event_semantics.rs @@ -270,7 +270,7 @@ impl NodeTimeSemanticsOps for EventSemantics { ) -> Option<(EventTime, Prop)> { if w.contains(&t) { node.tprop_iter_layers(view.layer_ids(), prop_id) - .filter_map(|prop| prop.last_before(t.next()).filter(|(t, _)| w.contains(t))) + .filter_map(|prop| prop.last_window(w.start..t.next())) .max_by_key(|(t, _)| *t) } else { None From d391cce84ffea23d80ac4684554a22ee41fc748f Mon Sep 17 00:00:00 2001 From: Fabian Murariu Date: Thu, 23 Apr 2026 17:18:35 +0100 Subject: [PATCH 06/15] fix some of the last_window issue and support Int32,UInt32 and UInt64 as time column --- .../time_semantics/base_time_semantics.rs | 11 +++++++++++ .../internal/time_semantics/event_semantics.rs | 12 ++++++++++++ .../time_semantics/persistent_semantics.rs | 18 ++++++++++++++++++ .../internal/time_semantics/time_semantics.rs | 10 ++++++++++ .../time_semantics/time_semantics_ops.rs | 8 ++++++++ .../time_semantics/window_time_semantics.rs | 13 ++++++++++++- raphtory/src/db/graph/node.rs | 12 ++++++++++-- raphtory/src/io/arrow/dataframe.rs | 18 ++++++++++++++++++ 8 files changed, 99 insertions(+), 3 deletions(-) diff --git a/raphtory/src/db/api/view/internal/time_semantics/base_time_semantics.rs b/raphtory/src/db/api/view/internal/time_semantics/base_time_semantics.rs index 1acf7088f6..0a4b7cf212 100644 --- a/raphtory/src/db/api/view/internal/time_semantics/base_time_semantics.rs +++ b/raphtory/src/db/api/view/internal/time_semantics/base_time_semantics.rs @@ -300,6 +300,17 @@ impl NodeTimeSemanticsOps for BaseTimeSemantics { ) -> Option<(EventTime, Prop)> { for_all!(self, semantics => semantics.node_tprop_last(node, view, prop_id)) } + + #[inline] + fn node_tprop_last_window<'graph, G: GraphView + 'graph>( + &self, + node: NodeStorageRef<'graph>, + view: G, + prop_id: usize, + w: Range, + ) -> Option<(EventTime, Prop)> { + for_all!(self, semantics => semantics.node_tprop_last_window(node, view, prop_id, w)) + } } impl EdgeTimeSemanticsOps for BaseTimeSemantics { diff --git a/raphtory/src/db/api/view/internal/time_semantics/event_semantics.rs b/raphtory/src/db/api/view/internal/time_semantics/event_semantics.rs index c836c49260..490bf401ec 100644 --- a/raphtory/src/db/api/view/internal/time_semantics/event_semantics.rs +++ b/raphtory/src/db/api/view/internal/time_semantics/event_semantics.rs @@ -248,6 +248,18 @@ impl NodeTimeSemanticsOps for EventSemantics { .max_by_key(|(t, _)| *t) } + fn node_tprop_last_window<'graph, G: GraphView + 'graph>( + &self, + node: NodeStorageRef<'graph>, + view: G, + prop_id: usize, + w: Range, + ) -> Option<(EventTime, Prop)> { + node.tprop_iter_layers(view.layer_ids(), prop_id) + .filter_map(|prop| prop.last_window(w.clone())) + .max_by_key(|(t, _)| *t) + } + fn node_tprop_last_at<'graph, G: GraphView + 'graph>( &self, node: NodeStorageRef<'graph>, diff --git a/raphtory/src/db/api/view/internal/time_semantics/persistent_semantics.rs b/raphtory/src/db/api/view/internal/time_semantics/persistent_semantics.rs index 54d8cbc938..a14e4bebed 100644 --- a/raphtory/src/db/api/view/internal/time_semantics/persistent_semantics.rs +++ b/raphtory/src/db/api/view/internal/time_semantics/persistent_semantics.rs @@ -515,6 +515,24 @@ impl NodeTimeSemanticsOps for PersistentSemantics { .max_by_key(|(t, _)| *t) } + fn node_tprop_last_window<'graph, G: GraphView + 'graph>( + &self, + node: NodeStorageRef<'graph>, + view: G, + prop_id: usize, + w: Range, + ) -> Option<(EventTime, Prop)> { + node.tprop_iter_layers(view.layer_ids(), prop_id) + .filter_map(|prop| { + if prop.active(w.start..EventTime::start(w.start.t().saturating_add(1))) { + None + } else { + prop.last_before(w.start).map(|(t, v)| (t.max(w.start), v)) + } + }) + .max_by_key(|(t, _)| *t) + } + fn node_tprop_last_at<'graph, G: GraphViewOps<'graph>>( &self, node: NodeStorageRef<'graph>, diff --git a/raphtory/src/db/api/view/internal/time_semantics/time_semantics.rs b/raphtory/src/db/api/view/internal/time_semantics/time_semantics.rs index bbc7cd79e5..8e629c067b 100644 --- a/raphtory/src/db/api/view/internal/time_semantics/time_semantics.rs +++ b/raphtory/src/db/api/view/internal/time_semantics/time_semantics.rs @@ -274,6 +274,16 @@ impl NodeTimeSemanticsOps for TimeSemantics { ) -> Option<(EventTime, Prop)> { for_all!(self, semantics => semantics.node_tprop_last(node, view, prop_id)) } + + fn node_tprop_last_window<'graph, G: GraphView + 'graph>( + &self, + node: NodeStorageRef<'graph>, + view: G, + prop_id: usize, + w: Range, + ) -> Option<(EventTime, Prop)> { + for_all!(self, semantics => semantics.node_tprop_last_window(node, view, prop_id, w)) + } } impl EdgeTimeSemanticsOps for TimeSemantics { diff --git a/raphtory/src/db/api/view/internal/time_semantics/time_semantics_ops.rs b/raphtory/src/db/api/view/internal/time_semantics/time_semantics_ops.rs index 20596eb401..fb7876f991 100644 --- a/raphtory/src/db/api/view/internal/time_semantics/time_semantics_ops.rs +++ b/raphtory/src/db/api/view/internal/time_semantics/time_semantics_ops.rs @@ -171,6 +171,14 @@ pub trait NodeTimeSemanticsOps { prop_id: usize, ) -> Option<(EventTime, Prop)>; + fn node_tprop_last_window<'graph, G: GraphView + 'graph>( + &self, + node: NodeStorageRef<'graph>, + view: G, + prop_id: usize, + w: Range, + ) -> Option<(EventTime, Prop)>; + fn node_tprop_last_at<'graph, G: GraphView + 'graph>( &self, node: NodeStorageRef<'graph>, diff --git a/raphtory/src/db/api/view/internal/time_semantics/window_time_semantics.rs b/raphtory/src/db/api/view/internal/time_semantics/window_time_semantics.rs index 2f728de642..ea2d6c3b2b 100644 --- a/raphtory/src/db/api/view/internal/time_semantics/window_time_semantics.rs +++ b/raphtory/src/db/api/view/internal/time_semantics/window_time_semantics.rs @@ -271,7 +271,18 @@ impl NodeTimeSemanticsOps for WindowTimeSemantics { prop_id: usize, ) -> Option<(EventTime, Prop)> { self.semantics - .node_tprop_last_at(node, view, prop_id, self.window.end.previous()) + .node_tprop_last_window(node, view, prop_id, self.window.clone()) + } + + fn node_tprop_last_window<'graph, G: GraphView + 'graph>( + &self, + node: NodeStorageRef<'graph>, + view: G, + prop_id: usize, + w: Range, + ) -> Option<(EventTime, Prop)> { + self.semantics + .node_tprop_last_window(node, view, prop_id, w) } #[inline] diff --git a/raphtory/src/db/graph/node.rs b/raphtory/src/db/graph/node.rs index 05fe9e567c..30c36ef0d3 100644 --- a/raphtory/src/db/graph/node.rs +++ b/raphtory/src/db/graph/node.rs @@ -255,9 +255,17 @@ impl<'graph, G: GraphViewOps<'graph>> InternalTemporalPropertyViewOps for NodeVi fn temporal_value(&self, id: usize) -> Option { let semantics = self.graph.node_time_semantics(); let node = self.graph.core_node(self.node); - semantics + let value = semantics .node_tprop_last(node.as_ref(), &self.graph, id) - .map(|(_, v)| v) + .map(|(_, v)| v); + + // let res = semantics + // .node_tprop_iter_rev(node.as_ref(), &self.graph, id) + // .next() + // .map(|(_, v)| v); + + // println!("temporal_value: new_value={:?}, orig={:?}", value, res); + value } fn temporal_iter(&self, id: usize) -> BoxedLIter<'_, (EventTime, Prop)> { diff --git a/raphtory/src/io/arrow/dataframe.rs b/raphtory/src/io/arrow/dataframe.rs index 74d75a5e3d..5ff795a658 100644 --- a/raphtory/src/io/arrow/dataframe.rs +++ b/raphtory/src/io/arrow/dataframe.rs @@ -83,6 +83,24 @@ impl TimeCol { } match arr.data_type() { DataType::Int64 => Ok(Self(arr.as_primitive::().clone())), + DataType::UInt64 => { + let arr = cast(arr, &DataType::Int64)? + .as_primitive::() + .clone(); + Ok(Self(arr)) + } + DataType::UInt32 => { + let arr = cast(arr, &DataType::Int64)? + .as_primitive::() + .clone(); + Ok(Self(arr)) + } + DataType::Int32 => { + let arr = cast(arr, &DataType::Int64)? + .as_primitive::() + .clone(); + Ok(Self(arr)) + } DataType::Date32 => { let arr = cast(arr, &DataType::Date64)? .as_primitive::() From 375019900249aa848974491caa44396e60dec0a5 Mon Sep 17 00:00:00 2001 From: Fabian Murariu Date: Thu, 23 Apr 2026 17:24:22 +0100 Subject: [PATCH 07/15] avoid some work when loading edges --- raphtory/src/io/arrow/df_loaders/edges.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/raphtory/src/io/arrow/df_loaders/edges.rs b/raphtory/src/io/arrow/df_loaders/edges.rs index 8edec1fb6f..48bf1da959 100644 --- a/raphtory/src/io/arrow/df_loaders/edges.rs +++ b/raphtory/src/io/arrow/df_loaders/edges.rs @@ -442,8 +442,8 @@ pub fn load_edges_from_df Date: Thu, 23 Apr 2026 18:53:19 +0100 Subject: [PATCH 08/15] variant of last_window for persistent graph --- .../time_semantics/persistent_semantics.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/raphtory/src/db/api/view/internal/time_semantics/persistent_semantics.rs b/raphtory/src/db/api/view/internal/time_semantics/persistent_semantics.rs index a14e4bebed..36764ffd0e 100644 --- a/raphtory/src/db/api/view/internal/time_semantics/persistent_semantics.rs +++ b/raphtory/src/db/api/view/internal/time_semantics/persistent_semantics.rs @@ -524,11 +524,17 @@ impl NodeTimeSemanticsOps for PersistentSemantics { ) -> Option<(EventTime, Prop)> { node.tprop_iter_layers(view.layer_ids(), prop_id) .filter_map(|prop| { - if prop.active(w.start..EventTime::start(w.start.t().saturating_add(1))) { - None - } else { - prop.last_before(w.start).map(|(t, v)| (t.max(w.start), v)) - } + // Get the last value in the window [w.start, w.end) + prop.last_before(w.end) + .filter(|(t, _)| *t >= w.start) + .or_else(|| { + // If no value in window, check for persisted value + if prop.active(w.start..EventTime::start(w.start.t().saturating_add(1))) { + None + } else { + prop.last_before(w.start).map(|(_, v)| (w.start, v)) + } + }) }) .max_by_key(|(t, _)| *t) } From 192047e38e68fea8025d52d2cc4a3ce2a30d09aa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 23 Apr 2026 21:36:34 +0000 Subject: [PATCH 09/15] chore: apply tidy-public auto-fixes --- raphtory-graphql/schema.graphql | 1 + 1 file changed, 1 insertion(+) diff --git a/raphtory-graphql/schema.graphql b/raphtory-graphql/schema.graphql index 28956786d7..dbe88f7daa 100644 --- a/raphtory-graphql/schema.graphql +++ b/raphtory-graphql/schema.graphql @@ -3491,3 +3491,4 @@ schema { query: QueryRoot mutation: MutRoot } + From 6a0a050efc018c71fa8fcccfe061309a7f584321 Mon Sep 17 00:00:00 2001 From: Fabian Murariu Date: Fri, 24 Apr 2026 11:06:33 +0100 Subject: [PATCH 10/15] update of last_window for tprop node persistent graph --- .../time_semantics/persistent_semantics.rs | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/raphtory/src/db/api/view/internal/time_semantics/persistent_semantics.rs b/raphtory/src/db/api/view/internal/time_semantics/persistent_semantics.rs index 36764ffd0e..4e183bcb2f 100644 --- a/raphtory/src/db/api/view/internal/time_semantics/persistent_semantics.rs +++ b/raphtory/src/db/api/view/internal/time_semantics/persistent_semantics.rs @@ -523,19 +523,7 @@ impl NodeTimeSemanticsOps for PersistentSemantics { w: Range, ) -> Option<(EventTime, Prop)> { node.tprop_iter_layers(view.layer_ids(), prop_id) - .filter_map(|prop| { - // Get the last value in the window [w.start, w.end) - prop.last_before(w.end) - .filter(|(t, _)| *t >= w.start) - .or_else(|| { - // If no value in window, check for persisted value - if prop.active(w.start..EventTime::start(w.start.t().saturating_add(1))) { - None - } else { - prop.last_before(w.start).map(|(_, v)| (w.start, v)) - } - }) - }) + .filter_map(|prop| prop.last_before(w.end)) .max_by_key(|(t, _)| *t) } From ed0f1dabf589857a7e4365c4a6f502c36b49e1f1 Mon Sep 17 00:00:00 2001 From: Fabian Murariu Date: Mon, 27 Apr 2026 13:51:45 +0100 Subject: [PATCH 11/15] update of temporal_value for tprop edge --- db4-storage/src/generic_t_props.rs | 4 +-- .../time_semantics/base_time_semantics.rs | 21 ++++++++++++ .../time_semantics/event_semantics.rs | 25 +++++++++++++++ .../time_semantics/persistent_semantics.rs | 32 +++++++++++++++++++ .../internal/time_semantics/time_semantics.rs | 19 +++++++++++ .../time_semantics/time_semantics_ops.rs | 16 ++++++++++ .../time_semantics/window_time_semantics.rs | 23 +++++++++++++ raphtory/src/db/graph/edge.rs | 25 ++++----------- raphtory/src/db/graph/node.rs | 12 ++----- raphtory/tests/db_tests.rs | 14 +++++--- 10 files changed, 155 insertions(+), 36 deletions(-) diff --git a/db4-storage/src/generic_t_props.rs b/db4-storage/src/generic_t_props.rs index 3d153dc13b..657f4f49b3 100644 --- a/db4-storage/src/generic_t_props.rs +++ b/db4-storage/src/generic_t_props.rs @@ -138,8 +138,6 @@ impl<'a, Ref: WithTProps<'a>> TPropOps<'a> for GenericTProps<'a, Ref> { } fn at(&self, ti: &EventTime) -> Option { - self.tprops(self.prop_id) - .flat_map(|t_props| t_props.at(ti)) - .next() // TODO: need to figure out how to handle this + self.tprops(self.prop_id).find_map(|t_props| t_props.at(ti)) } } diff --git a/raphtory/src/db/api/view/internal/time_semantics/base_time_semantics.rs b/raphtory/src/db/api/view/internal/time_semantics/base_time_semantics.rs index 0a4b7cf212..0bcd34658c 100644 --- a/raphtory/src/db/api/view/internal/time_semantics/base_time_semantics.rs +++ b/raphtory/src/db/api/view/internal/time_semantics/base_time_semantics.rs @@ -769,6 +769,27 @@ impl EdgeTimeSemanticsOps for BaseTimeSemantics { for_all!(self, semantics => semantics.temporal_edge_prop_last_at_window(e, view, prop_id, t, w)) } + #[inline] + fn temporal_edge_prop_last<'graph, G: GraphView + 'graph>( + &self, + e: EdgeEntryRef<'graph>, + view: G, + prop_id: usize, + ) -> Option { + for_all!(self, semantics => semantics.temporal_edge_prop_last(e, view, prop_id)) + } + + #[inline] + fn temporal_edge_prop_last_window<'graph, G: GraphView + 'graph>( + &self, + e: EdgeEntryRef<'graph>, + view: G, + prop_id: usize, + w: Range, + ) -> Option { + for_all!(self, semantics => semantics.temporal_edge_prop_last_window(e, view, prop_id, w)) + } + #[inline] fn temporal_edge_prop_hist<'graph, G: GraphView + 'graph>( self, diff --git a/raphtory/src/db/api/view/internal/time_semantics/event_semantics.rs b/raphtory/src/db/api/view/internal/time_semantics/event_semantics.rs index 490bf401ec..b0b1946ecc 100644 --- a/raphtory/src/db/api/view/internal/time_semantics/event_semantics.rs +++ b/raphtory/src/db/api/view/internal/time_semantics/event_semantics.rs @@ -840,6 +840,31 @@ impl EdgeTimeSemanticsOps for EventSemantics { } } + fn temporal_edge_prop_last<'graph, G: GraphView + 'graph>( + &self, + e: EdgeEntryRef<'graph>, + view: G, + prop_id: usize, + ) -> Option { + e.filtered_temporal_prop_iter(prop_id, &view, view.layer_ids()) + .filter_map(|(_, prop)| prop.last()) + .max_by(|(t1, _), (t2, _)| t1.cmp(t2)) + .map(|(_, v)| v) + } + + fn temporal_edge_prop_last_window<'graph, G: GraphView + 'graph>( + &self, + e: EdgeEntryRef<'graph>, + view: G, + prop_id: usize, + w: Range, + ) -> Option { + e.filtered_temporal_prop_iter(prop_id, &view, view.layer_ids()) + .filter_map(|(_, prop)| prop.last_window(w.clone())) + .max_by(|(t1, _), (t2, _)| t1.cmp(t2)) + .map(|(_, v)| v) + } + fn temporal_edge_prop_hist<'graph, G: GraphView + 'graph>( self, e: EdgeEntryRef<'graph>, diff --git a/raphtory/src/db/api/view/internal/time_semantics/persistent_semantics.rs b/raphtory/src/db/api/view/internal/time_semantics/persistent_semantics.rs index 4e183bcb2f..359060a156 100644 --- a/raphtory/src/db/api/view/internal/time_semantics/persistent_semantics.rs +++ b/raphtory/src/db/api/view/internal/time_semantics/persistent_semantics.rs @@ -1270,6 +1270,38 @@ impl EdgeTimeSemanticsOps for PersistentSemantics { EventSemantics.temporal_edge_prop_last_at(e, view, prop_id, t) // TODO: double check this } + fn temporal_edge_prop_last<'graph, G: GraphView + 'graph>( + &self, + e: EdgeEntryRef<'graph>, + view: G, + prop_id: usize, + ) -> Option { + EventSemantics.temporal_edge_prop_last(e, view, prop_id) + } + + fn temporal_edge_prop_last_window<'graph, G: GraphView + 'graph>( + &self, + e: EdgeEntryRef<'graph>, + view: G, + prop_id: usize, + w: Range, + ) -> Option { + e.filtered_updates_iter(&view, view.layer_ids()) + .filter_map(|(layer, additions, deletions)| { + let start = deletions + .merge(additions.invert()) + .range(EventTime::MIN..w.end) + .last() + .map(|t| t.next()) + .unwrap_or(EventTime::MIN); + e.filtered_temporal_prop_layer(layer, prop_id, &view) + .iter_inner_rev(Some(start..w.end)) + .next() + }) + .max_by(|(t1, _), (t2, _)| t1.cmp(t2)) + .map(|(_, v)| v) + } + fn temporal_edge_prop_last_at_window<'graph, G: GraphViewOps<'graph>>( &self, e: EdgeEntryRef, diff --git a/raphtory/src/db/api/view/internal/time_semantics/time_semantics.rs b/raphtory/src/db/api/view/internal/time_semantics/time_semantics.rs index 8e629c067b..5c6ed5983d 100644 --- a/raphtory/src/db/api/view/internal/time_semantics/time_semantics.rs +++ b/raphtory/src/db/api/view/internal/time_semantics/time_semantics.rs @@ -702,6 +702,25 @@ impl EdgeTimeSemanticsOps for TimeSemantics { for_all!(self, semantics => semantics.temporal_edge_prop_last_at_window(e, view, prop_id, t, w)) } + fn temporal_edge_prop_last<'graph, G: GraphView + 'graph>( + &self, + e: EdgeEntryRef<'graph>, + view: G, + prop_id: usize, + ) -> Option { + for_all!(self, semantics => semantics.temporal_edge_prop_last(e, view, prop_id)) + } + + fn temporal_edge_prop_last_window<'graph, G: GraphView + 'graph>( + &self, + e: EdgeEntryRef<'graph>, + view: G, + prop_id: usize, + w: Range, + ) -> Option { + for_all!(self, semantics => semantics.temporal_edge_prop_last_window(e, view, prop_id, w)) + } + fn temporal_edge_prop_hist<'graph, G: GraphView + 'graph>( self, e: EdgeEntryRef<'graph>, diff --git a/raphtory/src/db/api/view/internal/time_semantics/time_semantics_ops.rs b/raphtory/src/db/api/view/internal/time_semantics/time_semantics_ops.rs index fb7876f991..1c805da213 100644 --- a/raphtory/src/db/api/view/internal/time_semantics/time_semantics_ops.rs +++ b/raphtory/src/db/api/view/internal/time_semantics/time_semantics_ops.rs @@ -589,6 +589,22 @@ pub trait EdgeTimeSemanticsOps { w: Range, ) -> Option; + // /// Return the last value of a temporal edge property at or before a given point in time + fn temporal_edge_prop_last<'graph, G: GraphView + 'graph>( + &self, + e: EdgeEntryRef<'graph>, + view: G, + prop_id: usize, + ) -> Option; + + fn temporal_edge_prop_last_window<'graph, G: GraphView + 'graph>( + &self, + e: EdgeEntryRef<'graph>, + view: G, + prop_id: usize, + w: Range, + ) -> Option; + /// Return property history of an edge in temporal order /// /// Items are (timestamp, layer_id, property value) diff --git a/raphtory/src/db/api/view/internal/time_semantics/window_time_semantics.rs b/raphtory/src/db/api/view/internal/time_semantics/window_time_semantics.rs index ea2d6c3b2b..8d9c52d821 100644 --- a/raphtory/src/db/api/view/internal/time_semantics/window_time_semantics.rs +++ b/raphtory/src/db/api/view/internal/time_semantics/window_time_semantics.rs @@ -807,6 +807,29 @@ impl EdgeTimeSemanticsOps for WindowTimeSemantics { .temporal_edge_prop_last_at_window(e, view, prop_id, t, w) } + #[inline] + fn temporal_edge_prop_last<'graph, G: GraphView + 'graph>( + &self, + e: EdgeEntryRef<'graph>, + view: G, + prop_id: usize, + ) -> Option { + self.semantics + .temporal_edge_prop_last_window(e, view, prop_id, self.window.clone()) + } + + #[inline] + fn temporal_edge_prop_last_window<'graph, G: GraphView + 'graph>( + &self, + e: EdgeEntryRef<'graph>, + view: G, + prop_id: usize, + w: Range, + ) -> Option { + self.semantics + .temporal_edge_prop_last_window(e, view, prop_id, w) + } + #[inline] fn temporal_edge_prop_hist<'graph, G: GraphView + 'graph>( self, diff --git a/raphtory/src/db/graph/edge.rs b/raphtory/src/db/graph/edge.rs index 7cdb8498f3..a635dcd1d7 100644 --- a/raphtory/src/db/graph/edge.rs +++ b/raphtory/src/db/graph/edge.rs @@ -598,24 +598,13 @@ impl InternalTemporalPropertyViewOps for EdgeView { let edge = self.graph.core_edge(self.edge.pid()); match self.edge.time() { None => match self.edge.layer() { - None => time_semantics - .temporal_edge_prop_hist_rev( - edge.as_ref(), - &self.graph, - self.graph.layer_ids(), - id, - ) - .next(), - Some(layer) => time_semantics - .temporal_edge_prop_hist_rev( - edge.as_ref(), - &self.graph, - &LayerIds::One(layer), - id, - ) - .next(), - } - .map(|(_, _, v)| v), + None => time_semantics.temporal_edge_prop_last(edge.as_ref(), &self.graph, id), + Some(layer) => time_semantics.temporal_edge_prop_last( + edge.as_ref(), + LayeredGraph::new(&self.graph, LayerIds::One(layer)), + id, + ), + }, Some(t) => { let layer = self.edge.layer().expect("exploded edge should have layer"); time_semantics.temporal_edge_prop_exploded( diff --git a/raphtory/src/db/graph/node.rs b/raphtory/src/db/graph/node.rs index 30c36ef0d3..05fe9e567c 100644 --- a/raphtory/src/db/graph/node.rs +++ b/raphtory/src/db/graph/node.rs @@ -255,17 +255,9 @@ impl<'graph, G: GraphViewOps<'graph>> InternalTemporalPropertyViewOps for NodeVi fn temporal_value(&self, id: usize) -> Option { let semantics = self.graph.node_time_semantics(); let node = self.graph.core_node(self.node); - let value = semantics + semantics .node_tprop_last(node.as_ref(), &self.graph, id) - .map(|(_, v)| v); - - // let res = semantics - // .node_tprop_iter_rev(node.as_ref(), &self.graph, id) - // .next() - // .map(|(_, v)| v); - - // println!("temporal_value: new_value={:?}, orig={:?}", value, res); - value + .map(|(_, v)| v) } fn temporal_iter(&self, id: usize) -> BoxedLIter<'_, (EventTime, Prop)> { diff --git a/raphtory/tests/db_tests.rs b/raphtory/tests/db_tests.rs index 4e3219ea39..14977e9297 100644 --- a/raphtory/tests/db_tests.rs +++ b/raphtory/tests/db_tests.rs @@ -1577,11 +1577,15 @@ fn test_props() { g.add_edge(2, 1, 2, NO_PROPS, None).unwrap(); let exploded = g.edge(1, 2).unwrap().explode(); - let res = exploded - .properties() - .map(|p| p.as_vec().len()) - .collect_vec(); - assert_eq!(res, vec![1, 1, 0]); + let res = exploded.properties().map(|p| p.as_vec()).collect_vec(); + assert_eq!( + res, + vec![ + vec![("weight".into(), Prop::I64(1))], + vec![("weight".into(), Prop::I64(2))], + vec![] + ] + ); } #[test] From 72b502f98e228a15103b92be495c4d73daa6dbf8 Mon Sep 17 00:00:00 2001 From: Fabian Murariu Date: Tue, 28 Apr 2026 10:25:28 +0100 Subject: [PATCH 12/15] add benchmark for temporal_value --- Cargo.lock | 3 +- raphtory-benchmark/Cargo.toml | 5 + raphtory-benchmark/benches/temporal_props.rs | 459 +++++++++++++++++++ 3 files changed, 466 insertions(+), 1 deletion(-) create mode 100644 raphtory-benchmark/benches/temporal_props.rs diff --git a/Cargo.lock b/Cargo.lock index 17f43cd917..4aa31866a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6522,6 +6522,7 @@ dependencies = [ "itertools 0.13.0", "once_cell", "rand 0.9.4", + "rand_distr 0.5.1", "raphtory", "raphtory-api", "rayon", @@ -7562,7 +7563,7 @@ checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" [[package]] name = "snb" -version = "0.17.0" +version = "0.18.0" dependencies = [ "chrono", "flate2", diff --git a/raphtory-benchmark/Cargo.toml b/raphtory-benchmark/Cargo.toml index aa53e69535..bcaa54619f 100644 --- a/raphtory-benchmark/Cargo.toml +++ b/raphtory-benchmark/Cargo.toml @@ -14,6 +14,7 @@ raphtory = { workspace = true, features = [ raphtory-api = { workspace = true } sorted_vector_map = { workspace = true } rand = { workspace = true } +rand_distr = { workspace = true } rayon = { workspace = true } tempfile = { workspace = true } tracing = { workspace = true } @@ -81,6 +82,10 @@ name = "index_bench" harness = false required-features = ["search"] +[[bench]] +name = "temporal_props" +harness = false + [features] search = ["raphtory/search"] proto = ["raphtory/proto"] diff --git a/raphtory-benchmark/benches/temporal_props.rs b/raphtory-benchmark/benches/temporal_props.rs new file mode 100644 index 0000000000..894ffc1d1d --- /dev/null +++ b/raphtory-benchmark/benches/temporal_props.rs @@ -0,0 +1,459 @@ +use criterion::measurement::WallTime; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkGroup, Criterion}; +use rand::{Rng, SeedableRng}; +use rand_distr::{Distribution, Normal}; +use raphtory::db::api::properties::internal::InternalTemporalPropertyViewOps; +use raphtory::storage::core_ops::CoreGraphOps; +use raphtory::{graph_loader::lotr_graph::lotr_graph, prelude::*}; +use raphtory_api::core::storage::timeindex::AsTime; + +const PROP_NAME: &str = "bench_prop"; +const RNG_SEED: u64 = 42; + +/// Sample a time from a normal distribution centered at mid, clamped to [earliest, latest] +fn sample_time(rng: &mut impl rand::Rng, earliest: i64, latest: i64) -> i64 { + let mid = earliest + (latest - earliest) / 2; + let std_dev = ((latest - earliest) / 6) as f64; // ~99.7% within range + let normal = Normal::new(mid as f64, std_dev).unwrap(); + let sample = normal.sample(rng) as i64; + sample.clamp(earliest, latest) +} + +/// Setup graph with temporal properties on nodes and edges +/// Properties are added at times sampled from a normal distribution centered at midpoint +/// Returns (graph, property_id_for_nodes, property_id_for_edges, earliest_time, latest_time) +fn setup_graph_with_props() -> (Graph, usize, usize, i64, i64) { + let graph = lotr_graph(); + + let earliest = graph.earliest_time().expect("graph should have time").t(); + let latest = graph.latest_time().expect("graph should have time").t(); + + let mut rng = rand::rngs::StdRng::seed_from_u64(RNG_SEED); + + // Add temporal property to all nodes at times from normal distribution + let nodes = graph.nodes().id().collect::>(); + for node in nodes { + let t = sample_time(&mut rng, earliest, latest); + let node = graph.node(node).expect("node should exist"); + node.add_updates(t, [(PROP_NAME, Prop::I32(42))], None) + .expect("failed to add node property"); + } + + // Add temporal property to all edges at times from normal distribution + let edges = graph.edges().id().collect::>(); + for (src, dst) in edges { + let t = sample_time(&mut rng, earliest, latest); + let edge = graph.edge(src, dst).expect("edge should exist"); + edge.add_updates(t, [(PROP_NAME, Prop::I32(42))], None) + .expect("failed to add edge property"); + } + + let node_prop_id = graph + .node_meta() + .temporal_prop_mapper() + .get_id(PROP_NAME) + .expect("node property should exist"); + + let edge_prop_id = graph + .edge_meta() + .temporal_prop_mapper() + .get_id(PROP_NAME) + .expect("edge property should exist"); + + (graph, node_prop_id, edge_prop_id, earliest, latest) +} + +/// Setup layered graph with temporal properties +/// Properties are added at times sampled from a normal distribution centered at midpoint +/// Returns (graph, node_prop_id, edge_prop_id, earliest, latest, layer_names) +fn setup_layered_graph_with_props( + num_layers: usize, +) -> (Graph, usize, usize, i64, i64, Vec) { + let graph = lotr_graph(); + + let layer_names: Vec = (0..num_layers).map(|i| format!("layer_{}", i)).collect(); + + // Copy edges to all layers + let mut edges = vec![]; + for edge in graph.edges() { + edges.push(edge.edge); + } + + let earliest = graph.earliest_time().expect("graph should have time").t(); + let latest = graph.latest_time().expect("graph should have time").t(); + + let mut rng = rand::rngs::StdRng::seed_from_u64(RNG_SEED); + + // Add temporal property to all nodes at times from normal distribution + for node in graph.nodes().id().collect::>() { + let t = sample_time(&mut rng, earliest, latest); + let node = graph.node(node).expect("node should exist"); + let layer = format!("layer_{}", rng.random_range(0..num_layers)); + node.add_updates(t, [(PROP_NAME, Prop::I32(42))], Some(layer.as_str())) + .expect("failed to add node property"); + } + + // Add temporal property to all edges at times from normal distribution, on all layers + for edge in &edges { + let t = sample_time(&mut rng, earliest, latest); + let edge = graph + .edge(edge.src(), edge.dst()) + .expect("edge should exist"); + let layer = format!("layer_{}", rng.random_range(0..num_layers)); + edge.add_updates(t, [(PROP_NAME, Prop::I32(42))], Some(layer.as_str())) + .expect("failed to add edge property"); + } + + let node_prop_id = graph + .node_meta() + .temporal_prop_mapper() + .get_id(PROP_NAME) + .expect("node property should exist"); + + let edge_prop_id = graph + .edge_meta() + .temporal_prop_mapper() + .get_id(PROP_NAME) + .expect("edge property should exist"); + + ( + graph, + node_prop_id, + edge_prop_id, + earliest, + latest, + layer_names, + ) +} + +/// Calculate window bounds for a given coverage percentage +/// coverage: 0.1 = 10%, 0.5 = 50%, 1.0 = 100% +fn window_for_coverage(earliest: i64, latest: i64, coverage: f64) -> (i64, i64) { + let range = latest - earliest; + let window_size = (range as f64 * coverage) as i64; + let mid = earliest + range / 2; + let start = mid - window_size / 2; + let end = mid + window_size / 2 + 1; // +1 because window end is exclusive + (start, end) +} + +/// Window that ends before the property updates (most will return None) +fn window_before_prop(earliest: i64, latest: i64) -> (i64, i64) { + let mid = earliest + (latest - earliest) / 2; + let std_dev = (latest - earliest) / 6; + // Window ends 2 std devs before mid (covers ~2.5% of distribution) + (earliest, mid - 2 * std_dev) +} + +/// Window that contains most property updates (most will return Some) +fn window_contains_prop(earliest: i64, latest: i64) -> (i64, i64) { + let mid = earliest + (latest - earliest) / 2; + let std_dev = (latest - earliest) / 6; + // Window covers +/- 1 std dev from mid (~68% of distribution) + (mid - std_dev, mid + std_dev + 1) +} + +/// Window that starts after most property updates (most will return None for event semantics) +fn window_after_prop(earliest: i64, latest: i64) -> (i64, i64) { + let mid = earliest + (latest - earliest) / 2; + let std_dev = (latest - earliest) / 6; + // Window starts 2 std devs after mid (covers ~2.5% of distribution) + (mid + 2 * std_dev, latest + 1) +} + +fn bench_node_temporal_value( + group: &mut BenchmarkGroup, + name: &str, + graph: &Graph, + prop_id: usize, + window: Option<(i64, i64)>, +) { + let nodes: Vec<_> = graph.nodes().collect(); + + match window { + Some((start, end)) => { + let windowed = graph.window(start, end); + let windowed_nodes: Vec<_> = windowed.nodes().collect(); + group.bench_function(name, |b| { + b.iter(|| { + for node in &windowed_nodes { + black_box(node.temporal_value(prop_id)); + } + }) + }); + } + None => { + group.bench_function(name, |b| { + b.iter(|| { + for node in &nodes { + black_box(node.temporal_value(prop_id)); + } + }) + }); + } + } +} + +fn bench_edge_temporal_value( + group: &mut BenchmarkGroup, + name: &str, + graph: &Graph, + prop_id: usize, + window: Option<(i64, i64)>, + layers: Option<&[String]>, +) { + match (window, layers) { + (Some((start, end)), Some(layer_names)) => { + let layer_refs: Vec<&str> = layer_names.iter().map(|s| s.as_str()).collect(); + let view = graph.window(start, end).layers(layer_refs).unwrap(); + let edges: Vec<_> = view.edges().collect(); + group.bench_function(name, |b| { + b.iter(|| { + for edge in &edges { + black_box(edge.temporal_value(prop_id)); + } + }) + }); + } + (Some((start, end)), None) => { + let windowed = graph.window(start, end); + let edges: Vec<_> = windowed.edges().collect(); + group.bench_function(name, |b| { + b.iter(|| { + for edge in &edges { + black_box(edge.temporal_value(prop_id)); + } + }) + }); + } + (None, Some(layer_names)) => { + let layer_refs: Vec<&str> = layer_names.iter().map(|s| s.as_str()).collect(); + let view = graph.layers(layer_refs).unwrap(); + let edges: Vec<_> = view.edges().collect(); + group.bench_function(name, |b| { + b.iter(|| { + for edge in &edges { + black_box(edge.temporal_value(prop_id)); + } + }) + }); + } + (None, None) => { + let edges: Vec<_> = graph.edges().collect(); + group.bench_function(name, |b| { + b.iter(|| { + for edge in &edges { + black_box(edge.temporal_value(prop_id)); + } + }) + }); + } + } +} + +fn temporal_props_benchmarks(c: &mut Criterion) { + eprintln!("=== temporal_props_benchmarks STARTING ==="); + + // ========== Basic graph (no layers) ========== + eprintln!("Setting up graph with props..."); + let (graph, node_prop_id, edge_prop_id, earliest, latest) = setup_graph_with_props(); + eprintln!( + "Graph setup complete: {} nodes, {} edges", + graph.count_nodes(), + graph.count_edges() + ); + + // --- Node benchmarks --- + eprintln!("Starting node benchmarks..."); + let mut node_group = c.benchmark_group("node_temporal_value"); + + // No window (100% coverage, returns Some) + bench_node_temporal_value( + &mut node_group, + "no_window_some", + &graph, + node_prop_id, + None, + ); + + // Window coverage benchmarks (all return Some since they include midpoint) + for coverage in [0.1, 0.5, 1.0] { + let (start, end) = window_for_coverage(earliest, latest, coverage); + let name = format!("window_{}pct_some", (coverage * 100.0) as u32); + bench_node_temporal_value( + &mut node_group, + &name, + &graph, + node_prop_id, + Some((start, end)), + ); + } + + // Window position benchmarks + let (start, end) = window_before_prop(earliest, latest); + bench_node_temporal_value( + &mut node_group, + "window_before_none", + &graph, + node_prop_id, + Some((start, end)), + ); + + let (start, end) = window_contains_prop(earliest, latest); + bench_node_temporal_value( + &mut node_group, + "window_contains_some", + &graph, + node_prop_id, + Some((start, end)), + ); + + let (start, end) = window_after_prop(earliest, latest); + bench_node_temporal_value( + &mut node_group, + "window_after_none", + &graph, + node_prop_id, + Some((start, end)), + ); + + node_group.finish(); + eprintln!("Node benchmarks complete."); + + // --- Edge benchmarks (no layers) --- + eprintln!("Starting edge benchmarks..."); + let mut edge_group = c.benchmark_group("edge_temporal_value"); + + // No window (100% coverage, returns Some) + bench_edge_temporal_value( + &mut edge_group, + "no_window_some", + &graph, + edge_prop_id, + None, + None, + ); + + // Window coverage benchmarks + for coverage in [0.1, 0.5, 1.0] { + let (start, end) = window_for_coverage(earliest, latest, coverage); + let name = format!("window_{}pct_some", (coverage * 100.0) as u32); + bench_edge_temporal_value( + &mut edge_group, + &name, + &graph, + edge_prop_id, + Some((start, end)), + None, + ); + } + + // Window position benchmarks + let (start, end) = window_before_prop(earliest, latest); + bench_edge_temporal_value( + &mut edge_group, + "window_before_none", + &graph, + edge_prop_id, + Some((start, end)), + None, + ); + + let (start, end) = window_contains_prop(earliest, latest); + bench_edge_temporal_value( + &mut edge_group, + "window_contains_some", + &graph, + edge_prop_id, + Some((start, end)), + None, + ); + + let (start, end) = window_after_prop(earliest, latest); + bench_edge_temporal_value( + &mut edge_group, + "window_after_none", + &graph, + edge_prop_id, + Some((start, end)), + None, + ); + + edge_group.finish(); + eprintln!("Edge benchmarks complete."); + + // ========== Layered graph benchmarks ========== + for num_layers in [1, 2] { + eprintln!("Setting up layered graph with {} layers...", num_layers); + let (layered_graph, _node_prop_id, edge_prop_id, earliest, latest, layer_names) = + setup_layered_graph_with_props(num_layers); + eprintln!( + "Layered graph setup complete: {} nodes, {} edges", + layered_graph.count_nodes(), + layered_graph.count_edges() + ); + + let mut layered_group = + c.benchmark_group(format!("edge_temporal_value_{}layers", num_layers)); + + // No window, with layer filter + bench_edge_temporal_value( + &mut layered_group, + "no_window_some", + &layered_graph, + edge_prop_id, + None, + Some(&layer_names), + ); + + // Window coverage with layers + for coverage in [0.1, 0.5, 1.0] { + let (start, end) = window_for_coverage(earliest, latest, coverage); + let name = format!("window_{}pct_some", (coverage * 100.0) as u32); + bench_edge_temporal_value( + &mut layered_group, + &name, + &layered_graph, + edge_prop_id, + Some((start, end)), + Some(&layer_names), + ); + } + + // Window position with layers + let (start, end) = window_before_prop(earliest, latest); + bench_edge_temporal_value( + &mut layered_group, + "window_before_none", + &layered_graph, + edge_prop_id, + Some((start, end)), + Some(&layer_names), + ); + + let (start, end) = window_contains_prop(earliest, latest); + bench_edge_temporal_value( + &mut layered_group, + "window_contains_some", + &layered_graph, + edge_prop_id, + Some((start, end)), + Some(&layer_names), + ); + + let (start, end) = window_after_prop(earliest, latest); + bench_edge_temporal_value( + &mut layered_group, + "window_after_none", + &layered_graph, + edge_prop_id, + Some((start, end)), + Some(&layer_names), + ); + + layered_group.finish(); + } +} + +criterion_group!(benches, temporal_props_benchmarks); +criterion_main!(benches); From b55f44253e3172a849c79382106c94fa37b2d0d8 Mon Sep 17 00:00:00 2001 From: Fabian Murariu Date: Tue, 28 Apr 2026 12:43:28 +0100 Subject: [PATCH 13/15] fixes post benchmark --- raphtory-api/src/core/entities/properties/tprop.rs | 4 +--- raphtory-benchmark/benches/temporal_props.rs | 12 +++++++----- raphtory-core/src/entities/properties/props.rs | 5 +---- raphtory-core/src/entities/properties/tprop.rs | 7 +++++++ .../src/graph/variants/storage_variants2.rs | 4 ++++ .../src/graph/variants/storage_variants3.rs | 4 ++++ raphtory-storage/src/mutation/mod.rs | 5 +---- raphtory/src/db/api/state/generic_node_state.rs | 4 ++-- .../view/internal/time_semantics/filtered_edge.rs | 9 +++++++++ raphtory/src/io/arrow/df_loaders/mod.rs | 1 + 10 files changed, 37 insertions(+), 18 deletions(-) diff --git a/raphtory-api/src/core/entities/properties/tprop.rs b/raphtory-api/src/core/entities/properties/tprop.rs index b2387a02e1..5736ca15d0 100644 --- a/raphtory-api/src/core/entities/properties/tprop.rs +++ b/raphtory-api/src/core/entities/properties/tprop.rs @@ -18,9 +18,7 @@ pub trait TPropOps<'a>: Clone + Send + Sync + Sized + 'a { self.clone().iter_inner_rev(Some(EventTime::MIN..t)).next() } - fn last_window(&self, w: Range) -> Option<(EventTime, Prop)> { - self.clone().iter_window_rev(w).next() - } + fn last_window(&self, w: Range) -> Option<(EventTime, Prop)>; fn last(&self) -> Option<(EventTime, Prop)>; diff --git a/raphtory-benchmark/benches/temporal_props.rs b/raphtory-benchmark/benches/temporal_props.rs index 894ffc1d1d..300b165abf 100644 --- a/raphtory-benchmark/benches/temporal_props.rs +++ b/raphtory-benchmark/benches/temporal_props.rs @@ -1,10 +1,12 @@ -use criterion::measurement::WallTime; -use criterion::{black_box, criterion_group, criterion_main, BenchmarkGroup, Criterion}; +use criterion::{ + black_box, criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, +}; use rand::{Rng, SeedableRng}; use rand_distr::{Distribution, Normal}; -use raphtory::db::api::properties::internal::InternalTemporalPropertyViewOps; -use raphtory::storage::core_ops::CoreGraphOps; -use raphtory::{graph_loader::lotr_graph::lotr_graph, prelude::*}; +use raphtory::{ + db::api::properties::internal::InternalTemporalPropertyViewOps, + graph_loader::lotr_graph::lotr_graph, prelude::*, storage::core_ops::CoreGraphOps, +}; use raphtory_api::core::storage::timeindex::AsTime; const PROP_NAME: &str = "bench_prop"; diff --git a/raphtory-core/src/entities/properties/props.rs b/raphtory-core/src/entities/properties/props.rs index c3243d24a0..5a0d9bb875 100644 --- a/raphtory-core/src/entities/properties/props.rs +++ b/raphtory-core/src/entities/properties/props.rs @@ -1,7 +1,4 @@ -use crate::{ - entities::properties::tprop::IllegalPropType, - storage::{lazy_vec::IllegalSet, TPropColumnError}, -}; +use crate::storage::{lazy_vec::IllegalSet, TPropColumnError}; use raphtory_api::core::entities::properties::prop::Prop; use std::fmt::Debug; use thiserror::Error; diff --git a/raphtory-core/src/entities/properties/tprop.rs b/raphtory-core/src/entities/properties/tprop.rs index 728381659d..f40e56cb65 100644 --- a/raphtory-core/src/entities/properties/tprop.rs +++ b/raphtory-core/src/entities/properties/tprop.rs @@ -60,6 +60,13 @@ impl<'a> TPropOps<'a> for TPropCell<'a> { .find_map(|(t, pos)| self.log?.get(pos.as_ref().copied()?).map(|prop| (*t, prop))) } + fn last_window(&self, w: Range) -> Option<(EventTime, Prop)> { + self.t_cell? + .iter_window(w) + .rev() + .find_map(|(t, pos)| self.log?.get(pos.as_ref().copied()?).map(|prop| (*t, prop))) + } + fn iter_inner( self, range: Option>, diff --git a/raphtory-storage/src/graph/variants/storage_variants2.rs b/raphtory-storage/src/graph/variants/storage_variants2.rs index 3320f412ba..42fc62eada 100644 --- a/raphtory-storage/src/graph/variants/storage_variants2.rs +++ b/raphtory-storage/src/graph/variants/storage_variants2.rs @@ -51,6 +51,10 @@ impl<'a, Mem: TPropOps<'a> + 'a> TPropOps<'a> for SelfType!(Mem, Disk) { for_all!(self, props => props.last_before(t)) } + fn last_window(&self, w: Range) -> Option<(EventTime, Prop)> { + for_all!(self, props => props.last_window(w)) + } + fn last(&self) -> Option<(EventTime, Prop)> { for_all!(self, props => props.last()) } diff --git a/raphtory-storage/src/graph/variants/storage_variants3.rs b/raphtory-storage/src/graph/variants/storage_variants3.rs index 9809b75e45..1f3d23e76a 100644 --- a/raphtory-storage/src/graph/variants/storage_variants3.rs +++ b/raphtory-storage/src/graph/variants/storage_variants3.rs @@ -55,6 +55,10 @@ impl<'a, Mem: TPropOps<'a> + 'a, Unlocked: TPropOps<'a> + 'a> TPropOps<'a> for_all!(self, props => props.last_before(t)) } + fn last_window(&self, w: Range) -> Option<(EventTime, Prop)> { + for_all!(self, props => props.last_window(w)) + } + fn last(&self) -> Option<(EventTime, Prop)> { for_all!(self, props => props.last()) } diff --git a/raphtory-storage/src/mutation/mod.rs b/raphtory-storage/src/mutation/mod.rs index 30ff7fcc61..8b82b1dd4d 100644 --- a/raphtory-storage/src/mutation/mod.rs +++ b/raphtory-storage/src/mutation/mod.rs @@ -12,10 +12,7 @@ use raphtory_api::{ }; use raphtory_core::entities::{ graph::tgraph::TooManyLayers, - properties::{ - props::{MetadataError, TPropError}, - tprop::IllegalPropType, - }, + properties::props::{MetadataError, TPropError}, }; use std::sync::Arc; use storage::{ diff --git a/raphtory/src/db/api/state/generic_node_state.rs b/raphtory/src/db/api/state/generic_node_state.rs index 6b01686e75..903cc4b6b9 100644 --- a/raphtory/src/db/api/state/generic_node_state.rs +++ b/raphtory/src/db/api/state/generic_node_state.rs @@ -44,7 +44,7 @@ use parquet::{ use raphtory_api::core::entities::{ properties::{ meta::STATIC_GRAPH_LAYER_ID, - prop::{Prop, PropType, PropUntagged, PropUnwrap}, + prop::{Prop, PropUntagged, PropUnwrap}, }, LayerIds, }; @@ -58,7 +58,7 @@ use serde_arrow::{ use std::{ cmp::{Ordering, PartialEq}, collections::{BinaryHeap, HashMap}, - fmt::{Debug, Formatter, Pointer}, + fmt::{Debug, Formatter}, fs::File, hash::BuildHasher, marker::PhantomData, diff --git a/raphtory/src/db/api/view/internal/time_semantics/filtered_edge.rs b/raphtory/src/db/api/view/internal/time_semantics/filtered_edge.rs index dccc65a547..445eb14e0c 100644 --- a/raphtory/src/db/api/view/internal/time_semantics/filtered_edge.rs +++ b/raphtory/src/db/api/view/internal/time_semantics/filtered_edge.rs @@ -210,6 +210,15 @@ impl<'graph, G: GraphViewOps<'graph>, P: TPropOps<'graph>> TPropOps<'graph> self.props.last() } } + + fn last_window(&self, w: Range) -> Option<(EventTime, Prop)> { + if self.view.internal_exploded_edge_filtered() { + self.clone().iter_window_rev(w).next() + } else { + self.props.last_window(w) + } + } + fn iter_inner( self, range: Option>, diff --git a/raphtory/src/io/arrow/df_loaders/mod.rs b/raphtory/src/io/arrow/df_loaders/mod.rs index b0183b4869..2cbc1757f2 100644 --- a/raphtory/src/io/arrow/df_loaders/mod.rs +++ b/raphtory/src/io/arrow/df_loaders/mod.rs @@ -9,6 +9,7 @@ use crate::{ }, prelude::*, }; +#[cfg(feature = "progress")] use kdam::{Bar, BarBuilder, BarExt}; use raphtory_api::core::{ entities::properties::prop::PropType, From 09a4c4f39c4990a66564fe0ccb03513e4f764325 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 28 Apr 2026 15:27:35 +0000 Subject: [PATCH 14/15] chore: apply tidy-public auto-fixes --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 4aa31866a2..67f4db21e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7563,7 +7563,7 @@ checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" [[package]] name = "snb" -version = "0.18.0" +version = "0.17.0" dependencies = [ "chrono", "flate2", From a172391daab6723d1def3b64fdee5401998769e4 Mon Sep 17 00:00:00 2001 From: Fabian Murariu Date: Tue, 28 Apr 2026 17:17:17 +0100 Subject: [PATCH 15/15] for tprops go to disk first --- Cargo.lock | 226 +++++++++++++++-------------- db4-storage/src/generic_t_props.rs | 3 - 2 files changed, 117 insertions(+), 112 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 67f4db21e1..73bd280571 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -431,9 +431,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.41" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f9ee0f6e02ffd7ad5816e9464499fba7b3effd01123b515c41d1697c43dad1" +checksum = "e79b3f8a79cccc2898f31920fc69f304859b3bd567490f75ebf51ae1c792a9ac" dependencies = [ "compression-codecs", "compression-core", @@ -575,7 +575,7 @@ dependencies = [ "derive_builder", "eventsource-stream", "futures", - "rand 0.8.5", + "rand 0.8.6", "reqwest", "reqwest-eventsource", "secrecy", @@ -685,9 +685,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-rs" -version = "1.16.2" +version = "1.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc" +checksum = "0ec6fb3fe69024a75fa7e1bfb48aa6cf59706a101658ea01bfd33b2b248a038f" dependencies = [ "aws-lc-sys", "untrusted 0.7.1", @@ -696,9 +696,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.39.1" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a25cf98105baa966497416dbd42565ce3a8cf8dbfd59803ec9ad46f3126399" +checksum = "f50037ee5e1e41e7b8f9d161680a725bd1626cb6f8c7e901f91f942850852fe7" dependencies = [ "cc", "cmake", @@ -815,7 +815,7 @@ dependencies = [ "getrandom 0.2.17", "instant", "pin-project-lite", - "rand 0.8.5", + "rand 0.8.6", "tokio", ] @@ -939,9 +939,9 @@ dependencies = [ [[package]] name = "blake3" -version = "1.8.4" +version = "1.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d2d5991425dfd0785aed03aedcf0b321d61975c9b5b3689c774a2610ae0b51e" +checksum = "0aa83c34e62843d924f905e0f5c866eb1dd6545fc4d719e803d9ba6030371fce" dependencies = [ "arrayref", "arrayvec", @@ -1090,9 +1090,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.60" +version = "1.2.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" +checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" dependencies = [ "find-msvc-tools", "jobserver", @@ -1213,6 +1213,7 @@ dependencies = [ "comfy-table", "criterion", "db4-storage", + "either", "env_logger 0.10.2", "fastrand", "flate2", @@ -1247,9 +1248,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.0" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" dependencies = [ "clap_builder", "clap_derive", @@ -1269,9 +1270,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.6.0" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -1322,9 +1323,9 @@ dependencies = [ [[package]] name = "compression-codecs" -version = "0.4.37" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb7b51a7d9c967fc26773061ba86150f19c50c0d65c887cb1fbe295fd16619b7" +checksum = "ce2548391e9c1929c21bf6aa2680af86fe4c1b33e6cea9ac1cfeec0bd11218cf" dependencies = [ "brotli", "compression-core", @@ -1336,9 +1337,9 @@ dependencies = [ [[package]] name = "compression-core" -version = "0.4.31" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" +checksum = "cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789" [[package]] name = "concurrent-queue" @@ -1481,9 +1482,9 @@ dependencies = [ [[package]] name = "crc-catalog" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" [[package]] name = "crc32fast" @@ -1614,9 +1615,9 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "typenum", @@ -1730,9 +1731,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" [[package]] name = "datafusion" @@ -2765,9 +2766,9 @@ checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" [[package]] name = "ethnum" -version = "1.5.2" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca81e6b4777c89fd810c25a4be2b1bd93ea034fbe58e6a75216a34c6b82c539b" +checksum = "40404c3f5f511ec4da6fe866ddf6a717c309fdbb69fbbad7b0f3edab8f2e835f" [[package]] name = "event-listener" @@ -2809,7 +2810,7 @@ checksum = "aef603df4ba9adbca6a332db7da6f614f21eafefbaf8e087844e452fdec152d0" dependencies = [ "chrono", "deunicode", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -3072,9 +3073,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.9" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -3431,7 +3432,7 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", - "webpki-roots 1.0.6", + "webpki-roots 1.0.7", ] [[package]] @@ -3610,9 +3611,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" dependencies = [ "icu_normalizer", "icu_properties", @@ -3751,15 +3752,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.12.1" @@ -3795,9 +3787,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jiff" -version = "0.2.23" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359" +checksum = "f00b5dbd620d61dfdcb6007c9c1f6054ebd75319f163d886a9055cec1155073d" dependencies = [ "jiff-static", "jiff-tzdb-platform", @@ -3810,9 +3802,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.23" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4" +checksum = "e000de030ff8022ea1da3f466fbb0f3a809f5e51ed31f6dd931c35181ad8e6d7" dependencies = [ "proc-macro2", "quote", @@ -3846,9 +3838,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.95" +version = "0.3.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" +checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf" dependencies = [ "cfg-if", "futures-util", @@ -3967,7 +3959,7 @@ dependencies = [ "prost 0.14.3", "prost-types 0.14.3", "rand 0.9.4", - "roaring 0.11.3", + "roaring 0.11.4", "semver", "serde", "serde_json", @@ -4042,7 +4034,7 @@ dependencies = [ "pin-project", "prost 0.14.3", "rand 0.9.4", - "roaring 0.11.3", + "roaring 0.11.4", "serde_json", "snafu 0.9.0", "tempfile", @@ -4231,7 +4223,7 @@ dependencies = [ "rand_distr 0.5.1", "rangemap", "rayon", - "roaring 0.11.3", + "roaring 0.11.4", "serde", "serde_json", "smallvec", @@ -4385,7 +4377,7 @@ dependencies = [ "prost-types 0.14.3", "rand 0.9.4", "rangemap", - "roaring 0.11.3", + "roaring 0.11.4", "semver", "serde", "serde_json", @@ -4548,9 +4540,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.185" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libm" @@ -5296,7 +5288,7 @@ dependencies = [ "glob", "opentelemetry", "percent-encoding", - "rand 0.8.5", + "rand 0.8.6", "serde_json", "thiserror 1.0.69", "tokio", @@ -5641,7 +5633,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared 0.11.3", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -5809,9 +5801,9 @@ checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "portable-atomic-util" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "091397be61a01d4be58e7841595bd4bfedb15f1cd54977d79b8271e94ed799a3" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" dependencies = [ "portable-atomic", ] @@ -6243,9 +6235,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -6323,7 +6315,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" dependencies = [ "num-traits", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -6820,7 +6812,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 1.0.6", + "webpki-roots 1.0.7", ] [[package]] @@ -6880,9 +6872,9 @@ dependencies = [ [[package]] name = "roaring" -version = "0.11.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ba9ce64a8f45d7fc86358410bb1a82e8c987504c0d4900e9141d69a9f26c885" +checksum = "1dedc5658c6ecb3bdb5ef5f3295bb9253f42dcf3fd1402c03f6b1f7659c3c4a9" dependencies = [ "bytemuck", "byteorder", @@ -7016,9 +7008,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.38" +version = "0.23.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f9466fb2c14ea04357e91413efb882e2a6d4a406e625449bc0a5d360d53a21" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ "once_cell", "ring", @@ -7064,9 +7056,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.0" +version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" dependencies = [ "web-time", "zeroize", @@ -7074,9 +7066,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.12" +version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8279bb85272c9f10811ae6a6c547ff594d6a7f3c6c6b02ee9726d1d0dcfcdd06" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ "ring", "rustls-pki-types", @@ -7592,11 +7584,11 @@ dependencies = [ [[package]] name = "sorted_vector_map" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9167648c2ababdbe45294fe7f7b0ab56555fd754990a7637a5a420774461368" +checksum = "94bf565ee1681b4473aa5a9d71d807347c28021bd1d8947cb626b02f42a0141f" dependencies = [ - "itertools 0.11.0", + "itertools 0.14.0", "quickcheck", ] @@ -8109,9 +8101,9 @@ dependencies = [ [[package]] name = "test-log" -version = "0.2.19" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d53ac171c92a39e4769491c4b4dde7022c60042254b5fc044ae409d34a24d4" +checksum = "2f46bf474f0a4afebf92f076d54fd5e63423d9438b8c278a3d2ccb0f47f7cdb3" dependencies = [ "env_logger 0.11.10", "test-log-macros", @@ -8119,16 +8111,26 @@ dependencies = [ ] [[package]] -name = "test-log-macros" -version = "0.2.19" +name = "test-log-core" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be35209fd0781c5401458ab66e4f98accf63553e8fae7425503e92fdd319783b" +checksum = "37d4d41320b48bc4a211a9021678fcc0c99569b594ea31c93735b8e517102b4c" dependencies = [ "proc-macro2", "quote", "syn 2.0.117", ] +[[package]] +name = "test-log-macros" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9beb9249a81e430dffd42400a49019bcf548444f1968ff23080a625de0d4d320" +dependencies = [ + "syn 2.0.117", + "test-log-core", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -8296,9 +8298,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.52.0" +version = "1.52.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a91135f59b1cbf38c91e73cf3386fca9bb77915c45ce2771460c9d92f0f3d776" +checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" dependencies = [ "bytes", "libc", @@ -8423,7 +8425,7 @@ dependencies = [ "indexmap 2.14.0", "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow 1.0.1", + "winnow 1.0.2", ] [[package]] @@ -8432,7 +8434,7 @@ version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow 1.0.1", + "winnow 1.0.2", ] [[package]] @@ -8482,7 +8484,7 @@ dependencies = [ "indexmap 1.9.3", "pin-project", "pin-project-lite", - "rand 0.8.5", + "rand 0.8.6", "slab", "tokio", "tokio-util", @@ -8677,9 +8679,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" [[package]] name = "ucd-trie" @@ -8800,9 +8802,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.23.0" +version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9" +checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" dependencies = [ "getrandom 0.4.2", "js-sys", @@ -8864,11 +8866,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.2+wasi-0.2.9" +version = "1.0.3+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.57.1", ] [[package]] @@ -8877,14 +8879,14 @@ version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.51.0", ] [[package]] name = "wasm-bindgen" -version = "0.2.118" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" +checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" dependencies = [ "cfg-if", "once_cell", @@ -8895,9 +8897,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.68" +version = "0.4.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f371d383f2fb139252e0bfac3b81b265689bf45b6874af544ffa4c975ac1ebf8" +checksum = "af934872acec734c2d80e6617bbb5ff4f12b052dd8e6332b0817bce889516084" dependencies = [ "js-sys", "wasm-bindgen", @@ -8905,9 +8907,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.118" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" +checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -8915,9 +8917,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.118" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" +checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" dependencies = [ "bumpalo", "proc-macro2", @@ -8928,9 +8930,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.118" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" +checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" dependencies = [ "unicode-ident", ] @@ -8984,9 +8986,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.95" +version = "0.3.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2dfbb17949fa2088e5d39408c48368947b86f7834484e87b73de55bc14d97d" +checksum = "2eadbac71025cd7b0834f20d1fe8472e8495821b4e9801eb0a60bd1f19827602" dependencies = [ "js-sys", "wasm-bindgen", @@ -9008,14 +9010,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.6", + "webpki-roots 1.0.7", ] [[package]] name = "webpki-roots" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" dependencies = [ "rustls-pki-types", ] @@ -9292,9 +9294,9 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" +checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" dependencies = [ "memchr", ] @@ -9308,6 +9310,12 @@ dependencies = [ "wit-bindgen-rust-macro", ] +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + [[package]] name = "wit-bindgen-core" version = "0.51.0" diff --git a/db4-storage/src/generic_t_props.rs b/db4-storage/src/generic_t_props.rs index 657f4f49b3..c966189bc6 100644 --- a/db4-storage/src/generic_t_props.rs +++ b/db4-storage/src/generic_t_props.rs @@ -6,7 +6,6 @@ use raphtory_api::core::entities::{ LayerId, properties::{prop::Prop, tprop::TPropOps}, }; -use raphtory_api_macros::box_on_debug_lifetime; use raphtory_core::{entities::LayerIds, storage::timeindex::EventTime}; use crate::utils::Iter4; @@ -29,7 +28,6 @@ where prop_id: usize, ) -> impl Iterator + Send + Sync + 'a; - #[box_on_debug_lifetime] fn into_t_props_layers( self, layers: impl Borrow, @@ -85,7 +83,6 @@ impl<'a, Ref: WithTProps<'a>> GenericTProps<'a, Ref> { } impl<'a, Ref: WithTProps<'a>> GenericTProps<'a, Ref> { - #[box_on_debug_lifetime] fn tprops(self, prop_id: usize) -> impl Iterator + Send + Sync + 'a { match self.layer_id { Either::Left(layer_ids) => {