fix(correlations,bp-fast-path,health-score): exclude soft-deleted measurements

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.
This commit is contained in:
Marc-André Bombeck committed 2026-05-21 11:41:55 +02:00
1 parent 1f49041025
commit 37342744f9
3 files changed
+5 -2

No files matched your search

+1 -1
View File
@@ -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,
+1 -1
View File
@@ -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,
@@ -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" },