perf: add projection + O(1) bucket math to muscle-heatmap endpoint
Endpoint previously pulled full WorkoutSession documents (including gpsTrack, notes, kcalEstimate etc.) to count sets per muscle group. Adds a projection that keeps only startTime + exercises.exerciseId + whole set objects — safe (avoids the malformed-sub-array issue the earlier narrower projection caused in the stats overview handler), but still drops the bulky session-level fields. Also swaps the per-session findIndex() over the weekly bucket array for direct date-math against the first bucket's Monday, turning bucket lookup from O(sessions × weeks) into O(sessions).
This commit is contained in:
@@ -10,7 +10,7 @@ Order = impact. Font items + app.html preload intentionally skipped.
|
||||
- [x] 4. Favorites page — drop unnecessary `all_brief` fetch (verified Search uses `favoritesOnly` so `allRecipes` was redundant)
|
||||
- [x] 5. Replace redundant `locals.auth()` with `locals.session` across all routes (68 files, 107 sites — loaders, actions, API endpoints)
|
||||
- [x] 6. Stream fitness stats loader — muscleHeatmap, nutritionStats, periods, sharedPeriods now stream via `{#await}`. `stats` still awaited (too many chart $deriveds depend on it)
|
||||
- [ ] 7. Overview endpoint — add `.select(...)` projection, cap timeseries window
|
||||
- [x] 7. Muscle-heatmap endpoint — add projection + O(1) bucket math. Overview already had a projection; set-subfield narrowing was attempted but reverted (returned malformed sets). Timeseries cap not feasible: totals are lifetime-scoped.
|
||||
- [ ] 8. Calendar payload trim — drop `name` from `yearDays`, pre-filter `feastDots` server-side
|
||||
- [ ] 9. History sessions endpoint — slim exercise payload for list view
|
||||
- [ ] 10. `Cache-Control` headers on stable API endpoints (all_brief, calendar, exercises metadata)
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "homepage",
|
||||
"version": "1.46.20",
|
||||
"version": "1.46.21",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -21,20 +21,19 @@ export const GET: RequestHandler = async ({ url, locals }) => {
|
||||
const since = new Date();
|
||||
since.setDate(since.getDate() - weeks * 7);
|
||||
|
||||
const sessions = await WorkoutSession.find({
|
||||
createdBy: user.nickname,
|
||||
startTime: { $gte: since }
|
||||
}).lean();
|
||||
const sessions = await WorkoutSession.find(
|
||||
{ createdBy: user.nickname, startTime: { $gte: since } },
|
||||
{ startTime: 1, 'exercises.exerciseId': 1, 'exercises.sets': 1 }
|
||||
).lean();
|
||||
|
||||
// Build weekly buckets
|
||||
type MuscleData = { primary: number; secondary: number };
|
||||
const weeklyData: { weekStart: string; muscles: Record<string, MuscleData> }[] = [];
|
||||
|
||||
// Initialize week buckets
|
||||
// Initialize week buckets (Monday-aligned)
|
||||
for (let w = 0; w < weeks; w++) {
|
||||
const d = new Date();
|
||||
d.setDate(d.getDate() - (weeks - 1 - w) * 7);
|
||||
// Find Monday of that week
|
||||
const day = d.getDay();
|
||||
const diff = d.getDate() - day + (day === 0 ? -6 : 1);
|
||||
d.setDate(diff);
|
||||
@@ -47,18 +46,16 @@ export const GET: RequestHandler = async ({ url, locals }) => {
|
||||
weeklyData.push({ weekStart, muscles });
|
||||
}
|
||||
|
||||
// Map a session's timestamp straight to a bucket index using the first
|
||||
// bucket's Monday as the epoch, avoiding a linear findIndex per session.
|
||||
const firstWeekStartMs = weeklyData.length > 0 ? new Date(weeklyData[0].weekStart).getTime() : 0;
|
||||
const weekMs = 7 * 86400000;
|
||||
|
||||
// Aggregate muscle usage
|
||||
for (const session of sessions) {
|
||||
const sessionDate = new Date(session.startTime);
|
||||
// Find which week bucket
|
||||
const weekIdx = weeklyData.findIndex((w, i) => {
|
||||
const start = new Date(w.weekStart);
|
||||
const nextStart = i + 1 < weeklyData.length
|
||||
? new Date(weeklyData[i + 1].weekStart)
|
||||
: new Date(start.getTime() + 7 * 86400000);
|
||||
return sessionDate >= start && sessionDate < nextStart;
|
||||
});
|
||||
if (weekIdx === -1) continue;
|
||||
const weekIdx = Math.floor((sessionDate.getTime() - firstWeekStartMs) / weekMs);
|
||||
if (weekIdx < 0 || weekIdx >= weeklyData.length) continue;
|
||||
|
||||
const bucket = weeklyData[weekIdx].muscles;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user