{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Delete Strategies\n", "\n", "This notebook demonstrates the three deletion strategies supported by `deleteNode()`:\n", "\n", "- **`\"cascade\"`** (default): ON DELETE CASCADE — delete the node and all connected edges.\n", "- **`\"restrict\"`**: ON DELETE RESTRICT — refuse to delete if the node has connected edges.\n", "- **`\"set_null\"`**: ON DELETE SET NULL — delete the node and its edges, but keep neighbor nodes.\n", "\n", "It also covers cascade delete through `WeakNode` hierarchies via the `propagation` flag." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import importlib.util\n", "\n", "package_to_check = 'drm'\n", "spec = importlib.util.find_spec(package_to_check)\n", "\n", "if spec is None:\n", " print(f'⚠️ {package_to_check} no està instal·lat. Iniciant instal·lació...')\n", " %pip install -q --upgrade drm-tools\n", " print(\"✅ Instal·lació completada. L'estat del kernel PODRIA requerir un reinici.\")\n", "else:\n", " print(f'✅ {package_to_check} ja està present al sistema. Saltant instal·lació.')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from drm import NetworkXGraph, Node, WeakNode, Relation\n", "\n", "graph = NetworkXGraph()\n", "\n", "# Create a small graph:\n", "# Alice --WROTE--> Paper1 --CITES--> Paper2\n", "# Bob --WROTE--> Paper1\n", "#\n", "# Document1\n", "# |--CONTAINS--> Section1 (WeakNode)\n", "# |--CONTAINS--> Page1 (WeakNode of Section1)\n", "\n", "doc = Node(pk={\"doc\": \"DOC-001\"}, main_label=\"Document\")\n", "section = WeakNode(parent=doc, pk={\"section\": 1}, main_label=\"Section\")\n", "page = WeakNode(parent=section, pk={\"page\": 1}, main_label=\"Page\")\n", "\n", "alice = Node(pk={\"name\": \"Alice\"}, main_label=\"Author\")\n", "bob = Node(pk={\"name\": \"Bob\"}, main_label=\"Author\")\n", "paper1 = Node(pk={\"title\": \"P1\"}, main_label=\"Paper\")\n", "paper2 = Node(pk={\"title\": \"P2\"}, main_label=\"Paper\")\n", "\n", "# Insert all nodes\n", "graph.insertNode(doc)\n", "graph.insertNode(section, insert_parent=True)\n", "graph.insertNode(page, insert_parent=True)\n", "graph.insertNode(alice)\n", "graph.insertNode(bob)\n", "graph.insertNode(paper1)\n", "graph.insertNode(paper2)\n", "\n", "# Create relations\n", "graph.insertRelation(Relation(alice, paper1, \"WROTE\"))\n", "graph.insertRelation(Relation(bob, paper1, \"WROTE\"))\n", "graph.insertRelation(Relation(paper1, paper2, \"CITES\"))\n", "\n", "def show_state(label):\n", " print(f\"\\n=== {label} ===\")\n", " print(f\"Nodes: {graph.get_node_ids()}\")\n", " print(f\"Edges: {graph.get_edges()}\")\n", "\n", "show_state(\"Initial state\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Strategy 1: CASCADE (default)\n", "\n", "`on_delete=\"cascade\"` deletes the node and all connected edges.\n", "Neighbor nodes remain as standalone entities." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"Before deleting paper1:\")\n", "show_state(\"Before CASCADE delete of paper1\")\n", "\n", "# Delete paper1 with cascade (default)\n", "result = graph.deleteNode(paper1, on_delete=\"cascade\")\n", "print(f\"deleteNode returned: {result}\")\n", "\n", "print(\"\\nAfter deleting paper1 (CASCADE):\")\n", "show_state(\"After CASCADE delete of paper1\")\n", "print(\"Note: Alice, Bob, and paper2 still exist, but edges are gone.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Strategy 2: RESTRICT\n", "\n", "`on_delete=\"restrict\"` refuses to delete if the node has any connected edges.\n", "This is useful for enforcing referential integrity." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"Before trying to delete alice (who has edges):\")\n", "show_state(\"Before RESTRICT delete attempt on alice\")\n", "\n", "try:\n", " graph.deleteNode(alice, on_delete=\"restrict\")\n", " print(\"ERROR: Should have raised RuntimeError!\")\n", "except RuntimeError as e:\n", " print(f\"Caught expected error: {e}\")\n", "\n", "print(\"\\nGraph unchanged (alice was not deleted):\")\n", "show_state(\"After RESTRICT attempt (unchanged)\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Strategy 3: SET NULL\n", "\n", "`on_delete=\"set_null\"` deletes the node and its edges, but keeps\n", "neighbor nodes as standalone entities. Functionally similar to cascade\n", "for the graph structure, but with a different semantic meaning." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"Before deleting bob:\")\n", "show_state(\"Before SET NULL delete of bob\")\n", "\n", "result = graph.deleteNode(bob, on_delete=\"set_null\")\n", "print(f\"deleteNode returned: {result}\")\n", "\n", "print(\"\\nAfter deleting bob (SET NULL):\")\n", "show_state(\"After SET NULL delete of bob\")\n", "print(\"Note: alice and paper1 still exist, edges removed.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Cascade delete through WeakNode hierarchies\n", "\n", "When `propagation=True`, deleting a parent node also deletes all\n", "child WeakNodes connected via edges with `_propagate=True`.\n", "This is the default behavior for WeakNode relations." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Reset: create a fresh graph with a WeakNode hierarchy\n", "graph2 = NetworkXGraph()\n", "\n", "doc2 = Node(pk={\"doc\": \"DOC-002\"}, main_label=\"Document\")\n", "section2 = WeakNode(parent=doc2, pk={\"section\": 1}, main_label=\"Section\")\n", "page2 = WeakNode(parent=section2, pk={\"page\": 1}, main_label=\"Page\")\n", "\n", "graph2.insertNode(doc2)\n", "graph2.insertNode(section2, insert_parent=True)\n", "graph2.insertNode(page2, insert_parent=True)\n", "\n", "print(\"Before deleting the document:\")\n", "show_state(\"Before cascade delete of Document\")\n", "print(\"Hierarchy: Document -> Section -> Page\")\n", "\n", "# Delete the parent document with propagation\n", "# This cascades to Section and Page\n", "result = graph2.deleteNode(doc2, propagation=True, detach=True)\n", "print(f\"\\ndeleteNode returned: {result}\")\n", "\n", "print(\"\\nAfter deleting Document with propagation=True:\")\n", "show_state(\"After cascade delete of Document\")\n", "print(\"All nodes (Document, Section, Page) were deleted.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Summary table\n", "\n", "| Strategy | Deletes node | Deletes edges | Keeps neighbors | Raises on edges |\n", "|---|---|---|---|---|\n", "| `\"cascade\"` (default) | ✅ | ✅ | ✅ (standalone) | ❌ |\n", "| `\"restrict\"` | ❌ | ❌ | — | ✅ (RuntimeError) |\n", "| `\"set_null\"` | ✅ | ✅ | ✅ (standalone) | ❌ |\n", "\n", "With `propagation=True`, cascade/delete also removes child WeakNodes." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Cleanup" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "graph.close()\n", "graph2.close()\n", "print(\"All graphs closed.\")" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "name": "python", "version": "3.10.0" } }, "nbformat": 4, "nbformat_minor": 4 }