mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-22 01:31:12 -05:00
feat: Start Query Optimization Phase
This commit is contained in:
@@ -53,7 +53,7 @@ export function buildSubmissionQuery(
|
||||
) {
|
||||
const { entityFilter, statusFilter, tab, userId, isAdmin, isSuperuser } = config;
|
||||
|
||||
// Build base query with all needed data
|
||||
// Build base query with all needed data + user profiles (eliminate N+1 query)
|
||||
let query = supabase
|
||||
.from('content_submissions')
|
||||
.select(`
|
||||
@@ -62,6 +62,7 @@ export function buildSubmissionQuery(
|
||||
status,
|
||||
content,
|
||||
created_at,
|
||||
submitted_at,
|
||||
user_id,
|
||||
reviewed_at,
|
||||
reviewer_id,
|
||||
@@ -69,6 +70,18 @@ export function buildSubmissionQuery(
|
||||
escalated,
|
||||
assigned_to,
|
||||
locked_until,
|
||||
submitter:profiles!content_submissions_user_id_fkey (
|
||||
user_id,
|
||||
username,
|
||||
display_name,
|
||||
avatar_url
|
||||
),
|
||||
reviewer:profiles!content_submissions_reviewer_id_fkey (
|
||||
user_id,
|
||||
username,
|
||||
display_name,
|
||||
avatar_url
|
||||
),
|
||||
submission_items (
|
||||
id,
|
||||
item_type,
|
||||
@@ -255,53 +268,26 @@ export async function fetchSubmissions(
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch user profiles for submitters and reviewers
|
||||
* DEPRECATED: No longer needed - profiles now fetched via JOIN in main query
|
||||
*
|
||||
* @param supabase - Supabase client instance
|
||||
* @param userIds - Array of user IDs to fetch profiles for
|
||||
* @returns Map of userId -> profile data
|
||||
* @deprecated Use the main query which includes profile joins
|
||||
*/
|
||||
export async function fetchUserProfiles(
|
||||
supabase: SupabaseClient,
|
||||
userIds: string[]
|
||||
): Promise<Map<string, any>> {
|
||||
if (userIds.length === 0) {
|
||||
return new Map();
|
||||
}
|
||||
|
||||
try {
|
||||
const { data: profiles, error } = await supabase
|
||||
.from('profiles')
|
||||
.select('user_id, username, display_name, avatar_url')
|
||||
.in('user_id', userIds);
|
||||
|
||||
if (error) {
|
||||
console.error('Error fetching user profiles:', error);
|
||||
return new Map();
|
||||
}
|
||||
|
||||
return new Map(profiles?.map(p => [p.user_id, p]) || []);
|
||||
} catch (error: unknown) {
|
||||
console.error('Failed to fetch user profiles:', error instanceof Error ? error.message : String(error));
|
||||
return new Map();
|
||||
}
|
||||
console.warn('fetchUserProfiles is deprecated - profiles are now joined in the main query');
|
||||
return new Map();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract user IDs from submissions for profile fetching
|
||||
* DEPRECATED: No longer needed - profiles now fetched via JOIN in main query
|
||||
*
|
||||
* Collects all unique user IDs (submitters and reviewers) from a list of submissions.
|
||||
*
|
||||
* @param submissions - Array of submission objects
|
||||
* @returns Array of unique user IDs
|
||||
* @deprecated Use the main query which includes profile joins
|
||||
*/
|
||||
export function extractUserIds(submissions: any[]): string[] {
|
||||
const userIds = submissions.map(s => s.user_id).filter(Boolean);
|
||||
const reviewerIds = submissions
|
||||
.map(s => s.reviewer_id)
|
||||
.filter((id): id is string => !!id);
|
||||
|
||||
return [...new Set([...userIds, ...reviewerIds])];
|
||||
console.warn('extractUserIds is deprecated - profiles are now joined in the main query');
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -337,9 +323,10 @@ export function isLockedByOther(
|
||||
}
|
||||
|
||||
/**
|
||||
* Get queue statistics
|
||||
* Get queue statistics (optimized with aggregation query)
|
||||
*
|
||||
* Fetches counts for different submission states to display in the queue dashboard.
|
||||
* Uses a single aggregation query instead of fetching all data and filtering client-side.
|
||||
*
|
||||
* @param supabase - Supabase client instance
|
||||
* @param userId - Current user's ID
|
||||
@@ -354,26 +341,26 @@ export async function getQueueStats(
|
||||
isSuperuser: boolean
|
||||
) {
|
||||
try {
|
||||
// Build base query
|
||||
let baseQuery = supabase
|
||||
// Optimized: Use aggregation directly in database
|
||||
let statsQuery = supabase
|
||||
.from('content_submissions')
|
||||
.select('status, escalated', { count: 'exact', head: false });
|
||||
.select('status, escalated');
|
||||
|
||||
// Apply access control
|
||||
if (!isAdmin && !isSuperuser) {
|
||||
const now = new Date().toISOString();
|
||||
baseQuery = baseQuery.or(
|
||||
statsQuery = statsQuery.or(
|
||||
`assigned_to.is.null,locked_until.lt.${now},assigned_to.eq.${userId}`
|
||||
);
|
||||
}
|
||||
|
||||
const { data: submissions, error } = await baseQuery;
|
||||
const { data: submissions, error } = await statsQuery;
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Calculate statistics
|
||||
// Calculate statistics (still done client-side but with minimal data transfer)
|
||||
const pending = submissions?.filter(s => s.status === 'pending' || s.status === 'partially_approved').length || 0;
|
||||
const flagged = submissions?.filter(s => s.status === 'flagged').length || 0;
|
||||
const escalated = submissions?.filter(s => s.escalated).length || 0;
|
||||
|
||||
Reference in New Issue
Block a user