-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcwvDistributionController.js
More file actions
125 lines (113 loc) · 3.31 KB
/
cwvDistributionController.js
File metadata and controls
125 lines (113 loc) · 3.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import { bigquery } from '../utils/db.js';
import { convertToArray } from '../utils/helpers.js';
import {
handleControllerError,
generateETag,
isModified,
sendValidationError
} from '../utils/controllerHelpers.js';
/**
* Build the BigQuery SQL for the CWV distribution histogram.
* rank is applied to p.rank only (no rank column on device_summary).
*/
const buildQuery = (rankFilter) => {
const rankClause = rankFilter ? 'AND p.rank <= @rank' : '';
return `WITH metrics AS (
SELECT
client,
t.technology,
root_page,
ANY_VALUE(p75_lcp) AS lcp,
ANY_VALUE(p75_inp) AS inp,
ANY_VALUE(p75_cls) AS cls,
ANY_VALUE(p75_fcp) AS fcp,
ANY_VALUE(p75_ttfb) AS ttfb
FROM
\`httparchive.crawl.pages\` p,
UNNEST(technologies) t,
\`chrome-ux-report.materialized.device_summary\` c
WHERE
p.date = @date AND
c.date = @date AND
t.technology IN UNNEST(@technologies) AND
root_page = origin || '/' AND
IF(client = 'mobile', 'phone', 'desktop') = device
${rankClause}
GROUP BY
client,
t.technology,
root_page
)
SELECT
client,
technology,
bucket AS loading_bucket,
bucket / 4 AS inp_bucket,
bucket / 2000 AS cls_bucket,
COUNT(DISTINCT root_page WHERE lcp = bucket) AS lcp_origins,
COUNT(DISTINCT root_page WHERE inp = bucket / 4) AS inp_origins,
COUNT(DISTINCT root_page WHERE cls = bucket / 2000) AS cls_origins,
COUNT(DISTINCT root_page WHERE fcp = bucket) AS fcp_origins,
COUNT(DISTINCT root_page WHERE ttfb = bucket) AS ttfb_origins
FROM
metrics,
UNNEST(GENERATE_ARRAY(0.0, 10000.0, 100.0)) AS bucket
GROUP BY
client,
technology,
bucket
ORDER BY
client,
technology,
bucket`;
};
/**
* GET /v1/cwv-distribution
*
* Query parameters:
* technology (required) - comma-separated list of technologies, e.g. "Wix,WordPress"
* date (required) - crawl date in YYYY-MM-DD format, e.g. "2026-02-01"
* rank (optional) - numeric rank ceiling, e.g. "10000". Omit or set to "ALL" to include all ranks.
*/
export const listCWVDistributionData = async (req, res) => {
try {
const params = req.query;
const errors = [];
if (!params.technology) errors.push(['technology', 'missing technology parameter']);
if (!params.date) errors.push(['date', 'missing date parameter']);
if (errors.length > 0) {
sendValidationError(res, errors);
return;
}
const technologies = convertToArray(params.technology);
const date = params.date;
const rankParam = params.rank && params.rank !== 'ALL' ? params.rank : null;
const queryStr = buildQuery(rankParam !== null);
const queryOptions = {
query: queryStr,
params: {
technologies,
date,
...(rankParam !== null && { rank: parseInt(rankParam, 10) })
},
types: {
technologies: ['STRING'],
date: 'STRING',
...(rankParam !== null && { rank: 'INT64' })
}
};
const [rows] = await bigquery.query(queryOptions);
const jsonData = JSON.stringify(rows);
const etag = generateETag(jsonData);
res.setHeader('ETag', `"${etag}"`);
if (!isModified(req, etag)) {
res.statusCode = 304;
res.end();
return;
}
res.statusCode = 200;
res.end(jsonData);
} catch (error) {
handleControllerError(res, error, 'fetching CWV distribution data');
}
};