Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/excludeapis #1561

Merged
merged 8 commits into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@
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::db::api::storage::locked::LockedGraph;
use itertools::Itertools;

Check warning on line 18 in raphtory/src/db/graph/nodes.rs

View workflow job for this annotation

GitHub Actions / Run benchmarks / Rust Benchmark (ubuntu-latest)

unused import: `itertools::Itertools`
use rayon::iter::ParallelIterator;
use std::{marker::PhantomData, sync::Arc};

Expand Down Expand Up @@ -60,6 +62,7 @@
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>> 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
Loading