From d9e7e74a97be0bc12a904faab2dfa0d990b9dda1 Mon Sep 17 00:00:00 2001 From: Anthony Mahanna Date: Tue, 15 Oct 2024 15:03:13 -0400 Subject: [PATCH 1/9] wip: `mirror_crud_to_nxcg` --- nx_arangodb/classes/dict/adj.py | 2 +- nx_arangodb/classes/digraph.py | 35 ++++- nx_arangodb/classes/function.py | 12 ++ nx_arangodb/classes/graph.py | 200 +++++++++++++++++----------- nx_arangodb/classes/multidigraph.py | 21 ++- nx_arangodb/classes/multigraph.py | 82 +++++++----- 6 files changed, 238 insertions(+), 114 deletions(-) diff --git a/nx_arangodb/classes/dict/adj.py b/nx_arangodb/classes/dict/adj.py index 7126897..b1b2ddc 100644 --- a/nx_arangodb/classes/dict/adj.py +++ b/nx_arangodb/classes/dict/adj.py @@ -1815,7 +1815,7 @@ def propagate_edge_directed_symmetric( set_adj_inner_dict_mirror(src_node_id) set_adj_inner_dict_mirror(dst_node_id) - edge_attr_or_key_dict = set_edge_func( # type: ignore[operator] + edge_attr_or_key_dict = set_edge_func( src_node_id, dst_node_id, edge_or_edges ) diff --git a/nx_arangodb/classes/digraph.py b/nx_arangodb/classes/digraph.py index 3bfb943..26c00eb 100644 --- a/nx_arangodb/classes/digraph.py +++ b/nx_arangodb/classes/digraph.py @@ -9,7 +9,7 @@ from .dict.adj import AdjListOuterDict from .enum import TraversalDirection -from .function import get_node_id +from .function import get_node_id, mirror_to_nxcg networkx_api = nxadb.utils.decorators.networkx_class(nx.DiGraph) # type: ignore @@ -161,6 +161,7 @@ def __init__( symmetrize_edges: bool = False, use_arango_views: bool = False, overwrite_graph: bool = False, + mirror_crud_to_nxcg: bool = False, *args: Any, **kwargs: Any, ): @@ -179,16 +180,23 @@ def __init__( symmetrize_edges, use_arango_views, overwrite_graph, + mirror_crud_to_nxcg, *args, **kwargs, ) if self.graph_exists_in_db: self.clear_edges = self.clear_edges_override + self.reverse = self.reverse_override + self.add_node = self.add_node_override self.add_nodes_from = self.add_nodes_from_override self.remove_node = self.remove_node_override - self.reverse = self.reverse_override + self.remove_nodes_from = self.remove_nodes_from_override + self.add_edge = self.add_edge_override + self.add_edges_from = self.add_edges_from_override + self.remove_edge = self.remove_edge_override + self.remove_edges_from = self.remove_edges_from_override assert isinstance(self._succ, AdjListOuterDict) assert isinstance(self._pred, AdjListOuterDict) @@ -234,6 +242,7 @@ def clear_edges_override(self): super().clear_edges() + @mirror_to_nxcg def add_node_override(self, node_for_adding, **attr): if node_for_adding is None: raise ValueError("None cannot be a node") @@ -269,6 +278,7 @@ def add_node_override(self, node_for_adding, **attr): nx._clear_cache(self) + @mirror_to_nxcg def add_nodes_from_override(self, nodes_for_adding, **attr): for n in nodes_for_adding: try: @@ -312,6 +322,7 @@ def add_nodes_from_override(self, nodes_for_adding, **attr): nx._clear_cache(self) + @mirror_to_nxcg def remove_node_override(self, n): if isinstance(n, (str, int)): n = get_node_id(str(n), self.default_node_type) @@ -361,3 +372,23 @@ def remove_node_override(self, n): del self._succ[u][n] # remove all edges n-u in digraph del self._pred[n] # remove node from pred nx._clear_cache(self) + + @mirror_to_nxcg + def remove_nodes_from_override(self, nodes): + nx.DiGraph.remove_nodes_from(self, nodes) + + @mirror_to_nxcg + def add_edge_override(self, u, v, **attr): + nx.DiGraph.add_edge(self, u, v, **attr) + + @mirror_to_nxcg + def add_edges_from_override(self, ebunch_to_add, **attr): + nx.DiGraph.add_edges_from(self, ebunch_to_add, **attr) + + @mirror_to_nxcg + def remove_edge_override(self, u, v): + nx.DiGraph.remove_edge(self, u, v) + + @mirror_to_nxcg + def remove_edges_from_override(self, ebunch): + nx.DiGraph.remove_edges_from(self, ebunch) diff --git a/nx_arangodb/classes/function.py b/nx_arangodb/classes/function.py index 491c0cd..4b9078c 100644 --- a/nx_arangodb/classes/function.py +++ b/nx_arangodb/classes/function.py @@ -6,6 +6,7 @@ from __future__ import annotations +from functools import wraps from typing import Any, Callable, Generator, Tuple import networkx as nx @@ -932,3 +933,14 @@ def upsert_collection_edges( ) return results + + +def mirror_to_nxcg(func): + @wraps(func) + def wrapper(self, *args, **kwargs): + result = func(self, *args, **kwargs) + if self.mirror_to_nxcg and self.nxcg_graph is not None: + getattr(self.nxcg_graph, func.__name__)(*args, **kwargs) + return result + + return wrapper diff --git a/nx_arangodb/classes/graph.py b/nx_arangodb/classes/graph.py index 336cc3b..6728b1c 100644 --- a/nx_arangodb/classes/graph.py +++ b/nx_arangodb/classes/graph.py @@ -29,7 +29,7 @@ node_attr_dict_factory, node_dict_factory, ) -from .function import get_node_id +from .function import get_node_id, mirror_to_nxcg from .reportviews import ArangoEdgeView, ArangoNodeView networkx_api = nxadb.utils.decorators.networkx_class(nx.Graph) # type: ignore @@ -195,12 +195,14 @@ def __init__( symmetrize_edges: bool = False, use_arango_views: bool = False, overwrite_graph: bool = False, + mirror_crud_to_nxcg: bool = False, *args: Any, **kwargs: Any, ): self.__db = None self.__use_arango_views = use_arango_views self.__graph_exists_in_db = False + self.__mirror_crud_to_nxcg = mirror_crud_to_nxcg self.__set_db(db) if all([self.__db, name]): @@ -261,11 +263,19 @@ def __init__( self.subgraph = self.subgraph_override self.clear = self.clear_override self.clear_edges = self.clear_edges_override - self.add_node = self.add_node_override - self.add_nodes_from = self.add_nodes_from_override self.number_of_edges = self.number_of_edges_override self.nbunch_iter = self.nbunch_iter_override + self.add_node = self.add_node_override + self.add_nodes_from = self.add_nodes_from_override + self.remove_node = self.remove_node_override + self.remove_nodes_from = self.remove_nodes_from_override + self.add_edge = self.add_edge_override + self.add_edges_from = self.add_edges_from_override + self.remove_edge = self.remove_edge_override + self.remove_edges_from = self.remove_edges_from_override + self.update = self.update_override + # If incoming_graph_data wasn't loaded by the NetworkX Adapter, # then we can rely on the CRUD operations of the modified dictionaries # to load the data into the graph. However, if the graph is directed @@ -541,6 +551,10 @@ def is_smart(self) -> bool: def smart_field(self) -> str | None: return self.__smart_field + @property + def mirror_crud_to_nxcg(self) -> bool: + return self.__mirror_crud_to_nxcg + ########### # Setters # ########### @@ -691,81 +705,6 @@ def clear_edges_override(self): nbr_dict.clear() nx._clear_cache(self) - def add_node_override(self, node_for_adding, **attr): - if node_for_adding is None: - raise ValueError("None cannot be a node") - - if node_for_adding not in self._node: - self._adj[node_for_adding] = self.adjlist_inner_dict_factory() - - ###################### - # NOTE: monkey patch # - ###################### - - # Old: - # attr_dict = self._node[node_for_adding] = self.node_attr_dict_factory() - # attr_dict.update(attr) - - # New: - node_attr_dict = self.node_attr_dict_factory() - node_attr_dict.data = attr - self._node[node_for_adding] = node_attr_dict - - # Reason: - # We can optimize the process of adding a node by creating avoiding - # the creation of a new dictionary and updating it with the attributes. - # Instead, we can create a new node_attr_dict object and set the attributes - # directly. This only makes 1 network call to the database instead of 2. - - ########################### - - else: - self._node[node_for_adding].update(attr) - - nx._clear_cache(self) - - def add_nodes_from_override(self, nodes_for_adding, **attr): - for n in nodes_for_adding: - try: - newnode = n not in self._node - newdict = attr - except TypeError: - n, ndict = n - newnode = n not in self._node - newdict = attr.copy() - newdict.update(ndict) - if newnode: - if n is None: - raise ValueError("None cannot be a node") - self._adj[n] = self.adjlist_inner_dict_factory() - - ###################### - # NOTE: monkey patch # - ###################### - - # Old: - # self._node[n] = self.node_attr_dict_factory() - # - # self._node[n].update(newdict) - - # New: - node_attr_dict = self.node_attr_dict_factory() - node_attr_dict.data = newdict - self._node[n] = node_attr_dict - - else: - self._node[n].update(newdict) - - # Reason: - # We can optimize the process of adding a node by creating avoiding - # the creation of a new dictionary and updating it with the attributes. - # Instead, we create a new node_attr_dict object and set the attributes - # directly. This only makes 1 network call to the database instead of 2. - - ########################### - - nx._clear_cache(self) - def number_of_edges_override(self, u=None, v=None): if u is not None: return super().number_of_edges(u, v) @@ -847,3 +786,108 @@ def bunch_iter(nlist, adj): bunch = bunch_iter(nbunch, self._adj) return bunch + + @mirror_to_nxcg + def add_node_override(self, node_for_adding, **attr): + if node_for_adding is None: + raise ValueError("None cannot be a node") + + if node_for_adding not in self._node: + self._adj[node_for_adding] = self.adjlist_inner_dict_factory() + + ###################### + # NOTE: monkey patch # + ###################### + + # Old: + # attr_dict = self._node[node_for_adding] = self.node_attr_dict_factory() + # attr_dict.update(attr) + + # New: + node_attr_dict = self.node_attr_dict_factory() + node_attr_dict.data = attr + self._node[node_for_adding] = node_attr_dict + + # Reason: + # We can optimize the process of adding a node by creating avoiding + # the creation of a new dictionary and updating it with the attributes. + # Instead, we can create a new node_attr_dict object and set the attributes + # directly. This only makes 1 network call to the database instead of 2. + + ########################### + + else: + self._node[node_for_adding].update(attr) + + nx._clear_cache(self) + + @mirror_to_nxcg + def add_nodes_from_override(self, nodes_for_adding, **attr): + for n in nodes_for_adding: + try: + newnode = n not in self._node + newdict = attr + except TypeError: + n, ndict = n + newnode = n not in self._node + newdict = attr.copy() + newdict.update(ndict) + if newnode: + if n is None: + raise ValueError("None cannot be a node") + self._adj[n] = self.adjlist_inner_dict_factory() + + ###################### + # NOTE: monkey patch # + ###################### + + # Old: + # self._node[n] = self.node_attr_dict_factory() + # + # self._node[n].update(newdict) + + # New: + node_attr_dict = self.node_attr_dict_factory() + node_attr_dict.data = newdict + self._node[n] = node_attr_dict + + else: + self._node[n].update(newdict) + + # Reason: + # We can optimize the process of adding a node by creating avoiding + # the creation of a new dictionary and updating it with the attributes. + # Instead, we create a new node_attr_dict object and set the attributes + # directly. This only makes 1 network call to the database instead of 2. + + ########################### + + nx._clear_cache(self) + + @mirror_to_nxcg + def remove_node_override(self, n): + super().remove_node(n) + + @mirror_to_nxcg + def remove_nodes_from_override(self, nodes): + super().remove_nodes_from(nodes) + + @mirror_to_nxcg + def add_edge_override(self, u, v, **attr): + super().add_edge(u, v, **attr) + + @mirror_to_nxcg + def add_edges_from_override(self, ebunch_to_add, **attr): + super().add_edges_from(ebunch_to_add, **attr) + + @mirror_to_nxcg + def remove_edge_override(self, u, v): + super().remove_edge(u, v) + + @mirror_to_nxcg + def remove_edges_from_override(self, ebunch): + super().remove_edges_from(ebunch) + + @mirror_to_nxcg + def update_override(self, *args, **kwargs): + super().update(*args, **kwargs) diff --git a/nx_arangodb/classes/multidigraph.py b/nx_arangodb/classes/multidigraph.py index f115ab8..d0a3b5c 100644 --- a/nx_arangodb/classes/multidigraph.py +++ b/nx_arangodb/classes/multidigraph.py @@ -8,6 +8,8 @@ from nx_arangodb.classes.digraph import DiGraph from nx_arangodb.classes.multigraph import MultiGraph +from .function import mirror_to_nxcg + networkx_api = nxadb.utils.decorators.networkx_class(nx.MultiDiGraph) # type: ignore __all__ = ["MultiDiGraph"] @@ -172,6 +174,7 @@ def __init__( symmetrize_edges: bool = False, use_arango_views: bool = False, overwrite_graph: bool = False, + mirror_crud_to_nxcg: bool = False, *args: Any, **kwargs: Any, ): @@ -191,6 +194,7 @@ def __init__( symmetrize_edges, use_arango_views, overwrite_graph, + mirror_crud_to_nxcg, *args, **kwargs, ) @@ -199,6 +203,9 @@ def __init__( self.reverse = self.reverse_override self.to_undirected = self.to_undirected_override + self.add_edge = self.add_edge_override + self.remove_edge = self.remove_edge_override + ####################### # Init helper methods # ####################### @@ -211,11 +218,13 @@ def reverse_override(self, copy: bool = True) -> Any: if copy is False: raise NotImplementedError("In-place reverse is not supported yet.") - return super().reverse(copy=True) + return nx.MultiDiGraph.reverse(self, copy=True) def to_undirected_override(self, reciprocal=False, as_view=False): if reciprocal is False: - return super().to_undirected(reciprocal=False, as_view=as_view) + return nx.MultiDiGraph.to_undirected( + self, reciprocal=False, as_view=as_view + ) graph_class = self.to_undirected_class() if as_view is True: @@ -257,3 +266,11 @@ def to_undirected_override(self, reciprocal=False, as_view=False): ########################### return G + + @mirror_to_nxcg + def add_edge_override(self, u, v, key=None, **attr): + nx.MultiDiGraph.add_edge(self, u, v, key, **attr) + + @mirror_to_nxcg + def remove_edge_override(self, u, v, key=None): + nx.MultiDiGraph.remove_edge(self, u, v, key) diff --git a/nx_arangodb/classes/multigraph.py b/nx_arangodb/classes/multigraph.py index c494d34..96e097f 100644 --- a/nx_arangodb/classes/multigraph.py +++ b/nx_arangodb/classes/multigraph.py @@ -8,6 +8,7 @@ from nx_arangodb.logger import logger from .dict import edge_key_dict_factory +from .function import mirror_to_nxcg networkx_api = nxadb.utils.decorators.networkx_class(nx.MultiGraph) # type: ignore @@ -173,6 +174,7 @@ def __init__( symmetrize_edges: bool = False, use_arango_views: bool = False, overwrite_graph: bool = False, + mirror_crud_to_nxcg: bool = False, *args: Any, **kwargs: Any, ): @@ -191,15 +193,20 @@ def __init__( symmetrize_edges, use_arango_views, overwrite_graph, + mirror_crud_to_nxcg, *args, **kwargs, ) if self.graph_exists_in_db: - self.add_edge = self.add_edge_override self.has_edge = self.has_edge_override self.copy = self.copy_override + self.add_edge = self.add_edge_override + self.add_edges_from = self.add_edges_from_override + self.remove_edge = self.remove_edge_override + self.remove_edges_from = self.remove_edges_from_override + if incoming_graph_data is not None and not self._loaded_incoming_graph_data: # Taken from networkx.MultiGraph.__init__ if isinstance(incoming_graph_data, dict) and multigraph_input is not False: @@ -230,7 +237,7 @@ def __init__( ####################### def _set_factory_methods(self) -> None: - super()._set_factory_methods() + Graph._set_factory_methods(self) self.edge_key_dict_factory = edge_key_dict_factory( self.db, self.adb_graph, @@ -243,34 +250,6 @@ def _set_factory_methods(self) -> None: # nx.MultiGraph Overides # ########################## - def add_edge_override(self, u_for_edge, v_for_edge, key=None, **attr): - if key is not None: - m = "ArangoDB MultiGraph does not support custom edge keys yet." - logger.warning(m) - - _ = super().add_edge(u_for_edge, v_for_edge, key="-1", **attr) - - ###################### - # NOTE: monkey patch # - ###################### - - # Old: - # return key - - # New: - keys = list(self._adj[u_for_edge][v_for_edge].data.keys()) - last_key = keys[-1] - return last_key - - # Reason: - # nxadb.MultiGraph does not yet support the ability to work - # with custom edge keys. As a Database, we must rely on the official - # ArangoDB Edge _id to uniquely identify edges. The EdgeKeyDict.__setitem__ - # method will be responsible for setting the edge key to the _id of the edge - # document. This will allow us to use the edge key as a unique identifier - - ########################### - def has_edge_override(self, u, v, key=None): try: if key is None: @@ -299,6 +278,47 @@ def has_edge_override(self, u, v, key=None): def copy_override(self, *args, **kwargs): logger.warning("Note that copying a graph loses the connection to the database") - G = super().copy(*args, **kwargs) + G = Graph.copy(self, *args, **kwargs) G.edge_key_dict_factory = nx.MultiGraph.edge_key_dict_factory return G + + @mirror_to_nxcg + def add_edge_override(self, u_for_edge, v_for_edge, key=None, **attr): + if key is not None: + m = "ArangoDB MultiGraph does not support custom edge keys yet." + logger.warning(m) + + _ = nx.MultiGraph.add_edge(self, u_for_edge, v_for_edge, key="-1", **attr) + + ###################### + # NOTE: monkey patch # + ###################### + + # Old: + # return key + + # New: + keys = list(self._adj[u_for_edge][v_for_edge].data.keys()) + last_key = keys[-1] + return last_key + + # Reason: + # nxadb.MultiGraph does not yet support the ability to work + # with custom edge keys. As a Database, we must rely on the official + # ArangoDB Edge _id to uniquely identify edges. The EdgeKeyDict.__setitem__ + # method will be responsible for setting the edge key to the _id of the edge + # document. This will allow us to use the edge key as a unique identifier + + ########################### + + @mirror_to_nxcg + def add_edges_from_override(self, ebunch_to_add, **attr): + nx.MultiGraph.add_edges_from(self, ebunch_to_add, **attr) + + @mirror_to_nxcg + def remove_edge_override(self, u, v): + nx.MultiGraph.remove_edge(self, u, v) + + @mirror_to_nxcg + def remove_edges_from_override(self, ebunch): + nx.MultiGraph.remove_edges_from(self, ebunch) From 6e292c62b8c3c68fb82de4746209b039ad167e03 Mon Sep 17 00:00:00 2001 From: Anthony Mahanna Date: Tue, 15 Oct 2024 15:08:13 -0400 Subject: [PATCH 2/9] fix: typo --- nx_arangodb/classes/function.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nx_arangodb/classes/function.py b/nx_arangodb/classes/function.py index 4b9078c..3c6e107 100644 --- a/nx_arangodb/classes/function.py +++ b/nx_arangodb/classes/function.py @@ -939,7 +939,7 @@ def mirror_to_nxcg(func): @wraps(func) def wrapper(self, *args, **kwargs): result = func(self, *args, **kwargs) - if self.mirror_to_nxcg and self.nxcg_graph is not None: + if self.mirror_crud_to_nxcg and self.nxcg_graph is not None: getattr(self.nxcg_graph, func.__name__)(*args, **kwargs) return result From 4cac6a3ab552d576ed94ffdd4860f27c4a698d74 Mon Sep 17 00:00:00 2001 From: Anthony Mahanna Date: Tue, 15 Oct 2024 16:06:36 -0400 Subject: [PATCH 3/9] use `super()` --- nx_arangodb/classes/digraph.py | 10 +++++----- nx_arangodb/classes/multidigraph.py | 18 +++--------------- nx_arangodb/classes/multigraph.py | 12 ++++++------ 3 files changed, 14 insertions(+), 26 deletions(-) diff --git a/nx_arangodb/classes/digraph.py b/nx_arangodb/classes/digraph.py index 26c00eb..64a7249 100644 --- a/nx_arangodb/classes/digraph.py +++ b/nx_arangodb/classes/digraph.py @@ -375,20 +375,20 @@ def remove_node_override(self, n): @mirror_to_nxcg def remove_nodes_from_override(self, nodes): - nx.DiGraph.remove_nodes_from(self, nodes) + super().remove_nodes_from(nodes) @mirror_to_nxcg def add_edge_override(self, u, v, **attr): - nx.DiGraph.add_edge(self, u, v, **attr) + super().add_edge(u, v, **attr) @mirror_to_nxcg def add_edges_from_override(self, ebunch_to_add, **attr): - nx.DiGraph.add_edges_from(self, ebunch_to_add, **attr) + super().add_edges_from(ebunch_to_add, **attr) @mirror_to_nxcg def remove_edge_override(self, u, v): - nx.DiGraph.remove_edge(self, u, v) + super().remove_edge(u, v) @mirror_to_nxcg def remove_edges_from_override(self, ebunch): - nx.DiGraph.remove_edges_from(self, ebunch) + super().remove_edges_from(ebunch) diff --git a/nx_arangodb/classes/multidigraph.py b/nx_arangodb/classes/multidigraph.py index d0a3b5c..790b426 100644 --- a/nx_arangodb/classes/multidigraph.py +++ b/nx_arangodb/classes/multidigraph.py @@ -7,6 +7,7 @@ import nx_arangodb as nxadb from nx_arangodb.classes.digraph import DiGraph from nx_arangodb.classes.multigraph import MultiGraph +from nx_arangodb.logger import logger from .function import mirror_to_nxcg @@ -203,9 +204,6 @@ def __init__( self.reverse = self.reverse_override self.to_undirected = self.to_undirected_override - self.add_edge = self.add_edge_override - self.remove_edge = self.remove_edge_override - ####################### # Init helper methods # ####################### @@ -218,13 +216,11 @@ def reverse_override(self, copy: bool = True) -> Any: if copy is False: raise NotImplementedError("In-place reverse is not supported yet.") - return nx.MultiDiGraph.reverse(self, copy=True) + return super().reverse(copy=True) def to_undirected_override(self, reciprocal=False, as_view=False): if reciprocal is False: - return nx.MultiDiGraph.to_undirected( - self, reciprocal=False, as_view=as_view - ) + return super().to_undirected(reciprocal=False, as_view=as_view) graph_class = self.to_undirected_class() if as_view is True: @@ -266,11 +262,3 @@ def to_undirected_override(self, reciprocal=False, as_view=False): ########################### return G - - @mirror_to_nxcg - def add_edge_override(self, u, v, key=None, **attr): - nx.MultiDiGraph.add_edge(self, u, v, key, **attr) - - @mirror_to_nxcg - def remove_edge_override(self, u, v, key=None): - nx.MultiDiGraph.remove_edge(self, u, v, key) diff --git a/nx_arangodb/classes/multigraph.py b/nx_arangodb/classes/multigraph.py index 96e097f..39f5a59 100644 --- a/nx_arangodb/classes/multigraph.py +++ b/nx_arangodb/classes/multigraph.py @@ -237,7 +237,7 @@ def __init__( ####################### def _set_factory_methods(self) -> None: - Graph._set_factory_methods(self) + super()._set_factory_methods(self) self.edge_key_dict_factory = edge_key_dict_factory( self.db, self.adb_graph, @@ -288,7 +288,7 @@ def add_edge_override(self, u_for_edge, v_for_edge, key=None, **attr): m = "ArangoDB MultiGraph does not support custom edge keys yet." logger.warning(m) - _ = nx.MultiGraph.add_edge(self, u_for_edge, v_for_edge, key="-1", **attr) + _ = super().add_edge(u_for_edge, v_for_edge, key="-1", **attr) ###################### # NOTE: monkey patch # @@ -313,12 +313,12 @@ def add_edge_override(self, u_for_edge, v_for_edge, key=None, **attr): @mirror_to_nxcg def add_edges_from_override(self, ebunch_to_add, **attr): - nx.MultiGraph.add_edges_from(self, ebunch_to_add, **attr) + super().add_edges_from(ebunch_to_add, **attr) @mirror_to_nxcg - def remove_edge_override(self, u, v): - nx.MultiGraph.remove_edge(self, u, v) + def remove_edge_override(self, u, v, key=None): + super().remove_edge(u, v, key) @mirror_to_nxcg def remove_edges_from_override(self, ebunch): - nx.MultiGraph.remove_edges_from(self, ebunch) + super().remove_edges_from(ebunch) From 4436b503f28cfb13c259da712dbc1a83895117bb Mon Sep 17 00:00:00 2001 From: Anthony Mahanna Date: Tue, 15 Oct 2024 16:16:53 -0400 Subject: [PATCH 4/9] fix: typo --- nx_arangodb/classes/multigraph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nx_arangodb/classes/multigraph.py b/nx_arangodb/classes/multigraph.py index 39f5a59..3f65b8d 100644 --- a/nx_arangodb/classes/multigraph.py +++ b/nx_arangodb/classes/multigraph.py @@ -237,7 +237,7 @@ def __init__( ####################### def _set_factory_methods(self) -> None: - super()._set_factory_methods(self) + super()._set_factory_methods() self.edge_key_dict_factory = edge_key_dict_factory( self.db, self.adb_graph, From 5f1c9025255b308428bd0cf202e2aa095dfa7bb0 Mon Sep 17 00:00:00 2001 From: Anthony Mahanna Date: Tue, 15 Oct 2024 16:27:44 -0400 Subject: [PATCH 5/9] fix: `mirror_to_nxcg` --- nx_arangodb/classes/function.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/nx_arangodb/classes/function.py b/nx_arangodb/classes/function.py index 3c6e107..ef5ef1b 100644 --- a/nx_arangodb/classes/function.py +++ b/nx_arangodb/classes/function.py @@ -940,7 +940,12 @@ def mirror_to_nxcg(func): def wrapper(self, *args, **kwargs): result = func(self, *args, **kwargs) if self.mirror_crud_to_nxcg and self.nxcg_graph is not None: - getattr(self.nxcg_graph, func.__name__)(*args, **kwargs) + if "_override" not in func.__name__: + m = f"Function '{func.__name__}' is not an override function." + raise ValueError(m) + + func_name = func.__name__.replace("_override", "") + getattr(self.nxcg_graph, func_name)(*args, **kwargs) return result return wrapper From 601ec4949331801901606b2b7658acd881ec84ba Mon Sep 17 00:00:00 2001 From: Anthony Mahanna Date: Tue, 15 Oct 2024 16:34:31 -0400 Subject: [PATCH 6/9] fix: use `super` --- nx_arangodb/classes/multigraph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nx_arangodb/classes/multigraph.py b/nx_arangodb/classes/multigraph.py index 3f65b8d..b8e36e8 100644 --- a/nx_arangodb/classes/multigraph.py +++ b/nx_arangodb/classes/multigraph.py @@ -278,7 +278,7 @@ def has_edge_override(self, u, v, key=None): def copy_override(self, *args, **kwargs): logger.warning("Note that copying a graph loses the connection to the database") - G = Graph.copy(self, *args, **kwargs) + G = super().copy(*args, **kwargs) G.edge_key_dict_factory = nx.MultiGraph.edge_key_dict_factory return G From 9c65e17fdc9ea9908b8c55b6b360134ee47ab87d Mon Sep 17 00:00:00 2001 From: Anthony Mahanna Date: Tue, 15 Oct 2024 18:10:31 -0400 Subject: [PATCH 7/9] cleanup: remove unnecessary overrides --- nx_arangodb/classes/digraph.py | 20 -------------------- nx_arangodb/classes/multigraph.py | 8 -------- 2 files changed, 28 deletions(-) diff --git a/nx_arangodb/classes/digraph.py b/nx_arangodb/classes/digraph.py index 64a7249..223caa1 100644 --- a/nx_arangodb/classes/digraph.py +++ b/nx_arangodb/classes/digraph.py @@ -372,23 +372,3 @@ def remove_node_override(self, n): del self._succ[u][n] # remove all edges n-u in digraph del self._pred[n] # remove node from pred nx._clear_cache(self) - - @mirror_to_nxcg - def remove_nodes_from_override(self, nodes): - super().remove_nodes_from(nodes) - - @mirror_to_nxcg - def add_edge_override(self, u, v, **attr): - super().add_edge(u, v, **attr) - - @mirror_to_nxcg - def add_edges_from_override(self, ebunch_to_add, **attr): - super().add_edges_from(ebunch_to_add, **attr) - - @mirror_to_nxcg - def remove_edge_override(self, u, v): - super().remove_edge(u, v) - - @mirror_to_nxcg - def remove_edges_from_override(self, ebunch): - super().remove_edges_from(ebunch) diff --git a/nx_arangodb/classes/multigraph.py b/nx_arangodb/classes/multigraph.py index b8e36e8..a195287 100644 --- a/nx_arangodb/classes/multigraph.py +++ b/nx_arangodb/classes/multigraph.py @@ -311,14 +311,6 @@ def add_edge_override(self, u_for_edge, v_for_edge, key=None, **attr): ########################### - @mirror_to_nxcg - def add_edges_from_override(self, ebunch_to_add, **attr): - super().add_edges_from(ebunch_to_add, **attr) - @mirror_to_nxcg def remove_edge_override(self, u, v, key=None): super().remove_edge(u, v, key) - - @mirror_to_nxcg - def remove_edges_from_override(self, ebunch): - super().remove_edges_from(ebunch) From 3c8cfe41c15505d8c4fc7bae5d90a36f403b6514 Mon Sep 17 00:00:00 2001 From: Anthony Mahanna Date: Tue, 15 Oct 2024 18:12:38 -0400 Subject: [PATCH 8/9] more cleanup --- nx_arangodb/classes/digraph.py | 5 ----- nx_arangodb/classes/multigraph.py | 2 -- 2 files changed, 7 deletions(-) diff --git a/nx_arangodb/classes/digraph.py b/nx_arangodb/classes/digraph.py index 223caa1..8ebf039 100644 --- a/nx_arangodb/classes/digraph.py +++ b/nx_arangodb/classes/digraph.py @@ -192,11 +192,6 @@ def __init__( self.add_node = self.add_node_override self.add_nodes_from = self.add_nodes_from_override self.remove_node = self.remove_node_override - self.remove_nodes_from = self.remove_nodes_from_override - self.add_edge = self.add_edge_override - self.add_edges_from = self.add_edges_from_override - self.remove_edge = self.remove_edge_override - self.remove_edges_from = self.remove_edges_from_override assert isinstance(self._succ, AdjListOuterDict) assert isinstance(self._pred, AdjListOuterDict) diff --git a/nx_arangodb/classes/multigraph.py b/nx_arangodb/classes/multigraph.py index a195287..7fef1a2 100644 --- a/nx_arangodb/classes/multigraph.py +++ b/nx_arangodb/classes/multigraph.py @@ -203,9 +203,7 @@ def __init__( self.copy = self.copy_override self.add_edge = self.add_edge_override - self.add_edges_from = self.add_edges_from_override self.remove_edge = self.remove_edge_override - self.remove_edges_from = self.remove_edges_from_override if incoming_graph_data is not None and not self._loaded_incoming_graph_data: # Taken from networkx.MultiGraph.__init__ From 423a7b28a6a2b1b91fe4d875157c4f951c5d205c Mon Sep 17 00:00:00 2001 From: Anthony Mahanna Date: Wed, 16 Oct 2024 22:21:17 -0400 Subject: [PATCH 9/9] new: `mirror_crud_to_nxcg` docstring --- nx_arangodb/classes/digraph.py | 9 +++++++++ nx_arangodb/classes/graph.py | 9 +++++++++ nx_arangodb/classes/multidigraph.py | 9 +++++++++ nx_arangodb/classes/multigraph.py | 9 +++++++++ 4 files changed, 36 insertions(+) diff --git a/nx_arangodb/classes/digraph.py b/nx_arangodb/classes/digraph.py index 8ebf039..c247bcf 100644 --- a/nx_arangodb/classes/digraph.py +++ b/nx_arangodb/classes/digraph.py @@ -131,6 +131,15 @@ class DiGraph(Graph, nx.DiGraph): this operation is irreversible and will result in the loss of all data in the graph. NOTE: If set to True, Collection Indexes will also be lost. + mirror_crud_to_nxcg : bool (optional, default: False) + Whether to mirror any CRUD operations performed on the NetworkX-ArangoDB Graph + to the cached NetworkX-cuGraph Graph (if available). This allows you to maintain + an up-to-date in-memory NetworkX-cuGraph graph while performing CRUD operations + on the NetworkX-ArangoDB Graph. NOTE: The first time you perform a CRUD + operation on the NetworkX-ArangoDB Graph with an existing NetworkX-cuGraph cache + will require downtime to copy the NetworkX-cuGraph Graph from GPU memory to CPU + memory. Subsequent CRUD operations will not require this downtime. + args: positional arguments for nx.Graph Additional arguments passed to nx.Graph. diff --git a/nx_arangodb/classes/graph.py b/nx_arangodb/classes/graph.py index 6728b1c..a112dd8 100644 --- a/nx_arangodb/classes/graph.py +++ b/nx_arangodb/classes/graph.py @@ -165,6 +165,15 @@ class Graph(nx.Graph): this operation is irreversible and will result in the loss of all data in the graph. NOTE: If set to True, Collection Indexes will also be lost. + mirror_crud_to_nxcg : bool (optional, default: False) + Whether to mirror any CRUD operations performed on the NetworkX-ArangoDB Graph + to the cached NetworkX-cuGraph Graph (if available). This allows you to maintain + an up-to-date in-memory NetworkX-cuGraph graph while performing CRUD operations + on the NetworkX-ArangoDB Graph. NOTE: The first time you perform a CRUD + operation on the NetworkX-ArangoDB Graph with an existing NetworkX-cuGraph cache + will require downtime to copy the NetworkX-cuGraph Graph from GPU memory to CPU + memory. Subsequent CRUD operations will not require this downtime. + args: positional arguments for nx.Graph Additional arguments passed to nx.Graph. diff --git a/nx_arangodb/classes/multidigraph.py b/nx_arangodb/classes/multidigraph.py index 790b426..2f06942 100644 --- a/nx_arangodb/classes/multidigraph.py +++ b/nx_arangodb/classes/multidigraph.py @@ -144,6 +144,15 @@ class MultiDiGraph(MultiGraph, DiGraph, nx.MultiDiGraph): this operation is irreversible and will result in the loss of all data in the graph. NOTE: If set to True, Collection Indexes will also be lost. + mirror_crud_to_nxcg : bool (optional, default: False) + Whether to mirror any CRUD operations performed on the NetworkX-ArangoDB Graph + to the cached NetworkX-cuGraph Graph (if available). This allows you to maintain + an up-to-date in-memory NetworkX-cuGraph graph while performing CRUD operations + on the NetworkX-ArangoDB Graph. NOTE: The first time you perform a CRUD + operation on the NetworkX-ArangoDB Graph with an existing NetworkX-cuGraph cache + will require downtime to copy the NetworkX-cuGraph Graph from GPU memory to CPU + memory. Subsequent CRUD operations will not require this downtime. + args: positional arguments for nx.Graph Additional arguments passed to nx.Graph. diff --git a/nx_arangodb/classes/multigraph.py b/nx_arangodb/classes/multigraph.py index 7fef1a2..0eebd43 100644 --- a/nx_arangodb/classes/multigraph.py +++ b/nx_arangodb/classes/multigraph.py @@ -143,6 +143,15 @@ class MultiGraph(Graph, nx.MultiGraph): this operation is irreversible and will result in the loss of all data in the graph. NOTE: If set to True, Collection Indexes will also be lost. + mirror_crud_to_nxcg : bool (optional, default: False) + Whether to mirror any CRUD operations performed on the NetworkX-ArangoDB Graph + to the cached NetworkX-cuGraph Graph (if available). This allows you to maintain + an up-to-date in-memory NetworkX-cuGraph graph while performing CRUD operations + on the NetworkX-ArangoDB Graph. NOTE: The first time you perform a CRUD + operation on the NetworkX-ArangoDB Graph with an existing NetworkX-cuGraph cache + will require downtime to copy the NetworkX-cuGraph Graph from GPU memory to CPU + memory. Subsequent CRUD operations will not require this downtime. + args: positional arguments for nx.Graph Additional arguments passed to nx.Graph.