@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
package/dist/analyzer/index.mjs
CHANGED
|
@@ -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),
|
|
@@ -2482,17 +2368,6 @@ const sortResults$1 = createMetricSorter("lostClicks", {
|
|
|
2482
2368
|
declinePercent: "desc",
|
|
2483
2369
|
currentClicks: "asc"
|
|
2484
2370
|
});
|
|
2485
|
-
function str$12(v) {
|
|
2486
|
-
return v == null ? "" : String(v);
|
|
2487
|
-
}
|
|
2488
|
-
function parseJsonList$8(v) {
|
|
2489
|
-
if (Array.isArray(v)) return v;
|
|
2490
|
-
if (typeof v === "string" && v.length > 0) {
|
|
2491
|
-
const parsed = JSON.parse(v);
|
|
2492
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
2493
|
-
}
|
|
2494
|
-
return [];
|
|
2495
|
-
}
|
|
2496
2371
|
function analyzeDecay(input, options = {}) {
|
|
2497
2372
|
const { minPreviousClicks = 50, threshold = .2, sortBy = "lostClicks" } = options;
|
|
2498
2373
|
const currentMap = buildPeriodMap(input.current, (r) => r.page, (r) => ({
|
|
@@ -2619,7 +2494,7 @@ const decayAnalyzer = defineAnalyzer({
|
|
|
2619
2494
|
},
|
|
2620
2495
|
reduceSql(rows, params) {
|
|
2621
2496
|
const mapped = (Array.isArray(rows) ? rows : []).map((r) => ({
|
|
2622
|
-
page:
|
|
2497
|
+
page: rowString(r.page),
|
|
2623
2498
|
currentClicks: num(r.currentClicks),
|
|
2624
2499
|
previousClicks: num(r.previousClicks),
|
|
2625
2500
|
lostClicks: num(r.lostClicks),
|
|
@@ -2627,8 +2502,8 @@ const decayAnalyzer = defineAnalyzer({
|
|
|
2627
2502
|
currentPosition: num(r.currentPosition),
|
|
2628
2503
|
previousPosition: num(r.previousPosition),
|
|
2629
2504
|
positionDrop: num(r.positionDrop),
|
|
2630
|
-
series:
|
|
2631
|
-
week:
|
|
2505
|
+
series: parseJsonRows(r.seriesJson).map((s) => ({
|
|
2506
|
+
week: rowString(s.week),
|
|
2632
2507
|
clicks: num(s.clicks),
|
|
2633
2508
|
impressions: num(s.impressions)
|
|
2634
2509
|
}))
|
|
@@ -2666,9 +2541,6 @@ const decayAnalyzer = defineAnalyzer({
|
|
|
2666
2541
|
};
|
|
2667
2542
|
}
|
|
2668
2543
|
});
|
|
2669
|
-
function str$11(v) {
|
|
2670
|
-
return v == null ? "" : String(v);
|
|
2671
|
-
}
|
|
2672
2544
|
const deviceGapAnalyzer = defineAnalyzer({
|
|
2673
2545
|
id: "device-gap",
|
|
2674
2546
|
buildSql(params) {
|
|
@@ -2719,8 +2591,8 @@ const deviceGapAnalyzer = defineAnalyzer({
|
|
|
2719
2591
|
const arr = Array.isArray(rows) ? rows : [];
|
|
2720
2592
|
const { startDate, endDate } = periodOf(params);
|
|
2721
2593
|
const typed = arr.map((r) => ({
|
|
2722
|
-
date:
|
|
2723
|
-
device:
|
|
2594
|
+
date: rowString(r.date),
|
|
2595
|
+
device: rowString(r.device).toUpperCase(),
|
|
2724
2596
|
clicks: num(r.clicks),
|
|
2725
2597
|
impressions: num(r.impressions),
|
|
2726
2598
|
ctr: num(r.ctr),
|
|
@@ -2798,17 +2670,6 @@ const deviceGapAnalyzer = defineAnalyzer({
|
|
|
2798
2670
|
};
|
|
2799
2671
|
}
|
|
2800
2672
|
});
|
|
2801
|
-
function str$10(v) {
|
|
2802
|
-
return v == null ? "" : String(v);
|
|
2803
|
-
}
|
|
2804
|
-
function parseJsonList$7(v) {
|
|
2805
|
-
if (Array.isArray(v)) return v;
|
|
2806
|
-
if (typeof v === "string" && v.length > 0) {
|
|
2807
|
-
const parsed = JSON.parse(v);
|
|
2808
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
2809
|
-
}
|
|
2810
|
-
return [];
|
|
2811
|
-
}
|
|
2812
2673
|
const INTENT_ATLAS_STOP_WORDS = [
|
|
2813
2674
|
"the",
|
|
2814
2675
|
"a",
|
|
@@ -2951,14 +2812,14 @@ const intentAtlasAnalyzer = defineAnalyzer({
|
|
|
2951
2812
|
},
|
|
2952
2813
|
reduceSql(rows) {
|
|
2953
2814
|
const clusters = (Array.isArray(rows) ? rows : []).map((r) => ({
|
|
2954
|
-
clusterKey:
|
|
2815
|
+
clusterKey: rowString(r.clusterKey),
|
|
2955
2816
|
keywordCount: num(r.keywordCount),
|
|
2956
2817
|
totalImpressions: num(r.totalImpressions),
|
|
2957
2818
|
totalClicks: num(r.totalClicks),
|
|
2958
2819
|
ctr: num(r.ctr),
|
|
2959
2820
|
avgPosition: num(r.avgPosition),
|
|
2960
|
-
keywords:
|
|
2961
|
-
query:
|
|
2821
|
+
keywords: parseJsonRows(r.keywords).slice(0, 25).map((k) => ({
|
|
2822
|
+
query: rowString(k.query),
|
|
2962
2823
|
impressions: num(k.impressions),
|
|
2963
2824
|
clicks: num(k.clicks),
|
|
2964
2825
|
position: num(k.position)
|
|
@@ -2976,17 +2837,6 @@ const intentAtlasAnalyzer = defineAnalyzer({
|
|
|
2976
2837
|
};
|
|
2977
2838
|
}
|
|
2978
2839
|
});
|
|
2979
|
-
function str$9(v) {
|
|
2980
|
-
return v == null ? "" : String(v);
|
|
2981
|
-
}
|
|
2982
|
-
function parseJsonList$6(v) {
|
|
2983
|
-
if (Array.isArray(v)) return v;
|
|
2984
|
-
if (typeof v === "string" && v.length > 0) {
|
|
2985
|
-
const parsed = JSON.parse(v);
|
|
2986
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
2987
|
-
}
|
|
2988
|
-
return [];
|
|
2989
|
-
}
|
|
2990
2840
|
const keywordBreadthAnalyzer = defineAnalyzer({
|
|
2991
2841
|
id: "keyword-breadth",
|
|
2992
2842
|
buildSql(params) {
|
|
@@ -3061,18 +2911,18 @@ const keywordBreadthAnalyzer = defineAnalyzer({
|
|
|
3061
2911
|
const arr = Array.isArray(rows) ? rows : [];
|
|
3062
2912
|
const { startDate, endDate } = periodOf(params);
|
|
3063
2913
|
const row = arr[0] ?? {};
|
|
3064
|
-
const distribution =
|
|
3065
|
-
bucket:
|
|
2914
|
+
const distribution = parseJsonRows(row.distribution_json).sort((a, b) => num(a.sortKey) - num(b.sortKey)).map((r) => ({
|
|
2915
|
+
bucket: rowString(r.bucket),
|
|
3066
2916
|
pageCount: num(r.pageCount)
|
|
3067
2917
|
}));
|
|
3068
|
-
const fragile =
|
|
3069
|
-
url:
|
|
2918
|
+
const fragile = parseJsonRows(row.fragile_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)
|
|
3073
2923
|
}));
|
|
3074
|
-
const authority =
|
|
3075
|
-
url:
|
|
2924
|
+
const authority = parseJsonRows(row.authority_json).map((r) => ({
|
|
2925
|
+
url: rowString(r.url),
|
|
3076
2926
|
keywordCount: num(r.keywordCount),
|
|
3077
2927
|
clicks: num(r.clicks),
|
|
3078
2928
|
impressions: num(r.impressions)
|
|
@@ -3095,23 +2945,12 @@ const keywordBreadthAnalyzer = defineAnalyzer({
|
|
|
3095
2945
|
};
|
|
3096
2946
|
}
|
|
3097
2947
|
});
|
|
3098
|
-
function str$8(v) {
|
|
3099
|
-
return v == null ? "" : String(v);
|
|
3100
|
-
}
|
|
3101
|
-
function parseJsonList$5(v) {
|
|
3102
|
-
if (Array.isArray(v)) return v;
|
|
3103
|
-
if (typeof v === "string" && v.length > 0) {
|
|
3104
|
-
const parsed = JSON.parse(v);
|
|
3105
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
3106
|
-
}
|
|
3107
|
-
return [];
|
|
3108
|
-
}
|
|
3109
2948
|
function downsampleLogRank(points) {
|
|
3110
2949
|
const all = points.map((p) => ({
|
|
3111
2950
|
rank: num(p.rank),
|
|
3112
2951
|
impressions: num(p.impressions),
|
|
3113
2952
|
clicks: num(p.clicks),
|
|
3114
|
-
query:
|
|
2953
|
+
query: rowString(p.query)
|
|
3115
2954
|
}));
|
|
3116
2955
|
if (all.length <= 80) return all;
|
|
3117
2956
|
const top = all.slice(0, 10);
|
|
@@ -3220,7 +3059,7 @@ const longTailAnalyzer = defineAnalyzer({
|
|
|
3220
3059
|
},
|
|
3221
3060
|
reduceSql(rows) {
|
|
3222
3061
|
const results = (Array.isArray(rows) ? rows : []).map((r) => ({
|
|
3223
|
-
page:
|
|
3062
|
+
page: rowString(r.page),
|
|
3224
3063
|
queryCount: num(r.queryCount),
|
|
3225
3064
|
totalImpressions: num(r.totalImpressions),
|
|
3226
3065
|
totalClicks: num(r.totalClicks),
|
|
@@ -3229,8 +3068,8 @@ const longTailAnalyzer = defineAnalyzer({
|
|
|
3229
3068
|
r2: num(r.r2),
|
|
3230
3069
|
headImpressions: num(r.headImpressions),
|
|
3231
3070
|
headShare: num(r.headShare),
|
|
3232
|
-
fingerprint:
|
|
3233
|
-
points: downsampleLogRank(
|
|
3071
|
+
fingerprint: rowString(r.fingerprint),
|
|
3072
|
+
points: downsampleLogRank(parseJsonRows(r.pointsJson))
|
|
3234
3073
|
}));
|
|
3235
3074
|
const counts = {
|
|
3236
3075
|
"flat-tail": 0,
|
|
@@ -3252,17 +3091,6 @@ function percentDifference(current, previous) {
|
|
|
3252
3091
|
if (previous === 0) return current > 0 ? 100 : 0;
|
|
3253
3092
|
return (current - previous) / previous * 100;
|
|
3254
3093
|
}
|
|
3255
|
-
function str$7(v) {
|
|
3256
|
-
return v == null ? "" : String(v);
|
|
3257
|
-
}
|
|
3258
|
-
function parseJsonList$4(v) {
|
|
3259
|
-
if (Array.isArray(v)) return v;
|
|
3260
|
-
if (typeof v === "string" && v.length > 0) {
|
|
3261
|
-
const parsed = JSON.parse(v);
|
|
3262
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
3263
|
-
}
|
|
3264
|
-
return [];
|
|
3265
|
-
}
|
|
3266
3094
|
function withCanonicalFields(r) {
|
|
3267
3095
|
return {
|
|
3268
3096
|
...r,
|
|
@@ -3488,8 +3316,8 @@ const moversAnalyzer = defineAnalyzer({
|
|
|
3488
3316
|
const baselineImpressions = Math.round(num(r.baselineImpressions));
|
|
3489
3317
|
const baselinePosition = num(r.baselinePosition);
|
|
3490
3318
|
return {
|
|
3491
|
-
keyword:
|
|
3492
|
-
page: r.page == null ? null :
|
|
3319
|
+
keyword: rowString(r.keyword),
|
|
3320
|
+
page: r.page == null ? null : rowString(r.page),
|
|
3493
3321
|
recentClicks,
|
|
3494
3322
|
recentImpressions,
|
|
3495
3323
|
recentPosition,
|
|
@@ -3500,9 +3328,9 @@ const moversAnalyzer = defineAnalyzer({
|
|
|
3500
3328
|
clicksChangePercent: num(r.clicksChangePercent),
|
|
3501
3329
|
impressionsChangePercent: num(r.impressionsChangePercent),
|
|
3502
3330
|
positionChange: num(r.positionChange),
|
|
3503
|
-
direction:
|
|
3504
|
-
series:
|
|
3505
|
-
week:
|
|
3331
|
+
direction: rowString(r.direction),
|
|
3332
|
+
series: parseJsonRows(r.seriesJson).map((s) => ({
|
|
3333
|
+
week: rowString(s.week),
|
|
3506
3334
|
clicks: num(s.clicks),
|
|
3507
3335
|
impressions: num(s.impressions)
|
|
3508
3336
|
}))
|
|
@@ -3738,9 +3566,6 @@ const opportunityAnalyzer = defineAnalyzer({
|
|
|
3738
3566
|
};
|
|
3739
3567
|
}
|
|
3740
3568
|
});
|
|
3741
|
-
function str$6(v) {
|
|
3742
|
-
return v == null ? "" : String(v);
|
|
3743
|
-
}
|
|
3744
3569
|
const positionDistributionAnalyzer = defineAnalyzer({
|
|
3745
3570
|
id: "position-distribution",
|
|
3746
3571
|
buildSql(params) {
|
|
@@ -3777,7 +3602,7 @@ const positionDistributionAnalyzer = defineAnalyzer({
|
|
|
3777
3602
|
const { startDate, endDate } = periodOf(params);
|
|
3778
3603
|
return {
|
|
3779
3604
|
results: arr.map((r) => ({
|
|
3780
|
-
date:
|
|
3605
|
+
date: rowString(r.date),
|
|
3781
3606
|
pos_1_3: num(r.pos_1_3),
|
|
3782
3607
|
pos_4_10: num(r.pos_4_10),
|
|
3783
3608
|
pos_11_20: num(r.pos_11_20),
|
|
@@ -3792,9 +3617,6 @@ const positionDistributionAnalyzer = defineAnalyzer({
|
|
|
3792
3617
|
};
|
|
3793
3618
|
}
|
|
3794
3619
|
});
|
|
3795
|
-
function str$5(v) {
|
|
3796
|
-
return v == null ? "" : String(v);
|
|
3797
|
-
}
|
|
3798
3620
|
const positionVolatilityAnalyzer = defineAnalyzer({
|
|
3799
3621
|
id: "position-volatility",
|
|
3800
3622
|
buildSql(params) {
|
|
@@ -3895,8 +3717,8 @@ const positionVolatilityAnalyzer = defineAnalyzer({
|
|
|
3895
3717
|
const byPage = /* @__PURE__ */ new Map();
|
|
3896
3718
|
const allDates = /* @__PURE__ */ new Set();
|
|
3897
3719
|
for (const r of arr) {
|
|
3898
|
-
const page =
|
|
3899
|
-
const date =
|
|
3720
|
+
const page = rowString(r.page);
|
|
3721
|
+
const date = rowString(r.date);
|
|
3900
3722
|
allDates.add(date);
|
|
3901
3723
|
const entry = byPage.get(page) ?? {
|
|
3902
3724
|
page,
|
|
@@ -3931,17 +3753,6 @@ const positionVolatilityAnalyzer = defineAnalyzer({
|
|
|
3931
3753
|
};
|
|
3932
3754
|
}
|
|
3933
3755
|
});
|
|
3934
|
-
function str$4(v) {
|
|
3935
|
-
return v == null ? "" : String(v);
|
|
3936
|
-
}
|
|
3937
|
-
function parseJsonList$3(v) {
|
|
3938
|
-
if (Array.isArray(v)) return v;
|
|
3939
|
-
if (typeof v === "string" && v.length > 0) {
|
|
3940
|
-
const parsed = JSON.parse(v);
|
|
3941
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
3942
|
-
}
|
|
3943
|
-
return [];
|
|
3944
|
-
}
|
|
3945
3756
|
const queryMigrationAnalyzer = defineAnalyzer({
|
|
3946
3757
|
id: "query-migration",
|
|
3947
3758
|
buildSql(params) {
|
|
@@ -4063,17 +3874,17 @@ const queryMigrationAnalyzer = defineAnalyzer({
|
|
|
4063
3874
|
prevStart = toIsoDate(new Date(curStartMs - MS_PER_DAY - span));
|
|
4064
3875
|
}
|
|
4065
3876
|
const edges = arr.map((r) => ({
|
|
4066
|
-
sourcePage:
|
|
4067
|
-
targetPage:
|
|
3877
|
+
sourcePage: rowString(r.source_page),
|
|
3878
|
+
targetPage: rowString(r.target_page),
|
|
4068
3879
|
weight: num(r.weight),
|
|
4069
3880
|
queryCount: num(r.query_count),
|
|
4070
3881
|
exactCount: num(r.exact_count),
|
|
4071
3882
|
fuzzyCount: num(r.query_count) - num(r.exact_count),
|
|
4072
|
-
examples:
|
|
4073
|
-
sourceQuery:
|
|
4074
|
-
targetQuery:
|
|
3883
|
+
examples: parseJsonRows(r.examplesJson).slice(0, 8).map((e) => ({
|
|
3884
|
+
sourceQuery: rowString(e.sourceQuery),
|
|
3885
|
+
targetQuery: rowString(e.targetQuery),
|
|
4075
3886
|
absorbed: num(e.absorbed),
|
|
4076
|
-
matchType:
|
|
3887
|
+
matchType: rowString(e.matchType)
|
|
4077
3888
|
}))
|
|
4078
3889
|
}));
|
|
4079
3890
|
const nodeAgg = /* @__PURE__ */ new Map();
|
|
@@ -4112,12 +3923,6 @@ const queryMigrationAnalyzer = defineAnalyzer({
|
|
|
4112
3923
|
};
|
|
4113
3924
|
}
|
|
4114
3925
|
});
|
|
4115
|
-
function str$3(v) {
|
|
4116
|
-
return v == null ? "" : String(v);
|
|
4117
|
-
}
|
|
4118
|
-
function bool$1(v) {
|
|
4119
|
-
return v === true || v === 1 || v === "true";
|
|
4120
|
-
}
|
|
4121
3926
|
function calculateCV(values) {
|
|
4122
3927
|
if (values.length === 0) return 0;
|
|
4123
3928
|
const mean = values.reduce((a, b) => a + b, 0) / values.length;
|
|
@@ -4211,11 +4016,11 @@ const seasonalityAnalyzer = defineAnalyzer({
|
|
|
4211
4016
|
reduceSql(rows) {
|
|
4212
4017
|
const arr = Array.isArray(rows) ? rows : [];
|
|
4213
4018
|
const breakdown = arr.map((r) => ({
|
|
4214
|
-
month:
|
|
4019
|
+
month: rowString(r.month),
|
|
4215
4020
|
value: num(r.value),
|
|
4216
4021
|
vsAverage: num(r.vsAverage),
|
|
4217
|
-
isPeak:
|
|
4218
|
-
isTrough:
|
|
4022
|
+
isPeak: rowBoolean(r.isPeak),
|
|
4023
|
+
isTrough: rowBoolean(r.isTrough)
|
|
4219
4024
|
}));
|
|
4220
4025
|
const first = arr[0];
|
|
4221
4026
|
const strength = first ? num(first.strength) : 0;
|
|
@@ -4247,20 +4052,6 @@ const seasonalityAnalyzer = defineAnalyzer({
|
|
|
4247
4052
|
};
|
|
4248
4053
|
}
|
|
4249
4054
|
});
|
|
4250
|
-
function str$2(v) {
|
|
4251
|
-
return v == null ? "" : String(v);
|
|
4252
|
-
}
|
|
4253
|
-
function bool(v) {
|
|
4254
|
-
return v === true || v === 1 || v === "true";
|
|
4255
|
-
}
|
|
4256
|
-
function parseJsonList$2(v) {
|
|
4257
|
-
if (Array.isArray(v)) return v;
|
|
4258
|
-
if (typeof v === "string" && v.length > 0) {
|
|
4259
|
-
const parsed = JSON.parse(v);
|
|
4260
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
4261
|
-
}
|
|
4262
|
-
return [];
|
|
4263
|
-
}
|
|
4264
4055
|
const stlDecomposeAnalyzer = defineAnalyzer({
|
|
4265
4056
|
id: "stl-decompose",
|
|
4266
4057
|
buildSql(params) {
|
|
@@ -4413,21 +4204,21 @@ const stlDecomposeAnalyzer = defineAnalyzer({
|
|
|
4413
4204
|
const arr = Array.isArray(rows) ? rows : [];
|
|
4414
4205
|
const metric = params.metric === "clicks" ? "clicks" : "impressions";
|
|
4415
4206
|
const results = arr.map((r) => ({
|
|
4416
|
-
keyword:
|
|
4417
|
-
page:
|
|
4207
|
+
keyword: rowString(r.keyword),
|
|
4208
|
+
page: rowString(r.page),
|
|
4418
4209
|
totalImpressions: num(r.totalImpressions),
|
|
4419
4210
|
days: num(r.days),
|
|
4420
4211
|
seasonalStrength: num(r.seasonalStrength),
|
|
4421
4212
|
trendStrength: num(r.trendStrength),
|
|
4422
4213
|
residualAnomalies: num(r.residualAnomalies),
|
|
4423
4214
|
trendSlope: num(r.trendSlope),
|
|
4424
|
-
series:
|
|
4425
|
-
date:
|
|
4215
|
+
series: parseJsonRows(r.seriesJson).map((s) => ({
|
|
4216
|
+
date: rowString(s.date),
|
|
4426
4217
|
observed: num(s.observed),
|
|
4427
4218
|
trend: s.trend == null ? null : num(s.trend),
|
|
4428
4219
|
seasonal: s.seasonal == null ? null : num(s.seasonal),
|
|
4429
4220
|
residual: s.residual == null ? null : num(s.residual),
|
|
4430
|
-
anomaly:
|
|
4221
|
+
anomaly: rowBoolean(s.anomaly)
|
|
4431
4222
|
}))
|
|
4432
4223
|
}));
|
|
4433
4224
|
return {
|
|
@@ -4510,17 +4301,6 @@ const strikingDistanceAnalyzer = defineAnalyzer({
|
|
|
4510
4301
|
return { queries: queriesQueryState(periodOf(params), params.limit ?? DEFAULT_ROW_LIMIT$1) };
|
|
4511
4302
|
}
|
|
4512
4303
|
});
|
|
4513
|
-
function str$1(v) {
|
|
4514
|
-
return v == null ? "" : String(v);
|
|
4515
|
-
}
|
|
4516
|
-
function parseJsonList$1(v) {
|
|
4517
|
-
if (Array.isArray(v)) return v;
|
|
4518
|
-
if (typeof v === "string" && v.length > 0) {
|
|
4519
|
-
const parsed = JSON.parse(v);
|
|
4520
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
4521
|
-
}
|
|
4522
|
-
return [];
|
|
4523
|
-
}
|
|
4524
4304
|
const survivalAnalyzer = defineAnalyzer({
|
|
4525
4305
|
id: "survival",
|
|
4526
4306
|
buildSql(params) {
|
|
@@ -4673,7 +4453,7 @@ const survivalAnalyzer = defineAnalyzer({
|
|
|
4673
4453
|
const startDate = params.startDate ?? daysAgo(183);
|
|
4674
4454
|
const windowDays = Math.round((new Date(endDate).getTime() - new Date(startDate).getTime()) / MS_PER_DAY) + 1;
|
|
4675
4455
|
const results = arr.map((r) => {
|
|
4676
|
-
const curve =
|
|
4456
|
+
const curve = parseJsonRows(r.curveJson).map((p) => ({
|
|
4677
4457
|
tenure: num(p.tenure),
|
|
4678
4458
|
survival: num(p.survival),
|
|
4679
4459
|
atRisk: num(p.atRisk),
|
|
@@ -4696,7 +4476,7 @@ const survivalAnalyzer = defineAnalyzer({
|
|
|
4696
4476
|
const last = curve[curve.length - 1];
|
|
4697
4477
|
if (medianTenure === 0 && last && last.survival > .5) medianTenure = last.tenure;
|
|
4698
4478
|
return {
|
|
4699
|
-
cohort:
|
|
4479
|
+
cohort: rowString(r.cohort),
|
|
4700
4480
|
episodeCount: num(r.episodeCount),
|
|
4701
4481
|
censoringRate: num(r.censoringRate),
|
|
4702
4482
|
medianTenure,
|
|
@@ -4713,17 +4493,6 @@ const survivalAnalyzer = defineAnalyzer({
|
|
|
4713
4493
|
};
|
|
4714
4494
|
}
|
|
4715
4495
|
});
|
|
4716
|
-
function str(v) {
|
|
4717
|
-
return v == null ? "" : String(v);
|
|
4718
|
-
}
|
|
4719
|
-
function parseJsonList(v) {
|
|
4720
|
-
if (Array.isArray(v)) return v;
|
|
4721
|
-
if (typeof v === "string" && v.length > 0) {
|
|
4722
|
-
const parsed = JSON.parse(v);
|
|
4723
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
4724
|
-
}
|
|
4725
|
-
return [];
|
|
4726
|
-
}
|
|
4727
4496
|
const trendsAnalyzer = defineAnalyzer({
|
|
4728
4497
|
id: "trends",
|
|
4729
4498
|
buildSql(params) {
|
|
@@ -4834,20 +4603,20 @@ const trendsAnalyzer = defineAnalyzer({
|
|
|
4834
4603
|
const startDate = params.startDate || toIsoDate(/* @__PURE__ */ new Date(Date.parse(endDate) - (weeks * 7 - 1) * MS_PER_DAY));
|
|
4835
4604
|
const dim = params.dimension === "keywords" ? "keywords" : "pages";
|
|
4836
4605
|
const results = arr.map((r) => {
|
|
4837
|
-
const series =
|
|
4838
|
-
week:
|
|
4606
|
+
const series = parseJsonRows(r.seriesJson).map((s) => ({
|
|
4607
|
+
week: rowString(s.week),
|
|
4839
4608
|
clicks: num(s.clicks),
|
|
4840
4609
|
impressions: num(s.impressions)
|
|
4841
4610
|
}));
|
|
4842
4611
|
return {
|
|
4843
|
-
[dim === "keywords" ? "query" : "page"]:
|
|
4612
|
+
[dim === "keywords" ? "query" : "page"]: rowString(r.entity),
|
|
4844
4613
|
totalClicks: num(r.totalClicks),
|
|
4845
4614
|
totalImpressions: num(r.totalImpressions),
|
|
4846
4615
|
weeksWithData: num(r.weeksWithData),
|
|
4847
4616
|
slope: num(r.slope),
|
|
4848
4617
|
growthRatio: num(r.growthRatio),
|
|
4849
4618
|
avgPosition: num(r.avgPosition),
|
|
4850
|
-
trend:
|
|
4619
|
+
trend: rowString(r.trend),
|
|
4851
4620
|
series
|
|
4852
4621
|
};
|
|
4853
4622
|
});
|