Init Repo
This commit is contained in:
@@ -0,0 +1,255 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Create database indexes to optimize task status queries for shots and assets.
|
||||
This script implements the database schema optimization from the shot-asset-task-status-optimization spec.
|
||||
"""
|
||||
import sqlite3
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
def create_task_status_indexes():
|
||||
"""Create optimized indexes for task status queries."""
|
||||
|
||||
db_path = Path("database.db")
|
||||
if not db_path.exists():
|
||||
print("Error: database.db not found. Please run from the backend directory.")
|
||||
return False
|
||||
|
||||
conn = sqlite3.connect('database.db')
|
||||
cursor = conn.cursor()
|
||||
|
||||
try:
|
||||
print("Creating optimized indexes for task status queries...")
|
||||
|
||||
# Index 1: Optimize task lookups by shot_id (active tasks only)
|
||||
print("Creating idx_tasks_shot_id_active...")
|
||||
cursor.execute("""
|
||||
CREATE INDEX IF NOT EXISTS idx_tasks_shot_id_active
|
||||
ON tasks(shot_id)
|
||||
WHERE deleted_at IS NULL
|
||||
""")
|
||||
|
||||
# Index 2: Optimize task lookups by asset_id (active tasks only)
|
||||
print("Creating idx_tasks_asset_id_active...")
|
||||
cursor.execute("""
|
||||
CREATE INDEX IF NOT EXISTS idx_tasks_asset_id_active
|
||||
ON tasks(asset_id)
|
||||
WHERE deleted_at IS NULL
|
||||
""")
|
||||
|
||||
# Index 3: Optimize task status filtering (active tasks only)
|
||||
print("Creating idx_tasks_status_type_active...")
|
||||
cursor.execute("""
|
||||
CREATE INDEX IF NOT EXISTS idx_tasks_status_type_active
|
||||
ON tasks(status, task_type)
|
||||
WHERE deleted_at IS NULL
|
||||
""")
|
||||
|
||||
# Index 4: Composite index for shot + status + type queries (most common pattern)
|
||||
print("Creating idx_tasks_shot_status_type_active...")
|
||||
cursor.execute("""
|
||||
CREATE INDEX IF NOT EXISTS idx_tasks_shot_status_type_active
|
||||
ON tasks(shot_id, status, task_type)
|
||||
WHERE deleted_at IS NULL
|
||||
""")
|
||||
|
||||
# Index 5: Composite index for asset + status + type queries (most common pattern)
|
||||
print("Creating idx_tasks_asset_status_type_active...")
|
||||
cursor.execute("""
|
||||
CREATE INDEX IF NOT EXISTS idx_tasks_asset_status_type_active
|
||||
ON tasks(asset_id, status, task_type)
|
||||
WHERE deleted_at IS NULL
|
||||
""")
|
||||
|
||||
# Index 6: Optimize queries that need task details (id, type, status, assignee, updated_at)
|
||||
print("Creating idx_tasks_details_shot...")
|
||||
cursor.execute("""
|
||||
CREATE INDEX IF NOT EXISTS idx_tasks_details_shot
|
||||
ON tasks(shot_id, id, task_type, status, assigned_user_id, updated_at)
|
||||
WHERE deleted_at IS NULL
|
||||
""")
|
||||
|
||||
# Index 7: Optimize queries that need task details for assets
|
||||
print("Creating idx_tasks_details_asset...")
|
||||
cursor.execute("""
|
||||
CREATE INDEX IF NOT EXISTS idx_tasks_details_asset
|
||||
ON tasks(asset_id, id, task_type, status, assigned_user_id, updated_at)
|
||||
WHERE deleted_at IS NULL
|
||||
""")
|
||||
|
||||
# Index 8: Optimize project-wide task queries with status filtering
|
||||
print("Creating idx_tasks_project_status_active...")
|
||||
cursor.execute("""
|
||||
CREATE INDEX IF NOT EXISTS idx_tasks_project_status_active
|
||||
ON tasks(project_id, status, task_type)
|
||||
WHERE deleted_at IS NULL
|
||||
""")
|
||||
|
||||
conn.commit()
|
||||
print("✅ All indexes created successfully!")
|
||||
|
||||
# Verify indexes were created
|
||||
print("\nVerifying created indexes...")
|
||||
cursor.execute("""
|
||||
SELECT name FROM sqlite_master
|
||||
WHERE type='index'
|
||||
AND name LIKE 'idx_tasks_%_active'
|
||||
ORDER BY name
|
||||
""")
|
||||
|
||||
new_indexes = cursor.fetchall()
|
||||
for idx in new_indexes:
|
||||
print(f" ✓ {idx[0]}")
|
||||
|
||||
return True
|
||||
|
||||
except sqlite3.Error as e:
|
||||
print(f"❌ Error creating indexes: {e}")
|
||||
conn.rollback()
|
||||
return False
|
||||
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
def test_index_performance():
|
||||
"""Test the performance of the new indexes with sample queries."""
|
||||
|
||||
conn = sqlite3.connect('database.db')
|
||||
cursor = conn.cursor()
|
||||
|
||||
try:
|
||||
print("\n" + "="*50)
|
||||
print("TESTING INDEX PERFORMANCE")
|
||||
print("="*50)
|
||||
|
||||
# Test 1: Shot task status aggregation query
|
||||
print("\nTest 1: Shot task status aggregation")
|
||||
cursor.execute("EXPLAIN QUERY PLAN SELECT shot_id, task_type, status FROM tasks WHERE shot_id = 1 AND deleted_at IS NULL")
|
||||
plan = cursor.fetchall()
|
||||
for row in plan:
|
||||
print(f" {row}")
|
||||
|
||||
# Test 2: Asset task status aggregation query
|
||||
print("\nTest 2: Asset task status aggregation")
|
||||
cursor.execute("EXPLAIN QUERY PLAN SELECT asset_id, task_type, status FROM tasks WHERE asset_id = 1 AND deleted_at IS NULL")
|
||||
plan = cursor.fetchall()
|
||||
for row in plan:
|
||||
print(f" {row}")
|
||||
|
||||
# Test 3: Project-wide status filtering
|
||||
print("\nTest 3: Project-wide status filtering")
|
||||
cursor.execute("EXPLAIN QUERY PLAN SELECT * FROM tasks WHERE project_id = 1 AND status = 'in_progress' AND deleted_at IS NULL")
|
||||
plan = cursor.fetchall()
|
||||
for row in plan:
|
||||
print(f" {row}")
|
||||
|
||||
# Test 4: Complex join query (shots with task status)
|
||||
print("\nTest 4: Shots with task status join")
|
||||
cursor.execute("""
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT s.id, s.name, t.task_type, t.status
|
||||
FROM shots s
|
||||
LEFT JOIN tasks t ON s.id = t.shot_id AND t.deleted_at IS NULL
|
||||
WHERE s.deleted_at IS NULL
|
||||
LIMIT 10
|
||||
""")
|
||||
plan = cursor.fetchall()
|
||||
for row in plan:
|
||||
print(f" {row}")
|
||||
|
||||
print("\n✅ Index performance tests completed!")
|
||||
|
||||
except sqlite3.Error as e:
|
||||
print(f"❌ Error testing performance: {e}")
|
||||
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
def get_sample_data_stats():
|
||||
"""Get statistics about the current data to validate index effectiveness."""
|
||||
|
||||
conn = sqlite3.connect('database.db')
|
||||
cursor = conn.cursor()
|
||||
|
||||
try:
|
||||
print("\n" + "="*50)
|
||||
print("SAMPLE DATA STATISTICS")
|
||||
print("="*50)
|
||||
|
||||
# Count total tasks
|
||||
cursor.execute("SELECT COUNT(*) FROM tasks WHERE deleted_at IS NULL")
|
||||
total_tasks = cursor.fetchone()[0]
|
||||
print(f"Total active tasks: {total_tasks}")
|
||||
|
||||
# Count tasks by type
|
||||
cursor.execute("SELECT task_type, COUNT(*) FROM tasks WHERE deleted_at IS NULL GROUP BY task_type")
|
||||
task_types = cursor.fetchall()
|
||||
print("\nTasks by type:")
|
||||
for task_type, count in task_types:
|
||||
print(f" {task_type}: {count}")
|
||||
|
||||
# Count tasks by status
|
||||
cursor.execute("SELECT status, COUNT(*) FROM tasks WHERE deleted_at IS NULL GROUP BY status")
|
||||
task_statuses = cursor.fetchall()
|
||||
print("\nTasks by status:")
|
||||
for status, count in task_statuses:
|
||||
print(f" {status}: {count}")
|
||||
|
||||
# Count shots with tasks
|
||||
cursor.execute("""
|
||||
SELECT COUNT(DISTINCT s.id)
|
||||
FROM shots s
|
||||
INNER JOIN tasks t ON s.id = t.shot_id
|
||||
WHERE s.deleted_at IS NULL AND t.deleted_at IS NULL
|
||||
""")
|
||||
shots_with_tasks = cursor.fetchone()[0]
|
||||
print(f"\nShots with tasks: {shots_with_tasks}")
|
||||
|
||||
# Count assets with tasks
|
||||
cursor.execute("""
|
||||
SELECT COUNT(DISTINCT a.id)
|
||||
FROM assets a
|
||||
INNER JOIN tasks t ON a.id = t.asset_id
|
||||
WHERE a.deleted_at IS NULL AND t.deleted_at IS NULL
|
||||
""")
|
||||
assets_with_tasks = cursor.fetchone()[0]
|
||||
print(f"Assets with tasks: {assets_with_tasks}")
|
||||
|
||||
return {
|
||||
'total_tasks': total_tasks,
|
||||
'shots_with_tasks': shots_with_tasks,
|
||||
'assets_with_tasks': assets_with_tasks
|
||||
}
|
||||
|
||||
except sqlite3.Error as e:
|
||||
print(f"❌ Error getting statistics: {e}")
|
||||
return None
|
||||
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Shot-Asset Task Status Optimization: Database Index Creation")
|
||||
print("=" * 60)
|
||||
|
||||
# Get current data statistics
|
||||
stats = get_sample_data_stats()
|
||||
|
||||
# Create the indexes
|
||||
success = create_task_status_indexes()
|
||||
|
||||
if success:
|
||||
# Test index performance
|
||||
test_index_performance()
|
||||
|
||||
print("\n" + "="*60)
|
||||
print("INDEX CREATION COMPLETED SUCCESSFULLY!")
|
||||
print("="*60)
|
||||
print("\nNext steps:")
|
||||
print("1. Run backend optimization tests")
|
||||
print("2. Implement optimized query patterns in routers")
|
||||
print("3. Test with larger datasets")
|
||||
|
||||
else:
|
||||
print("\n❌ Index creation failed!")
|
||||
sys.exit(1)
|
||||
Reference in New Issue
Block a user