@uptimizr/db 0.8.2 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/uptimizr-db-migrate.js +4 -0
- package/bin/uptimizr-db-new-project.js +4 -0
- package/dist/cli/createProject.js +0 -0
- package/dist/cli/migrate.js +0 -0
- package/dist/env.d.ts +57 -2
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +31 -2
- package/dist/env.js.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/query/aggregations.d.ts +4 -4
- package/dist/query/aggregations.d.ts.map +1 -1
- package/dist/query/aggregations.js +200 -143
- package/dist/query/aggregations.js.map +1 -1
- package/dist/query/clickhouseDialect.d.ts.map +1 -1
- package/dist/query/clickhouseDialect.js +7 -2
- package/dist/query/clickhouseDialect.js.map +1 -1
- package/dist/query/dialect.d.ts +55 -11
- package/dist/query/dialect.d.ts.map +1 -1
- package/dist/query/dialect.js +6 -4
- package/dist/query/dialect.js.map +1 -1
- package/dist/query/duckdbDialect.d.ts.map +1 -1
- package/dist/query/duckdbDialect.js +7 -2
- package/dist/query/duckdbDialect.js.map +1 -1
- package/dist/query/index.d.ts +3 -0
- package/dist/query/index.d.ts.map +1 -1
- package/dist/query/index.js +6 -0
- package/dist/query/index.js.map +1 -1
- package/dist/query/mssqlDialect.d.ts +99 -0
- package/dist/query/mssqlDialect.d.ts.map +1 -0
- package/dist/query/mssqlDialect.js +398 -0
- package/dist/query/mssqlDialect.js.map +1 -0
- package/dist/query/postgresDialect.d.ts +48 -0
- package/dist/query/postgresDialect.d.ts.map +1 -0
- package/dist/query/postgresDialect.js +167 -0
- package/dist/query/postgresDialect.js.map +1 -0
- package/dist/query/relational.d.ts +69 -0
- package/dist/query/relational.d.ts.map +1 -0
- package/dist/query/relational.js +94 -0
- package/dist/query/relational.js.map +1 -0
- package/package.json +5 -4
|
@@ -43,7 +43,7 @@ export function buildListSessions(projectId, opts, d) {
|
|
|
43
43
|
SELECT
|
|
44
44
|
session_id,
|
|
45
45
|
${d.anyValue("visitor_id")} AS visitor_id,
|
|
46
|
-
count() AS events,
|
|
46
|
+
count(*) AS events,
|
|
47
47
|
min(ts) AS started_at,
|
|
48
48
|
max(ts) AS ended_at
|
|
49
49
|
FROM events
|
|
@@ -73,11 +73,11 @@ export function buildPointerHeatmap(projectId, opts, d) {
|
|
|
73
73
|
SELECT
|
|
74
74
|
floor(screen[1] * ${bins}) AS gx,
|
|
75
75
|
floor(screen[2] * ${bins}) AS gy,
|
|
76
|
-
count() AS count
|
|
76
|
+
count(*) AS count
|
|
77
77
|
FROM events
|
|
78
78
|
WHERE project_id = ${pid}
|
|
79
79
|
AND event_type IN ('pointer_move', 'pointer_click')
|
|
80
|
-
AND
|
|
80
|
+
AND ${d.arrayLength("screen")} = 2${range}${scene}${source}${session}${cameraMode}
|
|
81
81
|
GROUP BY gx, gy
|
|
82
82
|
ORDER BY count DESC
|
|
83
83
|
`,
|
|
@@ -109,7 +109,7 @@ export function buildMeshUvHeatmap(projectId, opts, d) {
|
|
|
109
109
|
const v = d.jsonFloat("payload", "uv", "1");
|
|
110
110
|
return {
|
|
111
111
|
query: `
|
|
112
|
-
SELECT gx, gy, count() AS count
|
|
112
|
+
SELECT gx, gy, count(*) AS count
|
|
113
113
|
FROM (
|
|
114
114
|
SELECT
|
|
115
115
|
floor(${u} * ${bins}) AS gx,
|
|
@@ -147,11 +147,11 @@ export function buildWorldHeatmap(projectId, opts, d) {
|
|
|
147
147
|
floor(hit_point[1] / ${cellSize}) AS vx,
|
|
148
148
|
floor(hit_point[2] / ${cellSize}) AS vy,
|
|
149
149
|
floor(hit_point[3] / ${cellSize}) AS vz,
|
|
150
|
-
count() AS count
|
|
150
|
+
count(*) AS count
|
|
151
151
|
FROM events
|
|
152
152
|
WHERE project_id = ${pid}
|
|
153
153
|
AND event_type IN ('pointer_move', 'pointer_click')
|
|
154
|
-
AND
|
|
154
|
+
AND ${d.arrayLength("hit_point")} = 3${range}${scene}${source}${cameraMode}${region}
|
|
155
155
|
GROUP BY vx, vy, vz
|
|
156
156
|
ORDER BY count DESC
|
|
157
157
|
LIMIT ${limit}
|
|
@@ -177,17 +177,17 @@ export function buildWorldHeatmapStats(projectId, opts, d) {
|
|
|
177
177
|
const region = regionClause(bag, opts, HIT_POINT_COLS);
|
|
178
178
|
return {
|
|
179
179
|
query: `
|
|
180
|
-
SELECT count() AS cells, coalesce(sum(c), 0) AS hits
|
|
180
|
+
SELECT count(*) AS cells, coalesce(sum(c), 0) AS hits
|
|
181
181
|
FROM (
|
|
182
182
|
SELECT
|
|
183
183
|
floor(hit_point[1] / ${cellSize}) AS vx,
|
|
184
184
|
floor(hit_point[2] / ${cellSize}) AS vy,
|
|
185
185
|
floor(hit_point[3] / ${cellSize}) AS vz,
|
|
186
|
-
count() AS c
|
|
186
|
+
count(*) AS c
|
|
187
187
|
FROM events
|
|
188
188
|
WHERE project_id = ${pid}
|
|
189
189
|
AND event_type IN ('pointer_move', 'pointer_click')
|
|
190
|
-
AND
|
|
190
|
+
AND ${d.arrayLength("hit_point")} = 3${range}${scene}${source}${cameraMode}${region}
|
|
191
191
|
GROUP BY vx, vy, vz
|
|
192
192
|
) t
|
|
193
193
|
`,
|
|
@@ -220,11 +220,11 @@ export function buildGazeHeatmap(projectId, opts, d) {
|
|
|
220
220
|
floor(hit_point[1] / ${cellSize}) AS vx,
|
|
221
221
|
floor(hit_point[2] / ${cellSize}) AS vy,
|
|
222
222
|
floor(hit_point[3] / ${cellSize}) AS vz,
|
|
223
|
-
count() AS count
|
|
223
|
+
count(*) AS count
|
|
224
224
|
FROM events
|
|
225
225
|
WHERE project_id = ${pid}
|
|
226
226
|
AND event_type = 'camera_sample'
|
|
227
|
-
AND
|
|
227
|
+
AND ${d.arrayLength("hit_point")} = 3${range}${scene}${session}${cameraMode}${region}
|
|
228
228
|
GROUP BY vx, vy, vz
|
|
229
229
|
ORDER BY count DESC
|
|
230
230
|
LIMIT ${limit}
|
|
@@ -248,17 +248,17 @@ export function buildGazeHeatmapStats(projectId, opts, d) {
|
|
|
248
248
|
const region = regionClause(bag, opts, HIT_POINT_COLS);
|
|
249
249
|
return {
|
|
250
250
|
query: `
|
|
251
|
-
SELECT count() AS cells, coalesce(sum(c), 0) AS hits
|
|
251
|
+
SELECT count(*) AS cells, coalesce(sum(c), 0) AS hits
|
|
252
252
|
FROM (
|
|
253
253
|
SELECT
|
|
254
254
|
floor(hit_point[1] / ${cellSize}) AS vx,
|
|
255
255
|
floor(hit_point[2] / ${cellSize}) AS vy,
|
|
256
256
|
floor(hit_point[3] / ${cellSize}) AS vz,
|
|
257
|
-
count() AS c
|
|
257
|
+
count(*) AS c
|
|
258
258
|
FROM events
|
|
259
259
|
WHERE project_id = ${pid}
|
|
260
260
|
AND event_type = 'camera_sample'
|
|
261
|
-
AND
|
|
261
|
+
AND ${d.arrayLength("hit_point")} = 3${range}${scene}${session}${cameraMode}${region}
|
|
262
262
|
GROUP BY vx, vy, vz
|
|
263
263
|
) t
|
|
264
264
|
`,
|
|
@@ -282,11 +282,11 @@ export function buildCameraDirectionHeatmap(projectId, opts, d) {
|
|
|
282
282
|
SELECT
|
|
283
283
|
floor((atan2(direction[3], direction[1]) + pi()) / (2 * pi()) * ${bins}) AS azimuth_bin,
|
|
284
284
|
floor((asin(direction[2] / greatest(${d.vectorNorm("direction")}, 1e-6)) + pi() / 2) / pi() * ${bins}) AS elevation_bin,
|
|
285
|
-
count() AS count
|
|
285
|
+
count(*) AS count
|
|
286
286
|
FROM events
|
|
287
287
|
WHERE project_id = ${pid}
|
|
288
288
|
AND event_type = 'camera_sample'
|
|
289
|
-
AND
|
|
289
|
+
AND ${d.arrayLength("direction")} = 3${range}${scene}${session}${cameraMode}
|
|
290
290
|
GROUP BY azimuth_bin, elevation_bin
|
|
291
291
|
ORDER BY count DESC
|
|
292
292
|
`,
|
|
@@ -319,7 +319,7 @@ export function buildViewCoverageHistogram(projectId, opts, d) {
|
|
|
319
319
|
query: `
|
|
320
320
|
SELECT
|
|
321
321
|
least(floor(coverage_pct / 25), 3) * 25 AS bucket,
|
|
322
|
-
count() AS sessions
|
|
322
|
+
count(*) AS sessions
|
|
323
323
|
FROM (
|
|
324
324
|
SELECT
|
|
325
325
|
session_id,
|
|
@@ -332,7 +332,7 @@ export function buildViewCoverageHistogram(projectId, opts, d) {
|
|
|
332
332
|
FROM events
|
|
333
333
|
WHERE project_id = ${pid}
|
|
334
334
|
AND event_type = 'camera_sample'
|
|
335
|
-
AND
|
|
335
|
+
AND ${d.arrayLength("direction")} = 3${range}${scene}${session}${cameraMode}
|
|
336
336
|
) binned
|
|
337
337
|
GROUP BY session_id
|
|
338
338
|
) per_session
|
|
@@ -364,11 +364,11 @@ export function buildCameraPositionHeatmap(projectId, opts, d) {
|
|
|
364
364
|
floor(position[1] / ${cellSize}) AS gx,
|
|
365
365
|
floor(position[3] / ${cellSize}) AS gz,
|
|
366
366
|
avg(position[2]) AS avg_y,
|
|
367
|
-
count() AS count
|
|
367
|
+
count(*) AS count
|
|
368
368
|
FROM events
|
|
369
369
|
WHERE project_id = ${pid}
|
|
370
370
|
AND event_type = 'camera_sample'
|
|
371
|
-
AND
|
|
371
|
+
AND ${d.arrayLength("position")} = 3${range}${scene}${session}${cameraMode}${region}
|
|
372
372
|
GROUP BY gx, gz
|
|
373
373
|
ORDER BY count DESC
|
|
374
374
|
LIMIT ${limit}
|
|
@@ -399,7 +399,7 @@ export function buildSessionTrajectory(projectId, opts, d) {
|
|
|
399
399
|
WHERE project_id = ${pid}
|
|
400
400
|
AND session_id = ${session}
|
|
401
401
|
AND event_type = 'camera_sample'
|
|
402
|
-
AND
|
|
402
|
+
AND ${d.arrayLength("position")} = 3${range}${scene}
|
|
403
403
|
ORDER BY ts ASC
|
|
404
404
|
LIMIT ${limit}
|
|
405
405
|
`,
|
|
@@ -436,7 +436,7 @@ export function buildAggregateTrajectories(projectId, opts, d) {
|
|
|
436
436
|
FROM events
|
|
437
437
|
WHERE project_id = ${pid}
|
|
438
438
|
AND event_type = 'camera_sample'
|
|
439
|
-
AND
|
|
439
|
+
AND ${d.arrayLength("position")} = 3${range}${scene}${cameraMode}
|
|
440
440
|
ORDER BY session_id ASC, ts ASC
|
|
441
441
|
LIMIT ${limit}
|
|
442
442
|
`,
|
|
@@ -489,26 +489,26 @@ export function buildClickGazeRay(projectId, opts, d) {
|
|
|
489
489
|
avg(j.hy) AS hit_y,
|
|
490
490
|
avg(j.hz) AS hit_z,
|
|
491
491
|
j.mesh AS mesh,
|
|
492
|
-
count() AS count
|
|
492
|
+
count(*) AS count
|
|
493
493
|
FROM (
|
|
494
494
|
SELECT
|
|
495
495
|
ja.hx AS hx, ja.hy AS hy, ja.hz AS hz, ja.mesh AS mesh,
|
|
496
496
|
CASE
|
|
497
|
-
WHEN ja.has_ray THEN ja.rox
|
|
498
|
-
WHEN ja.recon THEN ja.px + ja.dx * ja.near / ja.dlen
|
|
497
|
+
WHEN ja.has_ray = 1 THEN ja.rox
|
|
498
|
+
WHEN ja.recon = 1 THEN ja.px + ja.dx * ja.near / ja.dlen
|
|
499
499
|
+ (ja.dz / ja.hlen) * ja.off_r
|
|
500
500
|
+ (-(ja.dx * ja.dy) / (ja.dlen * ja.hlen)) * ja.off_u
|
|
501
501
|
WHEN ja.cam_present = 1 THEN ja.px
|
|
502
502
|
END AS ox,
|
|
503
503
|
CASE
|
|
504
|
-
WHEN ja.has_ray THEN ja.roy
|
|
505
|
-
WHEN ja.recon THEN ja.py + ja.dy * ja.near / ja.dlen
|
|
504
|
+
WHEN ja.has_ray = 1 THEN ja.roy
|
|
505
|
+
WHEN ja.recon = 1 THEN ja.py + ja.dy * ja.near / ja.dlen
|
|
506
506
|
+ (ja.hlen / ja.dlen) * ja.off_u
|
|
507
507
|
WHEN ja.cam_present = 1 THEN ja.py
|
|
508
508
|
END AS oy,
|
|
509
509
|
CASE
|
|
510
|
-
WHEN ja.has_ray THEN ja.roz
|
|
511
|
-
WHEN ja.recon THEN ja.pz + ja.dz * ja.near / ja.dlen
|
|
510
|
+
WHEN ja.has_ray = 1 THEN ja.roz
|
|
511
|
+
WHEN ja.recon = 1 THEN ja.pz + ja.dz * ja.near / ja.dlen
|
|
512
512
|
+ (-(ja.dx) / ja.hlen) * ja.off_r
|
|
513
513
|
+ (-(ja.dy * ja.dz) / (ja.dlen * ja.hlen)) * ja.off_u
|
|
514
514
|
WHEN ja.cam_present = 1 THEN ja.pz
|
|
@@ -524,23 +524,26 @@ export function buildClickGazeRay(projectId, opts, d) {
|
|
|
524
524
|
sqrt(m.dx * m.dx + m.dz * m.dz) AS hlen,
|
|
525
525
|
(2 * c.sx - 1) * m.cam_near * tan(m.cam_fov / 2) * m.cam_aspect AS off_r,
|
|
526
526
|
(1 - 2 * c.sy) * m.cam_near * tan(m.cam_fov / 2) AS off_u,
|
|
527
|
-
|
|
527
|
+
CASE WHEN c.has_ray = 0 AND c.has_screen = 1
|
|
528
528
|
AND m.cam_fov > 0 AND m.cam_aspect > 0 AND m.cam_near > 0
|
|
529
529
|
AND sqrt(m.dx * m.dx + m.dz * m.dz)
|
|
530
|
-
> 1e-6 * sqrt(m.dx * m.dx + m.dy * m.dy + m.dz * m.dz)
|
|
530
|
+
> 1e-6 * sqrt(m.dx * m.dx + m.dy * m.dy + m.dz * m.dz)
|
|
531
|
+
THEN 1 ELSE 0 END AS recon
|
|
531
532
|
FROM (
|
|
532
533
|
SELECT session_id, ts,
|
|
533
534
|
hit_point[1] AS hx, hit_point[2] AS hy, hit_point[3] AS hz, mesh,
|
|
534
|
-
|
|
535
|
+
CASE WHEN ${d.arrayLength("ray_origin")} = 3 THEN 1 ELSE 0 END AS has_ray,
|
|
535
536
|
ray_origin[1] AS rox, ray_origin[2] AS roy, ray_origin[3] AS roz,
|
|
536
|
-
|
|
537
|
+
CASE WHEN ${d.arrayLength("screen")} = 2 THEN 1 ELSE 0 END AS has_screen,
|
|
537
538
|
screen[1] AS sx, screen[2] AS sy
|
|
538
539
|
FROM events
|
|
539
540
|
WHERE project_id = ${pid}
|
|
540
541
|
AND event_type = 'pointer_click'
|
|
541
|
-
AND
|
|
542
|
+
AND ${d.arrayLength("hit_point")} = 3${range}${scene}${source}${session}
|
|
542
543
|
) AS c
|
|
543
|
-
${d.
|
|
544
|
+
${d.asofJoin({
|
|
545
|
+
kind: "left",
|
|
546
|
+
right: `(
|
|
544
547
|
SELECT session_id, ts, 1 AS cam_present,
|
|
545
548
|
position[1] AS px, position[2] AS py, position[3] AS pz,
|
|
546
549
|
direction[1] AS dx, direction[2] AS dy, direction[3] AS dz,
|
|
@@ -548,9 +551,14 @@ export function buildClickGazeRay(projectId, opts, d) {
|
|
|
548
551
|
FROM events
|
|
549
552
|
WHERE project_id = ${pid}
|
|
550
553
|
AND event_type = 'camera_sample'
|
|
551
|
-
AND
|
|
552
|
-
)
|
|
553
|
-
|
|
554
|
+
AND ${d.arrayLength("position")} = 3${range}${scene}${session}
|
|
555
|
+
)`,
|
|
556
|
+
alias: "m",
|
|
557
|
+
keys: [["c.session_id", "session_id"]],
|
|
558
|
+
leftTs: "c.ts",
|
|
559
|
+
op: ">=",
|
|
560
|
+
rightTs: "ts",
|
|
561
|
+
})}
|
|
554
562
|
) AS ja
|
|
555
563
|
) AS j
|
|
556
564
|
WHERE j.ox IS NOT NULL
|
|
@@ -594,7 +602,7 @@ export function buildFlowHeatmap(projectId, opts, d) {
|
|
|
594
602
|
floor((atan2(m.dz, m.dx) + pi()) / (2 * pi()) * ${bins}) AS azimuth_bin,
|
|
595
603
|
floor((asin(m.dy / greatest(sqrt(m.dx * m.dx + m.dy * m.dy + m.dz * m.dz), 1e-6)) + pi() / 2) / pi() * ${bins}) AS elevation_bin,
|
|
596
604
|
c.mesh AS mesh,
|
|
597
|
-
count() AS count
|
|
605
|
+
count(*) AS count
|
|
598
606
|
FROM (
|
|
599
607
|
SELECT session_id, ts, mesh
|
|
600
608
|
FROM events
|
|
@@ -602,14 +610,21 @@ export function buildFlowHeatmap(projectId, opts, d) {
|
|
|
602
610
|
AND event_type = 'pointer_click'
|
|
603
611
|
AND mesh != ''${range}${scene}${session}${cameraMode}
|
|
604
612
|
) AS c
|
|
605
|
-
${d.
|
|
613
|
+
${d.asofJoin({
|
|
614
|
+
kind: "inner",
|
|
615
|
+
right: `(
|
|
606
616
|
SELECT session_id, ts, direction[1] AS dx, direction[2] AS dy, direction[3] AS dz
|
|
607
617
|
FROM events
|
|
608
618
|
WHERE project_id = ${pid}
|
|
609
619
|
AND event_type = 'camera_sample'
|
|
610
|
-
AND
|
|
611
|
-
)
|
|
612
|
-
|
|
620
|
+
AND ${d.arrayLength("direction")} = 3${range}${scene}${session}${cameraMode}
|
|
621
|
+
)`,
|
|
622
|
+
alias: "m",
|
|
623
|
+
keys: [["c.session_id", "session_id"]],
|
|
624
|
+
leftTs: "c.ts",
|
|
625
|
+
op: ">=",
|
|
626
|
+
rightTs: "ts",
|
|
627
|
+
})}
|
|
613
628
|
GROUP BY azimuth_bin, elevation_bin, mesh
|
|
614
629
|
ORDER BY count DESC
|
|
615
630
|
LIMIT ${limit}
|
|
@@ -640,34 +655,41 @@ export function buildFlowHeatmap(projectId, opts, d) {
|
|
|
640
655
|
avg(j.oy) AS origin_y,
|
|
641
656
|
avg(j.oz) AS origin_z,
|
|
642
657
|
j.mesh AS mesh,
|
|
643
|
-
count() AS count
|
|
658
|
+
count(*) AS count
|
|
644
659
|
FROM (
|
|
645
660
|
SELECT
|
|
646
661
|
c.mesh AS mesh,
|
|
647
662
|
m.dx AS dx, m.dy AS dy, m.dz AS dz,
|
|
648
|
-
CASE WHEN c.has_ray THEN c.rox ELSE m.px END AS ox,
|
|
649
|
-
CASE WHEN c.has_ray THEN c.roy ELSE m.py END AS oy,
|
|
650
|
-
CASE WHEN c.has_ray THEN c.roz ELSE m.pz END AS oz
|
|
663
|
+
CASE WHEN c.has_ray = 1 THEN c.rox ELSE m.px END AS ox,
|
|
664
|
+
CASE WHEN c.has_ray = 1 THEN c.roy ELSE m.py END AS oy,
|
|
665
|
+
CASE WHEN c.has_ray = 1 THEN c.roz ELSE m.pz END AS oz
|
|
651
666
|
FROM (
|
|
652
667
|
SELECT session_id, ts, mesh,
|
|
653
|
-
|
|
668
|
+
CASE WHEN ${d.arrayLength("ray_origin")} = 3 THEN 1 ELSE 0 END AS has_ray,
|
|
654
669
|
ray_origin[1] AS rox, ray_origin[2] AS roy, ray_origin[3] AS roz
|
|
655
670
|
FROM events
|
|
656
671
|
WHERE project_id = ${pid}
|
|
657
672
|
AND event_type = 'pointer_click'
|
|
658
673
|
AND mesh != ''${range}${scene}${session}${cameraMode}
|
|
659
674
|
) AS c
|
|
660
|
-
${d.
|
|
675
|
+
${d.asofJoin({
|
|
676
|
+
kind: "inner",
|
|
677
|
+
right: `(
|
|
661
678
|
SELECT session_id, ts,
|
|
662
679
|
position[1] AS px, position[2] AS py, position[3] AS pz,
|
|
663
680
|
direction[1] AS dx, direction[2] AS dy, direction[3] AS dz
|
|
664
681
|
FROM events
|
|
665
682
|
WHERE project_id = ${pid}
|
|
666
683
|
AND event_type = 'camera_sample'
|
|
667
|
-
AND
|
|
668
|
-
AND
|
|
669
|
-
)
|
|
670
|
-
|
|
684
|
+
AND ${d.arrayLength("direction")} = 3
|
|
685
|
+
AND ${d.arrayLength("position")} = 3${range}${scene}${session}${cameraMode}
|
|
686
|
+
)`,
|
|
687
|
+
alias: "m",
|
|
688
|
+
keys: [["c.session_id", "session_id"]],
|
|
689
|
+
leftTs: "c.ts",
|
|
690
|
+
op: ">=",
|
|
691
|
+
rightTs: "ts",
|
|
692
|
+
})}
|
|
671
693
|
) AS j${originFilter}
|
|
672
694
|
GROUP BY azimuth_bin, elevation_bin, origin_vx, origin_vy, origin_vz, mesh
|
|
673
695
|
ORDER BY count DESC
|
|
@@ -685,7 +707,7 @@ export function buildTopMeshes(projectId, opts, d) {
|
|
|
685
707
|
const limit = bag.add("limit", "u32", opts.limit ?? 25);
|
|
686
708
|
return {
|
|
687
709
|
query: `
|
|
688
|
-
SELECT mesh, count() AS count
|
|
710
|
+
SELECT mesh, count(*) AS count
|
|
689
711
|
FROM events
|
|
690
712
|
WHERE project_id = ${pid} AND mesh != ''${range}${session}
|
|
691
713
|
GROUP BY mesh
|
|
@@ -714,7 +736,7 @@ export function buildTopMeshesBySource(projectId, opts, d) {
|
|
|
714
736
|
const limit = bag.add("limit", "u32", opts.limit ?? 200);
|
|
715
737
|
return {
|
|
716
738
|
query: `
|
|
717
|
-
SELECT mesh, source, count() AS count
|
|
739
|
+
SELECT mesh, source, count(*) AS count
|
|
718
740
|
FROM events
|
|
719
741
|
WHERE project_id = ${pid}
|
|
720
742
|
AND event_type IN ('mesh_interaction', 'pointer_click')
|
|
@@ -749,7 +771,7 @@ export function buildTopMeshesTrend(projectId, opts, d) {
|
|
|
749
771
|
SELECT
|
|
750
772
|
mesh,
|
|
751
773
|
${d.timeBucketMs("ts", interval)} AS bucket,
|
|
752
|
-
count() AS count
|
|
774
|
+
count(*) AS count
|
|
753
775
|
FROM events
|
|
754
776
|
WHERE project_id = ${pid}
|
|
755
777
|
AND event_type IN ('mesh_interaction', 'pointer_click')
|
|
@@ -780,7 +802,7 @@ export function buildMeshDwell(projectId, opts, d) {
|
|
|
780
802
|
sum(visible_ms) AS visible_ms,
|
|
781
803
|
sum(centered_ms) AS centered_ms,
|
|
782
804
|
max(screen_fraction) AS max_screen_fraction,
|
|
783
|
-
count() AS samples
|
|
805
|
+
count(*) AS samples
|
|
784
806
|
FROM events
|
|
785
807
|
WHERE project_id = ${pid} AND event_type = 'mesh_visibility' AND mesh != ''${range}${scene}${session}
|
|
786
808
|
GROUP BY mesh
|
|
@@ -865,7 +887,7 @@ export function buildMeshInteractionKinds(projectId, opts, d) {
|
|
|
865
887
|
SELECT
|
|
866
888
|
mesh,
|
|
867
889
|
name AS kind,
|
|
868
|
-
count() AS count
|
|
890
|
+
count(*) AS count
|
|
869
891
|
FROM events
|
|
870
892
|
WHERE project_id = ${pid} AND event_type = 'mesh_interaction' AND mesh != ''${range}${scene}${source}${session}
|
|
871
893
|
GROUP BY mesh, name
|
|
@@ -906,7 +928,7 @@ export function buildReachability(projectId, opts, d) {
|
|
|
906
928
|
SELECT
|
|
907
929
|
seg.mesh AS mesh,
|
|
908
930
|
floor(seg.dist / ${bucketSize}) AS bucket,
|
|
909
|
-
count() AS count,
|
|
931
|
+
count(*) AS count,
|
|
910
932
|
avg(seg.dist) AS avg_distance
|
|
911
933
|
FROM (
|
|
912
934
|
SELECT
|
|
@@ -923,17 +945,24 @@ export function buildReachability(projectId, opts, d) {
|
|
|
923
945
|
WHERE project_id = ${pid}
|
|
924
946
|
AND event_type = 'mesh_interaction'
|
|
925
947
|
AND mesh != ''
|
|
926
|
-
AND
|
|
948
|
+
AND ${d.arrayLength("hit_point")} = 3${range}${scene}${source}${session}
|
|
927
949
|
) AS i
|
|
928
|
-
${d.
|
|
950
|
+
${d.asofJoin({
|
|
951
|
+
kind: "inner",
|
|
952
|
+
right: `(
|
|
929
953
|
SELECT session_id, ts,
|
|
930
954
|
position[1] AS px, position[2] AS py, position[3] AS pz
|
|
931
955
|
FROM events
|
|
932
956
|
WHERE project_id = ${pid}
|
|
933
957
|
AND event_type = 'camera_sample'
|
|
934
|
-
AND
|
|
935
|
-
)
|
|
936
|
-
|
|
958
|
+
AND ${d.arrayLength("position")} = 3${range}${scene}${session}
|
|
959
|
+
)`,
|
|
960
|
+
alias: "m",
|
|
961
|
+
keys: [["i.session_id", "session_id"]],
|
|
962
|
+
leftTs: "i.ts",
|
|
963
|
+
op: ">=",
|
|
964
|
+
rightTs: "ts",
|
|
965
|
+
})}
|
|
937
966
|
) AS seg
|
|
938
967
|
GROUP BY mesh, bucket
|
|
939
968
|
ORDER BY count DESC
|
|
@@ -958,7 +987,7 @@ export function buildDeadClicks(projectId, opts, d) {
|
|
|
958
987
|
return {
|
|
959
988
|
query: `
|
|
960
989
|
SELECT
|
|
961
|
-
count() AS total_clicks,
|
|
990
|
+
count(*) AS total_clicks,
|
|
962
991
|
sum(CASE WHEN mesh = '' THEN 1 ELSE 0 END) AS dead_clicks
|
|
963
992
|
FROM events
|
|
964
993
|
WHERE project_id = ${pid} AND event_type = 'pointer_click'${range}${scene}${source}${session}
|
|
@@ -991,11 +1020,11 @@ export function buildRageClicks(projectId, opts, d) {
|
|
|
991
1020
|
session_id,
|
|
992
1021
|
mesh,
|
|
993
1022
|
${d.timeBucketMs("ts", interval)} AS bucket,
|
|
994
|
-
count() AS clicks
|
|
1023
|
+
count(*) AS clicks
|
|
995
1024
|
FROM events
|
|
996
1025
|
WHERE project_id = ${pid} AND event_type = 'pointer_click' AND mesh != ''${range}${scene}${source}${session}
|
|
997
1026
|
GROUP BY session_id, mesh, bucket
|
|
998
|
-
HAVING count() >= ${minRepeats}
|
|
1027
|
+
HAVING count(*) >= ${minRepeats}
|
|
999
1028
|
ORDER BY clicks DESC
|
|
1000
1029
|
LIMIT ${limit}
|
|
1001
1030
|
`,
|
|
@@ -1024,7 +1053,7 @@ export function buildHoverDwell(projectId, opts, d) {
|
|
|
1024
1053
|
mesh,
|
|
1025
1054
|
sum(visible_ms) AS dwell_ms,
|
|
1026
1055
|
max(visible_ms) AS max_dwell_ms,
|
|
1027
|
-
count() AS episodes
|
|
1056
|
+
count(*) AS episodes
|
|
1028
1057
|
FROM events
|
|
1029
1058
|
WHERE project_id = ${pid} AND event_type = 'hover_dwell' AND mesh != ''${range}${scene}${source}${session}
|
|
1030
1059
|
GROUP BY mesh
|
|
@@ -1053,7 +1082,7 @@ export function buildCompileStalls(projectId, opts, d) {
|
|
|
1053
1082
|
query: `
|
|
1054
1083
|
SELECT
|
|
1055
1084
|
name AS phase,
|
|
1056
|
-
count() AS stalls,
|
|
1085
|
+
count(*) AS stalls,
|
|
1057
1086
|
sum(visible_ms) AS total_ms,
|
|
1058
1087
|
avg(visible_ms) AS avg_ms,
|
|
1059
1088
|
max(visible_ms) AS max_ms
|
|
@@ -1081,7 +1110,7 @@ export function buildResourceSummary(projectId, opts, d) {
|
|
|
1081
1110
|
return {
|
|
1082
1111
|
query: `
|
|
1083
1112
|
SELECT
|
|
1084
|
-
count() AS samples,
|
|
1113
|
+
count(*) AS samples,
|
|
1085
1114
|
avg(NULLIF(js_heap_bytes, 0)) AS avg_js_heap_bytes,
|
|
1086
1115
|
max(js_heap_bytes) AS max_js_heap_bytes,
|
|
1087
1116
|
avg(NULLIF(triangles, 0)) AS avg_triangles,
|
|
@@ -1116,7 +1145,7 @@ export function buildResourcePercentiles(projectId, opts, d) {
|
|
|
1116
1145
|
return {
|
|
1117
1146
|
query: `
|
|
1118
1147
|
SELECT
|
|
1119
|
-
count() AS sessions,
|
|
1148
|
+
count(*) AS sessions,
|
|
1120
1149
|
sum(s_samples) AS samples,
|
|
1121
1150
|
${d.quantile("s_heap_p50", 0.5)} AS p50_js_heap_bytes,
|
|
1122
1151
|
${d.quantile("s_heap_p95", 0.5)} AS p95_js_heap_bytes,
|
|
@@ -1127,7 +1156,7 @@ export function buildResourcePercentiles(projectId, opts, d) {
|
|
|
1127
1156
|
FROM (
|
|
1128
1157
|
SELECT
|
|
1129
1158
|
session_id,
|
|
1130
|
-
count() AS s_samples,
|
|
1159
|
+
count(*) AS s_samples,
|
|
1131
1160
|
${d.quantile("nullIf(js_heap_bytes, 0)", 0.5)} AS s_heap_p50,
|
|
1132
1161
|
${d.quantile("nullIf(js_heap_bytes, 0)", 0.95)} AS s_heap_p95,
|
|
1133
1162
|
${d.quantile("nullIf(texture_bytes, 0)", 0.5)} AS s_tex_p50,
|
|
@@ -1160,7 +1189,7 @@ export function buildStabilityCounts(projectId, opts, d) {
|
|
|
1160
1189
|
SELECT
|
|
1161
1190
|
coalesce(sum(CASE WHEN event_type = 'context_lost' THEN 1 ELSE 0 END), 0) AS context_losses,
|
|
1162
1191
|
coalesce(sum(CASE WHEN event_type = 'compile_stall' THEN 1 ELSE 0 END), 0) AS compile_stalls,
|
|
1163
|
-
count() AS incidents
|
|
1192
|
+
count(*) AS incidents
|
|
1164
1193
|
FROM events
|
|
1165
1194
|
WHERE project_id = ${pid} AND event_type IN ('context_lost', 'compile_stall')${range}${scene}${session}
|
|
1166
1195
|
`,
|
|
@@ -1218,7 +1247,7 @@ export function buildGraphicsDiagnosticCounts(projectId, opts, d) {
|
|
|
1218
1247
|
*
|
|
1219
1248
|
* `position` is best-effort connector-side (the camera pose at the moment the
|
|
1220
1249
|
* error/diagnostic fired), so only rows that carry a full 3-vector participate
|
|
1221
|
-
* (`
|
|
1250
|
+
* (`arrayLength(position) = 3`); errors from pages with no 3D connector are naturally
|
|
1222
1251
|
* excluded. Reuses the already-promoted `position` column — no migration.
|
|
1223
1252
|
*
|
|
1224
1253
|
* Optional {@link ErrorHeatmapOptions} filters (severity/category/errorKind) read
|
|
@@ -1253,11 +1282,11 @@ export function buildErrorHeatmap(projectId, opts, d) {
|
|
|
1253
1282
|
floor(position[1] / ${cellSize}) AS vx,
|
|
1254
1283
|
floor(position[2] / ${cellSize}) AS vy,
|
|
1255
1284
|
floor(position[3] / ${cellSize}) AS vz,
|
|
1256
|
-
count() AS count
|
|
1285
|
+
count(*) AS count
|
|
1257
1286
|
FROM events
|
|
1258
1287
|
WHERE project_id = ${pid}
|
|
1259
1288
|
AND event_type IN ('runtime_error', 'graphics_diagnostic')
|
|
1260
|
-
AND
|
|
1289
|
+
AND ${d.arrayLength("position")} = 3${range}${scene}${session}${region}${filter}
|
|
1261
1290
|
GROUP BY vx, vy, vz
|
|
1262
1291
|
ORDER BY count DESC
|
|
1263
1292
|
LIMIT ${limit}
|
|
@@ -1277,7 +1306,7 @@ export function buildErrorHeatmap(projectId, opts, d) {
|
|
|
1277
1306
|
* in the already-promoted `position` column (no migration), and the WebXR
|
|
1278
1307
|
* bounds-check that produced it ran entirely on-device — the boundary polygon and
|
|
1279
1308
|
* room geometry are never captured or stored (ADR 0003 / ADR 0048). Only rows
|
|
1280
|
-
* with a full 3-vector participate (`
|
|
1309
|
+
* with a full 3-vector participate (`arrayLength(position) = 3`). `region` drill-down
|
|
1281
1310
|
* and `scene`/`session` scoping compose like the other spatial heatmaps.
|
|
1282
1311
|
*/
|
|
1283
1312
|
export function buildBoundaryHeatmap(projectId, opts, d) {
|
|
@@ -1295,11 +1324,11 @@ export function buildBoundaryHeatmap(projectId, opts, d) {
|
|
|
1295
1324
|
floor(position[1] / ${cellSize}) AS vx,
|
|
1296
1325
|
floor(position[2] / ${cellSize}) AS vy,
|
|
1297
1326
|
floor(position[3] / ${cellSize}) AS vz,
|
|
1298
|
-
count() AS count
|
|
1327
|
+
count(*) AS count
|
|
1299
1328
|
FROM events
|
|
1300
1329
|
WHERE project_id = ${pid}
|
|
1301
1330
|
AND event_type = 'xr_boundary_proximity'
|
|
1302
|
-
AND
|
|
1331
|
+
AND ${d.arrayLength("position")} = 3${range}${scene}${session}${region}
|
|
1303
1332
|
GROUP BY vx, vy, vz
|
|
1304
1333
|
ORDER BY count DESC
|
|
1305
1334
|
LIMIT ${limit}
|
|
@@ -1324,17 +1353,17 @@ export function buildBoundaryHeatmapStats(projectId, opts, d) {
|
|
|
1324
1353
|
const region = regionClause(bag, opts, POSITION_COLS);
|
|
1325
1354
|
return {
|
|
1326
1355
|
query: `
|
|
1327
|
-
SELECT count() AS cells, coalesce(sum(c), 0) AS hits
|
|
1356
|
+
SELECT count(*) AS cells, coalesce(sum(c), 0) AS hits
|
|
1328
1357
|
FROM (
|
|
1329
1358
|
SELECT
|
|
1330
1359
|
floor(position[1] / ${cellSize}) AS vx,
|
|
1331
1360
|
floor(position[2] / ${cellSize}) AS vy,
|
|
1332
1361
|
floor(position[3] / ${cellSize}) AS vz,
|
|
1333
|
-
count() AS c
|
|
1362
|
+
count(*) AS c
|
|
1334
1363
|
FROM events
|
|
1335
1364
|
WHERE project_id = ${pid}
|
|
1336
1365
|
AND event_type = 'xr_boundary_proximity'
|
|
1337
|
-
AND
|
|
1366
|
+
AND ${d.arrayLength("position")} = 3${range}${scene}${session}${region}
|
|
1338
1367
|
GROUP BY vx, vy, vz
|
|
1339
1368
|
) t
|
|
1340
1369
|
`,
|
|
@@ -1349,7 +1378,7 @@ export function buildBoundaryHeatmapStats(projectId, opts, d) {
|
|
|
1349
1378
|
* space didn't fit the experience.
|
|
1350
1379
|
*
|
|
1351
1380
|
* Each `xr_boundary_proximity` event is one approach (ADR 0048), so `contacts` is
|
|
1352
|
-
* a plain `count()` and `near_ms` sums the per-approach `durationMs` (stored in
|
|
1381
|
+
* a plain `count(*)` and `near_ms` sums the per-approach `durationMs` (stored in
|
|
1353
1382
|
* the shared `visible_ms` column, reused by `mesh_visibility`/`hover_dwell`/
|
|
1354
1383
|
* `compile_stall`). No boundary geometry participates — only the promoted position
|
|
1355
1384
|
* + duration each event already carries.
|
|
@@ -1365,7 +1394,7 @@ export function buildBoundaryContacts(projectId, opts, d) {
|
|
|
1365
1394
|
query: `
|
|
1366
1395
|
SELECT
|
|
1367
1396
|
session_id,
|
|
1368
|
-
count() AS contacts,
|
|
1397
|
+
count(*) AS contacts,
|
|
1369
1398
|
coalesce(sum(visible_ms), 0) AS near_ms
|
|
1370
1399
|
FROM events
|
|
1371
1400
|
WHERE project_id = ${pid}
|
|
@@ -1406,7 +1435,7 @@ export function buildRenderingTechnology(projectId, opts, d) {
|
|
|
1406
1435
|
coalesce(${backend}, '') AS backend,
|
|
1407
1436
|
coalesce(${apiVersion}, '') AS api_version,
|
|
1408
1437
|
coalesce(${shadingLanguage}, '') AS shading_language,
|
|
1409
|
-
count() AS sessions
|
|
1438
|
+
count(*) AS sessions
|
|
1410
1439
|
FROM events
|
|
1411
1440
|
WHERE project_id = ${pid} AND event_type = 'session_start'${range}${scene}${session}
|
|
1412
1441
|
GROUP BY api, backend, api_version, shading_language
|
|
@@ -1435,7 +1464,7 @@ export function buildCapabilityChanges(projectId, opts, d) {
|
|
|
1435
1464
|
name AS kind,
|
|
1436
1465
|
cap_from AS "from",
|
|
1437
1466
|
cap_to AS "to",
|
|
1438
|
-
count() AS changes
|
|
1467
|
+
count(*) AS changes
|
|
1439
1468
|
FROM events
|
|
1440
1469
|
WHERE project_id = ${pid} AND event_type = 'capability_change'${range}${scene}${session}
|
|
1441
1470
|
GROUP BY name, cap_from, cap_to
|
|
@@ -1466,7 +1495,7 @@ export function buildCameraGestures(projectId, opts, d) {
|
|
|
1466
1495
|
query: `
|
|
1467
1496
|
SELECT
|
|
1468
1497
|
name AS kind,
|
|
1469
|
-
count() AS gestures,
|
|
1498
|
+
count(*) AS gestures,
|
|
1470
1499
|
sum(visible_ms) AS total_ms,
|
|
1471
1500
|
avg(visible_ms) AS avg_ms,
|
|
1472
1501
|
max(visible_ms) AS max_ms
|
|
@@ -1488,7 +1517,7 @@ export function buildPerfSummary(projectId, opts, d) {
|
|
|
1488
1517
|
return {
|
|
1489
1518
|
query: `
|
|
1490
1519
|
SELECT
|
|
1491
|
-
count() AS samples,
|
|
1520
|
+
count(*) AS samples,
|
|
1492
1521
|
avg(fps) AS avg_fps,
|
|
1493
1522
|
min(fps) AS min_fps,
|
|
1494
1523
|
${d.quantile("fps", 0.5)} AS p50_fps
|
|
@@ -1519,7 +1548,7 @@ export function buildRenderScaleTruth(projectId, opts, d) {
|
|
|
1519
1548
|
return {
|
|
1520
1549
|
query: `
|
|
1521
1550
|
SELECT
|
|
1522
|
-
count() AS samples,
|
|
1551
|
+
count(*) AS samples,
|
|
1523
1552
|
avg(fps) AS avg_fps,
|
|
1524
1553
|
${d.quantile("fps", 0.5)} AS p50_fps,
|
|
1525
1554
|
avg(NULLIF(render_scale, 0)) AS avg_render_scale,
|
|
@@ -1549,7 +1578,7 @@ export function buildPerfDistribution(projectId, opts, d) {
|
|
|
1549
1578
|
return {
|
|
1550
1579
|
query: `
|
|
1551
1580
|
SELECT
|
|
1552
|
-
count() AS sessions,
|
|
1581
|
+
count(*) AS sessions,
|
|
1553
1582
|
sum(s_samples) AS samples,
|
|
1554
1583
|
${d.quantile("s_p05", 0.5)} AS p05_fps,
|
|
1555
1584
|
${d.quantile("s_p50", 0.5)} AS p50_fps,
|
|
@@ -1557,7 +1586,7 @@ export function buildPerfDistribution(projectId, opts, d) {
|
|
|
1557
1586
|
FROM (
|
|
1558
1587
|
SELECT
|
|
1559
1588
|
session_id,
|
|
1560
|
-
count() AS s_samples,
|
|
1589
|
+
count(*) AS s_samples,
|
|
1561
1590
|
${d.quantile("fps", 0.05)} AS s_p05,
|
|
1562
1591
|
${d.quantile("fps", 0.5)} AS s_p50,
|
|
1563
1592
|
${d.quantile("fps", 0.95)} AS s_p95
|
|
@@ -1588,7 +1617,7 @@ export function buildFpsHistogram(projectId, opts, d) {
|
|
|
1588
1617
|
query: `
|
|
1589
1618
|
SELECT
|
|
1590
1619
|
floor(s_p50 / ${bucket}) * ${bucket} AS bucket,
|
|
1591
|
-
count() AS sessions
|
|
1620
|
+
count(*) AS sessions
|
|
1592
1621
|
FROM (
|
|
1593
1622
|
SELECT
|
|
1594
1623
|
session_id,
|
|
@@ -1621,14 +1650,14 @@ export function buildFrameTimePercentiles(projectId, opts, d) {
|
|
|
1621
1650
|
return {
|
|
1622
1651
|
query: `
|
|
1623
1652
|
SELECT
|
|
1624
|
-
count() AS sessions,
|
|
1653
|
+
count(*) AS sessions,
|
|
1625
1654
|
sum(s_samples) AS samples,
|
|
1626
1655
|
${d.quantile("s_p50", 0.5)} AS p50_ms,
|
|
1627
1656
|
${d.quantile("s_p95", 0.5)} AS p95_ms
|
|
1628
1657
|
FROM (
|
|
1629
1658
|
SELECT
|
|
1630
1659
|
session_id,
|
|
1631
|
-
count() AS s_samples,
|
|
1660
|
+
count(*) AS s_samples,
|
|
1632
1661
|
${d.quantile("nullIf(frame_time_ms, 0)", 0.5)} AS s_p50,
|
|
1633
1662
|
max(nullIf(frame_time_p95_ms, 0)) AS s_p95
|
|
1634
1663
|
FROM events
|
|
@@ -1656,7 +1685,7 @@ export function buildJankRate(projectId, opts, d) {
|
|
|
1656
1685
|
return {
|
|
1657
1686
|
query: `
|
|
1658
1687
|
SELECT
|
|
1659
|
-
count() AS sessions,
|
|
1688
|
+
count(*) AS sessions,
|
|
1660
1689
|
sum(s_long) AS total_long_frames,
|
|
1661
1690
|
${d.quantile("s_rate", 0.5)} AS median_rate,
|
|
1662
1691
|
${d.quantile("s_rate", 0.9)} AS worst_decile_rate
|
|
@@ -1664,7 +1693,7 @@ export function buildJankRate(projectId, opts, d) {
|
|
|
1664
1693
|
SELECT
|
|
1665
1694
|
session_id,
|
|
1666
1695
|
sum(long_frames) AS s_long,
|
|
1667
|
-
sum(long_frames) * 1.0 / count() AS s_rate
|
|
1696
|
+
sum(long_frames) * 1.0 / count(*) AS s_rate
|
|
1668
1697
|
FROM events
|
|
1669
1698
|
WHERE project_id = ${pid} AND event_type = 'frame_perf'${range}${scene}${session}
|
|
1670
1699
|
GROUP BY session_id
|
|
@@ -1740,8 +1769,8 @@ export function buildPerfChurn(projectId, opts, d) {
|
|
|
1740
1769
|
GROUP BY ends.session_id
|
|
1741
1770
|
)
|
|
1742
1771
|
SELECT
|
|
1743
|
-
(SELECT count() FROM ends) AS sessions,
|
|
1744
|
-
count() AS churn_sessions,
|
|
1772
|
+
(SELECT count(*) FROM ends) AS sessions,
|
|
1773
|
+
count(*) AS churn_sessions,
|
|
1745
1774
|
sum(had_fps) AS fps_churn_sessions,
|
|
1746
1775
|
sum(had_stall) AS stall_churn_sessions
|
|
1747
1776
|
FROM correlated
|
|
@@ -1787,7 +1816,7 @@ export function buildPerfByDevice(projectId, opts, d) {
|
|
|
1787
1816
|
session_perf AS (
|
|
1788
1817
|
SELECT
|
|
1789
1818
|
session_id,
|
|
1790
|
-
count() AS s_samples,
|
|
1819
|
+
count(*) AS s_samples,
|
|
1791
1820
|
${d.quantile("fps", 0.5)} AS s_p50
|
|
1792
1821
|
FROM events
|
|
1793
1822
|
WHERE project_id = ${pid} AND event_type = 'frame_perf'${range}${scene}${session}
|
|
@@ -1799,7 +1828,7 @@ export function buildPerfByDevice(projectId, opts, d) {
|
|
|
1799
1828
|
coalesce(dev.renderer, '') AS renderer,
|
|
1800
1829
|
coalesce(dev.browser, '') AS browser,
|
|
1801
1830
|
coalesce(dev.os, '') AS os,
|
|
1802
|
-
count() AS sessions,
|
|
1831
|
+
count(*) AS sessions,
|
|
1803
1832
|
sum(perf.s_samples) AS samples,
|
|
1804
1833
|
${d.quantile("perf.s_p50", 0.5)} AS p50_fps
|
|
1805
1834
|
FROM session_perf perf
|
|
@@ -1827,14 +1856,14 @@ export function buildPerfByScene(projectId, opts, d) {
|
|
|
1827
1856
|
query: `
|
|
1828
1857
|
SELECT
|
|
1829
1858
|
scene_id,
|
|
1830
|
-
count() AS sessions,
|
|
1859
|
+
count(*) AS sessions,
|
|
1831
1860
|
sum(s_samples) AS samples,
|
|
1832
1861
|
${d.quantile("s_p50", 0.5)} AS p50_fps
|
|
1833
1862
|
FROM (
|
|
1834
1863
|
SELECT
|
|
1835
1864
|
session_id,
|
|
1836
1865
|
${d.anyValue("scene_id")} AS scene_id,
|
|
1837
|
-
count() AS s_samples,
|
|
1866
|
+
count(*) AS s_samples,
|
|
1838
1867
|
${d.quantile("fps", 0.5)} AS s_p50
|
|
1839
1868
|
FROM events
|
|
1840
1869
|
WHERE project_id = ${pid} AND event_type = 'frame_perf'${range}${scene}${session}
|
|
@@ -1909,7 +1938,7 @@ export function buildDistinctScenes(projectId, opts, d) {
|
|
|
1909
1938
|
query: `
|
|
1910
1939
|
SELECT
|
|
1911
1940
|
scene_id,
|
|
1912
|
-
count() AS events,
|
|
1941
|
+
count(*) AS events,
|
|
1913
1942
|
max(ts) AS last_seen
|
|
1914
1943
|
FROM events
|
|
1915
1944
|
WHERE project_id = ${pid}${range}
|
|
@@ -1938,7 +1967,7 @@ export function buildTimeseries(projectId, opts, d) {
|
|
|
1938
1967
|
query: `
|
|
1939
1968
|
SELECT
|
|
1940
1969
|
${d.timeBucketMs("ts", interval)} AS bucket,
|
|
1941
|
-
count() AS events,
|
|
1970
|
+
count(*) AS events,
|
|
1942
1971
|
${d.avgIf("fps", "event_type = 'frame_perf'")} AS avg_fps
|
|
1943
1972
|
FROM events
|
|
1944
1973
|
WHERE project_id = ${pid}${range}${scene}${type}
|
|
@@ -1959,7 +1988,7 @@ export function buildEventTypeCounts(projectId, opts, d) {
|
|
|
1959
1988
|
const scene = sceneClause(bag, opts);
|
|
1960
1989
|
return {
|
|
1961
1990
|
query: `
|
|
1962
|
-
SELECT event_type, count() AS count
|
|
1991
|
+
SELECT event_type, count(*) AS count
|
|
1963
1992
|
FROM events
|
|
1964
1993
|
WHERE project_id = ${pid}${range}${scene}
|
|
1965
1994
|
GROUP BY event_type
|
|
@@ -1990,11 +2019,11 @@ export function buildSceneCoverage(projectId, opts, d) {
|
|
|
1990
2019
|
floor(position[1] / ${cellSize}) AS vx,
|
|
1991
2020
|
floor(position[2] / ${cellSize}) AS vy,
|
|
1992
2021
|
floor(position[3] / ${cellSize}) AS vz,
|
|
1993
|
-
count() AS count
|
|
2022
|
+
count(*) AS count
|
|
1994
2023
|
FROM events
|
|
1995
2024
|
WHERE project_id = ${pid}
|
|
1996
2025
|
AND event_type = 'camera_sample'
|
|
1997
|
-
AND
|
|
2026
|
+
AND ${d.arrayLength("position")} = 3${range}${scene}${session}
|
|
1998
2027
|
GROUP BY vx, vy, vz
|
|
1999
2028
|
ORDER BY count DESC
|
|
2000
2029
|
LIMIT ${limit}
|
|
@@ -2029,13 +2058,13 @@ export function buildPerfHeatmap(projectId, opts, d) {
|
|
|
2029
2058
|
floor(position[1] / ${cellSize}) AS vx,
|
|
2030
2059
|
floor(position[2] / ${cellSize}) AS vy,
|
|
2031
2060
|
floor(position[3] / ${cellSize}) AS vz,
|
|
2032
|
-
count() AS samples,
|
|
2061
|
+
count(*) AS samples,
|
|
2033
2062
|
avg(fps) AS avg_fps,
|
|
2034
2063
|
min(fps) AS min_fps
|
|
2035
2064
|
FROM events
|
|
2036
2065
|
WHERE project_id = ${pid}
|
|
2037
2066
|
AND event_type = 'frame_perf'
|
|
2038
|
-
AND
|
|
2067
|
+
AND ${d.arrayLength("position")} = 3${range}${scene}${session}
|
|
2039
2068
|
GROUP BY vx, vy, vz
|
|
2040
2069
|
ORDER BY avg_fps ASC
|
|
2041
2070
|
LIMIT ${limit}
|
|
@@ -2070,11 +2099,11 @@ export function buildCameraDistance(projectId, opts, d) {
|
|
|
2070
2099
|
(position[2] - ${cy}) * (position[2] - ${cy}) +
|
|
2071
2100
|
(position[3] - ${cz}) * (position[3] - ${cz})
|
|
2072
2101
|
) / ${bucketSize}) AS bucket,
|
|
2073
|
-
count() AS count
|
|
2102
|
+
count(*) AS count
|
|
2074
2103
|
FROM events
|
|
2075
2104
|
WHERE project_id = ${pid}
|
|
2076
2105
|
AND event_type = 'camera_sample'
|
|
2077
|
-
AND
|
|
2106
|
+
AND ${d.arrayLength("position")} = 3${range}${scene}${session}
|
|
2078
2107
|
GROUP BY bucket
|
|
2079
2108
|
ORDER BY bucket ASC
|
|
2080
2109
|
LIMIT ${limit}
|
|
@@ -2103,12 +2132,12 @@ export function buildNavigationStats(projectId, opts, d) {
|
|
|
2103
2132
|
FROM events
|
|
2104
2133
|
WHERE project_id = ${pid}
|
|
2105
2134
|
AND event_type = 'camera_sample'
|
|
2106
|
-
AND
|
|
2135
|
+
AND ${d.arrayLength("position")} = 3${range}${scene}${session}`;
|
|
2107
2136
|
return {
|
|
2108
2137
|
query: `
|
|
2109
2138
|
SELECT
|
|
2110
2139
|
session_id,
|
|
2111
|
-
count() AS segments,
|
|
2140
|
+
count(*) AS segments,
|
|
2112
2141
|
sum(dist) AS total_distance,
|
|
2113
2142
|
sum(CASE WHEN dist >= ${moveThreshold} THEN 1 ELSE 0 END) AS active_segments,
|
|
2114
2143
|
sum(CASE WHEN dist >= ${moveThreshold} THEN dist ELSE 0 END) AS active_distance
|
|
@@ -2122,9 +2151,16 @@ export function buildNavigationStats(projectId, opts, d) {
|
|
|
2122
2151
|
) AS dist
|
|
2123
2152
|
FROM (${sampleSelect}
|
|
2124
2153
|
) AS c
|
|
2125
|
-
${d.
|
|
2126
|
-
|
|
2127
|
-
|
|
2154
|
+
${d.asofJoin({
|
|
2155
|
+
kind: "inner",
|
|
2156
|
+
right: `(${sampleSelect}
|
|
2157
|
+
)`,
|
|
2158
|
+
alias: "m",
|
|
2159
|
+
keys: [["c.session_id", "session_id"]],
|
|
2160
|
+
leftTs: "c.ts",
|
|
2161
|
+
op: ">",
|
|
2162
|
+
rightTs: "ts",
|
|
2163
|
+
})}
|
|
2128
2164
|
) AS seg
|
|
2129
2165
|
GROUP BY session_id
|
|
2130
2166
|
ORDER BY total_distance DESC
|
|
@@ -2153,7 +2189,7 @@ export function buildNavigationStats(projectId, opts, d) {
|
|
|
2153
2189
|
* Per (session, scene): `revisits = entries − distinct_cells` — every entry into
|
|
2154
2190
|
* a cell beyond its first is a re-entry. Pooled per scene, the leaderboard
|
|
2155
2191
|
* reports `backtrack_ratio = Σ revisits / Σ entries` alongside the raw counts.
|
|
2156
|
-
* Only plain `count()` and a dedup subquery are used (no multi-column
|
|
2192
|
+
* Only plain `count(*)` and a dedup subquery are used (no multi-column
|
|
2157
2193
|
* `COUNT(DISTINCT …)`), so DuckDB and ClickHouse agree.
|
|
2158
2194
|
*/
|
|
2159
2195
|
export function buildBacktrackRatio(projectId, opts, d) {
|
|
@@ -2171,24 +2207,31 @@ export function buildBacktrackRatio(projectId, opts, d) {
|
|
|
2171
2207
|
FROM events
|
|
2172
2208
|
WHERE project_id = ${pid}
|
|
2173
2209
|
AND event_type = 'camera_sample'
|
|
2174
|
-
AND
|
|
2210
|
+
AND ${d.arrayLength("position")} = 3${range}${scene}${session}`;
|
|
2175
2211
|
// Ordered cell entries (consecutive same-cell samples collapsed): a sample is
|
|
2176
2212
|
// an entry when it has no predecessor or its cell changed vs. the predecessor.
|
|
2177
2213
|
const entries = `
|
|
2178
2214
|
SELECT c.session_id AS session_id, c.scene AS scene, c.gx AS gx, c.gz AS gz
|
|
2179
2215
|
FROM (${sampleSelect}
|
|
2180
2216
|
) AS c
|
|
2181
|
-
${d.
|
|
2217
|
+
${d.asofJoin({
|
|
2218
|
+
kind: "left",
|
|
2219
|
+
right: `(
|
|
2182
2220
|
SELECT session_id, ts, 1 AS present, gx, gz FROM (${sampleSelect}
|
|
2183
2221
|
) AS s
|
|
2184
|
-
)
|
|
2185
|
-
|
|
2222
|
+
)`,
|
|
2223
|
+
alias: "m",
|
|
2224
|
+
keys: [["c.session_id", "session_id"]],
|
|
2225
|
+
leftTs: "c.ts",
|
|
2226
|
+
op: ">",
|
|
2227
|
+
rightTs: "ts",
|
|
2228
|
+
})}
|
|
2186
2229
|
WHERE m.present IS NULL OR m.present = 0 OR c.gx <> m.gx OR c.gz <> m.gz`;
|
|
2187
2230
|
return {
|
|
2188
2231
|
query: `
|
|
2189
2232
|
SELECT
|
|
2190
2233
|
scene,
|
|
2191
|
-
count() AS sessions,
|
|
2234
|
+
count(*) AS sessions,
|
|
2192
2235
|
sum(total_entries) AS entries,
|
|
2193
2236
|
sum(revisits) AS revisits,
|
|
2194
2237
|
CASE WHEN sum(total_entries) > 0
|
|
@@ -2197,12 +2240,12 @@ export function buildBacktrackRatio(projectId, opts, d) {
|
|
|
2197
2240
|
SELECT
|
|
2198
2241
|
e.session_id AS session_id,
|
|
2199
2242
|
e.scene AS scene,
|
|
2200
|
-
count() AS total_entries,
|
|
2201
|
-
count() - dc.distinct_cells AS revisits
|
|
2243
|
+
count(*) AS total_entries,
|
|
2244
|
+
count(*) - dc.distinct_cells AS revisits
|
|
2202
2245
|
FROM (${entries}
|
|
2203
2246
|
) AS e
|
|
2204
2247
|
JOIN (
|
|
2205
|
-
SELECT session_id, scene, count() AS distinct_cells
|
|
2248
|
+
SELECT session_id, scene, count(*) AS distinct_cells
|
|
2206
2249
|
FROM (
|
|
2207
2250
|
SELECT DISTINCT session_id, scene, gx, gz FROM (${entries}
|
|
2208
2251
|
) AS de
|
|
@@ -2249,7 +2292,7 @@ export function buildInteractionsBySource(projectId, opts, d) {
|
|
|
2249
2292
|
SELECT
|
|
2250
2293
|
event_type,
|
|
2251
2294
|
source,
|
|
2252
|
-
count() AS count,
|
|
2295
|
+
count(*) AS count,
|
|
2253
2296
|
count(DISTINCT session_id) AS sessions
|
|
2254
2297
|
FROM events
|
|
2255
2298
|
WHERE project_id = ${pid}
|
|
@@ -2283,7 +2326,7 @@ export function buildTopInputActions(projectId, opts, d) {
|
|
|
2283
2326
|
SELECT
|
|
2284
2327
|
name AS action,
|
|
2285
2328
|
source,
|
|
2286
|
-
count() AS count
|
|
2329
|
+
count(*) AS count
|
|
2287
2330
|
FROM events
|
|
2288
2331
|
WHERE project_id = ${pid} AND event_type = 'input_action' AND name != ''${range}${scene}${source}${session}
|
|
2289
2332
|
GROUP BY name, source
|
|
@@ -2316,12 +2359,12 @@ export function buildXrRotationRate(projectId, opts, d) {
|
|
|
2316
2359
|
FROM events
|
|
2317
2360
|
WHERE project_id = ${pid}
|
|
2318
2361
|
AND event_type = 'camera_sample'
|
|
2319
|
-
AND
|
|
2362
|
+
AND ${d.arrayLength("direction")} = 3${range}${scene}${session}`;
|
|
2320
2363
|
return {
|
|
2321
2364
|
query: `
|
|
2322
2365
|
SELECT
|
|
2323
2366
|
session_id,
|
|
2324
|
-
count() AS samples,
|
|
2367
|
+
count(*) AS samples,
|
|
2325
2368
|
avg(turn) AS avg_turn_rad,
|
|
2326
2369
|
max(turn) AS max_turn_rad,
|
|
2327
2370
|
sum(turn) AS total_turn_rad,
|
|
@@ -2336,9 +2379,16 @@ export function buildXrRotationRate(projectId, opts, d) {
|
|
|
2336
2379
|
))) AS turn
|
|
2337
2380
|
FROM (${sampleSelect}
|
|
2338
2381
|
) AS c
|
|
2339
|
-
${d.
|
|
2340
|
-
|
|
2341
|
-
|
|
2382
|
+
${d.asofJoin({
|
|
2383
|
+
kind: "inner",
|
|
2384
|
+
right: `(${sampleSelect}
|
|
2385
|
+
)`,
|
|
2386
|
+
alias: "m",
|
|
2387
|
+
keys: [["c.session_id", "session_id"]],
|
|
2388
|
+
leftTs: "c.ts",
|
|
2389
|
+
op: ">",
|
|
2390
|
+
rightTs: "ts",
|
|
2391
|
+
})}
|
|
2342
2392
|
) AS seg
|
|
2343
2393
|
GROUP BY session_id
|
|
2344
2394
|
ORDER BY total_turn_rad DESC
|
|
@@ -2365,7 +2415,7 @@ export function buildXrSourceUsage(projectId, opts, d) {
|
|
|
2365
2415
|
query: `
|
|
2366
2416
|
SELECT
|
|
2367
2417
|
source,
|
|
2368
|
-
count() AS interactions,
|
|
2418
|
+
count(*) AS interactions,
|
|
2369
2419
|
count(DISTINCT session_id) AS sessions
|
|
2370
2420
|
FROM events
|
|
2371
2421
|
WHERE project_id = ${pid}
|
|
@@ -2396,7 +2446,7 @@ export function buildXrAbandonment(projectId, opts, d) {
|
|
|
2396
2446
|
query: `
|
|
2397
2447
|
SELECT
|
|
2398
2448
|
session_id,
|
|
2399
|
-
count() AS events,
|
|
2449
|
+
count(*) AS events,
|
|
2400
2450
|
sum(CASE WHEN source IN ${XR_SOURCES} THEN 1 ELSE 0 END) AS xr_interactions,
|
|
2401
2451
|
min(ts) AS started_at,
|
|
2402
2452
|
max(ts) AS ended_at
|
|
@@ -2580,7 +2630,7 @@ export function buildArPlacementTimeToPlace(projectId, opts, d) {
|
|
|
2580
2630
|
query: `
|
|
2581
2631
|
SELECT
|
|
2582
2632
|
floor(coalesce(${timeToPlace}, 0) / ${bucket}) * ${bucket} AS bucket,
|
|
2583
|
-
count() AS placements
|
|
2633
|
+
count(*) AS placements
|
|
2584
2634
|
FROM events
|
|
2585
2635
|
WHERE project_id = ${pid} AND event_type = 'ar_placement'${range}${scene}${session}
|
|
2586
2636
|
GROUP BY bucket
|
|
@@ -2610,7 +2660,7 @@ export function buildArPlacementAttempts(projectId, opts, d) {
|
|
|
2610
2660
|
query: `
|
|
2611
2661
|
SELECT
|
|
2612
2662
|
coalesce(${attempts}, 1) AS attempts,
|
|
2613
|
-
count() AS placements
|
|
2663
|
+
count(*) AS placements
|
|
2614
2664
|
FROM events
|
|
2615
2665
|
WHERE project_id = ${pid} AND event_type = 'ar_placement'${range}${scene}${session}
|
|
2616
2666
|
GROUP BY attempts
|
|
@@ -2642,7 +2692,7 @@ export function buildArPlacementSurfaces(projectId, opts, d) {
|
|
|
2642
2692
|
query: `
|
|
2643
2693
|
SELECT
|
|
2644
2694
|
coalesce(${surface}, 'unknown') AS surface,
|
|
2645
|
-
count() AS placements,
|
|
2695
|
+
count(*) AS placements,
|
|
2646
2696
|
avg(${scale}) AS avg_scale
|
|
2647
2697
|
FROM events
|
|
2648
2698
|
WHERE project_id = ${pid} AND event_type = 'ar_placement'${range}${scene}${session}
|
|
@@ -2683,7 +2733,7 @@ export function buildFunnel(projectId, opts, d) {
|
|
|
2683
2733
|
)`;
|
|
2684
2734
|
});
|
|
2685
2735
|
const counts = steps
|
|
2686
|
-
.map((_step, i) => `SELECT ${i} AS step, count() AS sessions FROM s${i}`)
|
|
2736
|
+
.map((_step, i) => `SELECT ${i} AS step, count(*) AS sessions FROM s${i}`)
|
|
2687
2737
|
.join("\n UNION ALL ");
|
|
2688
2738
|
return {
|
|
2689
2739
|
query: `
|
|
@@ -2735,8 +2785,15 @@ export function buildSceneRetention(projectId, opts, d) {
|
|
|
2735
2785
|
SELECT a.scene_id AS from_scene, b.scene_id AS to_scene,
|
|
2736
2786
|
count(DISTINCT a.session_id) AS sessions
|
|
2737
2787
|
FROM sc AS a
|
|
2738
|
-
${d.
|
|
2739
|
-
|
|
2788
|
+
${d.asofJoin({
|
|
2789
|
+
kind: "inner",
|
|
2790
|
+
right: "sc",
|
|
2791
|
+
alias: "b",
|
|
2792
|
+
keys: [["a.session_id", "session_id"]],
|
|
2793
|
+
leftTs: "a.ts",
|
|
2794
|
+
op: "<",
|
|
2795
|
+
rightTs: "ts",
|
|
2796
|
+
})}
|
|
2740
2797
|
GROUP BY from_scene, to_scene
|
|
2741
2798
|
ORDER BY sessions DESC, from_scene ASC, to_scene ASC
|
|
2742
2799
|
LIMIT ${limit}
|
|
@@ -2801,7 +2858,7 @@ export function buildLoadBounceFunnel(projectId, opts, d) {
|
|
|
2801
2858
|
GROUP BY fl.session_id, fl.load_ts
|
|
2802
2859
|
),
|
|
2803
2860
|
engaged AS (
|
|
2804
|
-
SELECT lm.session_id AS session_id, count() AS interactions
|
|
2861
|
+
SELECT lm.session_id AS session_id, count(*) AS interactions
|
|
2805
2862
|
FROM events AS e JOIN load_ms AS lm
|
|
2806
2863
|
ON e.session_id = lm.session_id
|
|
2807
2864
|
WHERE e.project_id = ${pid}
|
|
@@ -2811,7 +2868,7 @@ export function buildLoadBounceFunnel(projectId, opts, d) {
|
|
|
2811
2868
|
)
|
|
2812
2869
|
SELECT
|
|
2813
2870
|
${bandCase} AS band,
|
|
2814
|
-
count() AS sessions,
|
|
2871
|
+
count(*) AS sessions,
|
|
2815
2872
|
sum(CASE WHEN coalesce(eng.interactions, 0) = 0 THEN 1 ELSE 0 END) AS bounced
|
|
2816
2873
|
FROM load_ms AS lm LEFT JOIN engaged AS eng ON lm.session_id = eng.session_id
|
|
2817
2874
|
WHERE lm.load_ms IS NOT NULL
|
|
@@ -2894,7 +2951,7 @@ export function buildVariantLeaderboard(projectId, opts, d) {
|
|
|
2894
2951
|
}
|
|
2895
2952
|
// Per-view aggregates: total views and distinct sessions per variant.
|
|
2896
2953
|
ctes.push(`view_counts AS (
|
|
2897
|
-
SELECT variant, count() AS views, count(DISTINCT session_id) AS sessions
|
|
2954
|
+
SELECT variant, count(*) AS views, count(DISTINCT session_id) AS sessions
|
|
2898
2955
|
FROM variant_views
|
|
2899
2956
|
GROUP BY variant
|
|
2900
2957
|
)`);
|