Graph Traversal & Relationships
Advanced guide to graph operations, edge semantics, and complex hierarchy queries. Learn how to work with DAGs, multiple relationship types, and optimize graph traversals.
Overview
This guide covers advanced graph concepts beyond basic hierarchies:
- Edge Table Structure: Understanding the underlying data model
- Direction Semantics: How edges are traversed
- Multiple Relationship Types: Matrix organizations and cross-functional teams
- DAG Support: Nodes with multiple parents
- Performance Optimization: Indexing, caching, and query strategies
- Complex Queries: Multi-hop traversal, path finding, subgraph extraction
Graph Configuration
Edge Table Structure
When you enable hierarchy: true or graph.enabled: true, Taruvi automatically creates an edges table:
Table Name Format: {table_name}_edges
Example: For table employees, the edges table is employees_edges
Schema:
CREATE TABLE employees_edges (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
from_id UUID NOT NULL, -- Source node (matches main table PK type)
to_id UUID NOT NULL, -- Target node (matches main table PK type)
type VARCHAR(50) NOT NULL, -- Relationship type
metadata JSONB, -- Additional edge data
created_at TIMESTAMP DEFAULT NOW(),
FOREIGN KEY (from_id) REFERENCES employees(id) ON DELETE CASCADE,
FOREIGN KEY (to_id) REFERENCES employees(id) ON DELETE CASCADE
);
-- Indexes for fast lookups
CREATE INDEX idx_edges_from_type ON employees_edges(from_id, type);
CREATE INDEX idx_edges_to_type ON employees_edges(to_id, type);
CREATE INDEX idx_edges_type ON employees_edges(type);
Edge Management API
Edges are managed via the Data Service graph endpoints:
GET /api/apps/{app_slug}/datatables/{name}/edges/
POST /api/apps/{app_slug}/datatables/{name}/edges/
PATCH /api/apps/{app_slug}/datatables/{name}/edges/{edge_id}/
DELETE /api/apps/{app_slug}/datatables/{name}/edges/
Use PATCH to update an edge by ID (full update of from, to, type, and metadata).
Relationship Types Definition
Define relationship types in your table schema:
{
"hierarchy": true,
"graph": {
"enabled": true,
"types": [
{
"name": "manager",
"inverse": "reports",
"constraints": {
"max_outgoing": 1
},
"description": "Primary reporting line"
},
{
"name": "dotted_line",
"inverse": "dotted_reports",
"description": "Matrix reporting (cross-functional)"
},
{
"name": "mentor",
"inverse": "mentees",
"description": "Mentorship relationship"
}
]
}
}
Fields:
name: Relationship type identifier (used in queries)inverse: Label for reverse direction (e.g., "manager" → "reports")constraints: Optional validation rulesmax_outgoing: Maximum edges from a node (e.g., 1 for single manager)max_incoming: Maximum edges to a node
description: Human-readable documentation
Examples in this guide use short integer IDs (1, 2, 3) for readability. In practice, single-PK tables use UUID primary keys that are auto-generated.
Edge Direction Semantics
Understanding edge direction is critical for correct queries.
Edge Format
Format: from_id → to_id (type: "relationship_name")
Meaning: "from_id's relationship_name is to_id"
Example:
2 → 1 (type: "manager")
This means: "Employee 2's manager is Employee 1" Or: "Employee 2 reports to Employee 1"
Direction in Traversal
When traversing the graph, direction determines which edges to follow:
Outgoing Traversal
Direction: outgoing
SQL: SELECT to_id WHERE from_id = start_node
Meaning: Follow edges FORWARD (find who this node points TO)
Use case: Finding ancestors/parents/managers
Example:
- REST API
- Python
- JavaScript
GET /data/4?include=ancestors
# Traverses OUTGOING from node 4
# Finds: 4 → 2 (manager) and 2 → 1 (manager)
# Returns: Bob (2) and Alice (1)
result = (
auth_client.database.from_("employees")
.filter("id", "eq", 4)
.include("ancestors")
.execute()
)
# Returns: Bob (2) and Alice (1)
const result = await database.from("employees")
.get(4, { include: "ancestors" })
// Returns: Bob (2) and Alice (1)
Incoming Traversal
Direction: incoming
SQL: SELECT from_id WHERE to_id = start_node
Meaning: Follow edges BACKWARD (find who points TO this node)
Use case: Finding descendants/children/reports
Example:
- REST API
- Python
- JavaScript
GET /data/1?include=descendants
# Traverses INCOMING to node 1
# Finds: 2 → 1 (manager) and 3 → 1 (manager)
# Returns: Bob (2) and Carol (3)
result = (
auth_client.database.from_("employees")
.filter("id", "eq", 1)
.include("descendants")
.execute()
)
# Returns: Bob (2) and Carol (3)
const result = await database.from("employees")
.get(1, { include: "descendants" })
// Returns: Bob (2) and Carol (3)
Quick Reference
| Query Type | Direction | SQL Condition | Finds |
|---|---|---|---|
| Descendants (reports) | incoming | to_id = node | Who reports to this node |
| Ancestors (managers) | outgoing | from_id = node | Who this node reports to |
Traversal Operations
Direct Relationships (include parameter)
Query one or two hops from a node:
GET Descendants
Query:
- REST API
- Python
- JavaScript
GET /data/1?include=descendants&depth=2
Traversal:
- Start at node 1
- Follow INCOMING edges (find who points to 1)
- Continue for 2 levels
Response Structure:
{
"data": {
"id": 1,
"name": "Alice Chen"
},
"reports": [
{"id": 2, "name": "Bob Smith", "_depth": 1},
{"id": 3, "name": "Carol White", "_depth": 1},
{"id": 4, "name": "David Lee", "_depth": 2}
]
}
result = (
auth_client.database.from_("employees")
.filter("id", "eq", 1)
.include("descendants")
.depth(2)
.execute()
)
# result["reports"] contains descendants up to 2 levels deep
const result = await database.from("employees")
.get(1, { include: "descendants", depth: 2 })
// result.reports contains descendants up to 2 levels deep
GET Ancestors
Query:
- REST API
- Python
- JavaScript
GET /data/4?include=ancestors
Traversal:
- Start at node 4
- Follow OUTGOING edges (find who 4 points to)
- Continue until no more edges
Response Structure:
{
"data": {
"id": 4,
"name": "David Lee"
},
"manager": [
{"id": 2, "name": "Bob Smith", "_depth": 1},
{"id": 1, "name": "Alice Chen", "_depth": 2}
]
}
result = (
auth_client.database.from_("employees")
.filter("id", "eq", 4)
.include("ancestors")
.execute()
)
# result["manager"] contains the chain up to the root
const result = await database.from("employees")
.get(4, { include: "ancestors" })
// result.manager contains the chain up to the root
GET Both Directions
Query:
- REST API
- Python
- JavaScript
GET /data/2?include=both&depth=1
Traversal:
- Follow INCOMING edges (find descendants)
- Follow OUTGOING edges (find ancestors)
- Depth 1 for both
Response Structure:
{
"data": {
"id": 2,
"name": "Bob Smith"
},
"reports": [
{"id": 4, "name": "David Lee", "_depth": 1}
],
"manager": [
{"id": 1, "name": "Alice Chen", "_depth": 1}
]
}
result = (
auth_client.database.from_("employees")
.filter("id", "eq", 2)
.include("both")
.depth(1)
.execute()
)
# result["reports"] = descendants, result["manager"] = ancestors
const result = await database.from("employees")
.get(2, { include: "both", depth: 1 })
// result.reports = descendants, result.manager = ancestors
Tree Traversal (format=tree)
Nested structure showing parent-child relationships:
Query:
- REST API
- Python
- JavaScript
GET /data/1?format=tree&depth=3
Traversal:
- Build tree from node 1 (or all roots if no ID specified)
- Follow INCOMING edges for children
- Follow OUTGOING edges for ancestors (to show full context)
- Nest children within parents
- Limit to depth 3
Response Structure:
{
"data": [{
"id": 1,
"name": "Alice Chen",
"_depth": 0,
"children": [
{
"id": 2,
"name": "Bob Smith",
"_depth": 1,
"_relationship_type": "manager",
"children": [
{
"id": 4,
"name": "David Lee",
"_depth": 2,
"_relationship_type": "manager",
"children": []
}
]
},
{
"id": 3,
"name": "Carol White",
"_depth": 1,
"_relationship_type": "manager",
"children": []
}
]
}],
"total": 4
}
result = (
auth_client.database.from_("employees")
.filter("id", "eq", 1)
.format("tree")
.depth(3)
.execute()
)
# result["data"] contains nested tree structure
const result = await database.from("employees")
.get(1, { format: "tree", depth: 3 })
// result.data contains nested tree structure
Use Cases:
- Org chart visualization
- Category tree display
- File system browser
- Department hierarchy
Graph Traversal (format=graph)
Returns complete subgraph as nodes + edges:
Query:
- REST API
- Python
- JavaScript
GET /data/?format=graph&relationship_type=manager
Traversal:
- Get all nodes in result set
- Query edges table for edges connecting those nodes
- Filter by relationship type if specified
Response Structure:
{
"data": {
"nodes": [
{
"id": 1,
"name": "Alice Chen",
"title": "CEO"
},
{
"id": 2,
"name": "Bob Smith",
"title": "VP Engineering"
}
],
"edges": [
{
"id": 9,
"from": 2,
"to": 1,
"type": "manager",
"metadata": {"primary": true}
}
]
},
"total": 4
}
result = (
auth_client.database.from_("employees")
.format("graph")
.relationship_types(["manager"])
.execute()
)
# result["data"]["nodes"] and result["data"]["edges"]
const result = await database.from("employees")
.format("graph")
.relationshipType("manager")
.execute()
// result.data.nodes and result.data.edges
Use Cases:
- Network visualization
- Relationship analysis
- Graph databases integration
- Custom graph algorithms
Advanced Queries
Multi-hop Traversal
Find all nodes within N hops:
3-hop traversal:
- REST API
- Python
- JavaScript
GET /data/1?include=descendants&depth=3
Returns:
- Depth 1: Direct reports
- Depth 2: Reports' reports
- Depth 3: Third level down
result = (
auth_client.database.from_("employees")
.filter("id", "eq", 1)
.include("descendants")
.depth(3)
.execute()
)
const result = await database.from("employees")
.get(1, { include: "descendants", depth: 3 })
Performance: O(V + E) where V=nodes, E=edges within depth
Path Finding
Find all paths between two nodes:
Query:
- REST API
- Python
- JavaScript
# Find path from David (4) to CEO (1)
GET /data/4?include=ancestors
Response shows complete path:
{
"data": {"id": 4, "name": "David Lee"},
"manager": [
{"id": 2, "name": "Bob Smith", "_depth": 1},
{"id": 1, "name": "Alice Chen", "_depth": 2}
]
}
Path: David → Bob → Alice
result = (
auth_client.database.from_("employees")
.filter("id", "eq", 4)
.include("ancestors")
.execute()
)
# Path: David → Bob → Alice
const result = await database.from("employees")
.get(4, { include: "ancestors" })
// Path: David → Bob → Alice
Filtering by Type
Query specific relationship types:
Single type:
- REST API
- Python
- JavaScript
GET /data/?format=graph&relationship_type=manager
result = (
auth_client.database.from_("employees")
.format("graph")
.relationship_types(["manager"])
.execute()
)
const result = await database.from("employees")
.format("graph")
.relationshipType("manager")
.execute()
Multiple types:
- REST API
- Python
- JavaScript
GET /data/?format=graph&relationship_type=manager&relationship_type=dotted_line
Response includes only specified types:
{
"data": {
"edges": [
{"from": 2, "to": 1, "type": "manager"},
{"from": 5, "to": 3, "type": "dotted_line"}
]
}
}
result = (
auth_client.database.from_("employees")
.format("graph")
.relationship_types(["manager", "dotted_line"])
.execute()
)
const result = await database.from("employees")
.format("graph")
.relationshipType("manager")
.relationshipType("dotted_line")
.execute()
Cross-functional Teams
Matrix organizations with multiple reporting lines:
Schema:
{
"graph": {
"types": [
{
"name": "manager",
"inverse": "reports",
"description": "Primary reporting line"
},
{
"name": "dotted_line",
"inverse": "dotted_reports",
"description": "Matrix reporting"
}
]
}
}
Example:
Alice (CEO)
├── Bob (VP Eng) [primary]
│ └── Emma (PM) [primary]
└── Carol (VP Sales) [primary]
└── Emma (PM) [dotted_line] # Emma has two managers!
Query Emma's all managers:
- REST API
- Python
- JavaScript
GET /data/5?include=ancestors
Response:
{
"data": {"id": 5, "name": "Emma Davis"},
"manager": [
{"id": 2, "name": "Bob Smith", "_depth": 1, "_relationship_type": "manager"},
{"id": 3, "name": "Carol White", "_depth": 1, "_relationship_type": "dotted_line"},
{"id": 1, "name": "Alice Chen", "_depth": 2, "_relationship_type": "manager"}
]
}
result = (
auth_client.database.from_("employees")
.filter("id", "eq", 5)
.include("ancestors")
.execute()
)
# result["manager"] includes both primary and dotted-line managers
const result = await database.from("employees")
.get(5, { include: "ancestors" })
// result.manager includes both primary and dotted-line managers
Performance Optimization
Index Usage
Edges table has composite indexes for fast lookups:
-- Descendant queries (incoming direction)
CREATE INDEX idx_edges_to_type ON employees_edges(to_id, type);
-- Ancestor queries (outgoing direction)
CREATE INDEX idx_edges_from_type ON employees_edges(from_id, type);
-- Type filtering
CREATE INDEX idx_edges_type ON employees_edges(type);
Query Plan:
EXPLAIN SELECT from_id FROM employees_edges WHERE to_id = 1 AND type = 'manager';
-- Uses index scan (fast!)
Index Scan using idx_edges_to_type on employees_edges
Index Cond: ((to_id = 1) AND (type = 'manager'))
Query Complexity
Time Complexity:
| Operation | Complexity | Notes |
|---|---|---|
| Direct relationships | O(1) | Indexed lookup |
| BFS traversal | O(V + E) | V=vertices, E=edges in subgraph |
| Depth-limited | O(k * b) | k=depth, b=branching factor |
| Full graph | O(V + E) | All nodes and edges |
Space Complexity:
- Include format: O(V) - flat list
- Tree format: O(V) - nested structure
- Graph format: O(V + E) - nodes + edges
Depth Limits
Prevent excessive recursion:
Settings:
# settings.py
DATA_SERVICE_GRAPH_MAX_DEPTH = 10 # Default
Request:
- REST API
- Python
- JavaScript
GET /data/1?include=descendants&depth=999
Response:
{
"error": "Validation failed",
"detail": "depth exceeds maximum allowed (10)"
}
# Raises an error if depth exceeds server maximum
result = (
auth_client.database.from_("employees")
.filter("id", "eq", 1)
.include("descendants")
.depth(999)
.execute()
)
# Error: depth exceeds maximum allowed (10)
// Throws an error if depth exceeds server maximum
const result = await database.from("employees")
.get(1, { include: "descendants", depth: 999 })
// Error: depth exceeds maximum allowed (10)
Recommendations:
- Small org (<100): depth=10 is fine
- Medium org (100-1000): depth=7
- Large org (>1000): depth=5
Caching Strategies
When to cache:
- ✅ Static org charts (updated infrequently)
- ✅ Department hierarchies (stable structure)
- ✅ Category trees (rarely change)
When NOT to cache:
- ❌ Real-time team updates
- ❌ Dynamic permission checks
- ❌ Frequently changing relationships
Cache key strategy:
cache_key = f"hierarchy:{table_name}:{node_id}:{include}:{depth}:{types}"
TTL recommendations:
- Org chart: 1 hour
- Permissions: 5 minutes
- Categories: 24 hours
Real-world Examples
Finding All Reports (3 levels)
Scenario: CEO wants to see entire organization 3 levels deep
Query:
- REST API
- Python
- JavaScript
GET /api/apps/hr/datatables/employees/data/1?include=descendants&depth=3
Expected Response:
{
"data": {
"id": 1,
"name": "Alice Chen",
"title": "CEO"
},
"reports": [
{"id": 2, "name": "Bob Smith", "title": "VP Engineering", "_depth": 1},
{"id": 3, "name": "Carol White", "title": "VP Sales", "_depth": 1},
{"id": 4, "name": "David Lee", "title": "Senior Engineer", "_depth": 2},
{"id": 5, "name": "Emma Davis", "title": "Product Manager", "_depth": 2},
{"id": 6, "name": "Frank Wilson", "title": "Sales Engineer", "_depth": 2},
{"id": 7, "name": "Grace Lee", "title": "Junior Engineer", "_depth": 3}
]
}
result = (
auth_client.database.from_("employees")
.filter("id", "eq", 1)
.include("descendants")
.depth(3)
.execute()
)
# result["reports"] contains all reports up to 3 levels deep
for report in result["reports"]:
indent = " " * report["_depth"]
print(f"{indent}{report['name']} - {report['title']}")
const result = await database.from("employees")
.get(1, { include: "descendants", depth: 3 })
// result.reports contains all reports up to 3 levels deep
for (const report of result.reports) {
const indent = " ".repeat(report._depth)
console.log(`${indent}${report.name} - ${report.title}`)
}
Use case: Executive dashboard showing organizational reach
Manager Chain to CEO
Scenario: Employee profile showing reporting path to top
Query:
- REST API
- Python
- JavaScript
GET /api/apps/hr/datatables/employees/data/7?include=ancestors
Expected Response:
{
"data": {
"id": 7,
"name": "Grace Lee",
"title": "Junior Engineer"
},
"manager": [
{"id": 4, "name": "David Lee", "title": "Senior Engineer", "_depth": 1},
{"id": 2, "name": "Bob Smith", "title": "VP Engineering", "_depth": 2},
{"id": 1, "name": "Alice Chen", "title": "CEO", "_depth": 3}
]
}
result = (
auth_client.database.from_("employees")
.filter("id", "eq", 7)
.include("ancestors")
.execute()
)
# Display reporting chain
print(f"{result['data']['name']} reports to:")
for manager in result["manager"]:
print(f" {'→ ' * manager['_depth']}{manager['name']} ({manager['title']})")
const result = await database.from("employees")
.get(7, { include: "ancestors" })
// Display reporting chain
console.log(`${result.data.name} reports to:`)
for (const manager of result.manager) {
console.log(`${"→ ".repeat(manager._depth)}${manager.name} (${manager.title})`)
}
Use case: "Reports to" section on employee profile page
Cross-department Collaboration
Scenario: Product Manager with dotted line to Sales
Setup:
-- Emma (5) reports primarily to Bob (2)
INSERT INTO employees_edges (from_id, to_id, type, metadata)
VALUES (5, 2, 'manager', '{"primary": true}'::jsonb);
-- Emma also has dotted line to Carol (3)
INSERT INTO employees_edges (from_id, to_id, type, metadata)
VALUES (5, 3, 'dotted_line', '{"percentage": 30}'::jsonb);
Query all reporting lines:
- REST API
- Python
- JavaScript
GET /api/apps/hr/datatables/employees/data/5?include=ancestors
Response:
{
"data": {
"id": 5,
"name": "Emma Davis",
"title": "Product Manager"
},
"manager": [
{
"id": 2,
"name": "Bob Smith",
"title": "VP Engineering",
"_depth": 1,
"_relationship_type": "manager"
},
{
"id": 3,
"name": "Carol White",
"title": "VP Sales",
"_depth": 1,
"_relationship_type": "dotted_line"
},
{
"id": 1,
"name": "Alice Chen",
"title": "CEO",
"_depth": 2,
"_relationship_type": "manager"
}
]
}
result = (
auth_client.database.from_("employees")
.filter("id", "eq", 5)
.include("ancestors")
.execute()
)
# Separate primary and dotted-line managers
primary = [m for m in result["manager"] if m.get("_relationship_type") == "manager"]
dotted = [m for m in result["manager"] if m.get("_relationship_type") == "dotted_line"]
print(f"Primary manager: {primary[0]['name']}")
print(f"Dotted-line to: {dotted[0]['name']}")
const result = await database.from("employees")
.get(5, { include: "ancestors" })
// Separate primary and dotted-line managers
const primary = result.manager.filter(m => m._relationship_type === "manager")
const dotted = result.manager.filter(m => m._relationship_type === "dotted_line")
console.log(`Primary manager: ${primary[0].name}`)
console.log(`Dotted-line to: ${dotted[0].name}`)
Use case: Matrix organization, resource allocation
Permission Inheritance
Scenario: Find all users who inherit access through manager
Query:
- REST API
- Python
- JavaScript
GET /api/apps/hr/datatables/employees/data/2?include=descendants
Response:
{
"data": {
"id": 2,
"name": "Bob Smith"
},
"reports": [
{"id": 4, "name": "David Lee", "_depth": 1},
{"id": 5, "name": "Emma Davis", "_depth": 1},
{"id": 7, "name": "Grace Lee", "_depth": 2}
]
}
result = (
auth_client.database.from_("employees")
.filter("id", "eq", 2)
.include("descendants")
.execute()
)
# All users who inherit access through Bob
user_ids = [r["id"] for r in result["reports"]]
print(f"Users with inherited access: {user_ids}")
const result = await database.from("employees")
.get(2, { include: "descendants" })
// All users who inherit access through Bob
const userIds = result.reports.map(r => r.id)
console.log(`Users with inherited access: ${userIds}`)
Use case:
- Permission audit: Who has access through Bob?
- Security check: Revoke access for Bob's team
- Notification: Broadcast message to Bob's org
Comparison with Other Approaches
vs. Foreign Keys
Hierarchy API:
- REST API
- Python
- JavaScript
GET /data/1?include=descendants&depth=3
# Returns: All descendants in single query
result = (
auth_client.database.from_("employees")
.filter("id", "eq", 1)
.include("descendants")
.depth(3)
.execute()
)
# All descendants in single query
const result = await database.from("employees")
.get(1, { include: "descendants", depth: 3 })
// All descendants in single query
Foreign Key Approach:
# Multiple database queries needed
manager = Employee.objects.get(id=1)
level1 = Employee.objects.filter(manager=manager)
level2 = Employee.objects.filter(manager__in=level1)
level3 = Employee.objects.filter(manager__in=level2)
# N+1 query problem!
Advantages:
- ✅ Single query vs. multiple
- ✅ Handles any depth
- ✅ Supports DAGs (multiple parents)
- ✅ Flexible relationship types
vs. Adjacency List
Hierarchy API:
- Edges stored separately in edges table
- Multiple relationship types supported
- Direction explicit in queries
Adjacency List:
# parent_id stored in employee table
class Employee:
parent_id = ForeignKey('self')
# Limited to single parent
# No relationship types
# Direction implicit
vs. Closure Table
Hierarchy API:
- Uses recursive CTEs for traversal
- Edges table only stores direct relationships
- Computed on-the-fly
Closure Table:
-- Pre-computed paths table
CREATE TABLE employee_paths (
ancestor_id INT,
descendant_id INT,
depth INT
);
-- Must rebuild on every change
-- High storage overhead
Trade-offs:
- Hierarchy API: Slower queries, faster writes
- Closure Table: Faster queries, slower writes
Python SDK Examples
Complete Org Chart Example
Build and query a complete organizational hierarchy:
- REST API
- Python
- JavaScript
# 1. Create employees
curl -X POST "https://your-site.taruvi.cloud/api/apps/my-company/datatables/employees/data/" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '[
{"id": 1, "name": "Alice Chen", "title": "CEO", "email": "alice@company.com"},
{"id": 2, "name": "Bob Smith", "title": "VP Engineering", "email": "bob@company.com"},
{"id": 3, "name": "Carol White", "title": "VP Sales", "email": "carol@company.com"},
{"id": 10, "name": "David Lee", "title": "Senior Engineer", "email": "david@company.com"}
]'
# 2. Create reporting relationships
curl -X POST "https://your-site.taruvi.cloud/api/apps/my-company/datatables/employees/edges/" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '[
{"from_id": 1, "to_id": 2, "type": "manager"},
{"from_id": 1, "to_id": 3, "type": "manager"},
{"from_id": 2, "to_id": 10, "type": "manager"}
]'
# 3. Query org chart as tree
curl -X GET "https://your-site.taruvi.cloud/api/apps/my-company/datatables/employees/data/1?format=tree&depth=5&relationship_type=manager" \
-H "Authorization: Bearer YOUR_TOKEN"
# 4. Get David's reporting chain
curl -X GET "https://your-site.taruvi.cloud/api/apps/my-company/datatables/employees/data/10?include=ancestors&relationship_type=manager" \
-H "Authorization: Bearer YOUR_TOKEN"
from taruvi import Client
# Initialize and authenticate
client = Client(
api_url="https://api.taruvi.cloud",
app_slug="my-company"
)
auth_client = client.auth.signInWithPassword(
username="admin@company.com",
password="secret"
)
# 1. Create employees
employees = auth_client.database.create("employees", [
{"id": 1, "name": "Alice Chen", "title": "CEO", "email": "alice@company.com"},
{"id": 2, "name": "Bob Smith", "title": "VP Engineering", "email": "bob@company.com"},
{"id": 3, "name": "Carol White", "title": "VP Sales", "email": "carol@company.com"},
{"id": 10, "name": "David Lee", "title": "Senior Engineer", "email": "david@company.com"},
])
print(f"Created {len(employees)} employees")
# 2. Create reporting relationships
edges = auth_client.database.create_edges("employees", [
{"from_id": 1, "to_id": 2, "type": "manager"}, # Alice → Bob
{"from_id": 1, "to_id": 3, "type": "manager"}, # Alice → Carol
{"from_id": 2, "to_id": 10, "type": "manager"}, # Bob → David
])
print(f"Created {edges['total']} reporting relationships")
# 3. Query org chart as tree
org_chart = (
auth_client.database.from_("employees")
.filter("id", "eq", 1)
.format("tree")
.include("descendants")
.depth(5)
.relationship_types(["manager"])
.execute()
)
# 4. Display org chart
def display_org_chart(node, indent=0):
print(" " * indent + f"├─ {node['name']} - {node['title']}")
for child in node.get('children', []):
display_org_chart(child, indent + 1)
print("\nOrganization Chart:")
display_org_chart(org_chart)
# 5. Get David's reporting chain
chain = (
auth_client.database.from_("employees")
.filter("id", "eq", 10)
.format("flat")
.include("ancestors")
.relationship_types(["manager"])
.execute()
)
print("\nDavid's Reporting Chain:")
for manager in reversed(chain):
print(f" → {manager['name']} ({manager['title']})")
import { createClient } from "@taruvi/sdk"
// Initialize and authenticate
const client = createClient({
url: "https://api.taruvi.cloud",
appSlug: "my-company",
})
const { database } = await client.auth.signInWithPassword({
username: "admin@company.com",
password: "secret",
})
// 1. Create employees
const employees = await database.from("employees")
.insert([
{ id: 1, name: "Alice Chen", title: "CEO", email: "alice@company.com" },
{ id: 2, name: "Bob Smith", title: "VP Engineering", email: "bob@company.com" },
{ id: 3, name: "Carol White", title: "VP Sales", email: "carol@company.com" },
{ id: 10, name: "David Lee", title: "Senior Engineer", email: "david@company.com" },
])
.execute()
console.log(`Created ${employees.length} employees`)
// 2. Create reporting relationships
const edges = await database.from("employees")
.createEdges([
{ from_id: 1, to_id: 2, type: "manager" },
{ from_id: 1, to_id: 3, type: "manager" },
{ from_id: 2, to_id: 10, type: "manager" },
])
.execute()
console.log(`Created ${edges.total} reporting relationships`)
// 3. Query org chart as tree
const orgChart = await database.from("employees")
.get(1, {
format: "tree",
include: "descendants",
depth: 5,
relationshipType: "manager",
})
// 4. Display org chart
function displayOrgChart(node: any, indent = 0) {
console.log(" ".repeat(indent) + `├─ ${node.name} - ${node.title}`)
for (const child of node.children ?? []) {
displayOrgChart(child, indent + 1)
}
}
console.log("\nOrganization Chart:")
displayOrgChart(orgChart)
// 5. Get David's reporting chain
const chain = await database.from("employees")
.get(10, { include: "ancestors", relationshipType: "manager" })
console.log("\nDavid's Reporting Chain:")
for (const manager of chain.manager.reverse()) {
console.log(` → ${manager.name} (${manager.title})`)
}
Matrix Organization Example
Handle multiple relationship types (manager + dotted line):
- REST API
- Python
- JavaScript
# Create matrix relationships
curl -X POST "https://your-site.taruvi.cloud/api/apps/my-company/datatables/employees/edges/" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '[
{"from_id": 10, "to_id": 2, "type": "manager"},
{"from_id": 10, "to_id": 5, "type": "dotted_line", "metadata": {"project": "AI Initiative", "allocation": "30%"}}
]'
# Get all relationships for an employee
curl -X GET "https://your-site.taruvi.cloud/api/apps/my-company/datatables/employees/data/10?include=both&depth=2&format=graph&relationship_type=manager&relationship_type=dotted_line" \
-H "Authorization: Bearer YOUR_TOKEN"
# Create matrix relationships
edges = auth_client.database.create_edges("employees", [
# Primary reporting lines
{"from_id": 10, "to_id": 2, "type": "manager"},
# Dotted line (project-based)
{"from_id": 10, "to_id": 5, "type": "dotted_line",
"metadata": {"project": "AI Initiative", "allocation": "30%"}}
])
# Get all relationships for an employee
relationships = (
auth_client.database.from_("employees")
.filter("id", "eq", 10)
.format("graph")
.include("both")
.depth(2)
.relationship_types(["manager", "dotted_line"])
.execute()
)
# Separate by type
managers = [e for e in relationships['edges'] if e['type'] == 'manager']
dotted_lines = [e for e in relationships['edges'] if e['type'] == 'dotted_line']
print(f"Direct managers: {len(managers)}")
print(f"Dotted line reports: {len(dotted_lines)}")
// Create matrix relationships
const edges = await database.from("employees")
.createEdges([
// Primary reporting lines
{ from_id: 10, to_id: 2, type: "manager" },
// Dotted line (project-based)
{
from_id: 10,
to_id: 5,
type: "dotted_line",
metadata: { project: "AI Initiative", allocation: "30%" },
},
])
.execute()
// Get all relationships for an employee
const relationships = await database.from("employees")
.get(10, {
format: "graph",
include: "both",
depth: 2,
relationshipType: ["manager", "dotted_line"],
})
// Separate by type
const managers = relationships.data.edges.filter(e => e.type === "manager")
const dottedLines = relationships.data.edges.filter(e => e.type === "dotted_line")
console.log(`Direct managers: ${managers.length}`)
console.log(`Dotted line reports: ${dottedLines.length}`)
Category Tree Example
Build product category hierarchy:
- REST API
- Python
- JavaScript
# Create categories
curl -X POST "https://your-site.taruvi.cloud/api/apps/shop/datatables/categories/data/" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '[
{"id": 1, "name": "Electronics", "slug": "electronics"},
{"id": 2, "name": "Computers", "slug": "computers"},
{"id": 3, "name": "Laptops", "slug": "laptops"},
{"id": 4, "name": "Desktops", "slug": "desktops"}
]'
# Create hierarchy
curl -X POST "https://your-site.taruvi.cloud/api/apps/shop/datatables/categories/edges/" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '[
{"from_id": 1, "to_id": 2, "type": "parent"},
{"from_id": 2, "to_id": 3, "type": "parent"},
{"from_id": 2, "to_id": 4, "type": "parent"}
]'
# Get category tree
curl -X GET "https://your-site.taruvi.cloud/api/apps/shop/datatables/categories/data/1?format=tree&include=descendants&depth=5" \
-H "Authorization: Bearer YOUR_TOKEN"
# Create categories
categories = auth_client.database.create("categories", [
{"id": 1, "name": "Electronics", "slug": "electronics"},
{"id": 2, "name": "Computers", "slug": "computers"},
{"id": 3, "name": "Laptops", "slug": "laptops"},
{"id": 4, "name": "Desktops", "slug": "desktops"},
])
# Create hierarchy
auth_client.database.create_edges("categories", [
{"from_id": 1, "to_id": 2, "type": "parent"}, # Electronics → Computers
{"from_id": 2, "to_id": 3, "type": "parent"}, # Computers → Laptops
{"from_id": 2, "to_id": 4, "type": "parent"}, # Computers → Desktops
])
# Get category tree
tree = (
auth_client.database.from_("categories")
.filter("id", "eq", 1) # Root category
.format("tree")
.include("descendants")
.depth(5)
.execute()
)
def print_categories(node, indent=0):
print(" " * indent + f"- {node['name']}")
for child in node.get('children', []):
print_categories(child, indent + 1)
print_categories(tree)
// Create categories
const categories = await database.from("categories")
.insert([
{ id: 1, name: "Electronics", slug: "electronics" },
{ id: 2, name: "Computers", slug: "computers" },
{ id: 3, name: "Laptops", slug: "laptops" },
{ id: 4, name: "Desktops", slug: "desktops" },
])
.execute()
// Create hierarchy
await database.from("categories")
.createEdges([
{ from_id: 1, to_id: 2, type: "parent" },
{ from_id: 2, to_id: 3, type: "parent" },
{ from_id: 2, to_id: 4, type: "parent" },
])
.execute()
// Get category tree
const tree = await database.from("categories")
.get(1, { format: "tree", include: "descendants", depth: 5 })
function printCategories(node: any, indent = 0) {
console.log(" ".repeat(indent) + `- ${node.name}`)
for (const child of node.children ?? []) {
printCategories(child, indent + 1)
}
}
printCategories(tree)
Pagination for Large Graphs
Handle large edge lists efficiently:
- REST API
- Python
- JavaScript
# Paginate through all edges
curl -X GET "https://your-site.taruvi.cloud/api/apps/hr/datatables/employees/edges/?page=1&page_size=100" \
-H "Authorization: Bearer YOUR_TOKEN"
# Paginate through all edges
page = 1
all_edges = []
while True:
edges = auth_client.database.list_edges(
"employees",
page=page,
page_size=100
)
all_edges.extend(edges['results'])
if page * 100 >= edges['total']:
break
page += 1
print(f"Total edges loaded: {len(all_edges)}")
// Paginate through all edges
let page = 1
const allEdges: any[] = []
while (true) {
const edges = await database.from("employees")
.listEdges({ page, pageSize: 100 })
allEdges.push(...edges.results)
if (page * 100 >= edges.total) break
page++
}
console.log(`Total edges loaded: ${allEdges.length}`)
Graph Format for Visualization
Get data in graph format for visualization libraries:
- REST API
- Python
- JavaScript
curl -X GET "https://your-site.taruvi.cloud/api/apps/hr/datatables/employees/data/1?format=graph&include=descendants&depth=3&relationship_type=manager&relationship_type=dotted_line" \
-H "Authorization: Bearer YOUR_TOKEN"
# Get graph data (nodes + edges)
graph = (
auth_client.database.from_("employees")
.filter("id", "eq", 1)
.format("graph")
.include("descendants")
.depth(3)
.relationship_types(["manager", "dotted_line"])
.execute()
)
# Use with visualization library (e.g., networkx, vis.js)
print(f"Nodes: {len(graph['nodes'])}")
print(f"Edges: {len(graph['edges'])}")
# Example: Convert to networkx
import networkx as nx
G = nx.DiGraph()
for node in graph['nodes']:
G.add_node(node['id'], **node)
for edge in graph['edges']:
G.add_edge(edge['from'], edge['to'], type=edge['type'])
// Get graph data (nodes + edges)
const graph = await database.from("employees")
.get(1, {
format: "graph",
include: "descendants",
depth: 3,
relationshipType: ["manager", "dotted_line"],
})
// Use with visualization library (e.g., vis.js, D3.js)
console.log(`Nodes: ${graph.data.nodes.length}`)
console.log(`Edges: ${graph.data.edges.length}`)
// Example: Convert to vis.js format
const visNodes = graph.data.nodes.map(n => ({ id: n.id, label: n.name }))
const visEdges = graph.data.edges.map(e => ({
from: e.from,
to: e.to,
label: e.type,
}))
Next Steps
- Hierarchy API Reference - Complete API specification
- Hierarchical Data & Org Charts - Basic hierarchy guide
- Data Service Querying - Standard query operations