{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Amazon Neptune Graph Store\n",
    "\n",
    "## Overview\n",
    "\n",
    "This notebook covers the Amazon Neptune Database integration in Semantica. Amazon Neptune is a fully managed graph database service that supports both property graphs (via OpenCypher/Gremlin) and RDF graphs (via SPARQL).\n",
    "\n",
    "### Key Features\n",
    "\n",
    "- **IAM Authentication**: Secure access using AWS SigV4 signatures via AuthManager\n",
    "- **OpenCypher Support**: Query using standard OpenCypher syntax\n",
    "- **Bolt Protocol**: Uses Neo4j Bolt driver for efficient binary communication\n",
    "- **Native ~id Support**: Leverages Neptune's native element ID handling\n",
    "- **Full CRUD Operations**: Create, read, update, delete nodes and relationships\n",
    "- **Automatic Retry**: Built-in retry logic with exponential backoff for transient errors\n",
    "\n",
    "### Prerequisites\n",
    "\n",
    "- An Amazon Neptune Database cluster\n",
    "- AWS credentials configured (boto3, environment variables, or IAM role)\n",
    "- Network access to your Neptune cluster (VPC, security groups)\n",
    "\n",
    "#### Quick Setup with CloudFormation\n",
    "\n",
    "If you don't have a Neptune cluster, use the provided CloudFormation template to create one with a public endpoint and IAM authentication:\n",
    "\n",
    "```bash\n",
    "# Deploy the Neptune stack (takes ~15-20 minutes)\n",
    "aws cloudformation create-stack \\\n",
    "  --stack-name semantica-neptune \\\n",
    "  --template-body file://neptune-setup.yaml \\\n",
    "  --capabilities CAPABILITY_NAMED_IAM\n",
    "\n",
    "# Wait for stack creation to complete\n",
    "aws cloudformation wait stack-create-complete --stack-name semantica-neptune\n",
    "\n",
    "# Get the outputs (endpoint, port, credentials)\n",
    "aws cloudformation describe-stacks --stack-name semantica-neptune \\\n",
    "  --query 'Stacks[0].Outputs' --output table\n",
    "```\n",
    "\n",
    "The template creates:\n",
    "- VPC with public subnets and Internet Gateway\n",
    "- Neptune cluster (`db.t3.medium`) with IAM authentication enabled\n",
    "- IAM user with least-privilege access for OpenCypher queries\n",
    "- Security group allowing Bolt protocol (port 8182) access\n",
    "\n",
    "> ⚠️ **Security Note**: This template creates an IAM User with static access keys for simplicity in demo/test environments. For production use, we recommend IAM Roles (EC2 instance roles, ECS task roles, Lambda execution roles) which provide temporary credentials that are automatically rotated. The secret access key in the Cloudformation outputs is provided in plaintext to simplify initial setup - in production, use AWS Secrets Manager.\n",
    "\n",
    "**Outputs:**\n",
    "- `NeptuneEndpoint` - Cluster hostname (use as `NEPTUNE_ENDPOINT`)\n",
    "- `NeptunePort` - 8182 (use as `NEPTUNE_PORT`)\n",
    "- `AwsAccessKeyId` - IAM user access key (use as `AWS_ACCESS_KEY_ID`)\n",
    "- `AwsSecretAccessKey` - IAM user secret key in **plaintext** (use as `AWS_SECRET_ACCESS_KEY`)\n",
    "- `AwsRegion` - Deployment region (use as `AWS_REGION`)\n",
    "\n",
    "**Cleanup:**\n",
    "```bash\n",
    "aws cloudformation delete-stack --stack-name semantica-neptune\n",
    "```\n",
    "\n",
    "**Estimated Monthly Cost (approximately 100-105 USD/month at 100% utilization):**\n",
    "\n",
    "| Resource | Cost (USD) |\n",
    "| --- | --- |\n",
    "| Neptune db.t3.medium instance | ~96/month (0.132/hr) |\n",
    "| Storage (10 GB) | ~1/month |\n",
    "| I/O requests | ~1-5/month |\n",
    "| Public IPv4 address | ~3.60/month (0.005/hr) |\n",
    "| VPC, subnets, route tables, Internet Gateway, IAM | No Additional Charge |\n",
    "\n",
    "> **Free Tier**: New Neptune users get 30 days free (750 hours of db.t3.medium, 10M I/Os, 1 GB storage). Delete the stack when not in use to avoid charges.\n",
    "\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Installation\n",
    "\n",
    "```bash\n",
    "# Install Semantica with Neptune support\n",
    "pip install semantica\n",
    "\n",
    "# Required dependencies (installed automatically)\n",
    "pip install boto3 neo4j\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip install semantica"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Configuration\n",
    "\n",
    "Set your Neptune cluster endpoint and AWS credentials. Replace the placeholder values with your actual configuration."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "\n",
    "# Neptune cluster configuration - REPLACE WITH YOUR VALUES\n",
    "# (Get these from CloudFormation stack outputs)\n",
    "os.environ[\"NEPTUNE_ENDPOINT\"] = \"your-cluster.us-east-1.neptune.amazonaws.com\"\n",
    "os.environ[\"NEPTUNE_PORT\"] = \"8182\"\n",
    "os.environ[\"AWS_REGION\"] = \"us-east-1\"\n",
    "\n",
    "# AWS credentials for IAM Authentication\n",
    "# Option 1: IAM User (static credentials from CloudFormation template)\n",
    "# os.environ[\"AWS_ACCESS_KEY_ID\"] = \"AKIA...\"  # From AwsAccessKeyId output\n",
    "# os.environ[\"AWS_SECRET_ACCESS_KEY\"] = \"...\"  # From AwsSecretAccessKey output\n",
    "# Note: No AWS_SESSION_TOKEN needed for IAM users\n",
    "\n",
    "# Option 2: IAM Role / Temporary credentials (e.g., STS AssumeRole, EC2 instance role)\n",
    "# os.environ[\"AWS_ACCESS_KEY_ID\"] = \"ASIA...\"  # Temporary access key\n",
    "# os.environ[\"AWS_SECRET_ACCESS_KEY\"] = \"...\"  # Temporary secret key\n",
    "# os.environ[\"AWS_SESSION_TOKEN\"] = \"...\"      # REQUIRED for temporary credentials\n",
    "\n",
    "print(f\"Neptune Endpoint: {os.environ.get('NEPTUNE_ENDPOINT')}\")\n",
    "print(f\"AWS Region: {os.environ.get('AWS_REGION')}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 1: Initialize Neptune Store\n",
    "\n",
    "Initialize a connection to your Amazon Neptune cluster with IAM authentication."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "from semantica.graph_store import GraphStore\n",
    "\n",
    "# Option 1: Using GraphStore factory (recommended)\n",
    "neptune_store = GraphStore(\n",
    "    backend=\"neptune\",\n",
    "    endpoint=os.environ.get(\"NEPTUNE_ENDPOINT\"),\n",
    "    port=int(os.environ.get(\"NEPTUNE_PORT\", 8182)),\n",
    "    region=os.environ.get(\"AWS_REGION\", \"us-east-1\"),\n",
    "    iam_auth=True,\n",
    ")\n",
    "\n",
    "# Connect to Neptune\n",
    "neptune_store.connect()\n",
    "print(\"Connected to Amazon Neptune!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Development/Testing Without IAM Auth\n",
    "\n",
    "For development or testing environments where IAM authentication is not required (e.g., Neptune notebooks or VPC-only access), you can disable IAM signing:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# For dev/test environments without IAM authentication\n",
    "neptune_store_dev = GraphStore(\n",
    "    backend=\"neptune\",\n",
    "    endpoint=os.environ.get(\"NEPTUNE_ENDPOINT\"),\n",
    "    port=int(os.environ.get(\"NEPTUNE_PORT\", 8182)),\n",
    "    region=os.environ.get(\"AWS_REGION\", \"us-east-1\"),\n",
    "    iam_auth=False,  # Disable IAM signing for dev/test\n",
    ")\n",
    "neptune_store_dev.connect()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Authentication Options\n",
    "\n",
    "IAM Authentication (recommended for production) automatically uses the AWS credential chain:\n",
    "1. Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)\n",
    "2. AWS credentials file (~/.aws/credentials)\n",
    "3. IAM role (for EC2, Lambda, ECS)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 2: Node Operations\n",
    "\n",
    "### Creating Nodes\n",
    "\n",
    "Nodes represent entities in your graph. Each node can have:\n",
    "- **ID**: A unique identifier (custom or auto-generated UUID)\n",
    "- **Labels**: Categories/types (e.g., `Person`, `Company`)\n",
    "- **Properties**: Key-value pairs (e.g., `{\"name\": \"Alice\", \"age\": 30}`)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create a single node with custom ID (id in properties)\n",
    "alice = neptune_store.create_node(\n",
    "    labels=[\"Person\"],\n",
    "    properties={\"id\": \"alice\", \"name\": \"Alice\", \"age\": 30, \"role\": \"Engineer\"}\n",
    ")\n",
    "print(f\"Created node: {alice}\")\n",
    "\n",
    "# Create a node with auto-generated UUID (no id in properties)\n",
    "bob = neptune_store.create_node(\n",
    "    labels=[\"Person\"],\n",
    "    properties={\"name\": \"Bob\", \"age\": 25, \"role\": \"Designer\"}\n",
    ")\n",
    "print(f\"Created node with UUID: {bob['id']}\")\n",
    "\n",
    "# Create a company node with auto-generated ID\n",
    "acme = neptune_store.create_node(\n",
    "    labels=[\"Company\"],\n",
    "    properties={\"name\": \"Acme Corp\", \"industry\": \"Technology\", \"founded\": 2010}\n",
    ")\n",
    "print(f\"Created company: {acme}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Creating Multiple Nodes (Batch)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Batch create nodes for better performance\n",
    "# Include 'id' in properties for custom IDs\n",
    "nodes_data = [\n",
    "    {\"labels\": [\"Person\"], \"properties\": {\"id\": \"charlie\", \"name\": \"Charlie\", \"age\": 35}},\n",
    "    {\"labels\": [\"Person\"], \"properties\": {\"id\": \"diana\", \"name\": \"Diana\", \"age\": 28}},\n",
    "    {\"labels\": [\"Location\"], \"properties\": {\"name\": \"San Francisco\", \"state\": \"CA\"}},\n",
    "]\n",
    "\n",
    "created_nodes = neptune_store.create_nodes(nodes_data)\n",
    "print(f\"Created {len(created_nodes)} nodes in batch\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Retrieving Nodes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Get a specific node by ID\n",
    "alice_node = neptune_store.get_node(node_id=\"alice\")\n",
    "print(f\"Retrieved: {alice_node}\")\n",
    "\n",
    "# Get nodes by label\n",
    "people = neptune_store.get_nodes(labels=[\"Person\"], limit=10)\n",
    "print(f\"Found {len(people)} Person nodes:\")\n",
    "for person in people:\n",
    "    print(f\"  - {person.get('properties', {}).get('name')}\")\n",
    "\n",
    "# Get nodes by properties\n",
    "engineers = neptune_store.get_nodes(\n",
    "    labels=[\"Person\"],\n",
    "    properties={\"role\": \"Engineer\"},\n",
    "    limit=5\n",
    ")\n",
    "print(f\"Found {len(engineers)} engineers\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Updating Nodes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Update node properties (merge mode - default)\n",
    "updated_alice = neptune_store.update_node(\n",
    "    node_id=\"alice\",\n",
    "    properties={\"age\": 31, \"department\": \"AI Research\"},\n",
    "    merge=True\n",
    ")\n",
    "print(f\"Updated Alice: {updated_alice}\")\n",
    "\n",
    "# Replace all properties (merge=False)\n",
    "# WARNING: This removes properties not in the update\n",
    "replaced = neptune_store.update_node(\n",
    "    node_id=\"charlie\",\n",
    "    properties={\"name\": \"Charlie\", \"age\": 36},\n",
    "    merge=False\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Deleting Nodes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Delete a node (with detach=True to also delete relationships)\n",
    "deleted = neptune_store.delete_node(node_id=\"diana\", detach=True)\n",
    "print(f\"Deleted diana: {deleted}\")\n",
    "\n",
    "# Without detach (fails if node has relationships)\n",
    "# neptune_store.delete_node(node_id=\"alice\", detach=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 3: Relationship Operations\n",
    "\n",
    "### Creating Relationships\n",
    "\n",
    "Relationships connect nodes and represent connections between entities."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create a relationship between Alice and Acme\n",
    "works_at = neptune_store.create_relationship(\n",
    "    start_node_id=\"alice\",\n",
    "    end_node_id=acme[\"id\"],\n",
    "    rel_type=\"WORKS_AT\",\n",
    "    properties={\"since\": 2020, \"position\": \"Senior Engineer\"}\n",
    ")\n",
    "print(f\"Created relationship: {works_at}\")\n",
    "\n",
    "# Create a KNOWS relationship between people\n",
    "knows_rel = neptune_store.create_relationship(\n",
    "    start_node_id=\"alice\",\n",
    "    end_node_id=bob[\"id\"],\n",
    "    rel_type=\"KNOWS\",\n",
    "    properties={\"since\": 2019}\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Retrieving Relationships"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Get all relationships for a node\n",
    "alice_rels = neptune_store.get_relationships(node_id=\"alice\", direction=\"both\")\n",
    "print(f\"Alice has {len(alice_rels)} relationships\")\n",
    "\n",
    "# Get outgoing relationships only\n",
    "outgoing = neptune_store.get_relationships(node_id=\"alice\", direction=\"out\")\n",
    "\n",
    "# Filter by relationship type\n",
    "works_rels = neptune_store.get_relationships(\n",
    "    node_id=\"alice\",\n",
    "    rel_type=\"WORKS_AT\",\n",
    "    direction=\"out\"\n",
    ")\n",
    "print(f\"Alice's work relationships: {len(works_rels)}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Deleting Relationships"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Delete a specific relationship by ID\n",
    "if works_at.get(\"id\"):\n",
    "    deleted = neptune_store.delete_relationship(rel_id=works_at[\"id\"])\n",
    "    print(f\"Deleted relationship: {deleted}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 4: OpenCypher Queries\n",
    "\n",
    "Amazon Neptune supports OpenCypher queries via the Bolt protocol. Execute complex graph patterns using standard Cypher syntax."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Simple query\n",
    "results = neptune_store.execute_query(\n",
    "    \"MATCH (p:Person) RETURN p.name, p.age ORDER BY p.age\"\n",
    ")\n",
    "print(\"People in the graph:\")\n",
    "for record in results.get(\"records\", []):\n",
    "    print(f\"  - {record.get('p.name')}: {record.get('p.age')} years old\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Using parameters (safer and more efficient)\n",
    "results = neptune_store.execute_query(\n",
    "    \"MATCH (p:Person) WHERE p.age > $min_age RETURN p.name, p.age\",\n",
    "    parameters={\"min_age\": 25}\n",
    ")\n",
    "print(f\"People over 25: {len(results.get('records', []))}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Find relationships between nodes\n",
    "results = neptune_store.execute_query(\"\"\"\n",
    "    MATCH (p:Person)-[r:WORKS_AT]->(c:Company)\n",
    "    RETURN p.name as employee, c.name as company, r.since as start_year\n",
    "\"\"\")\n",
    "for record in results.get(\"records\", []):\n",
    "    print(f\"{record['employee']} works at {record['company']} since {record['start_year']}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Count and aggregate\n",
    "results = neptune_store.execute_query(\"\"\"\n",
    "    MATCH (p:Person)\n",
    "    RETURN count(p) as total, avg(p.age) as avg_age, max(p.age) as max_age\n",
    "\"\"\")\n",
    "stats = results.get(\"records\", [{}])[0]\n",
    "print(f\"Total: {stats.get('total')}, Avg Age: {stats.get('avg_age'):.1f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 5: Graph Analytics\n",
    "\n",
    "### Get Neighbors\n",
    "\n",
    "Traverse the graph to find connected nodes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Get immediate neighbors (depth=1)\n",
    "neighbors = neptune_store.get_neighbors(\n",
    "    node_id=\"alice\",\n",
    "    direction=\"both\",\n",
    "    depth=1\n",
    ")\n",
    "print(f\"Alice's direct neighbors: {len(neighbors)}\")\n",
    "\n",
    "# Get neighbors up to 2 hops away\n",
    "extended = neptune_store.get_neighbors(\n",
    "    node_id=\"alice\",\n",
    "    direction=\"out\",\n",
    "    depth=2\n",
    ")\n",
    "print(f\"Nodes within 2 hops: {len(extended)}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Shortest Path\n",
    "\n",
    "Find the shortest path between two nodes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Find shortest path\n",
    "path = neptune_store.shortest_path(\n",
    "    start_node_id=\"alice\",\n",
    "    end_node_id=\"charlie\",\n",
    "    max_depth=5\n",
    ")\n",
    "\n",
    "if path:\n",
    "    print(\"Path found!\")\n",
    "    print(f\"  Length: {path.get('length')}\")\n",
    "    print(f\"  Nodes: {len(path.get('nodes', []))}\")\n",
    "    print(f\"  Relationships: {len(path.get('relationships', []))}\")\n",
    "else:\n",
    "    print(\"No path found between nodes\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 6: Graph Statistics\n",
    "\n",
    "Get comprehensive statistics about your graph."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Get graph statistics\n",
    "stats = neptune_store.get_stats()\n",
    "\n",
    "print(\"Graph Statistics:\")\n",
    "print(f\"  Total nodes: {stats.get('node_count', 'N/A')}\")\n",
    "print(f\"  Total relationships: {stats.get('relationship_count', 'N/A')}\")\n",
    "\n",
    "print(\"\\nNode labels:\")\n",
    "for label, count in stats.get('label_counts', {}).items():\n",
    "    print(f\"  - {label}: {count}\")\n",
    "\n",
    "print(\"\\nRelationship types:\")\n",
    "for rel_type, count in stats.get('relationship_type_counts', {}).items():\n",
    "    print(f\"  - {rel_type}: {count}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 7: Connection Management\n",
    "\n",
    "Always close connections when done to free resources."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Check connection status\n",
    "status = neptune_store.get_status()\n",
    "print(f\"Connection status: {status}\")\n",
    "\n",
    "# Close the connection\n",
    "neptune_store.close()\n",
    "print(\"Connection closed\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Neptune-Specific Considerations\n",
    "\n",
    "### Native Element IDs\n",
    "\n",
    "Neptune uses native `~id` for element identification. Include `id` in properties to set a custom ID:\n",
    "\n",
    "```python\n",
    "# Create a node with custom ID (include 'id' in properties)\n",
    "node = neptune_store.create_node(\n",
    "    labels=[\"Person\"],\n",
    "    properties={\"id\": \"my-custom-id\", \"name\": \"Test\"}\n",
    ")\n",
    "\n",
    "# Create a node with auto-generated UUID (omit 'id' from properties)\n",
    "node = neptune_store.create_node(\n",
    "    labels=[\"Person\"],\n",
    "    properties={\"name\": \"Test\"}\n",
    ")\n",
    "\n",
    "# The ID is used in id() function calls internally:\n",
    "# MATCH (n) WHERE id(n) = 'my-custom-id' RETURN n\n",
    "```\n",
    "\n",
    "### OpenCypher Considerations\n",
    "\n",
    "Amazon Neptune Database's OpenCypher implementation has some differences from Neo4j:\n",
    "\n",
    "1. **No `shortestPath()` function**: Use variable-length path patterns or `allShortestPaths()`\n",
    "2. **Labels syntax**: Use `labels(n)` function to retrieve node labels\n",
    "3. **Property updates**: Use `SET n += {props}` for merge behavior\n",
    "\n",
    "For the complete OpenCypher specification supported by Amazon Neptune Database, see the [AWS documentation](https://docs.aws.amazon.com/neptune/latest/userguide/access-graph-opencypher.html).\n",
    "\n",
    "### Amazon Neptune Analytics\n",
    "\n",
    "For analytical (OLAP) workloads such as graph algorithms, aggregations, and large-scale traversals, consider [Amazon Neptune Analytics](https://docs.aws.amazon.com/neptune-analytics/latest/userguide/what-is-neptune-analytics.html). Neptune Analytics complements Neptune Database by providing optimized performance for analytical queries while Neptune Database is optimized for transactional (OLTP) workloads.\n",
    "\n",
    "### Performance Tips\n",
    "\n",
    "1. **Use batch operations** for creating multiple nodes/relationships\n",
    "2. **Use parameters** in queries to enable query caching\n",
    "3. **Limit result sets** with `LIMIT` clause"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Summary\n",
    "\n",
    "This notebook covered the Amazon Neptune Graph Store integration:\n",
    "\n",
    "- **IAM Authentication**: Secure AWS SigV4 signing\n",
    "- **CRUD Operations**: Full node and relationship management\n",
    "- **OpenCypher Queries**: Standard graph query language\n",
    "- **Graph Analytics**: Neighbors and shortest path algorithms\n",
    "- **Statistics & Monitoring**: Graph metrics and status\n",
    "\n",
    "### Key Takeaways\n",
    "\n",
    "- Neptune uses native `~id` for element identification\n",
    "- IAM authentication is recommended for production\n",
    "- Bolt protocol provides efficient binary query interface\n",
    "- Semantica abstracts Neptune-specific syntax differences\n",
    "\n",
    "### Next Steps\n",
    "\n",
    "- [Graph Store (Neo4j/FalkorDB)](09_Graph_Store.ipynb) - Compare with other backends\n",
    "- [Building Knowledge Graphs](07_Building_Knowledge_Graphs.ipynb) - Build production KGs\n",
    "- [Graph Analytics](10_Graph_Analytics.ipynb) - Advanced analytics algorithms"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.9.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
