Skip to content

Commit

Permalink
Feature/excludeapis (#1561)
Browse files Browse the repository at this point in the history
* impl exclude layer api and tests

* impl exclude layers python and tests

* impl/test exclude_nodes_subgraph

* impl python exclude nodes api

* fmt

* add tests for nbr

* impl node type filter for nodes and subgraph

* fmt
  • Loading branch information
shivamka1 authored Apr 11, 2024
1 parent 0f74523 commit c398538
Show file tree
Hide file tree
Showing 19 changed files with 399 additions and 12 deletions.
28 changes: 27 additions & 1 deletion python/tests/test_graphdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -1359,12 +1359,17 @@ def test_layer():

g.add_edge(0, 1, 2)
g.add_edge(0, 1, 3, layer="layer1")
g.add_edge(0, 1, 5, layer="layer1")
g.add_edge(0, 1, 6, layer="layer1")
g.add_edge(0, 1, 4, layer="layer2")

assert g.default_layer().count_edges() == 1
assert g.layers(["layer1"]).count_edges() == 1
assert g.layers(["layer1"]).count_edges() == 3
assert g.layers(["layer2"]).count_edges() == 1

assert g.exclude_layers(["layer1"]).count_edges() == 2
assert g.exclude_layers(["layer1", "layer2"]).count_edges() == 1


def test_layer_node():
g = Graph()
Expand Down Expand Up @@ -1606,6 +1611,16 @@ def test_subgraph():
x.sort()
assert x == ["prop 1", "prop 2", "prop 3", "prop 4", "prop 5", "prop 6"]

def test_exclude_nodes():
g = create_graph()
exclude_nodes = g.exclude_nodes([1])
assert exclude_nodes.nodes.id.collect() == [2, 3]

def test_nbr():
g = create_graph()
r = [e.nbr.name for e in g.edges]
r.sort()
assert r == ['1', '1', '2', '2', '3']

def test_materialize_graph():
g = Graph()
Expand Down Expand Up @@ -1929,6 +1944,17 @@ def test_one_hop_filter_reset():
assert len(out_out_2) == 0


def test_node_types():
g = Graph()
g.add_node(1, 1, node_type="wallet")
g.add_node(1, 2, node_type="timer")
g.add_node(1, 3, node_type="timer")
g.add_node(1, 4, node_type="wallet")

assert g.nodes.type_filter(["wallet"]).node_type.collect() == ['1', '4']
assert g.subgraph_node_types(["timer"]).nodes.name.collect() == ['2', '3']


def test_time_exploded_edges():
g = Graph()
g.add_edge(1, 1, 2)
Expand Down
47 changes: 46 additions & 1 deletion raphtory/src/core/entities/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ impl From<usize> for EID {
}

pub(crate) enum VRef<'a> {
Entry(Entry<'a, NodeStore>), // returned from graph.node
Entry(Entry<'a, NodeStore>),
// returned from graph.node
LockedEntry(GraphEntry<NodeStore>), // returned from locked_nodes
}

Expand Down Expand Up @@ -155,6 +156,50 @@ impl LayerIds {
}
}

pub fn diff<'a>(
&self,
graph: impl crate::prelude::GraphViewOps<'a>,
other: &LayerIds,
) -> LayerIds {
match (self, other) {
(LayerIds::None, _) => LayerIds::None,
(this, LayerIds::None) => this.clone(),
(this, LayerIds::All) => LayerIds::None,
(LayerIds::One(id), other) => {
if other.contains(id) {
LayerIds::None
} else {
LayerIds::One(*id)
}
}
(LayerIds::Multiple(ids), other) => {
let ids: Vec<usize> = ids
.iter()
.filter(|id| !other.contains(id))
.copied()
.collect();
match ids.len() {
0 => LayerIds::None,
1 => LayerIds::One(ids[0]),
_ => LayerIds::Multiple(ids.into()),
}
}
(LayerIds::All, other) => {
let all_layer_ids: Vec<usize> = graph
.unique_layers()
.map(|name| graph.get_layer_id(name.as_ref()).unwrap())
.into_iter()
.filter(|id| !other.contains(id))
.collect();
match all_layer_ids.len() {
0 => LayerIds::None,
1 => LayerIds::One(all_layer_ids[0]),
_ => LayerIds::Multiple(all_layer_ids.into()),
}
}
}
}

pub fn constrain_from_edge(&self, e: EdgeRef) -> LayerIds {
match e.layer() {
None => self.clone(),
Expand Down
93 changes: 90 additions & 3 deletions raphtory/src/db/api/view/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,22 @@ use crate::{
view::{internal::*, *},
},
graph::{
edge::EdgeView, edges::Edges, node::NodeView, nodes::Nodes,
views::node_subgraph::NodeSubgraph,
edge::EdgeView,
edges::Edges,
node::NodeView,
nodes::Nodes,
views::{
node_subgraph::NodeSubgraph, node_type_filtered_subgraph::TypeFilteredSubgraph,
},
},
},
prelude::{DeletionOps, NO_PROPS},
};
use chrono::{DateTime, Utc};
use itertools::Itertools;
use rayon::prelude::*;
use rustc_hash::FxHashSet;
use std::sync::Arc;
use std::{borrow::Borrow, sync::Arc};

/// This trait GraphViewOps defines operations for accessing
/// information about a graph. The trait has associated types
Expand All @@ -45,8 +51,20 @@ pub trait GraphViewOps<'graph>: BoxableGraphView + Sized + Clone + 'graph {
/// Returns:
/// Graph - Returns clone of the graph
fn materialize(&self) -> Result<MaterializedGraph, GraphError>;

fn subgraph<I: IntoIterator<Item = V>, V: Into<NodeRef>>(&self, nodes: I)
-> NodeSubgraph<Self>;

fn subgraph_node_types<I: IntoIterator<Item = V>, V: Borrow<str>>(
&self,
nodes_types: I,
) -> TypeFilteredSubgraph<Self>;

fn exclude_nodes<I: IntoIterator<Item = V>, V: Into<NodeRef>>(
&self,
nodes: I,
) -> NodeSubgraph<Self>;

/// Return all the layer ids in the graph
fn unique_layers(&self) -> BoxedIter<ArcStr>;
/// Timestamp of earliest activity in the graph
Expand Down Expand Up @@ -178,6 +196,7 @@ impl<'graph, G: BoxableGraphView + Sized + Clone + 'graph> GraphViewOps<'graph>

Ok(self.new_base_graph(g))
}

fn subgraph<I: IntoIterator<Item = V>, V: Into<NodeRef>>(&self, nodes: I) -> NodeSubgraph<G> {
let _layer_ids = self.layer_ids();
let nodes: FxHashSet<VID> = nodes
Expand All @@ -187,6 +206,39 @@ impl<'graph, G: BoxableGraphView + Sized + Clone + 'graph> GraphViewOps<'graph>
NodeSubgraph::new(self.clone(), nodes)
}

fn subgraph_node_types<I: IntoIterator<Item = V>, V: Borrow<str>>(
&self,
nodes_types: I,
) -> TypeFilteredSubgraph<Self> {
let meta = self.node_meta().node_type_meta();
let r = nodes_types
.into_iter()
.flat_map(|nt| meta.get_id(nt.borrow()))
.collect_vec();
TypeFilteredSubgraph::new(self.clone(), r)
}

fn exclude_nodes<I: IntoIterator<Item = V>, V: Into<NodeRef>>(
&self,
nodes: I,
) -> NodeSubgraph<G> {
let _layer_ids = self.layer_ids();

let nodes_to_exclude: FxHashSet<VID> = nodes
.into_iter()
.flat_map(|v| (&self).node(v).map(|v| v.node))
.collect();

let nodes_to_include = self
.nodes()
.into_iter()
.filter(|node| !nodes_to_exclude.contains(&node.node))
.map(|node| node.node)
.collect();

NodeSubgraph::new(self.clone(), nodes_to_include)
}

/// Return all the layer ids in the graph
fn unique_layers(&self) -> BoxedIter<ArcStr> {
self.get_layer_names_from_ids(self.layer_ids())
Expand Down Expand Up @@ -412,6 +464,41 @@ mod test_materialize {
.contains("layer1"));
}

#[test]
fn test_subgraph() {
let g = Graph::new();
g.add_node(0, 1, NO_PROPS, None).unwrap();
g.add_node(0, 2, NO_PROPS, None).unwrap();
g.add_node(0, 3, NO_PROPS, None).unwrap();
g.add_node(0, 4, NO_PROPS, None).unwrap();
g.add_node(0, 5, NO_PROPS, None).unwrap();

let nodes_subgraph = g.subgraph(vec![4, 5]);
assert_eq!(
nodes_subgraph.nodes().name().collect::<Vec<String>>(),
vec!["4", "5"]
);
}

#[test]
fn test_exclude_nodes() {
let g = Graph::new();
g.add_node(0, 1, NO_PROPS, None).unwrap();
g.add_node(0, 2, NO_PROPS, None).unwrap();
g.add_node(0, 3, NO_PROPS, None).unwrap();
g.add_node(0, 4, NO_PROPS, None).unwrap();
g.add_node(0, 5, NO_PROPS, None).unwrap();

let exclude_nodes_subgraph = g.exclude_nodes(vec![4, 5]);
assert_eq!(
exclude_nodes_subgraph
.nodes()
.name()
.collect::<Vec<String>>(),
vec!["1", "2", "3"]
);
}

#[test]
fn testing_node_types() {
let g = Graph::new();
Expand Down
20 changes: 20 additions & 0 deletions raphtory/src/db/api/view/layer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ pub trait LayerOps<'graph> {
/// Return a graph containing the layers in `names`. Errors if one or more of the layers do not exists.
fn layers<L: Into<Layer>>(&self, names: L) -> Result<Self::LayeredViewType, GraphError>;

/// Return a graph containing the excluded layers in `names`. Errors if one or more of the layers do not exists.
fn exclude_layers<L: Into<Layer>>(
&self,
layers: L,
) -> Result<Self::LayeredViewType, GraphError>;

/// Check if `name` is a valid layer name
fn has_layer(&self, name: &str) -> bool;

Expand All @@ -40,6 +46,20 @@ impl<'graph, V: OneHopFilter<'graph> + 'graph> LayerOps<'graph> for V {
Ok(self.one_hop_filtered(LayeredGraph::new(self.current_filter().clone(), ids)))
}

fn exclude_layers<L: Into<Layer>>(
&self,
layers: L,
) -> Result<Self::LayeredViewType, GraphError> {
let all_layer_ids = self.current_filter().layer_ids();
let excluded_ids = self.current_filter().layer_ids_from_names(layers.into())?;
let included_ids = all_layer_ids.diff(self.current_filter().clone(), &excluded_ids);

Ok(self.one_hop_filtered(LayeredGraph::new(
self.current_filter().clone(),
included_ids,
)))
}

fn has_layer(&self, name: &str) -> bool {
!self
.current_filter()
Expand Down
2 changes: 2 additions & 0 deletions raphtory/src/db/api/view/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod graph;
pub(crate) mod internal;
mod layer;
pub(crate) mod node;
mod node_types_filter;
pub(crate) mod time;

pub(crate) use edge::BaseEdgeViewOps;
Expand All @@ -17,6 +18,7 @@ pub use internal::{
pub use layer::*;
pub(crate) use node::BaseNodeViewOps;
pub use node::NodeViewOps;
pub use node_types_filter::*;
pub use time::*;

pub type BoxedIter<T> = Box<dyn Iterator<Item = T> + Send>;
Expand Down
19 changes: 19 additions & 0 deletions raphtory/src/db/api/view/node_types_filter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use crate::db::{
api::view::internal::{CoreGraphOps, OneHopFilter},
graph::views::node_type_filtered_subgraph::TypeFilteredSubgraph,
};
use std::borrow::Borrow;

pub trait NodeTypesFilter<'graph>: OneHopFilter<'graph> {
fn type_filter<I: IntoIterator<Item = V>, V: Borrow<str>>(
&self,
node_types: I,
) -> Self::Filtered<TypeFilteredSubgraph<Self::FilteredGraph>> {
let meta = self.current_filter().node_meta().node_type_meta();
let r = node_types
.into_iter()
.flat_map(|nt| meta.get_id(nt.borrow()))
.collect();
self.one_hop_filtered(TypeFilteredSubgraph::new(self.current_filter().clone(), r))
}
}
4 changes: 4 additions & 0 deletions raphtory/src/db/graph/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -899,6 +899,10 @@ mod db_tests {
assert!(g.layers("layer2").unwrap().edge(11, 22).is_none());
assert!(g.layers("layer2").unwrap().edge(11, 44).is_some());

assert!(g.exclude_layers("layer2").unwrap().edge(11, 44).is_none());
assert!(g.exclude_layers("layer2").unwrap().edge(11, 33).is_some());
assert!(g.exclude_layers("layer2").unwrap().edge(11, 22).is_some());

let dft_layer = g.default_layer();
let layer1 = g.layers("layer1").expect("layer1");
let layer2 = g.layers("layer2").expect("layer2");
Expand Down
8 changes: 8 additions & 0 deletions raphtory/src/db/graph/nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::{
view::{
internal::{OneHopFilter, Static},
BaseNodeViewOps, BoxedLIter, DynamicGraph, IntoDynBoxed, IntoDynamic,
NodeTypesFilter,
},
},
graph::{edges::NestedEdges, node::NodeView, path::PathFromGraph},
Expand All @@ -14,6 +15,7 @@ use crate::{
};

use crate::db::api::storage::locked::LockedGraph;
use itertools::Itertools;
use rayon::iter::ParallelIterator;
use std::{marker::PhantomData, sync::Arc};

Expand Down Expand Up @@ -60,6 +62,7 @@ impl<'graph, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph>> Nodes<'graph, G,
let g = self.graph.core_graph();
g.into_nodes_iter(self.graph.clone())
}

pub fn iter(&self) -> BoxedLIter<'graph, NodeView<G, GH>> {
let base_graph = self.base_graph.clone();
let g = self.graph.clone();
Expand Down Expand Up @@ -190,6 +193,11 @@ impl<'graph, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph>> OneHopFilter<'gr
}
}

impl<'graph, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph>> NodeTypesFilter<'graph>
for Nodes<'graph, G, GH>
{
}

impl<'graph, G: GraphViewOps<'graph> + 'graph, GH: GraphViewOps<'graph> + 'graph> IntoIterator
for Nodes<'graph, G, GH>
{
Expand Down
10 changes: 10 additions & 0 deletions raphtory/src/db/graph/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,16 @@ impl<'graph, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph>> OneHopFilter<'gr
}
}

impl<'graph, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph>> NodeTypesFilter<'graph>
for PathFromGraph<'graph, G, GH>
{
}

impl<'graph, G: GraphViewOps<'graph>, GH: GraphViewOps<'graph>> NodeTypesFilter<'graph>
for PathFromNode<'graph, G, GH>
{
}

#[cfg(test)]
mod test {
use crate::prelude::*;
Expand Down
1 change: 1 addition & 0 deletions raphtory/src/db/graph/views/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod deletion_graph;
pub mod layer_graph;
pub mod node_subgraph;
pub mod node_type_filtered_subgraph;
pub mod window_graph;
Loading

0 comments on commit c398538

Please sign in to comment.