Update Graph Store module documentation and notebooks

- Enhanced Graph Store notebook with comprehensive examples and clean formatting
- Fixed GraphStore API usage across all documentation files
- Updated examples to use keyword arguments (labels, properties, start_node_id, end_node_id, rel_type)
- Removed emojis and links from notebook for cleaner markdown
- Made summary section more concise
- Ensured consistency across cookbook notebooks, docs, and module code
This commit is contained in:
KaifAhmad1
2025-12-06 13:31:56 +05:30
parent 7da9f902e3
commit 0c10d5c876
10 changed files with 756 additions and 136 deletions
@@ -189,26 +189,42 @@
"graph_store = GraphStore(backend=\"kuzu\", database_path=\"./analytics_graph_db\")\n",
"graph_store.connect()\n",
"\n",
"# Store entities as nodes\n",
"# Store entities as nodes and track node ID mapping\n",
"node_id_map = {}\n",
"for entity in entities:\n",
" node = graph_store.create_node(\n",
" labels=[entity[\"type\"]],\n",
" properties={\"name\": entity[\"name\"], \"original_id\": entity[\"id\"]}\n",
" )\n",
" print(f\"Stored node: {entity['name']}\")\n",
" node_id_map[entity[\"id\"]] = node.get(\"id\")\n",
" print(f\"Stored node: {entity['name']} (ID: {node.get('id')})\")\n",
"\n",
"# Store relationships\n",
"# Store relationships using mapped node IDs\n",
"for rel in relationships:\n",
" # In a real scenario, you'd lookup node IDs first\n",
" print(f\"Relationship: {rel['source']} -{rel['type']}-> {rel['target']}\")\n",
" source_id = node_id_map.get(rel[\"source\"])\n",
" target_id = node_id_map.get(rel[\"target\"])\n",
" \n",
" if source_id is not None and target_id is not None:\n",
" relationship = graph_store.create_relationship(\n",
" start_node_id=source_id,\n",
" end_node_id=target_id,\n",
" rel_type=rel[\"type\"],\n",
" properties=rel.get(\"properties\", {})\n",
" )\n",
" print(f\"Stored relationship: {rel['source']} -{rel['type']}-> {rel['target']}\")\n",
" else:\n",
" print(f\"Warning: Could not find node IDs for relationship {rel['source']} -> {rel['target']}\")\n",
"\n",
"# Query using Cypher\n",
"results = graph_store.execute_query(\"MATCH (n) RETURN n.name, labels(n) LIMIT 10\")\n",
"print(f\"\\nStored {len(results.get('records', []))} nodes in graph store\")\n",
"print(f\"\\nQuery results: {len(results.get('records', []))} nodes\")\n",
"\n",
"# Get statistics\n",
"stats = graph_store.get_stats()\n",
"print(f\"Graph store stats: {stats}\")\n",
"print(f\"\\nGraph store statistics:\")\n",
"print(f\" Node count: {stats.get('node_count', 'N/A')}\")\n",
"print(f\" Relationship count: {stats.get('relationship_count', 'N/A')}\")\n",
"print(f\" Label counts: {stats.get('label_counts', {})}\")\n",
"\n",
"graph_store.close()\n"
]
@@ -246,8 +246,20 @@
"from semantica.graph_store import GraphStore\n",
"store = GraphStore(backend=\"kuzu\", database_path=\"./my_graph_db\")\n",
"store.connect()\n",
"node = store.create_node([\"Person\"], {\"name\": \"John\", \"age\": 30})\n",
"store.create_relationship(node[\"id\"], other_id, \"KNOWS\", {\"since\": 2020})\n",
"node1 = store.create_node(\n",
" labels=[\"Person\"],\n",
" properties={\"name\": \"John\", \"age\": 30}\n",
")\n",
"node2 = store.create_node(\n",
" labels=[\"Person\"],\n",
" properties={\"name\": \"Jane\", \"age\": 28}\n",
")\n",
"store.create_relationship(\n",
" start_node_id=node1[\"id\"],\n",
" end_node_id=node2[\"id\"],\n",
" rel_type=\"KNOWS\",\n",
" properties={\"since\": 2020}\n",
")\n",
"results = store.execute_query(\"MATCH (p:Person) RETURN p.name\")\n",
"store.close()\n",
"```\n",
+331 -97
View File
@@ -4,50 +4,84 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Hawksight-AI/semantica/blob/main/cookbook/introduction/10_Graph_Store.ipynb)\n",
"\n",
"# Graph Store\n",
"# Graph Store Module\n",
"\n",
"## Overview\n",
"\n",
"This notebook demonstrates how to store and query property graphs using Semantica's graph store modules. You'll learn to use `GraphStore` with multiple backends including Neo4j, KuzuDB, and FalkorDB.\n",
"The Graph Store module provides a unified interface for working with property graph databases. It supports multiple backends (Neo4j, KuzuDB, FalkorDB) and offers comprehensive features for storing, querying, and analyzing graph data.\n",
"\n",
"**Documentation**: [API Reference](https://semantica.readthedocs.io/reference/graph_store/)\n",
"### Key Features\n",
"\n",
"- **Multi-Backend Support**: Neo4j (Enterprise), KuzuDB (Embedded), FalkorDB (Redis-based)\n",
"- **Full CRUD Operations**: Create, read, update, delete nodes and relationships\n",
"- **Cypher Query Language**: Execute complex graph queries with OpenCypher support\n",
"- **Graph Analytics**: Built-in algorithms for centrality, community detection, path finding\n",
"- **Batch Operations**: Optimized bulk data loading with progress tracking\n",
"- **Transaction Support**: ACID transactions with rollback capabilities\n",
"- **Index Management**: Create and manage indexes for performance optimization\n",
"- **Convenience Functions**: Simple function-based API for common operations\n",
"\n",
"### Learning Objectives\n",
"\n",
"- Use `GraphStore` to store nodes and relationships\n",
"- Execute Cypher queries for graph retrieval\n",
"- Use graph analytics (shortest path, neighbors)\n",
"- Compare different graph database backends\n",
"By the end of this notebook, you will be able to:\n",
"\n",
"## Installation\n",
"\n",
"Install Semantica from PyPI:\n",
"\n",
"```bash\n",
"pip install semantica\n",
"# Or with all optional dependencies:\n",
"pip install semantica[all]\n",
"```\n",
"1. Initialize and configure GraphStore with different backends\n",
"2. Perform CRUD operations on nodes and relationships\n",
"3. Execute Cypher queries for complex graph operations\n",
"4. Use graph analytics algorithms (shortest path, neighbors, centrality)\n",
"5. Update and delete graph data\n",
"6. Use batch operations for efficient data loading\n",
"7. Work with convenience functions and configuration management\n",
"8. Choose the right backend for your use case\n",
"\n",
"---\n",
"\n",
"## Prerequisites\n",
"## Installation\n",
"\n",
"Install the required graph database client:\n",
"### Core Installation\n",
"\n",
"```bash\n",
"# For Neo4j\n",
"# Install Semantica\n",
"pip install semantica\n",
"\n",
"# Or install with all optional dependencies\n",
"pip install semantica[all]\n",
"```\n",
"\n",
"### Backend-Specific Dependencies\n",
"\n",
"```bash\n",
"# For Neo4j (requires Neo4j server)\n",
"pip install neo4j\n",
"\n",
"# For KuzuDB (embedded - no server required)\n",
"pip install kuzu\n",
"\n",
"# For FalkorDB\n",
"# For FalkorDB (requires Redis/FalkorDB server)\n",
"pip install falkordb\n",
"# And run: docker run -p 6379:6379 -p 3000:3000 -it --rm falkordb/falkordb\n",
"```\n"
"```\n",
"\n",
"### Docker Setup (Optional)\n",
"\n",
"For FalkorDB, you can run it in Docker:\n",
"\n",
"```bash\n",
"docker run -p 6379:6379 -p 3000:3000 -it --rm \\\n",
" -v ./data:/var/lib/falkordb/data \\\n",
" falkordb/falkordb\n",
"```\n",
"\n",
"---\n",
"\n",
"## Backend Comparison\n",
"\n",
"| Backend | Best For | Deployment | Features |\n",
"|---------|----------|------------|----------|\n",
"| **Neo4j** | Enterprise applications, production systems | Server/Cloud | Full Cypher, APOC procedures, multi-database |\n",
"| **KuzuDB** | Analytics, embedded applications, development | Embedded (no server) | Fast analytical queries, zero-config |\n",
"| **FalkorDB** | LLM applications, real-time systems, high performance | Redis-based | Ultra-fast, sparse matrix operations |\n",
"\n",
"**Recommendation**: Start with **KuzuDB** for development and learning (no setup required), then move to **Neo4j** or **FalkorDB** for production.\n"
]
},
{
@@ -56,7 +90,7 @@
"source": [
"## Step 1: Initialize Graph Store\n",
"\n",
"Create a graph store with your preferred backend.\n"
"Initialize a `GraphStore` instance with your preferred backend. For this tutorial, we'll use **KuzuDB** (embedded, no server setup required).\n"
]
},
{
@@ -67,7 +101,7 @@
"source": [
"from semantica.graph_store import GraphStore\n",
"\n",
"# Option 1: Neo4j (requires Neo4j server)\n",
"# Option 1: Neo4j (requires Neo4j server running)\n",
"# store = GraphStore(\n",
"# backend=\"neo4j\",\n",
"# uri=\"bolt://localhost:7687\",\n",
@@ -75,7 +109,7 @@
"# password=\"password\"\n",
"# )\n",
"\n",
"# Option 2: KuzuDB (embedded - no server required)\n",
"# Option 2: KuzuDB (embedded - no server required) - Recommended for learning\n",
"store = GraphStore(\n",
" backend=\"kuzu\",\n",
" database_path=\"./demo_graph_db\"\n",
@@ -90,16 +124,21 @@
"# )\n",
"\n",
"# Connect to the database\n",
"store.connect()\n"
"store.connect()\n",
"print(\"Connected to graph database successfully!\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Step 2: Create Nodes\n",
"## Step 2: Node Operations\n",
"\n",
"Create nodes with labels and properties.\n"
"### Creating Nodes\n",
"\n",
"Nodes represent entities in your graph. Each node can have:\n",
"- **Labels**: Categories/types (e.g., `Person`, `Company`, `Location`)\n",
"- **Properties**: Key-value pairs (e.g., `{\"name\": \"Alice\", \"age\": 30}`)\n"
]
},
{
@@ -108,24 +147,24 @@
"metadata": {},
"outputs": [],
"source": [
"# Create individual nodes\n",
"# Create individual nodes with labels and properties\n",
"apple = store.create_node(\n",
" labels=[\"Company\"],\n",
" properties={\"name\": \"Apple Inc.\", \"founded\": 1976, \"industry\": \"Technology\"}\n",
")\n",
"print(f\"Created company node: {apple}\")\n",
"print(f\"Created company node: {apple.get('properties', {}).get('name')} (ID: {apple.get('id')})\")\n",
"\n",
"tim_cook = store.create_node(\n",
" labels=[\"Person\"],\n",
" properties={\"name\": \"Tim Cook\", \"title\": \"CEO\", \"age\": 63}\n",
")\n",
"print(f\"Created person node: {tim_cook}\")\n",
"print(f\"Created person node: {tim_cook.get('properties', {}).get('name')} (ID: {tim_cook.get('id')})\")\n",
"\n",
"cupertino = store.create_node(\n",
" labels=[\"Location\"],\n",
" properties={\"name\": \"Cupertino\", \"state\": \"California\", \"country\": \"USA\"}\n",
")\n",
"print(f\"Created location node: {cupertino}\")\n"
"print(f\"Created location node: {cupertino.get('properties', {}).get('name')} (ID: {cupertino.get('id')})\")\n"
]
},
{
@@ -134,7 +173,7 @@
"metadata": {},
"outputs": [],
"source": [
"# Create multiple nodes in batch\n",
"# Create multiple nodes in batch (more efficient for large datasets)\n",
"other_companies = store.create_nodes([\n",
" {\"labels\": [\"Company\"], \"properties\": {\"name\": \"Microsoft\", \"founded\": 1975}},\n",
" {\"labels\": [\"Company\"], \"properties\": {\"name\": \"Google\", \"founded\": 1998}},\n",
@@ -147,9 +186,14 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Step 3: Create Relationships\n",
"## Step 3: Relationship Operations\n",
"\n",
"Create relationships between nodes.\n"
"### Creating Relationships\n",
"\n",
"Relationships connect nodes and represent connections between entities. Each relationship has:\n",
"- **Type**: The relationship type (e.g., `CEO_OF`, `LOCATED_IN`, `KNOWS`)\n",
"- **Properties**: Key-value pairs (e.g., `{\"since\": 2011}`)\n",
"- **Direction**: From `start_node_id` to `end_node_id`\n"
]
},
{
@@ -158,14 +202,14 @@
"metadata": {},
"outputs": [],
"source": [
"# Create relationships\n",
"# Create relationships between nodes\n",
"ceo_rel = store.create_relationship(\n",
" start_node_id=tim_cook[\"id\"],\n",
" end_node_id=apple[\"id\"],\n",
" rel_type=\"CEO_OF\",\n",
" properties={\"since\": 2011}\n",
")\n",
"print(f\"Created CEO relationship: {ceo_rel}\")\n",
"print(f\"Created relationship: {ceo_rel.get('type')} (ID: {ceo_rel.get('id')})\")\n",
"\n",
"location_rel = store.create_relationship(\n",
" start_node_id=apple[\"id\"],\n",
@@ -173,16 +217,18 @@
" rel_type=\"HEADQUARTERED_IN\",\n",
" properties={\"since\": 1977}\n",
")\n",
"print(f\"Created location relationship: {location_rel}\")\n"
"print(f\"Created relationship: {location_rel.get('type')} (ID: {location_rel.get('id')})\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Step 4: Query Nodes and Relationships\n",
"## Step 4: Querying Nodes and Relationships\n",
"\n",
"Retrieve nodes and relationships from the graph.\n"
"### Retrieving Nodes\n",
"\n",
"You can query nodes by labels, properties, or node IDs.\n"
]
},
{
@@ -191,11 +237,18 @@
"metadata": {},
"outputs": [],
"source": [
"# Get all Company nodes\n",
"# Get nodes by label\n",
"companies = store.get_nodes(labels=[\"Company\"], limit=10)\n",
"print(f\"Found {len(companies)} companies:\")\n",
"for company in companies:\n",
" print(f\" - {company.get('properties', {}).get('name', 'Unknown')}\")\n"
" name = company.get('properties', {}).get('name', 'Unknown')\n",
" founded = company.get('properties', {}).get('founded', 'N/A')\n",
" print(f\" - {name} (founded: {founded})\")\n",
"\n",
"# Get a specific node by ID\n",
"if apple.get('id'):\n",
" node = store.get_node(node_id=apple[\"id\"])\n",
" print(f\"\\nRetrieved node by ID: {node.get('properties', {}).get('name')}\")\n"
]
},
{
@@ -208,16 +261,29 @@
"relationships = store.get_relationships(node_id=apple[\"id\"], direction=\"both\")\n",
"print(f\"Found {len(relationships)} relationships for Apple:\")\n",
"for rel in relationships:\n",
" print(f\" - Type: {rel.get('type')}, Properties: {rel.get('properties')}\")\n"
" rel_type = rel.get('type', 'Unknown')\n",
" props = rel.get('properties', {})\n",
" print(f\" - {rel_type}: {props}\")\n",
"\n",
"# Get relationships by type and direction\n",
"if tim_cook.get('id'):\n",
" outgoing = store.get_relationships(\n",
" node_id=tim_cook[\"id\"],\n",
" rel_type=\"CEO_OF\",\n",
" direction=\"out\"\n",
" )\n",
" print(f\"\\nOutgoing CEO_OF relationships: {len(outgoing)}\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Step 5: Execute Cypher Queries\n",
"## Step 5: Cypher Query Execution\n",
"\n",
"Use Cypher queries for complex graph operations.\n"
"### Executing Cypher Queries\n",
"\n",
"Cypher is a powerful graph query language that allows you to express complex graph patterns and operations. The Graph Store module supports **OpenCypher** syntax across all backends.\n"
]
},
{
@@ -226,14 +292,18 @@
"metadata": {},
"outputs": [],
"source": [
"# Execute a Cypher query\n",
"# Execute a Cypher query to find CEO relationships\n",
"results = store.execute_query(\"\"\"\n",
" MATCH (p:Person)-[r:CEO_OF]->(c:Company)\n",
" RETURN p.name as person, c.name as company, r.since as since\n",
"\"\"\")\n",
"\n",
"print(\"CEO Relationships:\")\n",
"for record in results.get(\"records\", []):\n",
" print(f\" {record}\")\n"
" person = record.get('person', 'Unknown')\n",
" company = record.get('company', 'Unknown')\n",
" since = record.get('since', 'N/A')\n",
" print(f\" - {person} is CEO of {company} since {since}\")\n"
]
},
{
@@ -242,14 +312,17 @@
"metadata": {},
"outputs": [],
"source": [
"# Parameterized query\n",
"# Parameterized query (safer and more efficient)\n",
"results = store.execute_query(\n",
" \"MATCH (c:Company) WHERE c.founded > $year RETURN c.name, c.founded\",\n",
" \"MATCH (c:Company) WHERE c.founded > $year RETURN c.name, c.founded ORDER BY c.founded\",\n",
" parameters={\"year\": 1990}\n",
")\n",
"\n",
"print(\"Companies founded after 1990:\")\n",
"for record in results.get(\"records\", []):\n",
" print(f\" {record}\")\n"
" name = record.get('c.name', 'Unknown')\n",
" founded = record.get('c.founded', 'N/A')\n",
" print(f\" - {name} (founded: {founded})\")\n"
]
},
{
@@ -258,7 +331,9 @@
"source": [
"## Step 6: Graph Analytics\n",
"\n",
"Use built-in graph analytics functions.\n"
"### Built-in Analytics Algorithms\n",
"\n",
"The Graph Store module provides several graph analytics algorithms for analyzing your graph structure.\n"
]
},
{
@@ -267,16 +342,19 @@
"metadata": {},
"outputs": [],
"source": [
"# Get neighbors of a node\n",
"neighbors = store.get_neighbors(\n",
" node_id=apple[\"id\"],\n",
" direction=\"both\",\n",
" depth=2\n",
")\n",
"\n",
"print(f\"Found {len(neighbors)} neighbors (up to depth 2):\")\n",
"for neighbor in neighbors:\n",
" print(f\" - {neighbor.get('properties', {}).get('name', 'Unknown')}\")\n"
"# Get neighbors of a node (traverse the graph)\n",
"if apple.get('id'):\n",
" neighbors = store.get_neighbors(\n",
" node_id=apple[\"id\"],\n",
" direction=\"both\",\n",
" depth=2\n",
" )\n",
" \n",
" print(f\"Found {len(neighbors)} neighbors (up to depth 2) for Apple:\")\n",
" for neighbor in neighbors:\n",
" name = neighbor.get('properties', {}).get('name', 'Unknown')\n",
" labels = neighbor.get('labels', [])\n",
" print(f\" - {name} ({', '.join(labels)})\")\n"
]
},
{
@@ -285,26 +363,32 @@
"metadata": {},
"outputs": [],
"source": [
"# Find shortest path (if nodes are connected)\n",
"path = store.shortest_path(\n",
" start_node_id=tim_cook[\"id\"],\n",
" end_node_id=cupertino[\"id\"],\n",
" max_depth=5\n",
")\n",
"\n",
"if path:\n",
" print(f\"Shortest path length: {path.get('length')}\")\n",
" print(f\"Nodes in path: {len(path.get('nodes', []))}\")\n",
"else:\n"
"# Find shortest path between two nodes\n",
"if tim_cook.get('id') and cupertino.get('id'):\n",
" path = store.shortest_path(\n",
" start_node_id=tim_cook[\"id\"],\n",
" end_node_id=cupertino[\"id\"],\n",
" max_depth=5\n",
" )\n",
" \n",
" if path:\n",
" print(f\"Shortest path found:\")\n",
" print(f\" - Path length: {path.get('length')}\")\n",
" print(f\" - Nodes in path: {len(path.get('nodes', []))}\")\n",
" print(f\" - Relationships: {len(path.get('relationships', []))}\")\n",
" else:\n",
" print(\"No path found between the nodes\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Step 7: Get Graph Statistics\n",
"## Step 7: Update and Delete Operations\n",
"\n",
"Get statistics about the graph.\n"
"### Updating Nodes\n",
"\n",
"You can update node properties using the `update_node` method.\n"
]
},
{
@@ -313,21 +397,173 @@
"metadata": {},
"outputs": [],
"source": [
"# Update node properties (merge mode - adds/updates properties)\n",
"if tim_cook.get('id'):\n",
" updated = store.update_node(\n",
" node_id=tim_cook[\"id\"],\n",
" properties={\"age\": 64, \"title\": \"CEO & President\"},\n",
" merge=True # Merge with existing properties\n",
" )\n",
" print(f\"Updated node: {updated.get('properties', {}).get('name')}\")\n",
" print(f\" New age: {updated.get('properties', {}).get('age')}\")\n",
" print(f\" New title: {updated.get('properties', {}).get('title')}\")\n",
"\n",
"# Example: Replace all properties (merge=False)\n",
"# updated = store.update_node(\n",
"# node_id=node_id,\n",
"# properties={\"name\": \"New Name\"},\n",
"# merge=False # Replace all properties\n",
"# )\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Step 8: Delete Operations\n",
"\n",
"### Deleting Nodes and Relationships\n",
"\n",
"You can delete nodes and relationships when needed.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Delete a relationship\n",
"if location_rel.get('id'):\n",
" deleted = store.delete_relationship(rel_id=location_rel[\"id\"])\n",
" if deleted:\n",
" print(f\"Deleted relationship (ID: {location_rel['id']})\")\n",
"\n",
"# Delete a node (with detach=True to also delete its relationships)\n",
"# WARNING: This will delete the node and all its relationships\n",
"# Uncomment to test:\n",
"# if cupertino.get('id'):\n",
"# deleted = store.delete_node(node_id=cupertino[\"id\"], detach=True)\n",
"# if deleted:\n",
"# print(f\"Deleted node: {cupertino.get('properties', {}).get('name')}\")\n",
"\n",
"print(\"\\nTip: Use detach=True to delete a node and all its relationships\")\n",
"print(\" Use detach=False to only delete the node (fails if relationships exist)\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Step 9: Graph Statistics\n",
"\n",
"Get comprehensive statistics about your graph.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Get comprehensive graph statistics\n",
"stats = store.get_stats()\n",
"\n",
"print(f\" Node count: {stats.get('node_count', 'N/A')}\")\n",
"print(f\" Relationship count: {stats.get('relationship_count', 'N/A')}\")\n",
"print(f\" Label counts: {stats.get('label_counts', {})}\")\n",
"print(f\" Relationship types: {stats.get('relationship_type_counts', {})}\")\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",
"print(f\"\\nNode labels:\")\n",
"for label, count in stats.get('label_counts', {}).items():\n",
" print(f\" - {label}: {count} nodes\")\n",
"print(f\"\\nRelationship types:\")\n",
"for rel_type, count in stats.get('relationship_type_counts', {}).items():\n",
" print(f\" - {rel_type}: {count} relationships\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Step 8: Clean Up\n",
"## Step 10: Convenience Functions\n",
"\n",
"Close the connection when done.\n"
"The Graph Store module provides convenience functions for simpler, function-based operations.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Using convenience functions (alternative to class methods)\n",
"from semantica.graph_store import (\n",
" create_node,\n",
" create_relationship,\n",
" get_nodes,\n",
" execute_query,\n",
" shortest_path\n",
")\n",
"\n",
"# These functions work with a default store instance\n",
"# For this example, we'll continue using the store instance we created\n",
"\n",
"# Example: Using convenience functions\n",
"# node = create_node(\n",
"# labels=[\"Person\"],\n",
"# properties={\"name\": \"Alice\", \"age\": 30}\n",
"# )\n",
"\n",
"print(\"Convenience functions available:\")\n",
"print(\" - create_node, create_nodes\")\n",
"print(\" - create_relationship, create_relationships\")\n",
"print(\" - get_nodes, get_relationships\")\n",
"print(\" - update_node, delete_node\")\n",
"print(\" - execute_query, shortest_path, get_neighbors\")\n",
"print(\" - run_analytics\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Step 11: Index Management\n",
"\n",
"Create indexes to improve query performance, especially for large graphs.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Create an index on a node property for faster lookups\n",
"# This is especially useful for frequently queried properties\n",
"\n",
"index_created = store.create_index(\n",
" label=\"Company\",\n",
" property_name=\"name\",\n",
" index_type=\"btree\" # Default index type\n",
")\n",
"\n",
"if index_created:\n",
" print(\"Created index on Company.name for faster queries\")\n",
"else:\n",
" print(\"Index may already exist or not be supported by this backend\")\n",
"\n",
"# Note: Index creation support varies by backend\n",
"# Neo4j: Full support for various index types\n",
"# KuzuDB: Automatic indexing on primary keys\n",
"# FalkorDB: Limited index support\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Step 12: Clean Up\n",
"\n",
"Always close the connection when you're done to free up resources.\n"
]
},
{
@@ -337,7 +573,8 @@
"outputs": [],
"source": [
"# Close the connection\n",
"store.close()\n"
"store.close()\n",
"print(\"Connection closed successfully\")\n"
]
},
{
@@ -346,24 +583,21 @@
"source": [
"## Summary\n",
"\n",
"You've learned how to use graph stores:\n",
"This notebook covered the Graph Store module, a unified interface for property graph databases supporting Neo4j, KuzuDB, and FalkorDB.\n",
"\n",
"- **GraphStore**: Unified interface for property graph databases\n",
"- **Multiple Backends**: Neo4j, KuzuDB, FalkorDB support\n",
"- **Node Operations**: Create, read, update, delete nodes\n",
"- **Relationship Operations**: Create and query relationships\n",
"- **Cypher Queries**: Execute powerful graph queries\n",
"- **Graph Analytics**: Shortest path, neighbors, centrality\n",
"### What You Learned\n",
"\n",
"### Backend Comparison\n",
"- **CRUD Operations**: Create, read, update, and delete nodes and relationships\n",
"- **Cypher Queries**: Execute complex graph queries with OpenCypher syntax\n",
"- **Graph Analytics**: Shortest path, neighbor traversal, and centrality algorithms\n",
"- **Batch Operations**: Efficient bulk data loading for large datasets\n",
"- **Index Management**: Performance optimization through indexing\n",
"\n",
"| Backend | Best For | Deployment |\n",
"|---------|----------|------------|\n",
"| **Neo4j** | Enterprise, full features | Server/Cloud |\n",
"| **KuzuDB** | Analytics, embedded | Embedded (no server) |\n",
"| **FalkorDB** | LLM apps, real-time | Redis-based |\n",
"### Key Takeaways\n",
"\n",
"Next: Learn how to visualize graphs in the Visualization notebook.\n"
"- **Backend Selection**: Use KuzuDB for development, Neo4j for production, FalkorDB for high-performance applications\n",
"- **Best Practices**: Use batch operations, parameterized queries, and proper connection management\n",
"- **Next Steps**: Explore advanced analytics, graph quality, and visualization modules\n"
]
}
],
@@ -732,7 +732,34 @@
"metadata": {},
"outputs": [],
"source": [
"# Optional: Store graph in persistent graph database# Uncomment to use KuzuDB (embedded, no server required)# graph_store = GraphStore(backend=\"kuzu\", database_path=\"./graphrag_db\")# graph_store.connect()# # # Store nodes# for node_id, node_data in knowledge_graph.nodes(data=True):# labels = [node_data.get('type', 'Entity')]# properties = {k: v for k, v in node_data.items() if k != 'type'}# graph_store.create_node(labels, properties)# # # Store relationships# for source, target, edge_data in knowledge_graph.edges(data=True):# rel_type = edge_data.get('type', 'RELATED_TO')# properties = {k: v for k, v in edge_data.items() if k != 'type'}# graph_store.create_relationship(source, target, rel_type, properties)# # graph_store.close()# print(\"Knowledge graph stored in database\")print(\"Graph storage is optional. The in-memory graph is ready for GraphRAG.\")\n"
"# Optional: Store graph in persistent graph database\n",
"# Uncomment to use KuzuDB (embedded, no server required)\n",
"# graph_store = GraphStore(backend=\"kuzu\", database_path=\"./graphrag_db\")\n",
"# graph_store.connect()\n",
"# \n",
"# # Store nodes and track node ID mapping\n",
"# node_id_map = {}\n",
"# for node_id, node_data in knowledge_graph.nodes(data=True):\n",
"# labels = [node_data.get('type', 'Entity')]\n",
"# properties = {k: v for k, v in node_data.items() if k != 'type'}\n",
"# created_node = graph_store.create_node(labels, properties)\n",
"# node_id_map[node_id] = created_node.get(\"id\")\n",
"# \n",
"# # Store relationships using mapped node IDs\n",
"# for source, target, edge_data in knowledge_graph.edges(data=True):\n",
"# if source in node_id_map and target in node_id_map:\n",
"# rel_type = edge_data.get('type', 'RELATED_TO')\n",
"# properties = {k: v for k, v in edge_data.items() if k != 'type'}\n",
"# graph_store.create_relationship(\n",
"# start_node_id=node_id_map[source],\n",
"# end_node_id=node_id_map[target],\n",
"# rel_type=rel_type,\n",
"# properties=properties\n",
"# )\n",
"# \n",
"# graph_store.close()\n",
"# print(\"Knowledge graph stored in database\")\n",
"print(\"Graph storage is optional. The in-memory graph is ready for GraphRAG.\")\n"
]
},
{
+13 -3
View File
@@ -185,9 +185,19 @@ store = GraphStore(
store.connect()
# Create nodes and relationships
apple = store.create_node(["Company"], {"name": "Apple Inc."})
tim = store.create_node(["Person"], {"name": "Tim Cook"})
store.create_relationship(tim["id"], apple["id"], "CEO_OF")
apple = store.create_node(
labels=["Company"],
properties={"name": "Apple Inc."}
)
tim = store.create_node(
labels=["Person"],
properties={"name": "Tim Cook"}
)
store.create_relationship(
start_node_id=tim["id"],
end_node_id=apple["id"],
rel_type="CEO_OF"
)
store.close()
```
+14 -3
View File
@@ -541,9 +541,20 @@ store = GraphStore(backend="neo4j", uri="bolt://localhost:7687")
store.connect()
# Create nodes and relationships
alice = store.create_node(["Person"], {"name": "Alice", "age": 30})
bob = store.create_node(["Person"], {"name": "Bob", "age": 25})
store.create_relationship(alice["id"], bob["id"], "KNOWS", {"since": 2020})
alice = store.create_node(
labels=["Person"],
properties={"name": "Alice", "age": 30}
)
bob = store.create_node(
labels=["Person"],
properties={"name": "Bob", "age": 25}
)
store.create_relationship(
start_node_id=alice["id"],
end_node_id=bob["id"],
rel_type="KNOWS",
properties={"since": 2020}
)
# Query with Cypher
results = store.execute_query("MATCH (p:Person) RETURN p.name")
+249 -17
View File
@@ -74,7 +74,9 @@
## Main Classes
### GraphStore
### Core Classes
#### GraphStore
The main facade for graph operations.
@@ -82,9 +84,28 @@ The main facade for graph operations.
| Method | Description |
|--------|-------------|
| `execute_query(query, params)` | Run Cypher query |
| `create_node(labels, props)` | Add node |
| `create_relationship(start, end, type)` | Add edge |
| `connect(**options)` | Connect to the graph database |
| `close()` | Close connection to the graph database |
| `create_node(labels, properties, **options)` | Create a single node |
| `create_nodes(nodes, **options)` | Create multiple nodes in batch |
| `get_node(node_id, **options)` | Get a node by ID |
| `get_nodes(labels, properties, limit, **options)` | Get nodes matching criteria |
| `update_node(node_id, properties, merge, **options)` | Update node properties |
| `delete_node(node_id, detach, **options)` | Delete a node |
| `create_relationship(start_node_id, end_node_id, rel_type, properties, **options)` | Create a relationship |
| `get_relationships(node_id, rel_type, direction, limit, **options)` | Get relationships |
| `delete_relationship(rel_id, **options)` | Delete a relationship |
| `execute_query(query, parameters, **options)` | Execute a Cypher/OpenCypher query |
| `shortest_path(start_node_id, end_node_id, rel_type, max_depth, **options)` | Find shortest path between nodes |
| `get_neighbors(node_id, rel_type, direction, depth, **options)` | Get neighboring nodes |
| `get_stats()` | Get graph statistics |
| `create_index(label, property_name, index_type, **options)` | Create an index |
**Properties:**
- `nodes` - Access to NodeManager
- `relationships` - Access to RelationshipManager
- `query_engine` - Access to QueryEngine
- `analytics` - Access to GraphAnalytics
**Example:**
@@ -92,51 +113,262 @@ The main facade for graph operations.
from semantica.graph_store import GraphStore
store = GraphStore(backend="neo4j")
store.connect()
store.execute_query(
"MATCH (n:Person {name: $name}) RETURN n",
params={"name": "Alice"}
parameters={"name": "Alice"}
)
store.close()
```
### Neo4jAdapter
#### GraphManager
Enterprise-grade backend.
Manager for graph store operations. Provides access to node, relationship, query, and analytics managers.
**Methods:**
- `get_stats()` - Get graph statistics
- `create_index(label, property_name, index_type, **options)` - Create an index
#### NodeManager
Manager for node CRUD operations.
**Methods:**
- `create(labels, properties, **options)` - Create a node
- `create_batch(nodes, **options)` - Create multiple nodes
- `get(node_id, labels, properties, limit, **options)` - Get node(s)
- `update(node_id, properties, merge, **options)` - Update a node
- `delete(node_id, detach, **options)` - Delete a node
#### RelationshipManager
Manager for relationship CRUD operations.
**Methods:**
- `create(start_node_id, end_node_id, rel_type, properties, **options)` - Create a relationship
- `get(node_id, rel_type, direction, limit, **options)` - Get relationships
- `delete(rel_id, **options)` - Delete a relationship
#### QueryEngine
Engine for query execution and optimization.
**Methods:**
- `execute(query, parameters, use_cache, **options)` - Execute a Cypher/OpenCypher query
- `clear_cache()` - Clear query cache
- `enable_cache()` - Enable query caching
- `disable_cache()` - Disable query caching
#### GraphAnalytics
Graph analytics and algorithms.
**Methods:**
- `shortest_path(start_node_id, end_node_id, rel_type, max_depth, **options)` - Find shortest path
- `get_neighbors(node_id, rel_type, direction, depth, **options)` - Get neighboring nodes
- `degree_centrality(labels, rel_type, direction, **options)` - Calculate degree centrality
- `connected_components(labels, **options)` - Find connected components
### Adapter Classes
#### Neo4jAdapter
Enterprise-grade Neo4j backend adapter.
**Features:**
- Bolt protocol support
- Cluster awareness
- APOC procedure integration
- Multi-database support
- Transaction support
### KuzuAdapter
**Related Classes:**
- `Neo4jDriver` - Neo4j driver wrapper
- `Neo4jSession` - Session management wrapper
- `Neo4jTransaction` - Transaction wrapper
Embedded, in-process backend.
#### KuzuAdapter
Embedded, in-process KuzuDB backend adapter.
**Features:**
- No external server required
- Columnar storage for speed
- Zero-copy integration with Arrow
- Schema-based node and relationship tables
- High-performance analytical queries
### FalkorDBAdapter
**Related Classes:**
- `KuzuDatabase` - Database wrapper
- `KuzuConnection` - Connection wrapper
- `KuzuQuery` - Query execution wrapper
High-performance Redis module.
**Special Methods:**
- `create_node_table(table_name, properties, primary_key, **options)` - Create node table with schema
- `create_rel_table(table_name, from_table, to_table, properties, **options)` - Create relationship table
- `bulk_load_nodes(table_name, file_path, **options)` - Bulk load nodes from CSV
#### FalkorDBAdapter
High-performance Redis-based FalkorDB backend adapter.
**Features:**
- Sparse matrix representation
- Ultra-low latency
- Redis protocol
- Multi-graph support
- Linear algebra based querying
**Related Classes:**
- `FalkorDBClient` - Client wrapper
- `FalkorDBGraph` - Graph wrapper with operations
- `FalkorDBQuery` - Query execution wrapper
**Special Methods:**
- `select_graph(graph_name)` - Select or create a graph
- `list_graphs()` - List all available graphs
- `delete_graph(graph_name)` - Delete a graph
### Configuration and Registry Classes
#### GraphStoreConfig
Configuration manager for graph store module. Supports environment variables, config files (YAML, JSON, TOML), and programmatic configuration.
**Methods:**
- `get(key, default)` - Get configuration value
- `set(key, value)` - Set configuration value
- `update(config)` - Update configuration with dictionary
- `get_method_config(method_name)` - Get method-specific configuration
- `set_method_config(method_name, config)` - Set method-specific configuration
- `get_all()` - Get all configuration
- `get_neo4j_config()` - Get Neo4j-specific configuration
- `get_kuzu_config()` - Get KuzuDB-specific configuration
- `get_falkordb_config()` - Get FalkorDB-specific configuration
- `reset()` - Reset configuration to defaults
**Global Instance:**
- `graph_store_config` - Global configuration instance
#### MethodRegistry
Registry for custom graph store methods, enabling extensibility.
**Methods:**
- `register(task, method_name, method_func, **metadata)` - Register a method
- `unregister(task, method_name)` - Unregister a method
- `get(task, method_name)` - Get a registered method
- `list_all(task)` - List all registered methods
- `has(task, method_name)` - Check if a method is registered
- `get_metadata(task, method_name)` - Get metadata for a registered method
**Supported Task Types:**
- `node` - Node CRUD methods
- `relationship` - Relationship CRUD methods
- `query` - Query execution methods
- `traversal` - Graph traversal methods
- `analytics` - Graph analytics methods
- `bulk` - Bulk operation methods
**Global Instance:**
- `method_registry` - Global method registry instance
---
## Convenience Functions
```python
from semantica.graph_store import execute_query, create_node
The module provides convenience functions for common graph operations. These functions use a global GraphStore instance and support method registration for extensibility.
# Quick query
results = execute_query("MATCH (n) RETURN count(n) as count")
### Node Operations
| Function | Description |
|----------|-------------|
| `create_node(labels, properties, method, **options)` | Create a single node |
| `create_nodes(nodes, method, **options)` | Create multiple nodes in batch |
| `get_nodes(labels, properties, limit, method, **options)` | Get nodes matching criteria |
| `update_node(node_id, properties, merge, method, **options)` | Update node properties |
| `delete_node(node_id, detach, method, **options)` | Delete a node |
### Relationship Operations
| Function | Description |
|----------|-------------|
| `create_relationship(start_id, end_id, rel_type, properties, method, **options)` | Create a relationship |
| `create_relationships(relationships, method, **options)` | Create multiple relationships in batch |
| `get_relationships(node_id, rel_type, direction, limit, method, **options)` | Get relationships matching criteria |
| `update_relationship(rel_id, properties, method, **options)` | Update relationship properties |
| `delete_relationship(rel_id, method, **options)` | Delete a relationship |
### Query Operations
| Function | Description |
|----------|-------------|
| `execute_query(query, parameters, method, **options)` | Execute a Cypher/OpenCypher query |
### Analytics Operations
| Function | Description |
|----------|-------------|
| `shortest_path(start_node_id, end_node_id, rel_type, max_depth, method, **options)` | Find shortest path between nodes |
| `get_neighbors(node_id, rel_type, direction, depth, method, **options)` | Get neighboring nodes |
| `run_analytics(algorithm, method, **options)` | Run graph analytics algorithm |
### Utility Functions
| Function | Description |
|----------|-------------|
| `get_graph_store_method(task, method_name)` | Get graph store method by task and name |
| `list_available_methods(task)` | List all available graph store methods |
**Example:**
```python
from semantica.graph_store import (
create_node,
create_nodes,
create_relationship,
execute_query,
shortest_path,
get_neighbors,
run_analytics
)
# Quick node creation
create_node(["Person"], {"name": "Bob"})
alice = create_node(["Person"], {"name": "Alice", "age": 30})
bob = create_node(["Person"], {"name": "Bob", "age": 25})
# Batch node creation
people = create_nodes([
{"labels": ["Person"], "properties": {"name": "Charlie"}},
{"labels": ["Person"], "properties": {"name": "Diana"}}
])
# Create relationship
rel = create_relationship(
start_id=alice["id"],
end_id=bob["id"],
rel_type="KNOWS",
properties={"since": 2020}
)
# Quick query
results = execute_query("MATCH (n:Person) RETURN count(n) as count")
# Find shortest path
path = shortest_path(
start_node_id=alice["id"],
end_node_id=bob["id"],
max_depth=5
)
# Get neighbors
neighbors = get_neighbors(node_id=alice["id"], depth=2)
# Run analytics
centrality = run_analytics(
algorithm="degree_centrality",
labels=["Person"]
)
```
---
@@ -189,7 +421,7 @@ MATCH (n)-[r]-(m)
WHERE elementId(n) IN $ids
RETURN n, r, m
"""
subgraph = graph_store.execute_query(query, params={"ids": node_ids})
subgraph = graph_store.execute_query(query, parameters={"ids": node_ids})
```
---
+1 -1
View File
@@ -84,7 +84,7 @@ Example Usage:
>>> from semantica.graph_store import GraphStore, create_node, create_relationship, execute_query
>>> # Using convenience functions
>>> node_id = create_node(labels=["Person"], properties={"name": "Alice", "age": 30})
>>> rel_id = create_relationship(start_id=node1_id, end_id=node2_id, type="KNOWS", properties={"since": 2020})
>>> rel_id = create_relationship(start_id=node1_id, end_id=node2_id, rel_type="KNOWS", properties={"since": 2020})
>>> results = execute_query("MATCH (p:Person) WHERE p.age > 25 RETURN p.name")
>>> # Using classes directly
>>> store = GraphStore(backend="neo4j", uri="bolt://localhost:7687")
+82 -4
View File
@@ -455,6 +455,81 @@ from semantica.graph_store import (
)
```
## Configuration Management
### Using GraphStoreConfig
The `GraphStoreConfig` class provides centralized configuration management:
```python
from semantica.graph_store import GraphStoreConfig, graph_store_config
# Get configuration value
default_backend = graph_store_config.get("default_backend", default="neo4j")
# Set configuration value
graph_store_config.set("default_backend", "falkordb")
# Update multiple values
graph_store_config.update({
"batch_size": 2000,
"timeout": 60
})
# Get backend-specific configuration
neo4j_config = graph_store_config.get_neo4j_config()
kuzu_config = graph_store_config.get_kuzu_config()
falkordb_config = graph_store_config.get_falkordb_config()
# Get all configuration
all_config = graph_store_config.get_all()
# Reset to defaults
graph_store_config.reset()
```
### Method Registry
The `MethodRegistry` class allows you to register custom methods for extensibility:
```python
from semantica.graph_store import MethodRegistry, method_registry
# Register a custom node creation method
def validated_create_node(labels, properties, **options):
"""Custom node creation with validation."""
if "name" not in properties:
raise ValueError("name is required")
from semantica.graph_store import _get_store
store = _get_store()
return store.create_node(labels, properties, **options)
# Register the custom method
method_registry.register("node", "validated", validated_create_node)
# Use the custom method
from semantica.graph_store import create_node
node = create_node(
labels=["Person"],
properties={"name": "Alice"},
method="validated"
)
# List available methods
available = method_registry.list_all("node")
# Returns: {"node": ["validated"]}
# Check if a method exists
exists = method_registry.has("node", "validated")
# Get method metadata
metadata = method_registry.get_metadata("node", "validated")
# Unregister a method
method_registry.unregister("node", "validated")
```
## Advanced Usage
### Context Manager
@@ -470,7 +545,7 @@ with GraphStore(backend="neo4j", uri="bolt://localhost:7687") as store:
### Custom Method Registration
```python
from semantica.graph_store import method_registry
from semantica.graph_store import method_registry, GraphStore
def custom_create_node(labels, properties, **options):
"""Custom node creation with validation."""
@@ -479,9 +554,12 @@ def custom_create_node(labels, properties, **options):
raise ValueError("name is required")
# Call default implementation
from semantica.graph_store import _get_store
store = _get_store()
return store.create_node(labels, properties, **options)
store = GraphStore()
store.connect()
try:
return store.create_node(labels, properties, **options)
finally:
store.close()
# Register custom method
method_registry.register("node", "validated", custom_create_node)
+1 -1
View File
@@ -54,7 +54,7 @@ Main Functions:
Example Usage:
>>> from semantica.graph_store.methods import create_node, create_relationship, execute_query
>>> node_id = create_node(labels=["Person"], properties={"name": "Alice"})
>>> rel = create_relationship(start_id=node1_id, end_id=node2_id, type="KNOWS")
>>> rel = create_relationship(start_id=node1_id, end_id=node2_id, rel_type="KNOWS")
>>> results = execute_query("MATCH (p:Person) RETURN p.name")
"""