Add performance optimizations and database indexes

Database Indexes (migrations 006-009):
- Add case-insensitive indexes for auth lookups (email, username)
- Add composite indexes for task kanban queries
- Add indexes for notification, document, and completion queries
- Add unique index for active share codes
- Remove redundant idx_share_code_active and idx_notification_user_sent

Repository Optimizations:
- Add FindResidenceIDsByUser() lightweight method (IDs only, no preloads)
- Optimize GetResidenceUsers() with single UNION query (was 2 queries)
- Optimize kanban completion preloads to minimal columns (id, task_id, completed_at)

Service Optimizations:
- Remove Category/Priority/Frequency preloads from task queries
- Remove summary calculations from CRUD responses (client calculates)
- Use lightweight FindResidenceIDsByUser() instead of full FindByUser()

These changes reduce database load and response times for common operations.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Trey t
2025-12-14 01:06:08 -06:00
parent 2ea5cea936
commit 0cf64cfb0c
22 changed files with 436 additions and 203 deletions

View File

@@ -67,6 +67,24 @@ func (r *ResidenceRepository) FindByUser(userID uint) ([]models.Residence, error
return residences, nil
}
// FindResidenceIDsByUser returns just the IDs of residences a user has access to.
// This is a lightweight alternative to FindByUser() when only IDs are needed.
// Avoids preloading Owner, Users, PropertyType relations.
func (r *ResidenceRepository) FindResidenceIDsByUser(userID uint) ([]uint, error) {
var ids []uint
err := r.db.Model(&models.Residence{}).
Where("is_active = ?", true).
Where("owner_id = ? OR id IN (?)",
userID,
r.db.Table("residence_residence_users").Select("residence_id").Where("user_id = ?", userID),
).
Pluck("id", &ids).Error
if err != nil {
return nil, err
}
return ids, nil
}
// FindOwnedByUser finds all residences owned by a user
func (r *ResidenceRepository) FindOwnedByUser(userID uint) ([]models.Residence, error) {
var residences []models.Residence
@@ -118,42 +136,41 @@ func (r *ResidenceRepository) RemoveUser(residenceID, userID uint) error {
).Error
}
// GetResidenceUsers returns all users with access to a residence
// GetResidenceUsers returns all users with access to a residence (owner + shared users).
// Optimized: Uses a single UNION query instead of preloading full residence with relations.
func (r *ResidenceRepository) GetResidenceUsers(residenceID uint) ([]models.User, error) {
residence, err := r.FindByID(residenceID)
var users []models.User
// Single query to get both owner and shared users
err := r.db.Raw(`
SELECT DISTINCT u.* FROM auth_user u
WHERE u.id IN (
SELECT owner_id FROM residence_residence WHERE id = ? AND is_active = true
UNION
SELECT user_id FROM residence_residence_users WHERE residence_id = ?
)
`, residenceID, residenceID).Scan(&users).Error
if err != nil {
return nil, err
}
users := make([]models.User, 0, len(residence.Users)+1)
users = append(users, residence.Owner)
users = append(users, residence.Users...)
return users, nil
}
// HasAccess checks if a user has access to a residence
func (r *ResidenceRepository) HasAccess(residenceID, userID uint) (bool, error) {
var count int64
// Check if user is owner
err := r.db.Model(&models.Residence{}).
Where("id = ? AND owner_id = ? AND is_active = ?", residenceID, userID, true).
Count(&count).Error
// Single query using UNION to check owner OR member access
err := r.db.Raw(`
SELECT COUNT(*) FROM (
SELECT 1 FROM residence_residence
WHERE id = ? AND owner_id = ? AND is_active = true
UNION
SELECT 1 FROM residence_residence_users
WHERE residence_id = ? AND user_id = ?
) access_check
`, residenceID, userID, residenceID, userID).Scan(&count).Error
if err != nil {
return false, err
}
if count > 0 {
return true, nil
}
// Check if user is in shared users
err = r.db.Table("residence_residence_users").
Where("residence_id = ? AND user_id = ?", residenceID, userID).
Count(&count).Error
if err != nil {
return false, err
}
return count > 0, nil
}