{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# GraphStore: Neo4j Database Basic Examples\n",
    "\n",
    "This notebook demonstrates how to use the `Neo4jGraphStore` in `dapr-agents` for basic graph-based tasks. We will explore:\n",
    "\n",
    "* Initializing the `Neo4jGraphStore` class.\n",
    "* Adding sample nodes.\n",
    "* Adding one sample relationship.\n",
    "* Querying graph database.\n",
    "* Resseting database."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Install Required Libraries\n",
    "\n",
    "Ensure dapr_agents and neo4j are installed:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip install dapr-agents python-dotenv neo4j"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Import Environment Variables\n",
    "\n",
    "Load your API keys or other configuration values using `dotenv`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from dotenv import load_dotenv\n",
    "load_dotenv()  # Load environment variables from a `.env` file"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Enable Logging"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import logging\n",
    "\n",
    "logging.basicConfig(level=logging.INFO)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Deploy Neo4j Graph Database as Docker Container"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#docker run \\\n",
    "#--restart always \\\n",
    "#--publish=7474:7474 --publish=7687:7687 \\\n",
    "#--env NEO4J_AUTH=neo4j/graphwardog \\\n",
    "#--volume=neo4j-data \\\n",
    "#--name neo4j-apoc \\\n",
    "#--env NEO4J_apoc_export_file_enabled=true \\\n",
    "#--env NEO4J_apoc_import_file_enabled=true \\\n",
    "#--env NEO4J_apoc_import_file_use__neo4j__config=true \\\n",
    "#--env NEO4J_PLUGINS=\\[\\\"apoc\\\"\\] \\\n",
    "#neo4j:latest"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Initialize Neo4jGraphStore\n",
    "\n",
    "Set the `NEO4J_URI`, `NEO4J_USERNAME` and `NEO4J_PASSWORD` variables in a `.env` file. The URI can be set to `bolt://localhost:7687`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:dapr_agents.storage.graphstores.neo4j.client:Successfully created the driver for URI: bolt://localhost:7687\n",
      "INFO:dapr_agents.storage.graphstores.neo4j.base:Neo4jGraphStore initialized with database neo4j\n"
     ]
    }
   ],
   "source": [
    "from dapr_agents.storage.graphstores.neo4j import Neo4jGraphStore\n",
    "import os\n",
    "\n",
    "# Initialize Neo4jGraphStore\n",
    "graph_store = Neo4jGraphStore(\n",
    "    uri=os.getenv(\"NEO4J_URI\"),\n",
    "    user=os.getenv(\"NEO4J_USERNAME\"),\n",
    "    password=os.getenv(\"NEO4J_PASSWORD\"),\n",
    "    database=\"neo4j\"\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:dapr_agents.storage.graphstores.neo4j.client:Connected to Neo4j Kernel version 5.15.0 (community edition)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Neo4j connection successful\n"
     ]
    }
   ],
   "source": [
    "# Test the connection\n",
    "assert graph_store.client.test_connection(), \"Connection to Neo4j failed\"\n",
    "print(\"Neo4j connection successful\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Add Sample Nodes\n",
    "Create and add nodes to the graph store:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:dapr_agents.storage.graphstores.neo4j.base:Processed batch 1/1\n",
      "INFO:dapr_agents.storage.graphstores.neo4j.base:Nodes with label `Person` added successfully.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Nodes added successfully\n"
     ]
    }
   ],
   "source": [
    "from dapr_agents.types import Node\n",
    "\n",
    "# Sample nodes\n",
    "nodes = [\n",
    "    Node(\n",
    "        id=\"1\",\n",
    "        label=\"Person\",\n",
    "        properties={\"name\": \"Alice\", \"age\": 30},\n",
    "        additional_labels=[\"Employee\"]\n",
    "    ),\n",
    "    Node(\n",
    "        id=\"2\",\n",
    "        label=\"Person\",\n",
    "        properties={\"name\": \"Bob\", \"age\": 25},\n",
    "        additional_labels=[\"Contractor\"]\n",
    "    )\n",
    "]\n",
    "\n",
    "# Add nodes\n",
    "graph_store.add_nodes(nodes)\n",
    "print(\"Nodes added successfully\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Add Sample Relationship\n",
    "Create and add a relationship to the graph store:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:dapr_agents.storage.graphstores.neo4j.base:Processed batch 1/1\n",
      "INFO:neo4j.notifications:Received notification from DBMS server: {severity: INFORMATION} {code: Neo.ClientNotification.Statement.CartesianProduct} {category: PERFORMANCE} {title: This query builds a cartesian product between disconnected patterns.} {description: If a part of a query contains multiple disconnected patterns, this will build a cartesian product between all those parts. This may produce a large amount of data and slow down query processing. While occasionally intended, it may often be possible to reformulate the query that avoids the use of this cross product, perhaps by adding a relationship between the different parts or by using OPTIONAL MATCH (identifier is: (b))} {position: line: 3, column: 25, offset: 45} for query: '\\n            UNWIND $data AS rel\\n            MATCH (a {id: rel.source_node_id}), (b {id: rel.target_node_id})\\n            MERGE (a)-[r:`KNOWS`]->(b)\\n            ON CREATE SET r.createdAt = rel.current_time\\n            SET r.updatedAt = rel.current_time, r += rel.properties\\n            RETURN r\\n            '\n",
      "INFO:dapr_agents.storage.graphstores.neo4j.base:Relationships of type `KNOWS` added successfully.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Relationships added successfully\n"
     ]
    }
   ],
   "source": [
    "from dapr_agents.types import Relationship\n",
    "\n",
    "# Sample relationships\n",
    "relationships = [\n",
    "    Relationship(\n",
    "        source_node_id=\"1\",\n",
    "        target_node_id=\"2\",\n",
    "        type=\"KNOWS\",\n",
    "        properties={\"since\": \"2023\"}\n",
    "    )\n",
    "]\n",
    "\n",
    "# Add relationships\n",
    "graph_store.add_relationships(relationships)\n",
    "print(\"Relationships added successfully\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Query Graph"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:dapr_agents.storage.graphstores.neo4j.base:Query executed successfully: MATCH (n) RETURN n | Time: 0.05 seconds | Results: 2\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Nodes in the database:\n",
      "{'n': {'createdAt': '2025-01-25T23:17:18.044482Z', 'name': 'Alice', 'id': '1', 'age': 30, 'updatedAt': '2025-01-25T23:17:18.044482Z'}}\n",
      "{'n': {'createdAt': '2025-01-25T23:17:18.044482Z', 'name': 'Bob', 'id': '2', 'age': 25, 'updatedAt': '2025-01-25T23:17:18.044482Z'}}\n"
     ]
    }
   ],
   "source": [
    "query = \"MATCH (n) RETURN n\"\n",
    "results = graph_store.query(query)\n",
    "print(\"Nodes in the database:\")\n",
    "for record in results:\n",
    "    print(record)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:dapr_agents.storage.graphstores.neo4j.base:Query executed successfully: \n",
      "MATCH (a)-[r]->(b)\n",
      "RETURN a.id AS source, b.id AS target, type(r) AS type, properties(r) AS properties\n",
      " | Time: 0.10 seconds | Results: 1\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Relationships in the database:\n",
      "{'source': '1', 'target': '2', 'type': 'KNOWS', 'properties': {'updatedAt': '2025-01-25T23:17:22.428873Z', 'createdAt': '2025-01-25T23:17:22.428873Z', 'since': '2023'}}\n"
     ]
    }
   ],
   "source": [
    "query = \"\"\"\n",
    "MATCH (a)-[r]->(b)\n",
    "RETURN a.id AS source, b.id AS target, type(r) AS type, properties(r) AS properties\n",
    "\"\"\"\n",
    "results = graph_store.query(query)\n",
    "print(\"Relationships in the database:\")\n",
    "for record in results:\n",
    "    print(record)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:dapr_agents.storage.graphstores.neo4j.base:Query executed successfully: \n",
      "MATCH (n)-[r]->(m)\n",
      "RETURN n, r, m\n",
      " | Time: 0.05 seconds | Results: 1\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Nodes and relationships in the database:\n",
      "{'n': {'createdAt': '2025-01-25T23:17:18.044482Z', 'name': 'Alice', 'id': '1', 'age': 30, 'updatedAt': '2025-01-25T23:17:18.044482Z'}, 'r': ({'createdAt': '2025-01-25T23:17:18.044482Z', 'name': 'Alice', 'id': '1', 'age': 30, 'updatedAt': '2025-01-25T23:17:18.044482Z'}, 'KNOWS', {'createdAt': '2025-01-25T23:17:18.044482Z', 'name': 'Bob', 'id': '2', 'age': 25, 'updatedAt': '2025-01-25T23:17:18.044482Z'}), 'm': {'createdAt': '2025-01-25T23:17:18.044482Z', 'name': 'Bob', 'id': '2', 'age': 25, 'updatedAt': '2025-01-25T23:17:18.044482Z'}}\n"
     ]
    }
   ],
   "source": [
    "query = \"\"\"\n",
    "MATCH (n)-[r]->(m)\n",
    "RETURN n, r, m\n",
    "\"\"\"\n",
    "results = graph_store.query(query)\n",
    "print(\"Nodes and relationships in the database:\")\n",
    "for record in results:\n",
    "    print(record)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Reset Graph"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:dapr_agents.storage.graphstores.neo4j.base:Database reset successfully\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Graph database has been reset.\n"
     ]
    }
   ],
   "source": [
    "graph_store.reset()\n",
    "print(\"Graph database has been reset.\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:dapr_agents.storage.graphstores.neo4j.base:Query executed successfully: MATCH (n) RETURN n | Time: 0.01 seconds | Results: 0\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Nodes in the database:\n"
     ]
    }
   ],
   "source": [
    "query = \"MATCH (n) RETURN n\"\n",
    "results = graph_store.query(query)\n",
    "print(\"Nodes in the database:\")\n",
    "for record in results:\n",
    "    print(record)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
