Delete Strategies

This notebook demonstrates the three deletion strategies supported by deleteNode():

  • ``”cascade”`` (default): ON DELETE CASCADE — delete the node and all connected edges.

  • ``”restrict”``: ON DELETE RESTRICT — refuse to delete if the node has connected edges.

  • ``”set_null”``: ON DELETE SET NULL — delete the node and its edges, but keep neighbor nodes.

It also covers cascade delete through WeakNode hierarchies via the propagation flag.

[ ]:
import importlib.util

package_to_check = 'drm'
spec = importlib.util.find_spec(package_to_check)

if spec is None:
    print(f'⚠️ {package_to_check} no està instal·lat. Iniciant instal·lació...')
    %pip install -q --upgrade drm-tools
    print("✅ Instal·lació completada. L'estat del kernel PODRIA requerir un reinici.")
else:
    print(f'✅ {package_to_check} ja està present al sistema. Saltant instal·lació.')
[ ]:
from drm import NetworkXGraph, Node, WeakNode, Relation

graph = NetworkXGraph()

# Create a small graph:
#   Alice --WROTE--> Paper1 --CITES--> Paper2
#   Bob    --WROTE--> Paper1
#
#   Document1
#     |--CONTAINS--> Section1 (WeakNode)
#                        |--CONTAINS--> Page1 (WeakNode of Section1)

doc = Node(pk={"doc": "DOC-001"}, main_label="Document")
section = WeakNode(parent=doc, pk={"section": 1}, main_label="Section")
page = WeakNode(parent=section, pk={"page": 1}, main_label="Page")

alice = Node(pk={"name": "Alice"}, main_label="Author")
bob = Node(pk={"name": "Bob"}, main_label="Author")
paper1 = Node(pk={"title": "P1"}, main_label="Paper")
paper2 = Node(pk={"title": "P2"}, main_label="Paper")

# Insert all nodes
graph.insertNode(doc)
graph.insertNode(section, insert_parent=True)
graph.insertNode(page, insert_parent=True)
graph.insertNode(alice)
graph.insertNode(bob)
graph.insertNode(paper1)
graph.insertNode(paper2)

# Create relations
graph.insertRelation(Relation(alice, paper1, "WROTE"))
graph.insertRelation(Relation(bob, paper1, "WROTE"))
graph.insertRelation(Relation(paper1, paper2, "CITES"))

def show_state(label):
    print(f"\n=== {label} ===")
    print(f"Nodes: {graph.get_node_ids()}")
    print(f"Edges: {graph.get_edges()}")

show_state("Initial state")

Strategy 1: CASCADE (default)

on_delete="cascade" deletes the node and all connected edges. Neighbor nodes remain as standalone entities.

[ ]:
print("Before deleting paper1:")
show_state("Before CASCADE delete of paper1")

# Delete paper1 with cascade (default)
result = graph.deleteNode(paper1, on_delete="cascade")
print(f"deleteNode returned: {result}")

print("\nAfter deleting paper1 (CASCADE):")
show_state("After CASCADE delete of paper1")
print("Note: Alice, Bob, and paper2 still exist, but edges are gone.")

Strategy 2: RESTRICT

on_delete="restrict" refuses to delete if the node has any connected edges. This is useful for enforcing referential integrity.

[ ]:
print("Before trying to delete alice (who has edges):")
show_state("Before RESTRICT delete attempt on alice")

try:
    graph.deleteNode(alice, on_delete="restrict")
    print("ERROR: Should have raised RuntimeError!")
except RuntimeError as e:
    print(f"Caught expected error: {e}")

print("\nGraph unchanged (alice was not deleted):")
show_state("After RESTRICT attempt (unchanged)")

Strategy 3: SET NULL

on_delete="set_null" deletes the node and its edges, but keeps neighbor nodes as standalone entities. Functionally similar to cascade for the graph structure, but with a different semantic meaning.

[ ]:
print("Before deleting bob:")
show_state("Before SET NULL delete of bob")

result = graph.deleteNode(bob, on_delete="set_null")
print(f"deleteNode returned: {result}")

print("\nAfter deleting bob (SET NULL):")
show_state("After SET NULL delete of bob")
print("Note: alice and paper1 still exist, edges removed.")

Cascade delete through WeakNode hierarchies

When propagation=True, deleting a parent node also deletes all child WeakNodes connected via edges with _propagate=True. This is the default behavior for WeakNode relations.

[ ]:
# Reset: create a fresh graph with a WeakNode hierarchy
graph2 = NetworkXGraph()

doc2 = Node(pk={"doc": "DOC-002"}, main_label="Document")
section2 = WeakNode(parent=doc2, pk={"section": 1}, main_label="Section")
page2 = WeakNode(parent=section2, pk={"page": 1}, main_label="Page")

graph2.insertNode(doc2)
graph2.insertNode(section2, insert_parent=True)
graph2.insertNode(page2, insert_parent=True)

print("Before deleting the document:")
show_state("Before cascade delete of Document")
print("Hierarchy: Document -> Section -> Page")

# Delete the parent document with propagation
# This cascades to Section and Page
result = graph2.deleteNode(doc2, propagation=True, detach=True)
print(f"\ndeleteNode returned: {result}")

print("\nAfter deleting Document with propagation=True:")
show_state("After cascade delete of Document")
print("All nodes (Document, Section, Page) were deleted.")

Summary table

Strategy

Deletes node

Deletes edges

Keeps neighbors

Raises on edges

"cascade" (default)

✅ (standalone)

"restrict"

✅ (RuntimeError)

"set_null"

✅ (standalone)

With propagation=True, cascade/delete also removes child WeakNodes.

Cleanup

[ ]:
graph.close()
graph2.close()
print("All graphs closed.")