"Session run length over time": one series per duration bucket, bucket-ordered.
(env: Env, range: Range)
| 412 | |
| 413 | /** "Session run length over time": one series per duration bucket, bucket-ordered. */ |
| 414 | async function durationBucketSeries(env: Env, range: Range): Promise<ApiResult> { |
| 415 | const { results } = await env.DB.prepare( |
| 416 | `SELECT day, value, sum(count) AS n |
| 417 | FROM daily_dim_counts |
| 418 | WHERE dim = 'duration_bucket' AND event = 'index' AND day BETWEEN ? AND ? |
| 419 | GROUP BY day, value`, |
| 420 | ) |
| 421 | .bind(range.from, range.to) |
| 422 | .all<{ day: string; value: string; n: number }>(); |
| 423 | |
| 424 | const labels = dayList(range); |
| 425 | const perBucket = new Map<string, Map<string, number>>(); |
| 426 | for (const row of results) { |
| 427 | let series = perBucket.get(row.value); |
| 428 | if (!series) perBucket.set(row.value, (series = new Map())); |
| 429 | series.set(row.day, row.n ?? 0); |
| 430 | } |
| 431 | |
| 432 | // Fixed buckets first and always present (a bucket with no runs is a real zero, |
| 433 | // and dropping it would silently renumber the ordinal colour ramp); anything |
| 434 | // unexpected from an older client is appended rather than hidden. |
| 435 | const extra = [...perBucket.keys()].filter((v) => !DURATION_BUCKETS.includes(v as never)).sort(); |
| 436 | const order = [...DURATION_BUCKETS, ...extra]; |
| 437 | |
| 438 | const datasets = order.map((bucket) => ({ |
| 439 | label: bucket, |
| 440 | data: densify(labels, perBucket.get(bucket) ?? new Map()), |
| 441 | })); |
| 442 | |
| 443 | return { |
| 444 | body: { |
| 445 | range, |
| 446 | metric: 'duration_buckets', |
| 447 | title: 'Indexing run length over time', |
| 448 | labels, |
| 449 | datasets, |
| 450 | rows: labels.map((day, i) => ({ |
| 451 | day, |
| 452 | ...Object.fromEntries(datasets.map((d) => [d.label, d.data[i] ?? 0])), |
| 453 | })), |
| 454 | }, |
| 455 | cacheControl: CACHE_CONTROL, |
| 456 | }; |
| 457 | } |
| 458 | |
| 459 | // --------------------------------------------------------------------------- |
| 460 | // /api/breakdown — the bars and pies |