use crate::{
core::entities::LayerIds,
db::{
api::{
properties::internal::{
InheritMetadataPropertiesOps, InternalTemporalPropertiesOps,
InternalTemporalPropertyViewOps,
},
state::Index,
view::{
internal::{
EdgeList, GraphTimeSemanticsOps, GraphView, Immutable, InheritLayerOps,
InheritMaterialize, InheritStorageOps, InternalEdgeFilterOps,
InternalEdgeLayerFilterOps, InternalExplodedEdgeFilterOps,
InternalNodeFilterOps, ListOps, NodeList, Static, TimeSemantics,
},
BoxedLIter, IntoDynBoxed,
},
},
graph::graph::graph_equal,
},
prelude::GraphViewOps,
};
use raphtory_api::{
core::{
entities::{
properties::prop::{Prop, PropType},
ELID,
},
storage::{arc_str::ArcStr, timeindex::EventTime},
},
inherit::Base,
iter::{BoxedLDIter, IntoDynDBoxed},
};
use raphtory_storage::{
core_ops::{CoreGraphOps, InheritCoreGraphOps},
graph::{edges::edge_ref::EdgeStorageRef, nodes::node_ref::NodeStorageRef},
};
use std::{
fmt::{Debug, Formatter},
iter,
ops::Range,
};
#[derive(Copy, Clone)]
pub struct WindowedGraph<G> {
pub graph: G,
pub start: Option<EventTime>,
pub end: Option<EventTime>,
}
impl<G> Static for WindowedGraph<G> {}
impl<G: Debug> Debug for WindowedGraph<G> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"WindowedGraph(start={:?}, end={:?}, graph={:?})",
self.start, self.end, self.graph,
)
}
}
impl<'graph1, 'graph2, G1: GraphViewOps<'graph1>, G2: GraphViewOps<'graph2>> PartialEq<G2>
for WindowedGraph<G1>
{
fn eq(&self, other: &G2) -> bool {
graph_equal(self, other)
}
}
impl<'graph, G: GraphViewOps<'graph>> Base for WindowedGraph<G> {
type Base = G;
#[inline(always)]
fn base(&self) -> &Self::Base {
&self.graph
}
}
impl<G: GraphView> WindowedGraph<G> {
#[inline(always)]
fn window_bound(&self) -> Range<EventTime> {
self.start_bound()..self.end_bound()
}
fn start_bound(&self) -> EventTime {
self.start.unwrap_or(EventTime::MIN)
}
#[inline(always)]
fn end_bound(&self) -> EventTime {
self.end.unwrap_or(EventTime::MAX)
}
#[inline(always)]
fn window_is_empty(&self) -> bool {
self.start_bound() >= self.end_bound()
}
#[inline]
fn start_is_bounding(&self) -> bool {
match self.start {
None => false,
Some(start) => match self.graph.core_graph().earliest_time() {
None => false,
Some(graph_earliest) => start >= graph_earliest, },
}
}
#[inline]
fn end_is_bounding(&self) -> bool {
match self.end {
None => false,
Some(end) => match self.core_graph().latest_time() {
None => false,
Some(graph_latest) => end <= graph_latest,
},
}
}
#[inline]
fn window_is_bounding(&self) -> bool {
self.start_is_bounding() || self.end_is_bounding()
}
}
impl<'graph, G: GraphViewOps<'graph>> Immutable for WindowedGraph<G> {}
impl<'graph, G: GraphViewOps<'graph>> InheritCoreGraphOps for WindowedGraph<G> {}
impl<'graph, G: GraphViewOps<'graph>> InheritStorageOps for WindowedGraph<G> {}
impl<'graph, G: GraphViewOps<'graph>> InheritMaterialize for WindowedGraph<G> {}
impl<'graph, G: GraphViewOps<'graph>> InheritMetadataPropertiesOps for WindowedGraph<G> {}
impl<'graph, G: GraphViewOps<'graph>> InheritLayerOps for WindowedGraph<G> {}
impl<'graph, G: GraphViewOps<'graph>> ListOps for WindowedGraph<G> {
fn node_list(&self) -> NodeList {
if self.window_is_empty() {
NodeList::List {
elems: Index::default(),
}
} else {
self.graph.node_list()
}
}
fn edge_list(&self) -> EdgeList {
if self.window_is_empty() {
EdgeList::List {
elems: Index::default(),
}
} else {
self.graph.edge_list()
}
}
}
impl<'graph, G: GraphViewOps<'graph>> InternalNodeFilterOps for WindowedGraph<G> {
#[inline]
fn internal_nodes_filtered(&self) -> bool {
self.window_is_empty() || self.graph.internal_nodes_filtered() || self.window_is_bounding()
}
#[inline]
fn internal_node_list_trusted(&self) -> bool {
self.window_is_empty()
|| (self.graph.internal_node_list_trusted() && !self.window_is_bounding())
}
#[inline]
fn edge_filter_includes_node_filter(&self) -> bool {
self.window_is_empty() || self.graph.edge_filter_includes_node_filter()
}
#[inline]
fn edge_layer_filter_includes_node_filter(&self) -> bool {
self.window_is_empty() || self.graph.edge_layer_filter_includes_node_filter()
}
#[inline]
fn exploded_edge_filter_includes_node_filter(&self) -> bool {
self.window_is_empty() || self.graph.exploded_edge_filter_includes_node_filter()
}
#[inline]
fn internal_filter_node(&self, node: NodeStorageRef, layer_ids: &LayerIds) -> bool {
!self.window_is_empty() && self.graph.internal_filter_node(node, layer_ids)
}
}
impl<'graph, G: GraphViewOps<'graph>> InternalTemporalPropertyViewOps for WindowedGraph<G> {
fn dtype(&self, id: usize) -> PropType {
self.graph
.graph_meta()
.temporal_mapper()
.get_dtype(id)
.unwrap()
}
fn temporal_value(&self, id: usize) -> Option<Prop> {
self.graph.temporal_value_at(id, self.end_bound())
}
fn temporal_iter(&self, id: usize) -> BoxedLIter<'_, (EventTime, Prop)> {
if self.window_is_empty() {
return iter::empty().into_dyn_boxed();
}
self.graph
.temporal_prop_iter_window(id, self.start_bound(), self.end_bound())
.into_dyn_boxed()
}
fn temporal_iter_rev(&self, id: usize) -> BoxedLIter<'_, (EventTime, Prop)> {
self.graph
.temporal_prop_iter_window(id, self.start_bound(), self.end_bound())
.rev()
.into_dyn_boxed()
}
fn temporal_value_at(&self, id: usize, t: EventTime) -> Option<Prop> {
self.graph
.temporal_prop_last_at_window(id, t, self.window_bound())
.map(|(_, p)| p)
}
}
impl<'graph, G: GraphViewOps<'graph>> InternalTemporalPropertiesOps for WindowedGraph<G> {
fn get_temporal_prop_id(&self, name: &str) -> Option<usize> {
self.graph
.get_temporal_prop_id(name)
.filter(|id| self.has_temporal_prop(*id))
}
fn get_temporal_prop_name(&self, id: usize) -> ArcStr {
self.graph.get_temporal_prop_name(id)
}
fn temporal_prop_ids(&self) -> BoxedLIter<'_, usize> {
Box::new(
self.graph
.temporal_prop_ids()
.filter(|id| self.has_temporal_prop(*id)),
)
}
}
impl<'graph, G: GraphViewOps<'graph>> GraphTimeSemanticsOps for WindowedGraph<G> {
fn node_time_semantics(&self) -> TimeSemantics {
self.graph.node_time_semantics().window(self.window_bound())
}
fn edge_time_semantics(&self) -> TimeSemantics {
self.graph.edge_time_semantics().window(self.window_bound())
}
fn view_start(&self) -> Option<EventTime> {
self.start
}
fn view_end(&self) -> Option<EventTime> {
self.end
}
#[inline]
fn earliest_time_global(&self) -> Option<i64> {
if self.window_is_empty() {
return None;
}
self.graph
.earliest_time_window(self.start_bound(), self.end_bound())
}
#[inline]
fn latest_time_global(&self) -> Option<i64> {
if self.window_is_empty() {
return None;
}
self.graph
.latest_time_window(self.start_bound(), self.end_bound())
}
#[inline]
fn earliest_time_window(&self, start: EventTime, end: EventTime) -> Option<i64> {
self.graph.earliest_time_window(start, end)
}
#[inline]
fn latest_time_window(&self, start: EventTime, end: EventTime) -> Option<i64> {
self.graph.latest_time_window(start, end)
}
fn has_temporal_prop(&self, prop_id: usize) -> bool {
if self.window_is_empty() {
return false;
}
self.graph
.has_temporal_prop_window(prop_id, self.start_bound()..self.end_bound())
}
fn temporal_prop_iter(&self, prop_id: usize) -> BoxedLDIter<'_, (EventTime, Prop)> {
if self.window_is_empty() {
return iter::empty().into_dyn_dboxed();
}
self.graph
.temporal_prop_iter_window(prop_id, self.start_bound(), self.end_bound())
}
fn has_temporal_prop_window(&self, prop_id: usize, w: Range<EventTime>) -> bool {
self.graph.has_temporal_prop_window(prop_id, w.start..w.end)
}
fn temporal_prop_iter_window(
&self,
prop_id: usize,
start: EventTime,
end: EventTime,
) -> BoxedLDIter<'_, (EventTime, Prop)> {
self.graph.temporal_prop_iter_window(prop_id, start, end)
}
fn temporal_prop_last_at(&self, prop_id: usize, t: EventTime) -> Option<(EventTime, Prop)> {
self.graph
.temporal_prop_last_at_window(prop_id, t, self.window_bound())
}
fn temporal_prop_last_at_window(
&self,
prop_id: usize,
t: EventTime,
w: Range<EventTime>,
) -> Option<(EventTime, Prop)> {
self.graph.temporal_prop_last_at_window(prop_id, t, w)
}
}
impl<G: GraphView> InternalEdgeFilterOps for WindowedGraph<G> {
fn internal_edge_filtered(&self) -> bool {
self.window_is_bounding() || self.graph.internal_edge_filtered()
}
fn internal_edge_list_trusted(&self) -> bool {
self.window_is_empty()
|| (!self.window_is_bounding() && self.graph.internal_edge_list_trusted())
}
fn internal_filter_edge(&self, edge: EdgeStorageRef, layer_ids: &LayerIds) -> bool {
self.graph.internal_filter_edge(edge, layer_ids)
}
fn node_filter_includes_edge_filter(&self) -> bool {
self.window_is_empty() || self.graph.node_filter_includes_edge_filter()
}
}
impl<G: GraphView> InternalEdgeLayerFilterOps for WindowedGraph<G> {
fn internal_edge_layer_filtered(&self) -> bool {
self.window_is_bounding() || self.graph.internal_edge_layer_filtered()
}
fn internal_layer_filter_edge_list_trusted(&self) -> bool {
self.window_is_empty()
|| (!self.window_is_bounding() && self.graph.internal_layer_filter_edge_list_trusted())
}
fn internal_filter_edge_layer(&self, edge: EdgeStorageRef, layer: usize) -> bool {
self.graph.internal_filter_edge_layer(edge, layer)
}
fn node_filter_includes_edge_layer_filter(&self) -> bool {
self.window_is_empty() || self.graph.node_filter_includes_edge_layer_filter()
}
}
impl<G: GraphView> InternalExplodedEdgeFilterOps for WindowedGraph<G> {
fn internal_exploded_edge_filtered(&self) -> bool {
self.graph.internal_exploded_edge_filtered()
}
fn internal_exploded_filter_edge_list_trusted(&self) -> bool {
self.window_is_empty()
|| (!self.window_is_bounding()
&& self.graph.internal_exploded_filter_edge_list_trusted())
}
fn internal_filter_exploded_edge(&self, eid: ELID, t: EventTime, layer_ids: &LayerIds) -> bool {
self.graph.internal_filter_exploded_edge(eid, t, layer_ids)
}
fn node_filter_includes_exploded_edge_filter(&self) -> bool {
self.window_is_empty() || self.graph.node_filter_includes_exploded_edge_filter()
}
}
impl<'graph, G: GraphViewOps<'graph>> WindowedGraph<G> {
pub fn new(graph: G, start: Option<EventTime>, end: Option<EventTime>) -> Self {
WindowedGraph { graph, start, end }
}
}