Files
rspade_system/app/RSpade/man/model_indexes.txt
root f6fac6c4bc Fix bin/publish: copy docs.dist from project root
Fix bin/publish: use correct .env path for rspade_system
Fix bin/publish script: prevent grep exit code 1 from terminating script

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-21 02:08:33 +00:00

445 lines
15 KiB
Plaintext
Executable File

NAME
model_indexes - Database index analysis and recommendation system
SYNOPSIS
Automatic detection of required database indexes from model relationships
and query patterns
DESCRIPTION
The RSX index analysis system automatically discovers which database
indexes are required by analyzing model relationships and query patterns
in your codebase. It generates optimized index recommendations using
covering index logic and produces reviewable migration files.
Unlike manual index management where developers must remember to create
indexes for every foreign key and query pattern, RSX automatically
detects missing indexes by analyzing:
1. Model relationships - Foreign key columns from #[Relationship] methods
2. Query patterns - AST analysis of Model::where() chains in /rsx/
Key differences from manual approaches:
- Manual: Developer creates indexes as they remember
- RSX: Automatic detection from code analysis
- Manual: No way to know if indexes are missing
- RSX: Command reports all missing indexes
- Manual: Risk of redundant indexes
- RSX: Covering index logic optimizes recommendations
Benefits:
- Never forget to index foreign keys
- Automatic detection of query patterns
- Optimized recommendations via covering logic
- Quota management prevents too many indexes
- Source attribution shows why each index is needed
COMMAND USAGE
Basic Analysis:
php artisan rsx:db:check_indexes
Shows summary of missing indexes and recommendations.
Generate Migration:
php artisan rsx:db:check_indexes --generate-migration
Creates migration file in database/migrations/ with DDL statements.
Detailed Analysis:
php artisan rsx:db:check_indexes --show-details
Shows verbose output with file counts and analysis progress.
Analyze Specific Table:
php artisan rsx:db:check_indexes --table=users
Limit analysis to single table.
Skip Relationship Analysis:
php artisan rsx:db:check_indexes --no-relationships
Only analyze query patterns, ignore relationship indexes.
Skip Query Pattern Analysis:
php artisan rsx:db:check_indexes --no-queries
Only analyze relationships, ignore query patterns.
RELATIONSHIP INDEX DETECTION
How It Works:
The system scans all models extending Rsx_Model_Abstract and finds
methods with #[Relationship] attribute. For each relationship, it
determines which table needs an index on which foreign key column.
Detected Relationships:
- hasMany() / hasOne() - Index on related table's foreign key
- belongsTo() - No index needed (we have the foreign key)
- belongsToMany() - Indexes on both pivot table foreign keys
- morphMany() / morphOne() - Index on related table's morph_id
Priority:
Relationship indexes have HIGHEST priority and are never cut due to
quota limits. Foreign keys must always be indexed for performance.
Example Detection:
// In User_Model.php
#[Relationship]
public function posts()
{
return $this->hasMany(Post_Model::class);
}
// Detected requirement: posts table needs index on user_id column
// Source attribution: rsx/models/user_model.php:45
QUERY PATTERN DETECTION
How It Works:
The system uses AST parsing to scan all PHP files in /rsx/ that
define classes. It finds Model::where() method chains and extracts
the column names in order.
Detected Patterns:
// Single where clause
User_Model::where('status', 'active')->get();
// Detected: Index on (status)
// Chained where clauses
User_Model::where('status', 'active')
->where('created_at', '>', '2024-01-01')
->get();
// Detected: Index on (status, created_at)
// Multiple columns preserve order
Order_Model::where('user_id', $id)
->where('status', 'pending')
->where('created_at', '>', $date)
->get();
// Detected: Index on (user_id, status, created_at)
Not Detected (Documented Limitations):
- Variable column names: where($column, 'value')
- Complex WHERE clauses: whereIn(), whereRaw(), whereExists()
- Join conditions (handled separately via relationship analysis)
- Runtime-determined queries
- orderBy() clauses (result set ordering, not filtering)
Priority:
Query pattern indexes have MEDIUM priority and are subject to quota
limits. Multi-column indexes are preferred over single-column.
Column Order:
The system preserves the exact order of columns from your where()
chain because MySQL index order matters for left-prefix matching.
COVERING INDEX OPTIMIZATION
Left-Prefix Matching Rule:
MySQL can use an index (a, b, c) to satisfy queries filtering on:
- (a) alone
- (a, b) together
- (a, b, c) together
But it CANNOT use the index for:
- (b) alone
- (c) alone
- (b, c) together
Optimization Strategy:
The system uses covering index logic to reduce redundant indexes:
Requirements: (status), (status, created_at), (status, role)
Analysis:
- (status, created_at) covers (status) via left-prefix
- (status, role) covers (status) via left-prefix
- Neither covers the other (different second columns)
Result: Create both (status, created_at) and (status, role)
Drop standalone (status) - it's covered
Reordering for Coverage:
If you need both (status) and (user_id, status), the system could
theoretically create (status, user_id) to cover both. However, this
would hurt performance for queries filtering on user_id alone.
Current Implementation: Does not reorder. Preserves query patterns.
INDEX QUOTA MANAGEMENT
MySQL Limits:
MySQL InnoDB has a hard limit of 64 indexes per table.
RSX Quota:
The system reserves 32 indexes per table for auto-generated indexes,
leaving 32 for user-defined custom indexes.
When Quota Exceeded:
If recommendations exceed available quota, the system prioritizes:
Priority Tier 1: Relationship indexes (never cut)
Priority Tier 2: Multi-column query indexes (2+ columns)
Priority Tier 3: Single-column query indexes (cut first)
The command displays a warning showing which indexes were cut and
suggests manual review for those that couldn't be auto-applied.
Example Warning:
⚠️ WARNING: Table 'users' exceeds index quota
Recommended indexes: 45
Available quota: 12 (64 max - 52 existing user indexes)
The following 33 indexes cannot be auto-applied:
- idx_auto_status (single-column, cut)
- idx_auto_email (single-column, cut)
...
Consider reviewing existing user-defined indexes for redundancy.
MIGRATION GENERATION
Generated Migration Format:
php artisan rsx:db:check_indexes --generate-migration
Creates: database/migrations/{timestamp}_auto_index_recommendations.php
Migration Content:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;
return new class extends Migration
{
public function up()
{
// Table: users
// ============================================
// DROP: Obsolete auto-generated indexes
DB::statement('DROP INDEX IF EXISTS idx_auto_old_column ON users');
// CREATE: New recommended indexes
// Source: rsx/app/frontend/users_controller.php:45
// Priority: query (where)
DB::statement('
CREATE INDEX idx_auto_status_created_at
ON users (status, created_at)
');
// Source: rsx/models/user_model.php:67 (#[Relationship])
// Priority: relationship (hasMany)
DB::statement('
CREATE INDEX idx_auto_organization_id
ON users (organization_id)
');
}
};
Index Naming Convention:
Auto-generated indexes use pattern: idx_auto_{col1}_{col2}_{col3}
Examples:
- idx_auto_user_id
- idx_auto_status_created_at
- idx_auto_status_role_created_at
Source Attribution:
Each CREATE statement includes comments showing:
- Source file and line number where requirement detected
- Priority level (relationship or query)
- Relationship type or query pattern type
Applying Migration:
After reviewing the generated migration:
php artisan migrate
The migration creates new indexes and drops obsolete ones that
are no longer needed.
AUTO-GENERATED INDEX MANAGEMENT
Identification:
The system identifies auto-generated indexes by the idx_auto_ prefix.
Lifecycle:
- System creates indexes with idx_auto_ prefix
- On subsequent runs, system checks which are still needed
- Obsolete auto-generated indexes are dropped
- User-defined indexes are NEVER touched
User-Defined Indexes:
Any index NOT starting with idx_auto_ is considered user-defined
and is completely ignored by the system. This allows developers
to manually create custom indexes without interference.
Idempotency:
Running the command multiple times without code changes produces
no recommendations. The system only suggests changes when:
- New relationships are added
- New query patterns are detected
- Existing relationships are removed
- Existing query patterns change
OUTPUT EXAMPLES
Success Output:
Database Index Analysis
==================================================
Found 12 indexes to create and 4 to drop
Table: users
+ Create index on (status, created_at) - rsx/controllers/users.php:23
+ Create index on (organization_id) - rsx/models/user_model.php:45
- Drop index idx_auto_old_status on (old_status)
Table: posts
+ Create index on (user_id) - rsx/models/post_model.php:12
To generate migration:
php artisan rsx:db:check_indexes --generate-migration
All Indexes Up To Date:
Database Index Analysis
==================================================
✅ All indexes are up to date
With Detailed Analysis:
Database Index Analysis
==================================================
Analyzing model relationships...
Found 23 relationships
Analyzing query patterns...
Scanning 47 PHP files for query patterns
Query pattern analysis complete
Total requirements collected: 87
Optimizing index requirements...
Found 12 indexes to create and 4 to drop
...
LIMITATIONS WARNING
The command always displays limitations to set expectations:
⚠️ LIMITATIONS
This analysis cannot detect:
✗ Dynamic column names: where($variable, ...)
✗ Join conditions (separate relationship analysis)
✗ Complex WHERE clauses (whereRaw, whereIn, etc.)
✗ Runtime-determined query patterns
Always benchmark critical queries and add custom indexes as needed.
Use EXPLAIN to verify query performance in production.
Why These Limitations:
Static analysis cannot predict runtime behavior. The system can
only analyze code patterns visible in source files.
Recommendations:
- Use EXPLAIN on critical queries in production
- Benchmark slow queries and add custom indexes
- Review query logs for patterns not detected by AST
- Run command periodically as codebase evolves
WORKFLOW
Regular Development:
1. Write models with #[Relationship] methods
2. Write controllers with Model::where() queries
3. Run: php artisan rsx:db:check_indexes
4. Review recommendations
5. Run: php artisan rsx:db:check_indexes --generate-migration
6. Review generated migration file
7. Run: php artisan migrate
Before Deployment:
Always run the index analysis before deploying to production to
ensure all required indexes exist. Missing indexes can cause
severe performance issues in production.
Periodic Audits:
Run the command periodically to catch missing indexes as your
codebase evolves. New features often introduce new relationships
and query patterns.
EXAMPLES
Analyze All Tables:
php artisan rsx:db:check_indexes
Generate Migration:
php artisan rsx:db:check_indexes --generate-migration
Check Specific Table:
php artisan rsx:db:check_indexes --table=users
Relationships Only:
php artisan rsx:db:check_indexes --no-queries
Queries Only:
php artisan rsx:db:check_indexes --no-relationships
Verbose Output:
php artisan rsx:db:check_indexes --show-details
TROUBLESHOOTING
No Indexes Detected:
- Verify models extend Rsx_Model_Abstract
- Check #[Relationship] attributes present on relationship methods
- Ensure PHP files in /rsx/ define classes
- Run with --show-details to see file count
Missing Query Pattern Detection:
- Verify Model::where() uses string literals, not variables
- Check model class names end with _Model suffix
- Ensure AST parser can parse file syntax
- Complex queries may not be detected (use custom indexes)
Quota Warnings:
- Review existing user-defined indexes for redundancy
- Consider if all existing indexes are still needed
- Manually select which auto-indexes to apply
- Some tables genuinely need many indexes
Migration Fails:
- Check MySQL version supports CREATE INDEX IF NOT EXISTS
- Verify table names are correct
- Ensure sufficient MySQL privileges
- Review migration file for syntax errors
BEST PRACTICES
Relationship Indexes:
Always use #[Relationship] attribute on relationship methods.
This ensures the system can detect foreign key requirements.
Query Patterns:
Use string literals in where() clauses when possible:
- Good: where('status', 'active')
- Bad: where($column, 'active')
Column Order Matters:
Order your where() clauses by selectivity:
- Most selective columns first
- Example: where('user_id', $id)->where('status', 'active')
- Better than: where('status', 'active')->where('user_id', $id)
Custom Indexes:
For queries the system can't detect, create custom indexes:
- Use standard naming: idx_users_status_created_at
- Avoid idx_auto_ prefix (reserved for system)
- Document why the index is needed
Regular Audits:
Run index analysis as part of deployment checklist.
Missing indexes are easier to add before production.
Migration Review:
Always review generated migrations before applying.
Verify recommendations make sense for your data patterns.
SEE ALSO
model - RSX model system with relationships
model_normalization - Schema normalization command
migrations - Database migration system
coding_standards - Code quality and standards