From 37342744f94829eb497253f4e69757bb77b763f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 21 May 2026 11:41:55 +0200 Subject: [PATCH] fix(correlations,bp-fast-path,health-score): exclude soft-deleted measurements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every chunked findMany walk in the three analytics fast-paths fed the raw `measurements` table through Prisma without a `deletedAt` filter. The correlation pairing, BP-in-target windowing, and Health Score weight / BP source-attribution would each fold tombstoned rows once iOS soft-deletes its first reading. Add `deletedAt: null` to: - `fetchSeriesChunked` in `correlations-fast-path.ts` - `fetchSeriesChunked` in `bp-in-target-fast-path.ts` - the WEIGHT source attribution + WEIGHT live-fallback + BP_SYS source attribution reads in `health-score-fast-path.ts` The DAY-bucket reads from `measurement_rollups` are unaffected — they ride the writer-side filter landed earlier in the chain. --- src/lib/analytics/bp-in-target-fast-path.ts | 2 +- src/lib/analytics/correlations-fast-path.ts | 2 +- src/lib/analytics/health-score-fast-path.ts | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lib/analytics/bp-in-target-fast-path.ts b/src/lib/analytics/bp-in-target-fast-path.ts index 1beddab44..a254a262a 100644 --- a/src/lib/analytics/bp-in-target-fast-path.ts +++ b/src/lib/analytics/bp-in-target-fast-path.ts @@ -432,7 +432,7 @@ async function fetchSeriesChunked( let cursorId: string | undefined; for (let page = 0; page < 1000; page++) { const chunk = await prisma.measurement.findMany({ - where: { userId, type, measuredAt: { gte: since } }, + where: { userId, type, measuredAt: { gte: since }, deletedAt: null }, orderBy: [{ measuredAt: "asc" }, { id: "asc" }], select: { id: true, measuredAt: true, value: true }, take: CHUNK, diff --git a/src/lib/analytics/correlations-fast-path.ts b/src/lib/analytics/correlations-fast-path.ts index d5fe75223..ba82bb7b3 100644 --- a/src/lib/analytics/correlations-fast-path.ts +++ b/src/lib/analytics/correlations-fast-path.ts @@ -342,7 +342,7 @@ async function fetchSeriesChunked( let cursorId: string | undefined; for (let page = 0; page < 1000; page++) { const chunk = await prisma.measurement.findMany({ - where: { userId, type, measuredAt: { gte: since } }, + where: { userId, type, measuredAt: { gte: since }, deletedAt: null }, orderBy: [{ measuredAt: "asc" }, { id: "asc" }], select: { id: true, measuredAt: true, value: true }, take: CHUNK, diff --git a/src/lib/analytics/health-score-fast-path.ts b/src/lib/analytics/health-score-fast-path.ts index 3350bbdf0..1257172cb 100644 --- a/src/lib/analytics/health-score-fast-path.ts +++ b/src/lib/analytics/health-score-fast-path.ts @@ -192,6 +192,7 @@ export async function computeUserHealthScoreFastPath( userId, type: "WEIGHT", measuredAt: { gte: since30d, lte: now }, + deletedAt: null, }, select: { measuredAt: true, source: true }, orderBy: { measuredAt: "asc" }, @@ -206,6 +207,7 @@ export async function computeUserHealthScoreFastPath( userId, type: "WEIGHT", measuredAt: { gte: prevSince30d, lte: now }, + deletedAt: null, }, select: { value: true, measuredAt: true, source: true }, orderBy: { measuredAt: "asc" }, @@ -235,6 +237,7 @@ export async function computeUserHealthScoreFastPath( userId, type: "BLOOD_PRESSURE_SYS", measuredAt: { gte: since30d, lte: now }, + deletedAt: null, }, select: { measuredAt: true, source: true }, orderBy: { measuredAt: "asc" },