@gscdump/analysis 0.33.0 → 0.33.3
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/dist/analyzer/index.mjs +153 -384
- package/dist/default-registry.mjs +153 -384
- package/dist/index.mjs +267 -609
- package/dist/report/index.mjs +114 -225
- package/dist/semantic/index.d.mts +8 -8
- package/dist/semantic/index.mjs +77 -77
- package/package.json +4 -4
|
@@ -7,15 +7,26 @@ import { err, ok, unwrapResult } from "gscdump/result";
|
|
|
7
7
|
import { between, date, extractDateRange, gsc, page, query } from "gscdump/query";
|
|
8
8
|
import { MS_PER_DAY, daysAgo, toIsoDate } from "gscdump";
|
|
9
9
|
import { buildExtrasQueries, buildTotalsSql, mergeExtras, resolveComparisonSQL, resolveToSQL, resolveToSQLOptimized } from "@gscdump/engine/resolver";
|
|
10
|
-
function
|
|
11
|
-
if (typeof
|
|
12
|
-
if (typeof
|
|
13
|
-
if (
|
|
14
|
-
const n = Number(
|
|
10
|
+
function rowNumber(value) {
|
|
11
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : 0;
|
|
12
|
+
if (typeof value === "bigint") return Number(value);
|
|
13
|
+
if (value == null) return 0;
|
|
14
|
+
const n = Number(value);
|
|
15
15
|
return Number.isFinite(n) ? n : 0;
|
|
16
16
|
}
|
|
17
|
-
function
|
|
18
|
-
return
|
|
17
|
+
function rowString(value) {
|
|
18
|
+
return value == null ? "" : String(value);
|
|
19
|
+
}
|
|
20
|
+
function rowBoolean(value) {
|
|
21
|
+
return value === true || value === 1 || value === "true";
|
|
22
|
+
}
|
|
23
|
+
function parseJsonRows(value) {
|
|
24
|
+
if (Array.isArray(value)) return value;
|
|
25
|
+
if (typeof value === "string" && value.length > 0) {
|
|
26
|
+
const parsed = JSON.parse(value);
|
|
27
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
28
|
+
}
|
|
29
|
+
return [];
|
|
19
30
|
}
|
|
20
31
|
const bayesianCtrAnalyzer = defineAnalyzer({
|
|
21
32
|
id: "bayesian-ctr",
|
|
@@ -155,24 +166,24 @@ const bayesianCtrAnalyzer = defineAnalyzer({
|
|
|
155
166
|
const arr = Array.isArray(rows) ? rows : [];
|
|
156
167
|
const minImpressions = params.minImpressions ?? 50;
|
|
157
168
|
const results = arr.map((r) => ({
|
|
158
|
-
keyword:
|
|
159
|
-
page:
|
|
160
|
-
clicks:
|
|
161
|
-
impressions:
|
|
162
|
-
observedCtr:
|
|
163
|
-
position:
|
|
164
|
-
bucket:
|
|
165
|
-
priorAlpha:
|
|
166
|
-
priorBeta:
|
|
167
|
-
bucketPriorMean:
|
|
168
|
-
posteriorMean:
|
|
169
|
-
posteriorSd:
|
|
170
|
-
ciLow:
|
|
171
|
-
ciHigh:
|
|
172
|
-
shrinkageDelta:
|
|
173
|
-
expectedClicksDelta:
|
|
174
|
-
significance:
|
|
175
|
-
classification:
|
|
169
|
+
keyword: rowString(r.keyword),
|
|
170
|
+
page: rowString(r.page),
|
|
171
|
+
clicks: rowNumber(r.clicks),
|
|
172
|
+
impressions: rowNumber(r.impressions),
|
|
173
|
+
observedCtr: rowNumber(r.observedCtr),
|
|
174
|
+
position: rowNumber(r.position),
|
|
175
|
+
bucket: rowNumber(r.bucket),
|
|
176
|
+
priorAlpha: rowNumber(r.priorAlpha),
|
|
177
|
+
priorBeta: rowNumber(r.priorBeta),
|
|
178
|
+
bucketPriorMean: rowNumber(r.bucketPriorMean),
|
|
179
|
+
posteriorMean: rowNumber(r.posteriorMean),
|
|
180
|
+
posteriorSd: rowNumber(r.posteriorSd),
|
|
181
|
+
ciLow: rowNumber(r.ciLow),
|
|
182
|
+
ciHigh: rowNumber(r.ciHigh),
|
|
183
|
+
shrinkageDelta: rowNumber(r.shrinkageDelta),
|
|
184
|
+
expectedClicksDelta: rowNumber(r.expectedClicksDelta),
|
|
185
|
+
significance: rowNumber(r.significance),
|
|
186
|
+
classification: rowString(r.classification)
|
|
176
187
|
}));
|
|
177
188
|
const under = results.filter((r) => r.classification === "underperforming").length;
|
|
178
189
|
const over = results.filter((r) => r.classification === "overperforming").length;
|
|
@@ -190,17 +201,6 @@ const bayesianCtrAnalyzer = defineAnalyzer({
|
|
|
190
201
|
});
|
|
191
202
|
const BIPARTITE_PAGERANK_ITERATIONS = 25;
|
|
192
203
|
const BIPARTITE_PAGERANK_DAMPING = .85;
|
|
193
|
-
function str$22(v) {
|
|
194
|
-
return v == null ? "" : String(v);
|
|
195
|
-
}
|
|
196
|
-
function parseJsonList$16(v) {
|
|
197
|
-
if (Array.isArray(v)) return v;
|
|
198
|
-
if (typeof v === "string" && v.length > 0) {
|
|
199
|
-
const parsed = JSON.parse(v);
|
|
200
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
201
|
-
}
|
|
202
|
-
return [];
|
|
203
|
-
}
|
|
204
204
|
const bipartitePagerankAnalyzer = defineAnalyzer({
|
|
205
205
|
id: "bipartite-pagerank",
|
|
206
206
|
buildSql(params) {
|
|
@@ -406,8 +406,8 @@ const bipartitePagerankAnalyzer = defineAnalyzer({
|
|
|
406
406
|
const iterations = BIPARTITE_PAGERANK_ITERATIONS;
|
|
407
407
|
const d = BIPARTITE_PAGERANK_DAMPING;
|
|
408
408
|
const results = arr.map((r) => ({
|
|
409
|
-
kind:
|
|
410
|
-
id:
|
|
409
|
+
kind: rowString(r.kind),
|
|
410
|
+
id: rowString(r.id),
|
|
411
411
|
rank: num(r.rank),
|
|
412
412
|
bridging: num(r.bridging),
|
|
413
413
|
anchoring: num(r.anchoring),
|
|
@@ -417,7 +417,7 @@ const bipartitePagerankAnalyzer = defineAnalyzer({
|
|
|
417
417
|
const first = arr[0] ?? {};
|
|
418
418
|
const queryCount = num(first.queryCount);
|
|
419
419
|
const urlCount = num(first.urlCount);
|
|
420
|
-
const deltas =
|
|
420
|
+
const deltas = parseJsonRows(first.deltasJson).map((e) => ({
|
|
421
421
|
step: num(e.step),
|
|
422
422
|
l1: num(e.l1)
|
|
423
423
|
}));
|
|
@@ -503,9 +503,6 @@ function analysisErrorToException(error) {
|
|
|
503
503
|
function escapeRegexAlt(s) {
|
|
504
504
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
505
505
|
}
|
|
506
|
-
function str$21(v) {
|
|
507
|
-
return v == null ? "" : String(v);
|
|
508
|
-
}
|
|
509
506
|
function requireBrandTermsResult(brandTerms) {
|
|
510
507
|
return brandTerms?.length ? ok(brandTerms) : err(analysisErrors.missingBrandTerms());
|
|
511
508
|
}
|
|
@@ -581,13 +578,13 @@ const brandAnalyzer = defineAnalyzer({
|
|
|
581
578
|
},
|
|
582
579
|
reduceSql(rows) {
|
|
583
580
|
const normalized = (Array.isArray(rows) ? rows : []).map((r) => ({
|
|
584
|
-
query:
|
|
585
|
-
page: r.page == null ? void 0 :
|
|
581
|
+
query: rowString(r.query),
|
|
582
|
+
page: r.page == null ? void 0 : rowString(r.page),
|
|
586
583
|
clicks: num(r.clicks),
|
|
587
584
|
impressions: num(r.impressions),
|
|
588
585
|
ctr: num(r.ctr),
|
|
589
586
|
position: num(r.position),
|
|
590
|
-
segment:
|
|
587
|
+
segment: rowString(r.segment)
|
|
591
588
|
}));
|
|
592
589
|
let brandClicks = 0;
|
|
593
590
|
let nonBrandClicks = 0;
|
|
@@ -664,17 +661,6 @@ const sortRowResults$1 = createSorter((item, metric) => {
|
|
|
664
661
|
case "pageCount": return item.pages.length;
|
|
665
662
|
}
|
|
666
663
|
}, "clicks");
|
|
667
|
-
function str$20(v) {
|
|
668
|
-
return v == null ? "" : String(v);
|
|
669
|
-
}
|
|
670
|
-
function parseJsonList$15(v) {
|
|
671
|
-
if (Array.isArray(v)) return v;
|
|
672
|
-
if (typeof v === "string" && v.length > 0) {
|
|
673
|
-
const parsed = JSON.parse(v);
|
|
674
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
675
|
-
}
|
|
676
|
-
return [];
|
|
677
|
-
}
|
|
678
664
|
function analyzeCannibalization(rows, options = {}) {
|
|
679
665
|
const { minImpressions = 10, maxPositionSpread = 10, minPages = 2, sortBy = "clicks", sortOrder = "desc" } = options;
|
|
680
666
|
const queryMap = /* @__PURE__ */ new Map();
|
|
@@ -827,19 +813,19 @@ const cannibalizationAnalyzer = defineAnalyzer({
|
|
|
827
813
|
},
|
|
828
814
|
reduceSql(rows) {
|
|
829
815
|
const events = (Array.isArray(rows) ? rows : []).map((r) => ({
|
|
830
|
-
keyword:
|
|
816
|
+
keyword: rowString(r.keyword),
|
|
831
817
|
totalImpressions: num(r.totalImpressions),
|
|
832
818
|
totalClicks: num(r.totalClicks),
|
|
833
819
|
competitorCount: num(r.competitorCount),
|
|
834
|
-
leaderUrl:
|
|
820
|
+
leaderUrl: rowString(r.leaderUrl),
|
|
835
821
|
leaderCtr: num(r.leaderCtr),
|
|
836
822
|
leaderPosition: num(r.leaderPosition),
|
|
837
823
|
hhi: num(r.hhi),
|
|
838
824
|
fragmentation: num(r.fragmentation),
|
|
839
825
|
stolenClicks: num(r.stolenClicks),
|
|
840
826
|
severity: num(r.severity),
|
|
841
|
-
competitors:
|
|
842
|
-
url:
|
|
827
|
+
competitors: parseJsonRows(r.competitors).map((c) => ({
|
|
828
|
+
url: rowString(c.url),
|
|
843
829
|
clicks: num(c.clicks),
|
|
844
830
|
impressions: num(c.impressions),
|
|
845
831
|
ctr: num(c.ctr),
|
|
@@ -916,24 +902,6 @@ const cannibalizationAnalyzer = defineAnalyzer({
|
|
|
916
902
|
};
|
|
917
903
|
}
|
|
918
904
|
});
|
|
919
|
-
function num$4(v) {
|
|
920
|
-
if (typeof v === "number") return v;
|
|
921
|
-
if (typeof v === "bigint") return Number(v);
|
|
922
|
-
if (v == null) return 0;
|
|
923
|
-
const n = Number(v);
|
|
924
|
-
return Number.isFinite(n) ? n : 0;
|
|
925
|
-
}
|
|
926
|
-
function str$19(v) {
|
|
927
|
-
return v == null ? "" : String(v);
|
|
928
|
-
}
|
|
929
|
-
function parseJsonList$14(v) {
|
|
930
|
-
if (Array.isArray(v)) return v;
|
|
931
|
-
if (typeof v === "string" && v.length > 0) {
|
|
932
|
-
const parsed = JSON.parse(v);
|
|
933
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
934
|
-
}
|
|
935
|
-
return [];
|
|
936
|
-
}
|
|
937
905
|
const changePointAnalyzer = defineAnalyzer({
|
|
938
906
|
id: "change-point",
|
|
939
907
|
buildSql(params) {
|
|
@@ -1086,24 +1054,24 @@ const changePointAnalyzer = defineAnalyzer({
|
|
|
1086
1054
|
const metric = params.metric === "clicks" || params.metric === "impressions" ? params.metric : "position";
|
|
1087
1055
|
const lowerIsBetter = metric === "position";
|
|
1088
1056
|
const results = arr.map((r) => {
|
|
1089
|
-
const delta =
|
|
1057
|
+
const delta = rowNumber(r.delta);
|
|
1090
1058
|
const improved = lowerIsBetter ? delta < 0 : delta > 0;
|
|
1091
1059
|
return {
|
|
1092
|
-
keyword:
|
|
1093
|
-
page:
|
|
1094
|
-
totalDays:
|
|
1095
|
-
totalImpressions:
|
|
1096
|
-
changeDate:
|
|
1097
|
-
llr:
|
|
1098
|
-
leftMean:
|
|
1099
|
-
rightMean:
|
|
1060
|
+
keyword: rowString(r.keyword),
|
|
1061
|
+
page: rowString(r.page),
|
|
1062
|
+
totalDays: rowNumber(r.totalDays),
|
|
1063
|
+
totalImpressions: rowNumber(r.totalImpressions),
|
|
1064
|
+
changeDate: rowString(r.changeDate),
|
|
1065
|
+
llr: rowNumber(r.llr),
|
|
1066
|
+
leftMean: rowNumber(r.leftMean),
|
|
1067
|
+
rightMean: rowNumber(r.rightMean),
|
|
1100
1068
|
delta,
|
|
1101
|
-
leftStddev:
|
|
1102
|
-
rightStddev:
|
|
1069
|
+
leftStddev: rowNumber(r.leftStddev),
|
|
1070
|
+
rightStddev: rowNumber(r.rightStddev),
|
|
1103
1071
|
direction: improved ? "improved" : "worsened",
|
|
1104
|
-
series:
|
|
1105
|
-
date:
|
|
1106
|
-
value:
|
|
1072
|
+
series: parseJsonRows(r.seriesJson).map((s) => ({
|
|
1073
|
+
date: rowString(s.date),
|
|
1074
|
+
value: rowNumber(s.value)
|
|
1107
1075
|
}))
|
|
1108
1076
|
};
|
|
1109
1077
|
});
|
|
@@ -1140,17 +1108,6 @@ const INTENT_PREFIXES = [
|
|
|
1140
1108
|
"near me"
|
|
1141
1109
|
];
|
|
1142
1110
|
const WHITESPACE_RE = /\s+/;
|
|
1143
|
-
function str$18(v) {
|
|
1144
|
-
return v == null ? "" : String(v);
|
|
1145
|
-
}
|
|
1146
|
-
function parseJsonList$13(v) {
|
|
1147
|
-
if (Array.isArray(v)) return v;
|
|
1148
|
-
if (typeof v === "string" && v.length > 0) {
|
|
1149
|
-
const parsed = JSON.parse(v);
|
|
1150
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
1151
|
-
}
|
|
1152
|
-
return [];
|
|
1153
|
-
}
|
|
1154
1111
|
function extractIntentPrefix(keyword) {
|
|
1155
1112
|
const lower = keyword.toLowerCase();
|
|
1156
1113
|
for (const prefix of INTENT_PREFIXES) if (lower.startsWith(`${prefix} `) || lower.startsWith(prefix)) return prefix;
|
|
@@ -1288,14 +1245,14 @@ const clusteringAnalyzer = defineAnalyzer({
|
|
|
1288
1245
|
},
|
|
1289
1246
|
reduceSql(rows) {
|
|
1290
1247
|
const clusters = (Array.isArray(rows) ? rows : []).map((r) => ({
|
|
1291
|
-
clusterName:
|
|
1292
|
-
clusterType:
|
|
1248
|
+
clusterName: rowString(r.clusterName),
|
|
1249
|
+
clusterType: rowString(r.clusterType),
|
|
1293
1250
|
keywordCount: num(r.keywordCount),
|
|
1294
1251
|
totalClicks: num(r.totalClicks),
|
|
1295
1252
|
totalImpressions: num(r.totalImpressions),
|
|
1296
1253
|
avgPosition: num(r.avgPosition),
|
|
1297
|
-
keywords:
|
|
1298
|
-
query:
|
|
1254
|
+
keywords: parseJsonRows(r.keywords).map((k) => ({
|
|
1255
|
+
query: rowString(k.query),
|
|
1299
1256
|
clicks: num(k.clicks),
|
|
1300
1257
|
impressions: num(k.impressions),
|
|
1301
1258
|
ctr: num(k.ctr),
|
|
@@ -1325,17 +1282,6 @@ const clusteringAnalyzer = defineAnalyzer({
|
|
|
1325
1282
|
};
|
|
1326
1283
|
}
|
|
1327
1284
|
});
|
|
1328
|
-
function str$17(v) {
|
|
1329
|
-
return v == null ? "" : String(v);
|
|
1330
|
-
}
|
|
1331
|
-
function parseJsonList$12(v) {
|
|
1332
|
-
if (Array.isArray(v)) return v;
|
|
1333
|
-
if (typeof v === "string" && v.length > 0) {
|
|
1334
|
-
const parsed = JSON.parse(v);
|
|
1335
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
1336
|
-
}
|
|
1337
|
-
return [];
|
|
1338
|
-
}
|
|
1339
1285
|
function calculateGini(values) {
|
|
1340
1286
|
if (values.length === 0) return 0;
|
|
1341
1287
|
const sorted = [...values].sort((a, b) => a - b);
|
|
@@ -1476,20 +1422,20 @@ const concentrationAnalyzer = defineAnalyzer({
|
|
|
1476
1422
|
},
|
|
1477
1423
|
reduceSql(rows, params) {
|
|
1478
1424
|
const r = (Array.isArray(rows) ? rows : [])[0] ?? {};
|
|
1479
|
-
const topRaw =
|
|
1425
|
+
const topRaw = parseJsonRows(r.topNItems);
|
|
1480
1426
|
return {
|
|
1481
1427
|
results: [{
|
|
1482
1428
|
giniCoefficient: num(r.giniCoefficient),
|
|
1483
1429
|
hhi: num(r.hhi),
|
|
1484
1430
|
topNConcentration: num(r.topNConcentration),
|
|
1485
1431
|
topNItems: topRaw.map((t) => ({
|
|
1486
|
-
key:
|
|
1432
|
+
key: rowString(t.key),
|
|
1487
1433
|
clicks: num(t.clicks),
|
|
1488
1434
|
share: num(t.share)
|
|
1489
1435
|
})),
|
|
1490
1436
|
totalItems: num(r.totalItems),
|
|
1491
1437
|
totalClicks: num(r.totalClicks),
|
|
1492
|
-
riskLevel:
|
|
1438
|
+
riskLevel: rowString(r.riskLevel)
|
|
1493
1439
|
}],
|
|
1494
1440
|
meta: {
|
|
1495
1441
|
total: 1,
|
|
@@ -1514,16 +1460,6 @@ const concentrationAnalyzer = defineAnalyzer({
|
|
|
1514
1460
|
};
|
|
1515
1461
|
}
|
|
1516
1462
|
});
|
|
1517
|
-
function num$3(v) {
|
|
1518
|
-
if (typeof v === "number") return v;
|
|
1519
|
-
if (typeof v === "bigint") return Number(v);
|
|
1520
|
-
if (v == null) return 0;
|
|
1521
|
-
const n = Number(v);
|
|
1522
|
-
return Number.isFinite(n) ? n : 0;
|
|
1523
|
-
}
|
|
1524
|
-
function str$16(v) {
|
|
1525
|
-
return v == null ? "" : String(v);
|
|
1526
|
-
}
|
|
1527
1463
|
const contentVelocityAnalyzer = defineAnalyzer({
|
|
1528
1464
|
id: "content-velocity",
|
|
1529
1465
|
buildSql(params) {
|
|
@@ -1580,9 +1516,9 @@ const contentVelocityAnalyzer = defineAnalyzer({
|
|
|
1580
1516
|
startDateD.setUTCDate(startDateD.getUTCDate() - days);
|
|
1581
1517
|
const startDate = toIsoDate(startDateD);
|
|
1582
1518
|
const weekly = arr.map((r) => ({
|
|
1583
|
-
week:
|
|
1584
|
-
newKeywords:
|
|
1585
|
-
totalKeywords:
|
|
1519
|
+
week: rowString(r.week),
|
|
1520
|
+
newKeywords: rowNumber(r.newKeywords),
|
|
1521
|
+
totalKeywords: rowNumber(r.totalKeywords)
|
|
1586
1522
|
}));
|
|
1587
1523
|
const total = weekly.reduce((s, w) => s + w.newKeywords, 0);
|
|
1588
1524
|
const avg = weekly.length > 0 ? total / weekly.length : 0;
|
|
@@ -1605,27 +1541,6 @@ const contentVelocityAnalyzer = defineAnalyzer({
|
|
|
1605
1541
|
};
|
|
1606
1542
|
}
|
|
1607
1543
|
});
|
|
1608
|
-
function num$2(v) {
|
|
1609
|
-
if (typeof v === "number") return v;
|
|
1610
|
-
if (typeof v === "bigint") return Number(v);
|
|
1611
|
-
if (v == null) return 0;
|
|
1612
|
-
const n = Number(v);
|
|
1613
|
-
return Number.isFinite(n) ? n : 0;
|
|
1614
|
-
}
|
|
1615
|
-
function str$15(v) {
|
|
1616
|
-
return v == null ? "" : String(v);
|
|
1617
|
-
}
|
|
1618
|
-
function bool$2(v) {
|
|
1619
|
-
return v === true || v === 1 || v === "true";
|
|
1620
|
-
}
|
|
1621
|
-
function parseJsonList$11(v) {
|
|
1622
|
-
if (Array.isArray(v)) return v;
|
|
1623
|
-
if (typeof v === "string" && v.length > 0) {
|
|
1624
|
-
const parsed = JSON.parse(v);
|
|
1625
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
1626
|
-
}
|
|
1627
|
-
return [];
|
|
1628
|
-
}
|
|
1629
1544
|
const ctrAnomalyAnalyzer = defineAnalyzer({
|
|
1630
1545
|
id: "ctr-anomaly",
|
|
1631
1546
|
buildSql(params) {
|
|
@@ -1767,26 +1682,26 @@ const ctrAnomalyAnalyzer = defineAnalyzer({
|
|
|
1767
1682
|
const minRollingN = 14;
|
|
1768
1683
|
const zThreshold = params.threshold ?? 2;
|
|
1769
1684
|
const anomalies = arr.map((r) => ({
|
|
1770
|
-
keyword:
|
|
1771
|
-
page:
|
|
1772
|
-
breachDaysDown:
|
|
1773
|
-
breachDaysUp:
|
|
1774
|
-
clicksLost:
|
|
1775
|
-
severity:
|
|
1776
|
-
maxZ:
|
|
1777
|
-
baselineCtr:
|
|
1778
|
-
baselinePosition:
|
|
1779
|
-
totalImpressions:
|
|
1780
|
-
totalClicks:
|
|
1781
|
-
series:
|
|
1782
|
-
date:
|
|
1783
|
-
ctr:
|
|
1784
|
-
position:
|
|
1785
|
-
impressions:
|
|
1786
|
-
rollingCtr: s.rollingCtr == null ? null :
|
|
1787
|
-
rollingStddev: s.rollingStddev == null ? null :
|
|
1788
|
-
z:
|
|
1789
|
-
breach:
|
|
1685
|
+
keyword: rowString(r.keyword),
|
|
1686
|
+
page: rowString(r.page),
|
|
1687
|
+
breachDaysDown: rowNumber(r.breachDaysDown),
|
|
1688
|
+
breachDaysUp: rowNumber(r.breachDaysUp),
|
|
1689
|
+
clicksLost: rowNumber(r.clicksLost),
|
|
1690
|
+
severity: rowNumber(r.severityRaw),
|
|
1691
|
+
maxZ: rowNumber(r.maxZ),
|
|
1692
|
+
baselineCtr: rowNumber(r.baselineCtr),
|
|
1693
|
+
baselinePosition: rowNumber(r.baselinePosition),
|
|
1694
|
+
totalImpressions: rowNumber(r.totalImpressions),
|
|
1695
|
+
totalClicks: rowNumber(r.totalClicks),
|
|
1696
|
+
series: parseJsonRows(r.seriesJson).map((s) => ({
|
|
1697
|
+
date: rowString(s.date),
|
|
1698
|
+
ctr: rowNumber(s.ctr),
|
|
1699
|
+
position: rowNumber(s.position),
|
|
1700
|
+
impressions: rowNumber(s.impressions),
|
|
1701
|
+
rollingCtr: s.rollingCtr == null ? null : rowNumber(s.rollingCtr),
|
|
1702
|
+
rollingStddev: s.rollingStddev == null ? null : rowNumber(s.rollingStddev),
|
|
1703
|
+
z: rowNumber(s.z),
|
|
1704
|
+
breach: rowBoolean(s.breach)
|
|
1790
1705
|
}))
|
|
1791
1706
|
}));
|
|
1792
1707
|
const totalClicksLost = anomalies.reduce((s, a) => s + a.clicksLost, 0);
|
|
@@ -1803,24 +1718,6 @@ const ctrAnomalyAnalyzer = defineAnalyzer({
|
|
|
1803
1718
|
};
|
|
1804
1719
|
}
|
|
1805
1720
|
});
|
|
1806
|
-
function num$1(v) {
|
|
1807
|
-
if (typeof v === "number") return v;
|
|
1808
|
-
if (typeof v === "bigint") return Number(v);
|
|
1809
|
-
if (v == null) return 0;
|
|
1810
|
-
const n = Number(v);
|
|
1811
|
-
return Number.isFinite(n) ? n : 0;
|
|
1812
|
-
}
|
|
1813
|
-
function str$14(v) {
|
|
1814
|
-
return v == null ? "" : String(v);
|
|
1815
|
-
}
|
|
1816
|
-
function parseJsonList$10(v) {
|
|
1817
|
-
if (Array.isArray(v)) return v;
|
|
1818
|
-
if (typeof v === "string" && v.length > 0) {
|
|
1819
|
-
const parsed = JSON.parse(v);
|
|
1820
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
1821
|
-
}
|
|
1822
|
-
return [];
|
|
1823
|
-
}
|
|
1824
1721
|
const ctrCurveAnalyzer = defineAnalyzer({
|
|
1825
1722
|
id: "ctr-curve",
|
|
1826
1723
|
buildSql(params) {
|
|
@@ -1915,22 +1812,22 @@ const ctrCurveAnalyzer = defineAnalyzer({
|
|
|
1915
1812
|
const arr = Array.isArray(rows) ? rows : [];
|
|
1916
1813
|
const { startDate, endDate } = periodOf(params);
|
|
1917
1814
|
const row = arr[0] ?? {};
|
|
1918
|
-
const curve =
|
|
1919
|
-
bucket:
|
|
1920
|
-
avgCtr:
|
|
1921
|
-
medianPosition:
|
|
1922
|
-
keywordCount:
|
|
1923
|
-
totalClicks:
|
|
1924
|
-
totalImpressions:
|
|
1815
|
+
const curve = parseJsonRows(row.curve_json).map((r) => ({
|
|
1816
|
+
bucket: rowString(r.bucket),
|
|
1817
|
+
avgCtr: rowNumber(r.avgCtr),
|
|
1818
|
+
medianPosition: rowNumber(r.medianPosition),
|
|
1819
|
+
keywordCount: rowNumber(r.keywordCount),
|
|
1820
|
+
totalClicks: rowNumber(r.totalClicks),
|
|
1821
|
+
totalImpressions: rowNumber(r.totalImpressions)
|
|
1925
1822
|
}));
|
|
1926
|
-
const outliers =
|
|
1927
|
-
query:
|
|
1928
|
-
clicks:
|
|
1929
|
-
impressions:
|
|
1930
|
-
ctr:
|
|
1931
|
-
position:
|
|
1932
|
-
expectedCtr:
|
|
1933
|
-
ctrDiff:
|
|
1823
|
+
const outliers = parseJsonRows(row.outliers_json).map((r) => ({
|
|
1824
|
+
query: rowString(r.query),
|
|
1825
|
+
clicks: rowNumber(r.clicks),
|
|
1826
|
+
impressions: rowNumber(r.impressions),
|
|
1827
|
+
ctr: rowNumber(r.ctr),
|
|
1828
|
+
position: rowNumber(r.position),
|
|
1829
|
+
expectedCtr: rowNumber(r.expectedCtr),
|
|
1830
|
+
ctrDiff: rowNumber(r.ctrDiff)
|
|
1934
1831
|
}));
|
|
1935
1832
|
return {
|
|
1936
1833
|
results: curve,
|
|
@@ -1943,17 +1840,6 @@ const ctrCurveAnalyzer = defineAnalyzer({
|
|
|
1943
1840
|
};
|
|
1944
1841
|
}
|
|
1945
1842
|
});
|
|
1946
|
-
function str$13(v) {
|
|
1947
|
-
return v == null ? "" : String(v);
|
|
1948
|
-
}
|
|
1949
|
-
function parseJsonList$9(v) {
|
|
1950
|
-
if (Array.isArray(v)) return v;
|
|
1951
|
-
if (typeof v === "string" && v.length > 0) {
|
|
1952
|
-
const parsed = JSON.parse(v);
|
|
1953
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
1954
|
-
}
|
|
1955
|
-
return [];
|
|
1956
|
-
}
|
|
1957
1843
|
const darkTrafficAnalyzer = defineAnalyzer({
|
|
1958
1844
|
id: "dark-traffic",
|
|
1959
1845
|
buildSql(params) {
|
|
@@ -2055,8 +1941,8 @@ const darkTrafficAnalyzer = defineAnalyzer({
|
|
|
2055
1941
|
const darkClicks = Math.max(0, totalClicks - attributedClicks);
|
|
2056
1942
|
const darkPercent = totalClicks > 0 ? darkClicks / totalClicks : 0;
|
|
2057
1943
|
return {
|
|
2058
|
-
results:
|
|
2059
|
-
url:
|
|
1944
|
+
results: parseJsonRows(row.pages_json).map((r) => ({
|
|
1945
|
+
url: rowString(r.url),
|
|
2060
1946
|
totalClicks: num(r.totalClicks),
|
|
2061
1947
|
attributedClicks: num(r.attributedClicks),
|
|
2062
1948
|
darkClicks: num(r.darkClicks),
|
|
@@ -2476,17 +2362,6 @@ const sortResults$1 = createMetricSorter("lostClicks", {
|
|
|
2476
2362
|
declinePercent: "desc",
|
|
2477
2363
|
currentClicks: "asc"
|
|
2478
2364
|
});
|
|
2479
|
-
function str$12(v) {
|
|
2480
|
-
return v == null ? "" : String(v);
|
|
2481
|
-
}
|
|
2482
|
-
function parseJsonList$8(v) {
|
|
2483
|
-
if (Array.isArray(v)) return v;
|
|
2484
|
-
if (typeof v === "string" && v.length > 0) {
|
|
2485
|
-
const parsed = JSON.parse(v);
|
|
2486
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
2487
|
-
}
|
|
2488
|
-
return [];
|
|
2489
|
-
}
|
|
2490
2365
|
function analyzeDecay(input, options = {}) {
|
|
2491
2366
|
const { minPreviousClicks = 50, threshold = .2, sortBy = "lostClicks" } = options;
|
|
2492
2367
|
const currentMap = buildPeriodMap(input.current, (r) => r.page, (r) => ({
|
|
@@ -2613,7 +2488,7 @@ const decayAnalyzer = defineAnalyzer({
|
|
|
2613
2488
|
},
|
|
2614
2489
|
reduceSql(rows, params) {
|
|
2615
2490
|
const mapped = (Array.isArray(rows) ? rows : []).map((r) => ({
|
|
2616
|
-
page:
|
|
2491
|
+
page: rowString(r.page),
|
|
2617
2492
|
currentClicks: num(r.currentClicks),
|
|
2618
2493
|
previousClicks: num(r.previousClicks),
|
|
2619
2494
|
lostClicks: num(r.lostClicks),
|
|
@@ -2621,8 +2496,8 @@ const decayAnalyzer = defineAnalyzer({
|
|
|
2621
2496
|
currentPosition: num(r.currentPosition),
|
|
2622
2497
|
previousPosition: num(r.previousPosition),
|
|
2623
2498
|
positionDrop: num(r.positionDrop),
|
|
2624
|
-
series:
|
|
2625
|
-
week:
|
|
2499
|
+
series: parseJsonRows(r.seriesJson).map((s) => ({
|
|
2500
|
+
week: rowString(s.week),
|
|
2626
2501
|
clicks: num(s.clicks),
|
|
2627
2502
|
impressions: num(s.impressions)
|
|
2628
2503
|
}))
|
|
@@ -2660,9 +2535,6 @@ const decayAnalyzer = defineAnalyzer({
|
|
|
2660
2535
|
};
|
|
2661
2536
|
}
|
|
2662
2537
|
});
|
|
2663
|
-
function str$11(v) {
|
|
2664
|
-
return v == null ? "" : String(v);
|
|
2665
|
-
}
|
|
2666
2538
|
const deviceGapAnalyzer = defineAnalyzer({
|
|
2667
2539
|
id: "device-gap",
|
|
2668
2540
|
buildSql(params) {
|
|
@@ -2713,8 +2585,8 @@ const deviceGapAnalyzer = defineAnalyzer({
|
|
|
2713
2585
|
const arr = Array.isArray(rows) ? rows : [];
|
|
2714
2586
|
const { startDate, endDate } = periodOf(params);
|
|
2715
2587
|
const typed = arr.map((r) => ({
|
|
2716
|
-
date:
|
|
2717
|
-
device:
|
|
2588
|
+
date: rowString(r.date),
|
|
2589
|
+
device: rowString(r.device).toUpperCase(),
|
|
2718
2590
|
clicks: num(r.clicks),
|
|
2719
2591
|
impressions: num(r.impressions),
|
|
2720
2592
|
ctr: num(r.ctr),
|
|
@@ -2792,17 +2664,6 @@ const deviceGapAnalyzer = defineAnalyzer({
|
|
|
2792
2664
|
};
|
|
2793
2665
|
}
|
|
2794
2666
|
});
|
|
2795
|
-
function str$10(v) {
|
|
2796
|
-
return v == null ? "" : String(v);
|
|
2797
|
-
}
|
|
2798
|
-
function parseJsonList$7(v) {
|
|
2799
|
-
if (Array.isArray(v)) return v;
|
|
2800
|
-
if (typeof v === "string" && v.length > 0) {
|
|
2801
|
-
const parsed = JSON.parse(v);
|
|
2802
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
2803
|
-
}
|
|
2804
|
-
return [];
|
|
2805
|
-
}
|
|
2806
2667
|
const INTENT_ATLAS_STOP_WORDS = [
|
|
2807
2668
|
"the",
|
|
2808
2669
|
"a",
|
|
@@ -2945,14 +2806,14 @@ const intentAtlasAnalyzer = defineAnalyzer({
|
|
|
2945
2806
|
},
|
|
2946
2807
|
reduceSql(rows) {
|
|
2947
2808
|
const clusters = (Array.isArray(rows) ? rows : []).map((r) => ({
|
|
2948
|
-
clusterKey:
|
|
2809
|
+
clusterKey: rowString(r.clusterKey),
|
|
2949
2810
|
keywordCount: num(r.keywordCount),
|
|
2950
2811
|
totalImpressions: num(r.totalImpressions),
|
|
2951
2812
|
totalClicks: num(r.totalClicks),
|
|
2952
2813
|
ctr: num(r.ctr),
|
|
2953
2814
|
avgPosition: num(r.avgPosition),
|
|
2954
|
-
keywords:
|
|
2955
|
-
query:
|
|
2815
|
+
keywords: parseJsonRows(r.keywords).slice(0, 25).map((k) => ({
|
|
2816
|
+
query: rowString(k.query),
|
|
2956
2817
|
impressions: num(k.impressions),
|
|
2957
2818
|
clicks: num(k.clicks),
|
|
2958
2819
|
position: num(k.position)
|
|
@@ -2970,17 +2831,6 @@ const intentAtlasAnalyzer = defineAnalyzer({
|
|
|
2970
2831
|
};
|
|
2971
2832
|
}
|
|
2972
2833
|
});
|
|
2973
|
-
function str$9(v) {
|
|
2974
|
-
return v == null ? "" : String(v);
|
|
2975
|
-
}
|
|
2976
|
-
function parseJsonList$6(v) {
|
|
2977
|
-
if (Array.isArray(v)) return v;
|
|
2978
|
-
if (typeof v === "string" && v.length > 0) {
|
|
2979
|
-
const parsed = JSON.parse(v);
|
|
2980
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
2981
|
-
}
|
|
2982
|
-
return [];
|
|
2983
|
-
}
|
|
2984
2834
|
const keywordBreadthAnalyzer = defineAnalyzer({
|
|
2985
2835
|
id: "keyword-breadth",
|
|
2986
2836
|
buildSql(params) {
|
|
@@ -3055,18 +2905,18 @@ const keywordBreadthAnalyzer = defineAnalyzer({
|
|
|
3055
2905
|
const arr = Array.isArray(rows) ? rows : [];
|
|
3056
2906
|
const { startDate, endDate } = periodOf(params);
|
|
3057
2907
|
const row = arr[0] ?? {};
|
|
3058
|
-
const distribution =
|
|
3059
|
-
bucket:
|
|
2908
|
+
const distribution = parseJsonRows(row.distribution_json).sort((a, b) => num(a.sortKey) - num(b.sortKey)).map((r) => ({
|
|
2909
|
+
bucket: rowString(r.bucket),
|
|
3060
2910
|
pageCount: num(r.pageCount)
|
|
3061
2911
|
}));
|
|
3062
|
-
const fragile =
|
|
3063
|
-
url:
|
|
2912
|
+
const fragile = parseJsonRows(row.fragile_json).map((r) => ({
|
|
2913
|
+
url: rowString(r.url),
|
|
3064
2914
|
keywordCount: num(r.keywordCount),
|
|
3065
2915
|
clicks: num(r.clicks),
|
|
3066
2916
|
impressions: num(r.impressions)
|
|
3067
2917
|
}));
|
|
3068
|
-
const authority =
|
|
3069
|
-
url:
|
|
2918
|
+
const authority = parseJsonRows(row.authority_json).map((r) => ({
|
|
2919
|
+
url: rowString(r.url),
|
|
3070
2920
|
keywordCount: num(r.keywordCount),
|
|
3071
2921
|
clicks: num(r.clicks),
|
|
3072
2922
|
impressions: num(r.impressions)
|
|
@@ -3089,23 +2939,12 @@ const keywordBreadthAnalyzer = defineAnalyzer({
|
|
|
3089
2939
|
};
|
|
3090
2940
|
}
|
|
3091
2941
|
});
|
|
3092
|
-
function str$8(v) {
|
|
3093
|
-
return v == null ? "" : String(v);
|
|
3094
|
-
}
|
|
3095
|
-
function parseJsonList$5(v) {
|
|
3096
|
-
if (Array.isArray(v)) return v;
|
|
3097
|
-
if (typeof v === "string" && v.length > 0) {
|
|
3098
|
-
const parsed = JSON.parse(v);
|
|
3099
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
3100
|
-
}
|
|
3101
|
-
return [];
|
|
3102
|
-
}
|
|
3103
2942
|
function downsampleLogRank(points) {
|
|
3104
2943
|
const all = points.map((p) => ({
|
|
3105
2944
|
rank: num(p.rank),
|
|
3106
2945
|
impressions: num(p.impressions),
|
|
3107
2946
|
clicks: num(p.clicks),
|
|
3108
|
-
query:
|
|
2947
|
+
query: rowString(p.query)
|
|
3109
2948
|
}));
|
|
3110
2949
|
if (all.length <= 80) return all;
|
|
3111
2950
|
const top = all.slice(0, 10);
|
|
@@ -3214,7 +3053,7 @@ const longTailAnalyzer = defineAnalyzer({
|
|
|
3214
3053
|
},
|
|
3215
3054
|
reduceSql(rows) {
|
|
3216
3055
|
const results = (Array.isArray(rows) ? rows : []).map((r) => ({
|
|
3217
|
-
page:
|
|
3056
|
+
page: rowString(r.page),
|
|
3218
3057
|
queryCount: num(r.queryCount),
|
|
3219
3058
|
totalImpressions: num(r.totalImpressions),
|
|
3220
3059
|
totalClicks: num(r.totalClicks),
|
|
@@ -3223,8 +3062,8 @@ const longTailAnalyzer = defineAnalyzer({
|
|
|
3223
3062
|
r2: num(r.r2),
|
|
3224
3063
|
headImpressions: num(r.headImpressions),
|
|
3225
3064
|
headShare: num(r.headShare),
|
|
3226
|
-
fingerprint:
|
|
3227
|
-
points: downsampleLogRank(
|
|
3065
|
+
fingerprint: rowString(r.fingerprint),
|
|
3066
|
+
points: downsampleLogRank(parseJsonRows(r.pointsJson))
|
|
3228
3067
|
}));
|
|
3229
3068
|
const counts = {
|
|
3230
3069
|
"flat-tail": 0,
|
|
@@ -3246,17 +3085,6 @@ function percentDifference(current, previous) {
|
|
|
3246
3085
|
if (previous === 0) return current > 0 ? 100 : 0;
|
|
3247
3086
|
return (current - previous) / previous * 100;
|
|
3248
3087
|
}
|
|
3249
|
-
function str$7(v) {
|
|
3250
|
-
return v == null ? "" : String(v);
|
|
3251
|
-
}
|
|
3252
|
-
function parseJsonList$4(v) {
|
|
3253
|
-
if (Array.isArray(v)) return v;
|
|
3254
|
-
if (typeof v === "string" && v.length > 0) {
|
|
3255
|
-
const parsed = JSON.parse(v);
|
|
3256
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
3257
|
-
}
|
|
3258
|
-
return [];
|
|
3259
|
-
}
|
|
3260
3088
|
function withCanonicalFields(r) {
|
|
3261
3089
|
return {
|
|
3262
3090
|
...r,
|
|
@@ -3482,8 +3310,8 @@ const moversAnalyzer = defineAnalyzer({
|
|
|
3482
3310
|
const baselineImpressions = Math.round(num(r.baselineImpressions));
|
|
3483
3311
|
const baselinePosition = num(r.baselinePosition);
|
|
3484
3312
|
return {
|
|
3485
|
-
keyword:
|
|
3486
|
-
page: r.page == null ? null :
|
|
3313
|
+
keyword: rowString(r.keyword),
|
|
3314
|
+
page: r.page == null ? null : rowString(r.page),
|
|
3487
3315
|
recentClicks,
|
|
3488
3316
|
recentImpressions,
|
|
3489
3317
|
recentPosition,
|
|
@@ -3494,9 +3322,9 @@ const moversAnalyzer = defineAnalyzer({
|
|
|
3494
3322
|
clicksChangePercent: num(r.clicksChangePercent),
|
|
3495
3323
|
impressionsChangePercent: num(r.impressionsChangePercent),
|
|
3496
3324
|
positionChange: num(r.positionChange),
|
|
3497
|
-
direction:
|
|
3498
|
-
series:
|
|
3499
|
-
week:
|
|
3325
|
+
direction: rowString(r.direction),
|
|
3326
|
+
series: parseJsonRows(r.seriesJson).map((s) => ({
|
|
3327
|
+
week: rowString(s.week),
|
|
3500
3328
|
clicks: num(s.clicks),
|
|
3501
3329
|
impressions: num(s.impressions)
|
|
3502
3330
|
}))
|
|
@@ -3732,9 +3560,6 @@ const opportunityAnalyzer = defineAnalyzer({
|
|
|
3732
3560
|
};
|
|
3733
3561
|
}
|
|
3734
3562
|
});
|
|
3735
|
-
function str$6(v) {
|
|
3736
|
-
return v == null ? "" : String(v);
|
|
3737
|
-
}
|
|
3738
3563
|
const positionDistributionAnalyzer = defineAnalyzer({
|
|
3739
3564
|
id: "position-distribution",
|
|
3740
3565
|
buildSql(params) {
|
|
@@ -3771,7 +3596,7 @@ const positionDistributionAnalyzer = defineAnalyzer({
|
|
|
3771
3596
|
const { startDate, endDate } = periodOf(params);
|
|
3772
3597
|
return {
|
|
3773
3598
|
results: arr.map((r) => ({
|
|
3774
|
-
date:
|
|
3599
|
+
date: rowString(r.date),
|
|
3775
3600
|
pos_1_3: num(r.pos_1_3),
|
|
3776
3601
|
pos_4_10: num(r.pos_4_10),
|
|
3777
3602
|
pos_11_20: num(r.pos_11_20),
|
|
@@ -3786,9 +3611,6 @@ const positionDistributionAnalyzer = defineAnalyzer({
|
|
|
3786
3611
|
};
|
|
3787
3612
|
}
|
|
3788
3613
|
});
|
|
3789
|
-
function str$5(v) {
|
|
3790
|
-
return v == null ? "" : String(v);
|
|
3791
|
-
}
|
|
3792
3614
|
const positionVolatilityAnalyzer = defineAnalyzer({
|
|
3793
3615
|
id: "position-volatility",
|
|
3794
3616
|
buildSql(params) {
|
|
@@ -3889,8 +3711,8 @@ const positionVolatilityAnalyzer = defineAnalyzer({
|
|
|
3889
3711
|
const byPage = /* @__PURE__ */ new Map();
|
|
3890
3712
|
const allDates = /* @__PURE__ */ new Set();
|
|
3891
3713
|
for (const r of arr) {
|
|
3892
|
-
const page =
|
|
3893
|
-
const date =
|
|
3714
|
+
const page = rowString(r.page);
|
|
3715
|
+
const date = rowString(r.date);
|
|
3894
3716
|
allDates.add(date);
|
|
3895
3717
|
const entry = byPage.get(page) ?? {
|
|
3896
3718
|
page,
|
|
@@ -3925,17 +3747,6 @@ const positionVolatilityAnalyzer = defineAnalyzer({
|
|
|
3925
3747
|
};
|
|
3926
3748
|
}
|
|
3927
3749
|
});
|
|
3928
|
-
function str$4(v) {
|
|
3929
|
-
return v == null ? "" : String(v);
|
|
3930
|
-
}
|
|
3931
|
-
function parseJsonList$3(v) {
|
|
3932
|
-
if (Array.isArray(v)) return v;
|
|
3933
|
-
if (typeof v === "string" && v.length > 0) {
|
|
3934
|
-
const parsed = JSON.parse(v);
|
|
3935
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
3936
|
-
}
|
|
3937
|
-
return [];
|
|
3938
|
-
}
|
|
3939
3750
|
const queryMigrationAnalyzer = defineAnalyzer({
|
|
3940
3751
|
id: "query-migration",
|
|
3941
3752
|
buildSql(params) {
|
|
@@ -4057,17 +3868,17 @@ const queryMigrationAnalyzer = defineAnalyzer({
|
|
|
4057
3868
|
prevStart = toIsoDate(new Date(curStartMs - MS_PER_DAY - span));
|
|
4058
3869
|
}
|
|
4059
3870
|
const edges = arr.map((r) => ({
|
|
4060
|
-
sourcePage:
|
|
4061
|
-
targetPage:
|
|
3871
|
+
sourcePage: rowString(r.source_page),
|
|
3872
|
+
targetPage: rowString(r.target_page),
|
|
4062
3873
|
weight: num(r.weight),
|
|
4063
3874
|
queryCount: num(r.query_count),
|
|
4064
3875
|
exactCount: num(r.exact_count),
|
|
4065
3876
|
fuzzyCount: num(r.query_count) - num(r.exact_count),
|
|
4066
|
-
examples:
|
|
4067
|
-
sourceQuery:
|
|
4068
|
-
targetQuery:
|
|
3877
|
+
examples: parseJsonRows(r.examplesJson).slice(0, 8).map((e) => ({
|
|
3878
|
+
sourceQuery: rowString(e.sourceQuery),
|
|
3879
|
+
targetQuery: rowString(e.targetQuery),
|
|
4069
3880
|
absorbed: num(e.absorbed),
|
|
4070
|
-
matchType:
|
|
3881
|
+
matchType: rowString(e.matchType)
|
|
4071
3882
|
}))
|
|
4072
3883
|
}));
|
|
4073
3884
|
const nodeAgg = /* @__PURE__ */ new Map();
|
|
@@ -4106,12 +3917,6 @@ const queryMigrationAnalyzer = defineAnalyzer({
|
|
|
4106
3917
|
};
|
|
4107
3918
|
}
|
|
4108
3919
|
});
|
|
4109
|
-
function str$3(v) {
|
|
4110
|
-
return v == null ? "" : String(v);
|
|
4111
|
-
}
|
|
4112
|
-
function bool$1(v) {
|
|
4113
|
-
return v === true || v === 1 || v === "true";
|
|
4114
|
-
}
|
|
4115
3920
|
function calculateCV(values) {
|
|
4116
3921
|
if (values.length === 0) return 0;
|
|
4117
3922
|
const mean = values.reduce((a, b) => a + b, 0) / values.length;
|
|
@@ -4205,11 +4010,11 @@ const seasonalityAnalyzer = defineAnalyzer({
|
|
|
4205
4010
|
reduceSql(rows) {
|
|
4206
4011
|
const arr = Array.isArray(rows) ? rows : [];
|
|
4207
4012
|
const breakdown = arr.map((r) => ({
|
|
4208
|
-
month:
|
|
4013
|
+
month: rowString(r.month),
|
|
4209
4014
|
value: num(r.value),
|
|
4210
4015
|
vsAverage: num(r.vsAverage),
|
|
4211
|
-
isPeak:
|
|
4212
|
-
isTrough:
|
|
4016
|
+
isPeak: rowBoolean(r.isPeak),
|
|
4017
|
+
isTrough: rowBoolean(r.isTrough)
|
|
4213
4018
|
}));
|
|
4214
4019
|
const first = arr[0];
|
|
4215
4020
|
const strength = first ? num(first.strength) : 0;
|
|
@@ -4241,20 +4046,6 @@ const seasonalityAnalyzer = defineAnalyzer({
|
|
|
4241
4046
|
};
|
|
4242
4047
|
}
|
|
4243
4048
|
});
|
|
4244
|
-
function str$2(v) {
|
|
4245
|
-
return v == null ? "" : String(v);
|
|
4246
|
-
}
|
|
4247
|
-
function bool(v) {
|
|
4248
|
-
return v === true || v === 1 || v === "true";
|
|
4249
|
-
}
|
|
4250
|
-
function parseJsonList$2(v) {
|
|
4251
|
-
if (Array.isArray(v)) return v;
|
|
4252
|
-
if (typeof v === "string" && v.length > 0) {
|
|
4253
|
-
const parsed = JSON.parse(v);
|
|
4254
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
4255
|
-
}
|
|
4256
|
-
return [];
|
|
4257
|
-
}
|
|
4258
4049
|
const stlDecomposeAnalyzer = defineAnalyzer({
|
|
4259
4050
|
id: "stl-decompose",
|
|
4260
4051
|
buildSql(params) {
|
|
@@ -4407,21 +4198,21 @@ const stlDecomposeAnalyzer = defineAnalyzer({
|
|
|
4407
4198
|
const arr = Array.isArray(rows) ? rows : [];
|
|
4408
4199
|
const metric = params.metric === "clicks" ? "clicks" : "impressions";
|
|
4409
4200
|
const results = arr.map((r) => ({
|
|
4410
|
-
keyword:
|
|
4411
|
-
page:
|
|
4201
|
+
keyword: rowString(r.keyword),
|
|
4202
|
+
page: rowString(r.page),
|
|
4412
4203
|
totalImpressions: num(r.totalImpressions),
|
|
4413
4204
|
days: num(r.days),
|
|
4414
4205
|
seasonalStrength: num(r.seasonalStrength),
|
|
4415
4206
|
trendStrength: num(r.trendStrength),
|
|
4416
4207
|
residualAnomalies: num(r.residualAnomalies),
|
|
4417
4208
|
trendSlope: num(r.trendSlope),
|
|
4418
|
-
series:
|
|
4419
|
-
date:
|
|
4209
|
+
series: parseJsonRows(r.seriesJson).map((s) => ({
|
|
4210
|
+
date: rowString(s.date),
|
|
4420
4211
|
observed: num(s.observed),
|
|
4421
4212
|
trend: s.trend == null ? null : num(s.trend),
|
|
4422
4213
|
seasonal: s.seasonal == null ? null : num(s.seasonal),
|
|
4423
4214
|
residual: s.residual == null ? null : num(s.residual),
|
|
4424
|
-
anomaly:
|
|
4215
|
+
anomaly: rowBoolean(s.anomaly)
|
|
4425
4216
|
}))
|
|
4426
4217
|
}));
|
|
4427
4218
|
return {
|
|
@@ -4504,17 +4295,6 @@ const strikingDistanceAnalyzer = defineAnalyzer({
|
|
|
4504
4295
|
return { queries: queriesQueryState(periodOf(params), params.limit ?? DEFAULT_ROW_LIMIT$1) };
|
|
4505
4296
|
}
|
|
4506
4297
|
});
|
|
4507
|
-
function str$1(v) {
|
|
4508
|
-
return v == null ? "" : String(v);
|
|
4509
|
-
}
|
|
4510
|
-
function parseJsonList$1(v) {
|
|
4511
|
-
if (Array.isArray(v)) return v;
|
|
4512
|
-
if (typeof v === "string" && v.length > 0) {
|
|
4513
|
-
const parsed = JSON.parse(v);
|
|
4514
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
4515
|
-
}
|
|
4516
|
-
return [];
|
|
4517
|
-
}
|
|
4518
4298
|
const survivalAnalyzer = defineAnalyzer({
|
|
4519
4299
|
id: "survival",
|
|
4520
4300
|
buildSql(params) {
|
|
@@ -4667,7 +4447,7 @@ const survivalAnalyzer = defineAnalyzer({
|
|
|
4667
4447
|
const startDate = params.startDate ?? daysAgo(183);
|
|
4668
4448
|
const windowDays = Math.round((new Date(endDate).getTime() - new Date(startDate).getTime()) / MS_PER_DAY) + 1;
|
|
4669
4449
|
const results = arr.map((r) => {
|
|
4670
|
-
const curve =
|
|
4450
|
+
const curve = parseJsonRows(r.curveJson).map((p) => ({
|
|
4671
4451
|
tenure: num(p.tenure),
|
|
4672
4452
|
survival: num(p.survival),
|
|
4673
4453
|
atRisk: num(p.atRisk),
|
|
@@ -4690,7 +4470,7 @@ const survivalAnalyzer = defineAnalyzer({
|
|
|
4690
4470
|
const last = curve[curve.length - 1];
|
|
4691
4471
|
if (medianTenure === 0 && last && last.survival > .5) medianTenure = last.tenure;
|
|
4692
4472
|
return {
|
|
4693
|
-
cohort:
|
|
4473
|
+
cohort: rowString(r.cohort),
|
|
4694
4474
|
episodeCount: num(r.episodeCount),
|
|
4695
4475
|
censoringRate: num(r.censoringRate),
|
|
4696
4476
|
medianTenure,
|
|
@@ -4707,17 +4487,6 @@ const survivalAnalyzer = defineAnalyzer({
|
|
|
4707
4487
|
};
|
|
4708
4488
|
}
|
|
4709
4489
|
});
|
|
4710
|
-
function str(v) {
|
|
4711
|
-
return v == null ? "" : String(v);
|
|
4712
|
-
}
|
|
4713
|
-
function parseJsonList(v) {
|
|
4714
|
-
if (Array.isArray(v)) return v;
|
|
4715
|
-
if (typeof v === "string" && v.length > 0) {
|
|
4716
|
-
const parsed = JSON.parse(v);
|
|
4717
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
4718
|
-
}
|
|
4719
|
-
return [];
|
|
4720
|
-
}
|
|
4721
4490
|
const trendsAnalyzer = defineAnalyzer({
|
|
4722
4491
|
id: "trends",
|
|
4723
4492
|
buildSql(params) {
|
|
@@ -4828,20 +4597,20 @@ const trendsAnalyzer = defineAnalyzer({
|
|
|
4828
4597
|
const startDate = params.startDate || toIsoDate(/* @__PURE__ */ new Date(Date.parse(endDate) - (weeks * 7 - 1) * MS_PER_DAY));
|
|
4829
4598
|
const dim = params.dimension === "keywords" ? "keywords" : "pages";
|
|
4830
4599
|
const results = arr.map((r) => {
|
|
4831
|
-
const series =
|
|
4832
|
-
week:
|
|
4600
|
+
const series = parseJsonRows(r.seriesJson).map((s) => ({
|
|
4601
|
+
week: rowString(s.week),
|
|
4833
4602
|
clicks: num(s.clicks),
|
|
4834
4603
|
impressions: num(s.impressions)
|
|
4835
4604
|
}));
|
|
4836
4605
|
return {
|
|
4837
|
-
[dim === "keywords" ? "query" : "page"]:
|
|
4606
|
+
[dim === "keywords" ? "query" : "page"]: rowString(r.entity),
|
|
4838
4607
|
totalClicks: num(r.totalClicks),
|
|
4839
4608
|
totalImpressions: num(r.totalImpressions),
|
|
4840
4609
|
weeksWithData: num(r.weeksWithData),
|
|
4841
4610
|
slope: num(r.slope),
|
|
4842
4611
|
growthRatio: num(r.growthRatio),
|
|
4843
4612
|
avgPosition: num(r.avgPosition),
|
|
4844
|
-
trend:
|
|
4613
|
+
trend: rowString(r.trend),
|
|
4845
4614
|
series
|
|
4846
4615
|
};
|
|
4847
4616
|
});
|