@gscdump/analysis 0.7.1 → 0.7.5
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/README.md +43 -108
- package/dist/analyzer/index.d.mts +38 -179
- package/dist/analyzer/index.mjs +269 -502
- package/dist/default-registry.d.mts +2 -86
- package/dist/default-registry.mjs +2986 -204
- package/dist/index.d.mts +75 -234
- package/dist/index.mjs +3275 -800
- package/dist/query/index.d.mts +1 -1
- package/dist/query/index.mjs +1 -33
- package/dist/rollups.d.mts +163 -0
- package/dist/rollups.mjs +346 -0
- package/dist/source/index.d.mts +31 -219
- package/dist/source/index.mjs +27 -388
- package/package.json +23 -17
- package/dist/period/index.d.mts +0 -57
- package/dist/period/index.mjs +0 -150
package/dist/analyzer/index.mjs
CHANGED
|
@@ -1,122 +1,11 @@
|
|
|
1
|
+
import { defineAnalyzer } from "@gscdump/engine/analyzer";
|
|
2
|
+
import { comparisonOf, defaultEndDate, padTimeseries, periodOf } from "@gscdump/engine/period";
|
|
1
3
|
import { enumeratePartitions } from "@gscdump/engine/planner";
|
|
2
4
|
import { METRIC_EXPR } from "@gscdump/engine/sql-fragments";
|
|
3
|
-
import {
|
|
5
|
+
import { num } from "@gscdump/engine/analysis-types";
|
|
4
6
|
import { between, date, extractDateRange, gsc, page, query } from "gscdump/query";
|
|
7
|
+
import { MS_PER_DAY, daysAgo, toIsoDate } from "gscdump";
|
|
5
8
|
import { buildExtrasQueries, buildTotalsSql, mergeExtras, pgResolverAdapter, resolveComparisonSQL, resolveToSQL, resolveToSQLOptimized } from "@gscdump/engine/resolver";
|
|
6
|
-
const DEFAULT_SQL_REQUIRES = ["executeSql", "partitionedParquet"];
|
|
7
|
-
function defineAnalyzer(opts) {
|
|
8
|
-
const { id, reduce, reduceSql, reduceRows, buildSql, buildRows, sqlRequires = DEFAULT_SQL_REQUIRES, rowsRequires = [] } = opts;
|
|
9
|
-
const sqlReducer = reduceSql ?? reduce;
|
|
10
|
-
const rowsReducer = reduceRows ?? reduce;
|
|
11
|
-
if (buildSql && !sqlReducer) throw new Error(`defineAnalyzer(${id}): buildSql requires reduce or reduceSql`);
|
|
12
|
-
if (buildRows && !rowsReducer) throw new Error(`defineAnalyzer(${id}): buildRows requires reduce or reduceRows`);
|
|
13
|
-
const wrap = (fn) => (rows, params, ctx) => {
|
|
14
|
-
return fn(Array.isArray(rows) ? rows : pickSingle(rows) ?? rows, params, ctx);
|
|
15
|
-
};
|
|
16
|
-
return {
|
|
17
|
-
id,
|
|
18
|
-
sql: buildSql && sqlReducer ? {
|
|
19
|
-
id,
|
|
20
|
-
requires: sqlRequires,
|
|
21
|
-
build(params) {
|
|
22
|
-
const spec = buildSql(params);
|
|
23
|
-
return {
|
|
24
|
-
kind: "sql",
|
|
25
|
-
sql: spec.sql,
|
|
26
|
-
params: spec.params,
|
|
27
|
-
current: spec.current,
|
|
28
|
-
previous: spec.previous,
|
|
29
|
-
extraFiles: spec.extraFiles,
|
|
30
|
-
extraQueries: spec.extraQueries,
|
|
31
|
-
requiresAttachedTables: spec.requiresAttachedTables
|
|
32
|
-
};
|
|
33
|
-
},
|
|
34
|
-
reduce(rows, ctx) {
|
|
35
|
-
const { results, meta } = wrap(sqlReducer)(rows, ctx.params, { extras: ctx.extras });
|
|
36
|
-
return {
|
|
37
|
-
results,
|
|
38
|
-
meta
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
} : void 0,
|
|
42
|
-
rows: buildRows && rowsReducer ? {
|
|
43
|
-
id,
|
|
44
|
-
requires: rowsRequires,
|
|
45
|
-
build(params) {
|
|
46
|
-
const queries = buildRows(params);
|
|
47
|
-
return {
|
|
48
|
-
kind: "rows",
|
|
49
|
-
queries: Object.fromEntries(Object.entries(queries).map(([k, state]) => [k, { state }]))
|
|
50
|
-
};
|
|
51
|
-
},
|
|
52
|
-
reduce(rows, ctx) {
|
|
53
|
-
const { results, meta } = wrap(rowsReducer)(rows, ctx.params, {});
|
|
54
|
-
return {
|
|
55
|
-
results,
|
|
56
|
-
meta
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
} : void 0
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
function pickSingle(rows) {
|
|
63
|
-
const keys = Object.keys(rows);
|
|
64
|
-
return keys.length === 1 ? rows[keys[0]] : void 0;
|
|
65
|
-
}
|
|
66
|
-
function defaultEndDate() {
|
|
67
|
-
return daysAgo(3);
|
|
68
|
-
}
|
|
69
|
-
function defaultStartDate() {
|
|
70
|
-
return daysAgo(31);
|
|
71
|
-
}
|
|
72
|
-
function periodOf(params) {
|
|
73
|
-
return {
|
|
74
|
-
startDate: params.startDate || defaultStartDate(),
|
|
75
|
-
endDate: params.endDate || defaultEndDate()
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
function comparisonOf(params) {
|
|
79
|
-
if (!params.prevStartDate || !params.prevEndDate) throw new Error(`${params.type} analysis requires prevStartDate and prevEndDate`);
|
|
80
|
-
return {
|
|
81
|
-
current: periodOf(params),
|
|
82
|
-
previous: {
|
|
83
|
-
startDate: params.prevStartDate,
|
|
84
|
-
endDate: params.prevEndDate
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
const DEFAULT_FILL = {
|
|
89
|
-
clicks: 0,
|
|
90
|
-
impressions: 0,
|
|
91
|
-
ctr: 0,
|
|
92
|
-
position: 0
|
|
93
|
-
};
|
|
94
|
-
function padTimeseries(rows, options) {
|
|
95
|
-
const { startDate, endDate } = options;
|
|
96
|
-
const dateKey = options.dateKey ?? "date";
|
|
97
|
-
const fill = options.fill ?? DEFAULT_FILL;
|
|
98
|
-
const byDate = /* @__PURE__ */ new Map();
|
|
99
|
-
for (const row of rows) {
|
|
100
|
-
const d = String(row[dateKey]);
|
|
101
|
-
const bucket = byDate.get(d);
|
|
102
|
-
if (bucket) bucket.push(row);
|
|
103
|
-
else byDate.set(d, [row]);
|
|
104
|
-
}
|
|
105
|
-
const result = [];
|
|
106
|
-
const start = /* @__PURE__ */ new Date(`${startDate}T00:00:00Z`);
|
|
107
|
-
const end = /* @__PURE__ */ new Date(`${endDate}T00:00:00Z`);
|
|
108
|
-
if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime())) throw new Error(`padTimeseries: invalid date range ${startDate}..${endDate}`);
|
|
109
|
-
for (let cursorMs = start.getTime(), endMs = end.getTime(); cursorMs <= endMs; cursorMs += MS_PER_DAY) {
|
|
110
|
-
const dateStr = toIsoDate(new Date(cursorMs));
|
|
111
|
-
const existing = byDate.get(dateStr);
|
|
112
|
-
if (existing) result.push(...existing);
|
|
113
|
-
else result.push({
|
|
114
|
-
...fill,
|
|
115
|
-
[dateKey]: dateStr
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
return result;
|
|
119
|
-
}
|
|
120
9
|
function num$5(v) {
|
|
121
10
|
if (typeof v === "number") return v;
|
|
122
11
|
if (typeof v === "bigint") return Number(v);
|
|
@@ -298,32 +187,6 @@ const bayesianCtrAnalyzer = defineAnalyzer({
|
|
|
298
187
|
};
|
|
299
188
|
}
|
|
300
189
|
});
|
|
301
|
-
function num$4(v) {
|
|
302
|
-
if (typeof v === "number") return v;
|
|
303
|
-
if (typeof v === "bigint") return Number(v);
|
|
304
|
-
if (v == null) return 0;
|
|
305
|
-
return Number(v);
|
|
306
|
-
}
|
|
307
|
-
function buildPeriodMap(rows, key, value, filter) {
|
|
308
|
-
const out = /* @__PURE__ */ new Map();
|
|
309
|
-
for (const row of rows) {
|
|
310
|
-
if (filter && !filter(row)) continue;
|
|
311
|
-
out.set(key(row), value(row));
|
|
312
|
-
}
|
|
313
|
-
return out;
|
|
314
|
-
}
|
|
315
|
-
function createSorter(getValue, defaultMetric, defaultOrder = "desc") {
|
|
316
|
-
return (items, sortBy = defaultMetric, sortOrder = defaultOrder) => {
|
|
317
|
-
const mult = sortOrder === "desc" ? -1 : 1;
|
|
318
|
-
return [...items].sort((a, b) => (getValue(a, sortBy) - getValue(b, sortBy)) * mult);
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
|
-
function createMetricSorter(defaultMetric, orderByMetric) {
|
|
322
|
-
return (items, sortBy = defaultMetric) => {
|
|
323
|
-
const mult = orderByMetric[sortBy] === "desc" ? -1 : 1;
|
|
324
|
-
return [...items].sort((a, b) => (a[sortBy] - b[sortBy]) * mult);
|
|
325
|
-
};
|
|
326
|
-
}
|
|
327
190
|
const BIPARTITE_PAGERANK_ITERATIONS = 25;
|
|
328
191
|
const BIPARTITE_PAGERANK_DAMPING = .85;
|
|
329
192
|
function str$22(v) {
|
|
@@ -544,18 +407,18 @@ const bipartitePagerankAnalyzer = defineAnalyzer({
|
|
|
544
407
|
const results = arr.map((r) => ({
|
|
545
408
|
kind: str$22(r.kind),
|
|
546
409
|
id: str$22(r.id),
|
|
547
|
-
rank: num
|
|
548
|
-
bridging: num
|
|
549
|
-
anchoring: num
|
|
550
|
-
degree: num
|
|
551
|
-
impressions: num
|
|
410
|
+
rank: num(r.rank),
|
|
411
|
+
bridging: num(r.bridging),
|
|
412
|
+
anchoring: num(r.anchoring),
|
|
413
|
+
degree: num(r.degree),
|
|
414
|
+
impressions: num(r.impressions)
|
|
552
415
|
}));
|
|
553
416
|
const first = arr[0] ?? {};
|
|
554
|
-
const queryCount = num
|
|
555
|
-
const urlCount = num
|
|
417
|
+
const queryCount = num(first.queryCount);
|
|
418
|
+
const urlCount = num(first.urlCount);
|
|
556
419
|
const deltas = parseJsonList$16(first.deltasJson).map((e) => ({
|
|
557
|
-
step: num
|
|
558
|
-
l1: num
|
|
420
|
+
step: num(e.step),
|
|
421
|
+
l1: num(e.l1)
|
|
559
422
|
}));
|
|
560
423
|
const convergenceDelta = deltas.length > 0 ? deltas[deltas.length - 1].l1 : 0;
|
|
561
424
|
return {
|
|
@@ -594,12 +457,12 @@ function analyzeBrandSegmentation(keywords, options) {
|
|
|
594
457
|
const brand = [];
|
|
595
458
|
const nonBrand = [];
|
|
596
459
|
for (const row of keywords) {
|
|
597
|
-
if (num
|
|
460
|
+
if (num(row.impressions) < minImpressions) continue;
|
|
598
461
|
if (lowerBrandTerms.some((term) => row.query.toLowerCase().includes(term))) brand.push(row);
|
|
599
462
|
else nonBrand.push(row);
|
|
600
463
|
}
|
|
601
|
-
const brandClicks = brand.reduce((sum, k) => sum + num
|
|
602
|
-
const nonBrandClicks = nonBrand.reduce((sum, k) => sum + num
|
|
464
|
+
const brandClicks = brand.reduce((sum, k) => sum + num(k.clicks), 0);
|
|
465
|
+
const nonBrandClicks = nonBrand.reduce((sum, k) => sum + num(k.clicks), 0);
|
|
603
466
|
const totalClicks = brandClicks + nonBrandClicks;
|
|
604
467
|
return {
|
|
605
468
|
brand,
|
|
@@ -608,8 +471,8 @@ function analyzeBrandSegmentation(keywords, options) {
|
|
|
608
471
|
brandClicks,
|
|
609
472
|
nonBrandClicks,
|
|
610
473
|
brandShare: totalClicks > 0 ? brandClicks / totalClicks : 0,
|
|
611
|
-
brandImpressions: brand.reduce((sum, k) => sum + num
|
|
612
|
-
nonBrandImpressions: nonBrand.reduce((sum, k) => sum + num
|
|
474
|
+
brandImpressions: brand.reduce((sum, k) => sum + num(k.impressions), 0),
|
|
475
|
+
nonBrandImpressions: nonBrand.reduce((sum, k) => sum + num(k.impressions), 0)
|
|
613
476
|
}
|
|
614
477
|
};
|
|
615
478
|
}
|
|
@@ -659,10 +522,10 @@ const brandAnalyzer = defineAnalyzer({
|
|
|
659
522
|
const normalized = (Array.isArray(rows) ? rows : []).map((r) => ({
|
|
660
523
|
query: str$21(r.query),
|
|
661
524
|
page: r.page == null ? void 0 : str$21(r.page),
|
|
662
|
-
clicks: num
|
|
663
|
-
impressions: num
|
|
664
|
-
ctr: num
|
|
665
|
-
position: num
|
|
525
|
+
clicks: num(r.clicks),
|
|
526
|
+
impressions: num(r.impressions),
|
|
527
|
+
ctr: num(r.ctr),
|
|
528
|
+
position: num(r.position),
|
|
666
529
|
segment: str$21(r.segment)
|
|
667
530
|
}));
|
|
668
531
|
let brandClicks = 0;
|
|
@@ -712,6 +575,26 @@ const brandAnalyzer = defineAnalyzer({
|
|
|
712
575
|
};
|
|
713
576
|
}
|
|
714
577
|
});
|
|
578
|
+
function buildPeriodMap(rows, key, value, filter) {
|
|
579
|
+
const out = /* @__PURE__ */ new Map();
|
|
580
|
+
for (const row of rows) {
|
|
581
|
+
if (filter && !filter(row)) continue;
|
|
582
|
+
out.set(key(row), value(row));
|
|
583
|
+
}
|
|
584
|
+
return out;
|
|
585
|
+
}
|
|
586
|
+
function createSorter(getValue, defaultMetric, defaultOrder = "desc") {
|
|
587
|
+
return (items, sortBy = defaultMetric, sortOrder = defaultOrder) => {
|
|
588
|
+
const mult = sortOrder === "desc" ? -1 : 1;
|
|
589
|
+
return [...items].sort((a, b) => (getValue(a, sortBy) - getValue(b, sortBy)) * mult);
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
function createMetricSorter(defaultMetric, orderByMetric) {
|
|
593
|
+
return (items, sortBy = defaultMetric) => {
|
|
594
|
+
const mult = orderByMetric[sortBy] === "desc" ? -1 : 1;
|
|
595
|
+
return [...items].sort((a, b) => (a[sortBy] - b[sortBy]) * mult);
|
|
596
|
+
};
|
|
597
|
+
}
|
|
715
598
|
const sortRowResults$1 = createSorter((item, metric) => {
|
|
716
599
|
switch (metric) {
|
|
717
600
|
case "clicks": return item.totalClicks;
|
|
@@ -884,24 +767,24 @@ const cannibalizationAnalyzer = defineAnalyzer({
|
|
|
884
767
|
reduceSql(rows) {
|
|
885
768
|
const events = (Array.isArray(rows) ? rows : []).map((r) => ({
|
|
886
769
|
keyword: str$20(r.keyword),
|
|
887
|
-
totalImpressions: num
|
|
888
|
-
totalClicks: num
|
|
889
|
-
competitorCount: num
|
|
770
|
+
totalImpressions: num(r.totalImpressions),
|
|
771
|
+
totalClicks: num(r.totalClicks),
|
|
772
|
+
competitorCount: num(r.competitorCount),
|
|
890
773
|
leaderUrl: str$20(r.leaderUrl),
|
|
891
|
-
leaderCtr: num
|
|
892
|
-
leaderPosition: num
|
|
893
|
-
hhi: num
|
|
894
|
-
fragmentation: num
|
|
895
|
-
stolenClicks: num
|
|
896
|
-
severity: num
|
|
774
|
+
leaderCtr: num(r.leaderCtr),
|
|
775
|
+
leaderPosition: num(r.leaderPosition),
|
|
776
|
+
hhi: num(r.hhi),
|
|
777
|
+
fragmentation: num(r.fragmentation),
|
|
778
|
+
stolenClicks: num(r.stolenClicks),
|
|
779
|
+
severity: num(r.severity),
|
|
897
780
|
competitors: parseJsonList$15(r.competitors).map((c) => ({
|
|
898
781
|
url: str$20(c.url),
|
|
899
|
-
clicks: num
|
|
900
|
-
impressions: num
|
|
901
|
-
ctr: num
|
|
902
|
-
position: num
|
|
903
|
-
share: num
|
|
904
|
-
rank: num
|
|
782
|
+
clicks: num(c.clicks),
|
|
783
|
+
impressions: num(c.impressions),
|
|
784
|
+
ctr: num(c.ctr),
|
|
785
|
+
position: num(c.position),
|
|
786
|
+
share: num(c.share),
|
|
787
|
+
rank: num(c.rank)
|
|
905
788
|
}))
|
|
906
789
|
}));
|
|
907
790
|
const nodeAgg = /* @__PURE__ */ new Map();
|
|
@@ -972,7 +855,7 @@ const cannibalizationAnalyzer = defineAnalyzer({
|
|
|
972
855
|
};
|
|
973
856
|
}
|
|
974
857
|
});
|
|
975
|
-
function num$
|
|
858
|
+
function num$4(v) {
|
|
976
859
|
if (typeof v === "number") return v;
|
|
977
860
|
if (typeof v === "bigint") return Number(v);
|
|
978
861
|
if (v == null) return 0;
|
|
@@ -1139,24 +1022,24 @@ const changePointAnalyzer = defineAnalyzer({
|
|
|
1139
1022
|
const metric = params.metric === "clicks" || params.metric === "impressions" ? params.metric : "position";
|
|
1140
1023
|
const lowerIsBetter = metric === "position";
|
|
1141
1024
|
const results = arr.map((r) => {
|
|
1142
|
-
const delta = num$
|
|
1025
|
+
const delta = num$4(r.delta);
|
|
1143
1026
|
const improved = lowerIsBetter ? delta < 0 : delta > 0;
|
|
1144
1027
|
return {
|
|
1145
1028
|
keyword: str$19(r.keyword),
|
|
1146
1029
|
page: str$19(r.page),
|
|
1147
|
-
totalDays: num$
|
|
1148
|
-
totalImpressions: num$
|
|
1030
|
+
totalDays: num$4(r.totalDays),
|
|
1031
|
+
totalImpressions: num$4(r.totalImpressions),
|
|
1149
1032
|
changeDate: str$19(r.changeDate),
|
|
1150
|
-
llr: num$
|
|
1151
|
-
leftMean: num$
|
|
1152
|
-
rightMean: num$
|
|
1033
|
+
llr: num$4(r.llr),
|
|
1034
|
+
leftMean: num$4(r.leftMean),
|
|
1035
|
+
rightMean: num$4(r.rightMean),
|
|
1153
1036
|
delta,
|
|
1154
|
-
leftStddev: num$
|
|
1155
|
-
rightStddev: num$
|
|
1037
|
+
leftStddev: num$4(r.leftStddev),
|
|
1038
|
+
rightStddev: num$4(r.rightStddev),
|
|
1156
1039
|
direction: improved ? "improved" : "worsened",
|
|
1157
1040
|
series: parseJsonList$14(r.seriesJson).map((s) => ({
|
|
1158
1041
|
date: str$19(s.date),
|
|
1159
|
-
value: num$
|
|
1042
|
+
value: num$4(s.value)
|
|
1160
1043
|
}))
|
|
1161
1044
|
};
|
|
1162
1045
|
});
|
|
@@ -1216,7 +1099,7 @@ function extractWordPrefix(keyword, wordCount = 2) {
|
|
|
1216
1099
|
}
|
|
1217
1100
|
function analyzeClustering(keywords, options = {}) {
|
|
1218
1101
|
const { minClusterSize = 2, minImpressions = 10, clusterBy = "both" } = options;
|
|
1219
|
-
const filtered = keywords.filter((k) => num
|
|
1102
|
+
const filtered = keywords.filter((k) => num(k.impressions) >= minImpressions);
|
|
1220
1103
|
const clusterMap = /* @__PURE__ */ new Map();
|
|
1221
1104
|
const clusteredKeywords = /* @__PURE__ */ new Set();
|
|
1222
1105
|
if (clusterBy === "intent" || clusterBy === "both") for (const kw of filtered) {
|
|
@@ -1253,9 +1136,9 @@ function analyzeClustering(keywords, options = {}) {
|
|
|
1253
1136
|
const clusters = [];
|
|
1254
1137
|
for (const [name, data] of clusterMap) {
|
|
1255
1138
|
if (data.keywords.length < minClusterSize) continue;
|
|
1256
|
-
const totalClicks = data.keywords.reduce((sum, k) => sum + num
|
|
1257
|
-
const totalImpressions = data.keywords.reduce((sum, k) => sum + num
|
|
1258
|
-
const avgPosition = data.keywords.reduce((sum, k) => sum + num
|
|
1139
|
+
const totalClicks = data.keywords.reduce((sum, k) => sum + num(k.clicks), 0);
|
|
1140
|
+
const totalImpressions = data.keywords.reduce((sum, k) => sum + num(k.impressions), 0);
|
|
1141
|
+
const avgPosition = data.keywords.reduce((sum, k) => sum + num(k.position), 0) / data.keywords.length;
|
|
1259
1142
|
clusters.push({
|
|
1260
1143
|
clusterName: name,
|
|
1261
1144
|
clusterType: data.type,
|
|
@@ -1343,16 +1226,16 @@ const clusteringAnalyzer = defineAnalyzer({
|
|
|
1343
1226
|
const clusters = (Array.isArray(rows) ? rows : []).map((r) => ({
|
|
1344
1227
|
clusterName: str$18(r.clusterName),
|
|
1345
1228
|
clusterType: str$18(r.clusterType),
|
|
1346
|
-
keywordCount: num
|
|
1347
|
-
totalClicks: num
|
|
1348
|
-
totalImpressions: num
|
|
1349
|
-
avgPosition: num
|
|
1229
|
+
keywordCount: num(r.keywordCount),
|
|
1230
|
+
totalClicks: num(r.totalClicks),
|
|
1231
|
+
totalImpressions: num(r.totalImpressions),
|
|
1232
|
+
avgPosition: num(r.avgPosition),
|
|
1350
1233
|
keywords: parseJsonList$13(r.keywords).map((k) => ({
|
|
1351
1234
|
query: str$18(k.query),
|
|
1352
|
-
clicks: num
|
|
1353
|
-
impressions: num
|
|
1354
|
-
ctr: num
|
|
1355
|
-
position: num
|
|
1235
|
+
clicks: num(k.clicks),
|
|
1236
|
+
impressions: num(k.impressions),
|
|
1237
|
+
ctr: num(k.ctr),
|
|
1238
|
+
position: num(k.position)
|
|
1356
1239
|
}))
|
|
1357
1240
|
}));
|
|
1358
1241
|
return {
|
|
@@ -1442,13 +1325,13 @@ function analyzeConcentration(items, options = {}) {
|
|
|
1442
1325
|
function analyzePageConcentration(pages, options) {
|
|
1443
1326
|
return analyzeConcentration(pages.map((p) => ({
|
|
1444
1327
|
key: p.page,
|
|
1445
|
-
clicks: num
|
|
1328
|
+
clicks: num(p.clicks)
|
|
1446
1329
|
})), options);
|
|
1447
1330
|
}
|
|
1448
1331
|
function analyzeKeywordConcentration(keywords, options) {
|
|
1449
1332
|
return analyzeConcentration(keywords.map((k) => ({
|
|
1450
1333
|
key: k.query,
|
|
1451
|
-
clicks: num
|
|
1334
|
+
clicks: num(k.clicks)
|
|
1452
1335
|
})), options);
|
|
1453
1336
|
}
|
|
1454
1337
|
const concentrationAnalyzer = defineAnalyzer({
|
|
@@ -1532,16 +1415,16 @@ const concentrationAnalyzer = defineAnalyzer({
|
|
|
1532
1415
|
const topRaw = parseJsonList$12(r.topNItems);
|
|
1533
1416
|
return {
|
|
1534
1417
|
results: [{
|
|
1535
|
-
giniCoefficient: num
|
|
1536
|
-
hhi: num
|
|
1537
|
-
topNConcentration: num
|
|
1418
|
+
giniCoefficient: num(r.giniCoefficient),
|
|
1419
|
+
hhi: num(r.hhi),
|
|
1420
|
+
topNConcentration: num(r.topNConcentration),
|
|
1538
1421
|
topNItems: topRaw.map((t) => ({
|
|
1539
1422
|
key: str$17(t.key),
|
|
1540
|
-
clicks: num
|
|
1541
|
-
share: num
|
|
1423
|
+
clicks: num(t.clicks),
|
|
1424
|
+
share: num(t.share)
|
|
1542
1425
|
})),
|
|
1543
|
-
totalItems: num
|
|
1544
|
-
totalClicks: num
|
|
1426
|
+
totalItems: num(r.totalItems),
|
|
1427
|
+
totalClicks: num(r.totalClicks),
|
|
1545
1428
|
riskLevel: str$17(r.riskLevel)
|
|
1546
1429
|
}],
|
|
1547
1430
|
meta: {
|
|
@@ -1567,7 +1450,7 @@ const concentrationAnalyzer = defineAnalyzer({
|
|
|
1567
1450
|
};
|
|
1568
1451
|
}
|
|
1569
1452
|
});
|
|
1570
|
-
function num$
|
|
1453
|
+
function num$3(v) {
|
|
1571
1454
|
if (typeof v === "number") return v;
|
|
1572
1455
|
if (typeof v === "bigint") return Number(v);
|
|
1573
1456
|
if (v == null) return 0;
|
|
@@ -1597,7 +1480,7 @@ const contentVelocityAnalyzer = defineAnalyzer({
|
|
|
1597
1480
|
),
|
|
1598
1481
|
per_week AS (
|
|
1599
1482
|
SELECT
|
|
1600
|
-
strftime(date, '%G-W%V') AS week,
|
|
1483
|
+
strftime(CAST(date AS DATE), '%G-W%V') AS week,
|
|
1601
1484
|
MIN(date) AS week_start,
|
|
1602
1485
|
CAST(COUNT(DISTINCT query) AS DOUBLE) AS totalKeywords
|
|
1603
1486
|
FROM src
|
|
@@ -1605,7 +1488,7 @@ const contentVelocityAnalyzer = defineAnalyzer({
|
|
|
1605
1488
|
),
|
|
1606
1489
|
new_per_week AS (
|
|
1607
1490
|
SELECT
|
|
1608
|
-
strftime(first_date, '%G-W%V') AS week,
|
|
1491
|
+
strftime(CAST(first_date AS DATE), '%G-W%V') AS week,
|
|
1609
1492
|
CAST(COUNT(*) AS DOUBLE) AS newKeywords
|
|
1610
1493
|
FROM first_seen
|
|
1611
1494
|
GROUP BY week
|
|
@@ -1634,8 +1517,8 @@ const contentVelocityAnalyzer = defineAnalyzer({
|
|
|
1634
1517
|
const startDate = toIsoDate(startDateD);
|
|
1635
1518
|
const weekly = arr.map((r) => ({
|
|
1636
1519
|
week: str$16(r.week),
|
|
1637
|
-
newKeywords: num$
|
|
1638
|
-
totalKeywords: num$
|
|
1520
|
+
newKeywords: num$3(r.newKeywords),
|
|
1521
|
+
totalKeywords: num$3(r.totalKeywords)
|
|
1639
1522
|
}));
|
|
1640
1523
|
const total = weekly.reduce((s, w) => s + w.newKeywords, 0);
|
|
1641
1524
|
const avg = weekly.length > 0 ? total / weekly.length : 0;
|
|
@@ -1658,7 +1541,7 @@ const contentVelocityAnalyzer = defineAnalyzer({
|
|
|
1658
1541
|
};
|
|
1659
1542
|
}
|
|
1660
1543
|
});
|
|
1661
|
-
function num$
|
|
1544
|
+
function num$2(v) {
|
|
1662
1545
|
if (typeof v === "number") return v;
|
|
1663
1546
|
if (typeof v === "bigint") return Number(v);
|
|
1664
1547
|
if (v == null) return 0;
|
|
@@ -1819,23 +1702,23 @@ const ctrAnomalyAnalyzer = defineAnalyzer({
|
|
|
1819
1702
|
const anomalies = arr.map((r) => ({
|
|
1820
1703
|
keyword: str$15(r.keyword),
|
|
1821
1704
|
page: str$15(r.page),
|
|
1822
|
-
breachDaysDown: num$
|
|
1823
|
-
breachDaysUp: num$
|
|
1824
|
-
clicksLost: num$
|
|
1825
|
-
severity: num$
|
|
1826
|
-
maxZ: num$
|
|
1827
|
-
baselineCtr: num$
|
|
1828
|
-
baselinePosition: num$
|
|
1829
|
-
totalImpressions: num$
|
|
1830
|
-
totalClicks: num$
|
|
1705
|
+
breachDaysDown: num$2(r.breachDaysDown),
|
|
1706
|
+
breachDaysUp: num$2(r.breachDaysUp),
|
|
1707
|
+
clicksLost: num$2(r.clicksLost),
|
|
1708
|
+
severity: num$2(r.severityRaw),
|
|
1709
|
+
maxZ: num$2(r.maxZ),
|
|
1710
|
+
baselineCtr: num$2(r.baselineCtr),
|
|
1711
|
+
baselinePosition: num$2(r.baselinePosition),
|
|
1712
|
+
totalImpressions: num$2(r.totalImpressions),
|
|
1713
|
+
totalClicks: num$2(r.totalClicks),
|
|
1831
1714
|
series: parseJsonList$11(r.seriesJson).map((s) => ({
|
|
1832
1715
|
date: str$15(s.date),
|
|
1833
|
-
ctr: num$
|
|
1834
|
-
position: num$
|
|
1835
|
-
impressions: num$
|
|
1836
|
-
rollingCtr: s.rollingCtr == null ? null : num$
|
|
1837
|
-
rollingStddev: s.rollingStddev == null ? null : num$
|
|
1838
|
-
z: num$
|
|
1716
|
+
ctr: num$2(s.ctr),
|
|
1717
|
+
position: num$2(s.position),
|
|
1718
|
+
impressions: num$2(s.impressions),
|
|
1719
|
+
rollingCtr: s.rollingCtr == null ? null : num$2(s.rollingCtr),
|
|
1720
|
+
rollingStddev: s.rollingStddev == null ? null : num$2(s.rollingStddev),
|
|
1721
|
+
z: num$2(s.z),
|
|
1839
1722
|
breach: bool$2(s.breach)
|
|
1840
1723
|
}))
|
|
1841
1724
|
}));
|
|
@@ -1853,7 +1736,7 @@ const ctrAnomalyAnalyzer = defineAnalyzer({
|
|
|
1853
1736
|
};
|
|
1854
1737
|
}
|
|
1855
1738
|
});
|
|
1856
|
-
function num(v) {
|
|
1739
|
+
function num$1(v) {
|
|
1857
1740
|
if (typeof v === "number") return v;
|
|
1858
1741
|
if (typeof v === "bigint") return Number(v);
|
|
1859
1742
|
if (v == null) return 0;
|
|
@@ -1967,20 +1850,20 @@ const ctrCurveAnalyzer = defineAnalyzer({
|
|
|
1967
1850
|
const row = arr[0] ?? {};
|
|
1968
1851
|
const curve = parseJsonList$10(row.curve_json).map((r) => ({
|
|
1969
1852
|
bucket: str$14(r.bucket),
|
|
1970
|
-
avgCtr: num(r.avgCtr),
|
|
1971
|
-
medianPosition: num(r.medianPosition),
|
|
1972
|
-
keywordCount: num(r.keywordCount),
|
|
1973
|
-
totalClicks: num(r.totalClicks),
|
|
1974
|
-
totalImpressions: num(r.totalImpressions)
|
|
1853
|
+
avgCtr: num$1(r.avgCtr),
|
|
1854
|
+
medianPosition: num$1(r.medianPosition),
|
|
1855
|
+
keywordCount: num$1(r.keywordCount),
|
|
1856
|
+
totalClicks: num$1(r.totalClicks),
|
|
1857
|
+
totalImpressions: num$1(r.totalImpressions)
|
|
1975
1858
|
}));
|
|
1976
1859
|
const outliers = parseJsonList$10(row.outliers_json).map((r) => ({
|
|
1977
1860
|
query: str$14(r.query),
|
|
1978
|
-
clicks: num(r.clicks),
|
|
1979
|
-
impressions: num(r.impressions),
|
|
1980
|
-
ctr: num(r.ctr),
|
|
1981
|
-
position: num(r.position),
|
|
1982
|
-
expectedCtr: num(r.expectedCtr),
|
|
1983
|
-
ctrDiff: num(r.ctrDiff)
|
|
1861
|
+
clicks: num$1(r.clicks),
|
|
1862
|
+
impressions: num$1(r.impressions),
|
|
1863
|
+
ctr: num$1(r.ctr),
|
|
1864
|
+
position: num$1(r.position),
|
|
1865
|
+
expectedCtr: num$1(r.expectedCtr),
|
|
1866
|
+
ctrDiff: num$1(r.ctrDiff)
|
|
1984
1867
|
}));
|
|
1985
1868
|
return {
|
|
1986
1869
|
results: curve,
|
|
@@ -2098,20 +1981,20 @@ const darkTrafficAnalyzer = defineAnalyzer({
|
|
|
2098
1981
|
const row = arr[0] ?? {};
|
|
2099
1982
|
const pageTotals = typeof row.page_totals_json === "string" ? JSON.parse(row.page_totals_json) : row.page_totals_json ?? {};
|
|
2100
1983
|
const kwTotals = typeof row.kw_totals_json === "string" ? JSON.parse(row.kw_totals_json) : row.kw_totals_json ?? {};
|
|
2101
|
-
const totalClicks = num
|
|
2102
|
-
const totalImpressions = num
|
|
2103
|
-
const attributedClicks = num
|
|
2104
|
-
const attributedImpressions = num
|
|
1984
|
+
const totalClicks = num(pageTotals.totalClicks);
|
|
1985
|
+
const totalImpressions = num(pageTotals.totalImpressions);
|
|
1986
|
+
const attributedClicks = num(kwTotals.attributedClicks);
|
|
1987
|
+
const attributedImpressions = num(kwTotals.attributedImpressions);
|
|
2105
1988
|
const darkClicks = Math.max(0, totalClicks - attributedClicks);
|
|
2106
1989
|
const darkPercent = totalClicks > 0 ? darkClicks / totalClicks : 0;
|
|
2107
1990
|
return {
|
|
2108
1991
|
results: parseJsonList$9(row.pages_json).map((r) => ({
|
|
2109
1992
|
url: str$13(r.url),
|
|
2110
|
-
totalClicks: num
|
|
2111
|
-
attributedClicks: num
|
|
2112
|
-
darkClicks: num
|
|
2113
|
-
darkPercent: num
|
|
2114
|
-
keywordCount: num
|
|
1993
|
+
totalClicks: num(r.totalClicks),
|
|
1994
|
+
attributedClicks: num(r.attributedClicks),
|
|
1995
|
+
darkClicks: num(r.darkClicks),
|
|
1996
|
+
darkPercent: num(r.darkPercent),
|
|
1997
|
+
keywordCount: num(r.keywordCount)
|
|
2115
1998
|
})),
|
|
2116
1999
|
meta: {
|
|
2117
2000
|
summary: {
|
|
@@ -2350,13 +2233,13 @@ function parseJsonList$8(v) {
|
|
|
2350
2233
|
function analyzeDecay(input, options = {}) {
|
|
2351
2234
|
const { minPreviousClicks = 50, threshold = .2, sortBy = "lostClicks" } = options;
|
|
2352
2235
|
const currentMap = buildPeriodMap(input.current, (r) => r.page, (r) => ({
|
|
2353
|
-
clicks: num
|
|
2354
|
-
position: num
|
|
2236
|
+
clicks: num(r.clicks),
|
|
2237
|
+
position: num(r.position)
|
|
2355
2238
|
}));
|
|
2356
2239
|
const previousMap = buildPeriodMap(input.previous, (r) => r.page, (r) => ({
|
|
2357
|
-
clicks: num
|
|
2358
|
-
position: num
|
|
2359
|
-
}), (r) => num
|
|
2240
|
+
clicks: num(r.clicks),
|
|
2241
|
+
position: num(r.position)
|
|
2242
|
+
}), (r) => num(r.clicks) >= minPreviousClicks);
|
|
2360
2243
|
const results = [];
|
|
2361
2244
|
for (const [page, prev] of previousMap) {
|
|
2362
2245
|
const curr = currentMap.get(page) || {
|
|
@@ -2477,17 +2360,17 @@ const decayAnalyzer = defineAnalyzer({
|
|
|
2477
2360
|
return {
|
|
2478
2361
|
results: arr.map((r) => ({
|
|
2479
2362
|
page: str$12(r.page),
|
|
2480
|
-
currentClicks: num
|
|
2481
|
-
previousClicks: num
|
|
2482
|
-
lostClicks: num
|
|
2483
|
-
declinePercent: num
|
|
2484
|
-
currentPosition: num
|
|
2485
|
-
previousPosition: num
|
|
2486
|
-
positionDrop: num
|
|
2363
|
+
currentClicks: num(r.currentClicks),
|
|
2364
|
+
previousClicks: num(r.previousClicks),
|
|
2365
|
+
lostClicks: num(r.lostClicks),
|
|
2366
|
+
declinePercent: num(r.declinePercent),
|
|
2367
|
+
currentPosition: num(r.currentPosition),
|
|
2368
|
+
previousPosition: num(r.previousPosition),
|
|
2369
|
+
positionDrop: num(r.positionDrop),
|
|
2487
2370
|
series: parseJsonList$8(r.seriesJson).map((s) => ({
|
|
2488
2371
|
week: str$12(s.week),
|
|
2489
|
-
clicks: num
|
|
2490
|
-
impressions: num
|
|
2372
|
+
clicks: num(s.clicks),
|
|
2373
|
+
impressions: num(s.impressions)
|
|
2491
2374
|
}))
|
|
2492
2375
|
})),
|
|
2493
2376
|
meta: { total: arr.length }
|
|
@@ -2552,10 +2435,10 @@ const deviceGapAnalyzer = defineAnalyzer({
|
|
|
2552
2435
|
const typed = arr.map((r) => ({
|
|
2553
2436
|
date: str$11(r.date),
|
|
2554
2437
|
device: str$11(r.device).toUpperCase(),
|
|
2555
|
-
clicks: num
|
|
2556
|
-
impressions: num
|
|
2557
|
-
ctr: num
|
|
2558
|
-
position: num
|
|
2438
|
+
clicks: num(r.clicks),
|
|
2439
|
+
impressions: num(r.impressions),
|
|
2440
|
+
ctr: num(r.ctr),
|
|
2441
|
+
position: num(r.position)
|
|
2559
2442
|
}));
|
|
2560
2443
|
const byDate = /* @__PURE__ */ new Map();
|
|
2561
2444
|
for (const r of typed) {
|
|
@@ -2783,16 +2666,16 @@ const intentAtlasAnalyzer = defineAnalyzer({
|
|
|
2783
2666
|
reduceSql(rows) {
|
|
2784
2667
|
const clusters = (Array.isArray(rows) ? rows : []).map((r) => ({
|
|
2785
2668
|
clusterKey: str$10(r.clusterKey),
|
|
2786
|
-
keywordCount: num
|
|
2787
|
-
totalImpressions: num
|
|
2788
|
-
totalClicks: num
|
|
2789
|
-
ctr: num
|
|
2790
|
-
avgPosition: num
|
|
2669
|
+
keywordCount: num(r.keywordCount),
|
|
2670
|
+
totalImpressions: num(r.totalImpressions),
|
|
2671
|
+
totalClicks: num(r.totalClicks),
|
|
2672
|
+
ctr: num(r.ctr),
|
|
2673
|
+
avgPosition: num(r.avgPosition),
|
|
2791
2674
|
keywords: parseJsonList$7(r.keywords).slice(0, 25).map((k) => ({
|
|
2792
2675
|
query: str$10(k.query),
|
|
2793
|
-
impressions: num
|
|
2794
|
-
clicks: num
|
|
2795
|
-
position: num
|
|
2676
|
+
impressions: num(k.impressions),
|
|
2677
|
+
clicks: num(k.clicks),
|
|
2678
|
+
position: num(k.position)
|
|
2796
2679
|
}))
|
|
2797
2680
|
}));
|
|
2798
2681
|
const totalImpressions = clusters.reduce((s, c) => s + c.totalImpressions, 0);
|
|
@@ -2893,21 +2776,21 @@ const keywordBreadthAnalyzer = defineAnalyzer({
|
|
|
2893
2776
|
const arr = Array.isArray(rows) ? rows : [];
|
|
2894
2777
|
const { startDate, endDate } = periodOf(params);
|
|
2895
2778
|
const row = arr[0] ?? {};
|
|
2896
|
-
const distribution = parseJsonList$6(row.distribution_json).sort((a, b) => num
|
|
2779
|
+
const distribution = parseJsonList$6(row.distribution_json).sort((a, b) => num(a.sortKey) - num(b.sortKey)).map((r) => ({
|
|
2897
2780
|
bucket: str$9(r.bucket),
|
|
2898
|
-
pageCount: num
|
|
2781
|
+
pageCount: num(r.pageCount)
|
|
2899
2782
|
}));
|
|
2900
2783
|
const fragile = parseJsonList$6(row.fragile_json).map((r) => ({
|
|
2901
2784
|
url: str$9(r.url),
|
|
2902
|
-
keywordCount: num
|
|
2903
|
-
clicks: num
|
|
2904
|
-
impressions: num
|
|
2785
|
+
keywordCount: num(r.keywordCount),
|
|
2786
|
+
clicks: num(r.clicks),
|
|
2787
|
+
impressions: num(r.impressions)
|
|
2905
2788
|
}));
|
|
2906
2789
|
const authority = parseJsonList$6(row.authority_json).map((r) => ({
|
|
2907
2790
|
url: str$9(r.url),
|
|
2908
|
-
keywordCount: num
|
|
2909
|
-
clicks: num
|
|
2910
|
-
impressions: num
|
|
2791
|
+
keywordCount: num(r.keywordCount),
|
|
2792
|
+
clicks: num(r.clicks),
|
|
2793
|
+
impressions: num(r.impressions)
|
|
2911
2794
|
}));
|
|
2912
2795
|
const stats = typeof row.stats_json === "string" ? JSON.parse(row.stats_json) : row.stats_json ?? {};
|
|
2913
2796
|
return {
|
|
@@ -2916,10 +2799,10 @@ const keywordBreadthAnalyzer = defineAnalyzer({
|
|
|
2916
2799
|
fragilePages: fragile,
|
|
2917
2800
|
authorityPages: authority,
|
|
2918
2801
|
summary: {
|
|
2919
|
-
totalPages: num
|
|
2920
|
-
avgKeywordsPerPage: num
|
|
2921
|
-
fragileCount: num
|
|
2922
|
-
authorityCount: num
|
|
2802
|
+
totalPages: num(stats.totalPages),
|
|
2803
|
+
avgKeywordsPerPage: num(stats.avgKeywordsPerPage),
|
|
2804
|
+
fragileCount: num(stats.fragileCount),
|
|
2805
|
+
authorityCount: num(stats.authorityCount)
|
|
2923
2806
|
},
|
|
2924
2807
|
startDate,
|
|
2925
2808
|
endDate
|
|
@@ -2940,9 +2823,9 @@ function parseJsonList$5(v) {
|
|
|
2940
2823
|
}
|
|
2941
2824
|
function downsampleLogRank(points) {
|
|
2942
2825
|
const all = points.map((p) => ({
|
|
2943
|
-
rank: num
|
|
2944
|
-
impressions: num
|
|
2945
|
-
clicks: num
|
|
2826
|
+
rank: num(p.rank),
|
|
2827
|
+
impressions: num(p.impressions),
|
|
2828
|
+
clicks: num(p.clicks),
|
|
2946
2829
|
query: str$8(p.query)
|
|
2947
2830
|
}));
|
|
2948
2831
|
if (all.length <= 80) return all;
|
|
@@ -3053,14 +2936,14 @@ const longTailAnalyzer = defineAnalyzer({
|
|
|
3053
2936
|
reduceSql(rows) {
|
|
3054
2937
|
const results = (Array.isArray(rows) ? rows : []).map((r) => ({
|
|
3055
2938
|
page: str$8(r.page),
|
|
3056
|
-
queryCount: num
|
|
3057
|
-
totalImpressions: num
|
|
3058
|
-
totalClicks: num
|
|
3059
|
-
slope: num
|
|
3060
|
-
intercept: num
|
|
3061
|
-
r2: num
|
|
3062
|
-
headImpressions: num
|
|
3063
|
-
headShare: num
|
|
2939
|
+
queryCount: num(r.queryCount),
|
|
2940
|
+
totalImpressions: num(r.totalImpressions),
|
|
2941
|
+
totalClicks: num(r.totalClicks),
|
|
2942
|
+
slope: num(r.slope),
|
|
2943
|
+
intercept: num(r.intercept),
|
|
2944
|
+
r2: num(r.r2),
|
|
2945
|
+
headImpressions: num(r.headImpressions),
|
|
2946
|
+
headShare: num(r.headShare),
|
|
3064
2947
|
fingerprint: str$8(r.fingerprint),
|
|
3065
2948
|
points: downsampleLogRank(parseJsonList$5(r.pointsJson))
|
|
3066
2949
|
}));
|
|
@@ -3099,9 +2982,9 @@ function analyzeMovers(input, options = {}) {
|
|
|
3099
2982
|
const { changeThreshold = .2, minImpressions = 50, sortBy = "clicksChange" } = options;
|
|
3100
2983
|
const normFactor = input.normalizationFactor ?? 1;
|
|
3101
2984
|
const baselineMap = buildPeriodMap(input.previous, (r) => r.query, (r) => ({
|
|
3102
|
-
clicks: num
|
|
3103
|
-
impressions: num
|
|
3104
|
-
position: num
|
|
2985
|
+
clicks: num(r.clicks) / normFactor,
|
|
2986
|
+
impressions: num(r.impressions) / normFactor,
|
|
2987
|
+
position: num(r.position),
|
|
3105
2988
|
page: r.page ?? null
|
|
3106
2989
|
}));
|
|
3107
2990
|
const pageMap = /* @__PURE__ */ new Map();
|
|
@@ -3111,9 +2994,9 @@ function analyzeMovers(input, options = {}) {
|
|
|
3111
2994
|
const declining = [];
|
|
3112
2995
|
const stable = [];
|
|
3113
2996
|
for (const row of input.current) {
|
|
3114
|
-
const impressions = num
|
|
3115
|
-
const clicks = num
|
|
3116
|
-
const position = num
|
|
2997
|
+
const impressions = num(row.impressions);
|
|
2998
|
+
const clicks = num(row.clicks);
|
|
2999
|
+
const position = num(row.position);
|
|
3117
3000
|
if (impressions < minImpressions) continue;
|
|
3118
3001
|
const baseline = baselineMap.get(row.query) || {
|
|
3119
3002
|
clicks: 0,
|
|
@@ -3276,21 +3159,21 @@ const moversAnalyzer = defineAnalyzer({
|
|
|
3276
3159
|
const normalized = (Array.isArray(rows) ? rows : []).map((r) => ({
|
|
3277
3160
|
keyword: str$7(r.keyword),
|
|
3278
3161
|
page: r.page == null ? null : str$7(r.page),
|
|
3279
|
-
recentClicks: num
|
|
3280
|
-
recentImpressions: num
|
|
3281
|
-
recentPosition: num
|
|
3282
|
-
baselineClicks: Math.round(num
|
|
3283
|
-
baselineImpressions: Math.round(num
|
|
3284
|
-
baselinePosition: num
|
|
3285
|
-
clicksChange: num
|
|
3286
|
-
clicksChangePercent: num
|
|
3287
|
-
impressionsChangePercent: num
|
|
3288
|
-
positionChange: num
|
|
3162
|
+
recentClicks: num(r.recentClicks),
|
|
3163
|
+
recentImpressions: num(r.recentImpressions),
|
|
3164
|
+
recentPosition: num(r.recentPosition),
|
|
3165
|
+
baselineClicks: Math.round(num(r.baselineClicks)),
|
|
3166
|
+
baselineImpressions: Math.round(num(r.baselineImpressions)),
|
|
3167
|
+
baselinePosition: num(r.baselinePosition),
|
|
3168
|
+
clicksChange: num(r.clicksChange),
|
|
3169
|
+
clicksChangePercent: num(r.clicksChangePercent),
|
|
3170
|
+
impressionsChangePercent: num(r.impressionsChangePercent),
|
|
3171
|
+
positionChange: num(r.positionChange),
|
|
3289
3172
|
direction: str$7(r.direction),
|
|
3290
3173
|
series: parseJsonList$4(r.seriesJson).map((s) => ({
|
|
3291
3174
|
week: str$7(s.week),
|
|
3292
|
-
clicks: num
|
|
3293
|
-
impressions: num
|
|
3175
|
+
clicks: num(s.clicks),
|
|
3176
|
+
impressions: num(s.impressions)
|
|
3294
3177
|
}))
|
|
3295
3178
|
}));
|
|
3296
3179
|
const rising = normalized.filter((r) => r.direction === "rising");
|
|
@@ -3500,16 +3383,16 @@ const opportunityAnalyzer = defineAnalyzer({
|
|
|
3500
3383
|
results: arr.map((r) => ({
|
|
3501
3384
|
keyword: r.keyword == null ? "" : String(r.keyword),
|
|
3502
3385
|
page: r.page == null ? null : String(r.page),
|
|
3503
|
-
clicks: num
|
|
3504
|
-
impressions: num
|
|
3505
|
-
ctr: num
|
|
3506
|
-
position: num
|
|
3507
|
-
opportunityScore: num
|
|
3508
|
-
potentialClicks: num
|
|
3386
|
+
clicks: num(r.clicks),
|
|
3387
|
+
impressions: num(r.impressions),
|
|
3388
|
+
ctr: num(r.ctr),
|
|
3389
|
+
position: num(r.position),
|
|
3390
|
+
opportunityScore: num(r.opportunityScore),
|
|
3391
|
+
potentialClicks: num(r.potentialClicks),
|
|
3509
3392
|
factors: {
|
|
3510
|
-
positionScore: num
|
|
3511
|
-
impressionScore: num
|
|
3512
|
-
ctrGapScore: num
|
|
3393
|
+
positionScore: num(r.positionScore),
|
|
3394
|
+
impressionScore: num(r.impressionScore),
|
|
3395
|
+
ctrGapScore: num(r.ctrGapScore)
|
|
3513
3396
|
}
|
|
3514
3397
|
})),
|
|
3515
3398
|
meta: { total: arr.length }
|
|
@@ -3527,10 +3410,10 @@ const opportunityAnalyzer = defineAnalyzer({
|
|
|
3527
3410
|
const sortBy = "opportunityScore";
|
|
3528
3411
|
const results = [];
|
|
3529
3412
|
for (const row of keywords) {
|
|
3530
|
-
const impressions = num
|
|
3531
|
-
const position = num
|
|
3532
|
-
const ctr = num
|
|
3533
|
-
const clicks = num
|
|
3413
|
+
const impressions = num(row.impressions);
|
|
3414
|
+
const position = num(row.position);
|
|
3415
|
+
const ctr = num(row.ctr);
|
|
3416
|
+
const clicks = num(row.clicks);
|
|
3534
3417
|
if (impressions < minImpressions) continue;
|
|
3535
3418
|
const positionScore = calculatePositionScore(position);
|
|
3536
3419
|
const impressionScore = calculateImpressionScore(impressions);
|
|
@@ -3609,11 +3492,11 @@ const positionDistributionAnalyzer = defineAnalyzer({
|
|
|
3609
3492
|
return {
|
|
3610
3493
|
results: arr.map((r) => ({
|
|
3611
3494
|
date: str$6(r.date),
|
|
3612
|
-
pos_1_3: num
|
|
3613
|
-
pos_4_10: num
|
|
3614
|
-
pos_11_20: num
|
|
3615
|
-
pos_20_plus: num
|
|
3616
|
-
total: num
|
|
3495
|
+
pos_1_3: num(r.pos_1_3),
|
|
3496
|
+
pos_4_10: num(r.pos_4_10),
|
|
3497
|
+
pos_11_20: num(r.pos_11_20),
|
|
3498
|
+
pos_20_plus: num(r.pos_20_plus),
|
|
3499
|
+
total: num(r.total)
|
|
3617
3500
|
})),
|
|
3618
3501
|
meta: {
|
|
3619
3502
|
total: arr.length,
|
|
@@ -3728,21 +3611,21 @@ const positionVolatilityAnalyzer = defineAnalyzer({
|
|
|
3728
3611
|
allDates.add(date);
|
|
3729
3612
|
const entry = byPage.get(page) ?? {
|
|
3730
3613
|
page,
|
|
3731
|
-
avgVolatility: num
|
|
3732
|
-
peakVolatility: num
|
|
3733
|
-
totalImpressions: num
|
|
3614
|
+
avgVolatility: num(r.pageAvgVolatility),
|
|
3615
|
+
peakVolatility: num(r.pagePeakVolatility),
|
|
3616
|
+
totalImpressions: num(r.pageTotalImpressions),
|
|
3734
3617
|
days: []
|
|
3735
3618
|
};
|
|
3736
3619
|
entry.days.push({
|
|
3737
3620
|
date,
|
|
3738
|
-
queryCount: num
|
|
3739
|
-
dayImpressions: num
|
|
3740
|
-
avgPosition: num
|
|
3741
|
-
posStddev: num
|
|
3742
|
-
bestPosition: num
|
|
3743
|
-
worstPosition: num
|
|
3744
|
-
dodShift: num
|
|
3745
|
-
volatility: num
|
|
3621
|
+
queryCount: num(r.queryCount),
|
|
3622
|
+
dayImpressions: num(r.dayImpressions),
|
|
3623
|
+
avgPosition: num(r.avgPosition),
|
|
3624
|
+
posStddev: num(r.posStddev),
|
|
3625
|
+
bestPosition: num(r.bestPosition),
|
|
3626
|
+
worstPosition: num(r.worstPosition),
|
|
3627
|
+
dodShift: num(r.dodShift),
|
|
3628
|
+
volatility: num(r.volatility)
|
|
3746
3629
|
});
|
|
3747
3630
|
byPage.set(page, entry);
|
|
3748
3631
|
}
|
|
@@ -3893,14 +3776,14 @@ const queryMigrationAnalyzer = defineAnalyzer({
|
|
|
3893
3776
|
const edges = arr.map((r) => ({
|
|
3894
3777
|
sourcePage: str$4(r.source_page),
|
|
3895
3778
|
targetPage: str$4(r.target_page),
|
|
3896
|
-
weight: num
|
|
3897
|
-
queryCount: num
|
|
3898
|
-
exactCount: num
|
|
3899
|
-
fuzzyCount: num
|
|
3779
|
+
weight: num(r.weight),
|
|
3780
|
+
queryCount: num(r.query_count),
|
|
3781
|
+
exactCount: num(r.exact_count),
|
|
3782
|
+
fuzzyCount: num(r.query_count) - num(r.exact_count),
|
|
3900
3783
|
examples: parseJsonList$3(r.examplesJson).slice(0, 8).map((e) => ({
|
|
3901
3784
|
sourceQuery: str$4(e.sourceQuery),
|
|
3902
3785
|
targetQuery: str$4(e.targetQuery),
|
|
3903
|
-
absorbed: num
|
|
3786
|
+
absorbed: num(e.absorbed),
|
|
3904
3787
|
matchType: str$4(e.matchType)
|
|
3905
3788
|
}))
|
|
3906
3789
|
}));
|
|
@@ -4040,14 +3923,14 @@ const seasonalityAnalyzer = defineAnalyzer({
|
|
|
4040
3923
|
const arr = Array.isArray(rows) ? rows : [];
|
|
4041
3924
|
const breakdown = arr.map((r) => ({
|
|
4042
3925
|
month: str$3(r.month),
|
|
4043
|
-
value: num
|
|
4044
|
-
vsAverage: num
|
|
3926
|
+
value: num(r.value),
|
|
3927
|
+
vsAverage: num(r.vsAverage),
|
|
4045
3928
|
isPeak: bool$1(r.isPeak),
|
|
4046
3929
|
isTrough: bool$1(r.isTrough)
|
|
4047
3930
|
}));
|
|
4048
3931
|
const first = arr[0];
|
|
4049
|
-
const strength = first ? num
|
|
4050
|
-
const monthCount = first ? num
|
|
3932
|
+
const strength = first ? num(first.strength) : 0;
|
|
3933
|
+
const monthCount = first ? num(first.monthCount) : 0;
|
|
4051
3934
|
const peakMonths = [...new Set(breakdown.filter((m) => m.isPeak).map((m) => m.month.substring(5, 7)))];
|
|
4052
3935
|
const troughMonths = [...new Set(breakdown.filter((m) => m.isTrough).map((m) => m.month.substring(5, 7)))];
|
|
4053
3936
|
const hasSeasonality = peakMonths.length > 0 || troughMonths.length > 0 || strength > .3;
|
|
@@ -4240,18 +4123,18 @@ const stlDecomposeAnalyzer = defineAnalyzer({
|
|
|
4240
4123
|
const results = arr.map((r) => ({
|
|
4241
4124
|
keyword: str$2(r.keyword),
|
|
4242
4125
|
page: str$2(r.page),
|
|
4243
|
-
totalImpressions: num
|
|
4244
|
-
days: num
|
|
4245
|
-
seasonalStrength: num
|
|
4246
|
-
trendStrength: num
|
|
4247
|
-
residualAnomalies: num
|
|
4248
|
-
trendSlope: num
|
|
4126
|
+
totalImpressions: num(r.totalImpressions),
|
|
4127
|
+
days: num(r.days),
|
|
4128
|
+
seasonalStrength: num(r.seasonalStrength),
|
|
4129
|
+
trendStrength: num(r.trendStrength),
|
|
4130
|
+
residualAnomalies: num(r.residualAnomalies),
|
|
4131
|
+
trendSlope: num(r.trendSlope),
|
|
4249
4132
|
series: parseJsonList$2(r.seriesJson).map((s) => ({
|
|
4250
4133
|
date: str$2(s.date),
|
|
4251
|
-
observed: num
|
|
4252
|
-
trend: s.trend == null ? null : num
|
|
4253
|
-
seasonal: s.seasonal == null ? null : num
|
|
4254
|
-
residual: s.residual == null ? null : num
|
|
4134
|
+
observed: num(s.observed),
|
|
4135
|
+
trend: s.trend == null ? null : num(s.trend),
|
|
4136
|
+
seasonal: s.seasonal == null ? null : num(s.seasonal),
|
|
4137
|
+
residual: s.residual == null ? null : num(s.residual),
|
|
4255
4138
|
anomaly: bool(s.anomaly)
|
|
4256
4139
|
}))
|
|
4257
4140
|
}));
|
|
@@ -4277,10 +4160,10 @@ const strikingDistanceAnalyzer = defineAnalyzer({
|
|
|
4277
4160
|
const limit = params.limit ?? 1e3;
|
|
4278
4161
|
const results = [];
|
|
4279
4162
|
for (const row of arr) {
|
|
4280
|
-
const position = num
|
|
4281
|
-
const impressions = num
|
|
4282
|
-
const ctr = num
|
|
4283
|
-
const clicks = num
|
|
4163
|
+
const position = num(row.position);
|
|
4164
|
+
const impressions = num(row.impressions);
|
|
4165
|
+
const ctr = num(row.ctr);
|
|
4166
|
+
const clicks = num(row.clicks);
|
|
4284
4167
|
if (position < minPosition || position > maxPosition) continue;
|
|
4285
4168
|
if (impressions < minImpressions) continue;
|
|
4286
4169
|
if (ctr > maxCtr) continue;
|
|
@@ -4497,10 +4380,10 @@ const survivalAnalyzer = defineAnalyzer({
|
|
|
4497
4380
|
const windowDays = Math.round((new Date(endDate).getTime() - new Date(startDate).getTime()) / MS_PER_DAY) + 1;
|
|
4498
4381
|
const results = arr.map((r) => {
|
|
4499
4382
|
const curve = parseJsonList$1(r.curveJson).map((p) => ({
|
|
4500
|
-
tenure: num
|
|
4501
|
-
survival: num
|
|
4502
|
-
atRisk: num
|
|
4503
|
-
events: num
|
|
4383
|
+
tenure: num(p.tenure),
|
|
4384
|
+
survival: num(p.survival),
|
|
4385
|
+
atRisk: num(p.atRisk),
|
|
4386
|
+
events: num(p.events)
|
|
4504
4387
|
}));
|
|
4505
4388
|
let medianTenure = 0;
|
|
4506
4389
|
for (let i = 0; i < curve.length; i++) {
|
|
@@ -4520,8 +4403,8 @@ const survivalAnalyzer = defineAnalyzer({
|
|
|
4520
4403
|
if (medianTenure === 0 && last && last.survival > .5) medianTenure = last.tenure;
|
|
4521
4404
|
return {
|
|
4522
4405
|
cohort: str$1(r.cohort),
|
|
4523
|
-
episodeCount: num
|
|
4524
|
-
censoringRate: num
|
|
4406
|
+
episodeCount: num(r.episodeCount),
|
|
4407
|
+
censoringRate: num(r.censoringRate),
|
|
4525
4408
|
medianTenure,
|
|
4526
4409
|
curve
|
|
4527
4410
|
};
|
|
@@ -4659,17 +4542,17 @@ const trendsAnalyzer = defineAnalyzer({
|
|
|
4659
4542
|
const results = arr.map((r) => {
|
|
4660
4543
|
const series = parseJsonList(r.seriesJson).map((s) => ({
|
|
4661
4544
|
week: str(s.week),
|
|
4662
|
-
clicks: num
|
|
4663
|
-
impressions: num
|
|
4545
|
+
clicks: num(s.clicks),
|
|
4546
|
+
impressions: num(s.impressions)
|
|
4664
4547
|
}));
|
|
4665
4548
|
return {
|
|
4666
4549
|
[dim === "keywords" ? "query" : "page"]: str(r.entity),
|
|
4667
|
-
totalClicks: num
|
|
4668
|
-
totalImpressions: num
|
|
4669
|
-
weeksWithData: num
|
|
4670
|
-
slope: num
|
|
4671
|
-
growthRatio: num
|
|
4672
|
-
avgPosition: num
|
|
4550
|
+
totalClicks: num(r.totalClicks),
|
|
4551
|
+
totalImpressions: num(r.totalImpressions),
|
|
4552
|
+
weeksWithData: num(r.weeksWithData),
|
|
4553
|
+
slope: num(r.slope),
|
|
4554
|
+
growthRatio: num(r.growthRatio),
|
|
4555
|
+
avgPosition: num(r.avgPosition),
|
|
4673
4556
|
trend: str(r.trend),
|
|
4674
4557
|
series
|
|
4675
4558
|
};
|
|
@@ -4760,11 +4643,11 @@ const zeroClickAnalyzer = defineAnalyzer({
|
|
|
4760
4643
|
results: arr.map((r) => ({
|
|
4761
4644
|
query: r.query == null ? "" : String(r.query),
|
|
4762
4645
|
page: r.page == null ? "" : String(r.page),
|
|
4763
|
-
clicks: num
|
|
4764
|
-
impressions: num
|
|
4765
|
-
ctr: num
|
|
4766
|
-
position: num
|
|
4767
|
-
missedClicks: num
|
|
4646
|
+
clicks: num(r.clicks),
|
|
4647
|
+
impressions: num(r.impressions),
|
|
4648
|
+
ctr: num(r.ctr),
|
|
4649
|
+
position: num(r.position),
|
|
4650
|
+
missedClicks: num(r.missedClicks)
|
|
4768
4651
|
})),
|
|
4769
4652
|
meta: {
|
|
4770
4653
|
total: arr.length,
|
|
@@ -4813,122 +4696,6 @@ const zeroClickAnalyzer = defineAnalyzer({
|
|
|
4813
4696
|
};
|
|
4814
4697
|
}
|
|
4815
4698
|
});
|
|
4816
|
-
var AnalyzerCapabilityError = class extends Error {
|
|
4817
|
-
constructor(tool, missing) {
|
|
4818
|
-
super(`analyzer "${tool}" requires capabilities [${missing.join(", ")}] not provided by source`);
|
|
4819
|
-
this.tool = tool;
|
|
4820
|
-
this.missing = missing;
|
|
4821
|
-
this.name = "AnalyzerCapabilityError";
|
|
4822
|
-
}
|
|
4823
|
-
};
|
|
4824
|
-
function sourceCapabilities(source) {
|
|
4825
|
-
const caps = /* @__PURE__ */ new Set();
|
|
4826
|
-
if (source.executeSql) caps.add("executeSql");
|
|
4827
|
-
if (source.capabilities.fileSets) caps.add("partitionedParquet");
|
|
4828
|
-
if (source.capabilities.regex) caps.add("regex");
|
|
4829
|
-
if (source.capabilities.windowTotals) caps.add("windowTotals");
|
|
4830
|
-
if (source.capabilities.comparisonJoin) caps.add("comparisonJoin");
|
|
4831
|
-
if (source.capabilities.attachedTables) caps.add("attachedTables");
|
|
4832
|
-
return caps;
|
|
4833
|
-
}
|
|
4834
|
-
function assertSatisfies(analyzer, caps) {
|
|
4835
|
-
const missing = analyzer.requires.filter((c) => !caps.has(c));
|
|
4836
|
-
if (missing.length > 0) throw new AnalyzerCapabilityError(analyzer.id, missing);
|
|
4837
|
-
}
|
|
4838
|
-
async function runAnalyzerFromSource(source, params, registry) {
|
|
4839
|
-
const caps = sourceCapabilities(source);
|
|
4840
|
-
const analyzer = registry.resolveAnalyzer(params.type, caps.has("executeSql") || caps.has("attachedTables"));
|
|
4841
|
-
if (!analyzer) throw new AnalyzerCapabilityError(params.type, ["executeSql"]);
|
|
4842
|
-
assertSatisfies(analyzer, caps);
|
|
4843
|
-
const plan = analyzer.build(params);
|
|
4844
|
-
if (plan.kind === "rows") return runRowsPlanAgainstSource(source, analyzer, plan, params);
|
|
4845
|
-
return runSqlPlanAgainstSource(source, analyzer, plan, params);
|
|
4846
|
-
}
|
|
4847
|
-
async function runRowsPlanAgainstSource(source, analyzer, plan, params) {
|
|
4848
|
-
const entries = Object.entries(plan.queries);
|
|
4849
|
-
const resolved = await Promise.all(entries.map(async ([k, q]) => [k, await source.queryRows(q.state)]));
|
|
4850
|
-
const rowMap = Object.fromEntries(resolved);
|
|
4851
|
-
const { results, meta } = analyzer.reduce(rowMap, { params });
|
|
4852
|
-
return {
|
|
4853
|
-
results,
|
|
4854
|
-
meta: {
|
|
4855
|
-
tool: params.type,
|
|
4856
|
-
...meta
|
|
4857
|
-
}
|
|
4858
|
-
};
|
|
4859
|
-
}
|
|
4860
|
-
function fileSetsFor(plan) {
|
|
4861
|
-
const fileSets = { FILES: plan.current };
|
|
4862
|
-
if (plan.previous) fileSets.FILES_PREV = plan.previous;
|
|
4863
|
-
if (plan.extraFiles) for (const [key, fs] of Object.entries(plan.extraFiles)) fileSets[`FILES_${key}`] = fs;
|
|
4864
|
-
return fileSets;
|
|
4865
|
-
}
|
|
4866
|
-
async function runSqlPlanAgainstSource(source, analyzer, plan, params) {
|
|
4867
|
-
if (!source.executeSql) throw new AnalyzerCapabilityError(analyzer.id, ["executeSql"]);
|
|
4868
|
-
if (plan.requiresAttachedTables && !source.capabilities.attachedTables) throw new AnalyzerCapabilityError(analyzer.id, ["attachedTables"]);
|
|
4869
|
-
const fileSets = source.capabilities.fileSets ? fileSetsFor(plan) : void 0;
|
|
4870
|
-
const rows = await source.executeSql(plan.sql, plan.params, fileSets ? { fileSets } : void 0);
|
|
4871
|
-
const extras = {};
|
|
4872
|
-
if (plan.extraQueries) for (const q of plan.extraQueries) {
|
|
4873
|
-
const extraRows = await source.executeSql(q.sql, q.params, fileSets ? { fileSets } : void 0);
|
|
4874
|
-
extras[q.name] = extraRows;
|
|
4875
|
-
}
|
|
4876
|
-
const { results, meta } = analyzer.reduce(rows, {
|
|
4877
|
-
params,
|
|
4878
|
-
extras
|
|
4879
|
-
});
|
|
4880
|
-
const sourceMeta = source.capabilities.localSource ? { source: "local" } : {};
|
|
4881
|
-
return {
|
|
4882
|
-
results,
|
|
4883
|
-
meta: {
|
|
4884
|
-
tool: params.type,
|
|
4885
|
-
...sourceMeta,
|
|
4886
|
-
...meta
|
|
4887
|
-
}
|
|
4888
|
-
};
|
|
4889
|
-
}
|
|
4890
|
-
function createAnalyzerRegistry(init = {}) {
|
|
4891
|
-
const byId = /* @__PURE__ */ new Map();
|
|
4892
|
-
for (const a of init.rows ?? []) {
|
|
4893
|
-
const entry = byId.get(a.id) ?? {};
|
|
4894
|
-
entry.rows = a;
|
|
4895
|
-
byId.set(a.id, entry);
|
|
4896
|
-
}
|
|
4897
|
-
for (const a of init.sql ?? []) {
|
|
4898
|
-
const entry = byId.get(a.id) ?? {};
|
|
4899
|
-
entry.sql = a;
|
|
4900
|
-
byId.set(a.id, entry);
|
|
4901
|
-
}
|
|
4902
|
-
const listAnalyzerIds = () => [...byId.keys()].sort();
|
|
4903
|
-
const getAnalyzerVariants = (id) => byId.get(id);
|
|
4904
|
-
const resolveAnalyzer = (id, sourceSupportsSql) => {
|
|
4905
|
-
const variants = byId.get(id);
|
|
4906
|
-
if (!variants) return void 0;
|
|
4907
|
-
if (sourceSupportsSql) return variants.sql ?? variants.rows;
|
|
4908
|
-
return variants.rows;
|
|
4909
|
-
};
|
|
4910
|
-
const listAnalyzersFor = (sourceSupportsSql) => {
|
|
4911
|
-
const out = [];
|
|
4912
|
-
for (const id of listAnalyzerIds()) {
|
|
4913
|
-
const a = resolveAnalyzer(id, sourceSupportsSql);
|
|
4914
|
-
if (a) out.push(a);
|
|
4915
|
-
}
|
|
4916
|
-
return out;
|
|
4917
|
-
};
|
|
4918
|
-
const listAnalyzerIdsFor = (source) => {
|
|
4919
|
-
const sourceSupportsSql = typeof source.executeSql === "function";
|
|
4920
|
-
const out = [];
|
|
4921
|
-
for (const id of listAnalyzerIds()) if (resolveAnalyzer(id, sourceSupportsSql)) out.push(id);
|
|
4922
|
-
return out;
|
|
4923
|
-
};
|
|
4924
|
-
return {
|
|
4925
|
-
listAnalyzerIds,
|
|
4926
|
-
getAnalyzerVariants,
|
|
4927
|
-
resolveAnalyzer,
|
|
4928
|
-
listAnalyzersFor,
|
|
4929
|
-
listAnalyzerIdsFor
|
|
4930
|
-
};
|
|
4931
|
-
}
|
|
4932
4699
|
const ROW_ANALYZERS = [
|
|
4933
4700
|
strikingDistanceAnalyzer.rows,
|
|
4934
4701
|
opportunityAnalyzer.rows,
|
|
@@ -4941,4 +4708,4 @@ const ROW_ANALYZERS = [
|
|
|
4941
4708
|
cannibalizationAnalyzer.rows,
|
|
4942
4709
|
zeroClickAnalyzer.rows
|
|
4943
4710
|
];
|
|
4944
|
-
export {
|
|
4711
|
+
export { ROW_ANALYZERS, bayesianCtrAnalyzer, bipartitePagerankAnalyzer, brandAnalyzer, cannibalizationAnalyzer, changePointAnalyzer, clampLimit, clampOffset, clusteringAnalyzer, concentrationAnalyzer, contentVelocityAnalyzer, ctrAnomalyAnalyzer, ctrCurveAnalyzer, darkTrafficAnalyzer, dataDetailAnalyzer, dataQueryAnalyzer, datesQueryState, decayAnalyzer, deviceGapAnalyzer, intentAtlasAnalyzer, keywordBreadthAnalyzer, keywordsQueryState, longTailAnalyzer, moversAnalyzer, opportunityAnalyzer, pagesQueryState, paginateClause, paginateInMemory, positionDistributionAnalyzer, positionVolatilityAnalyzer, queryMigrationAnalyzer, resolveSort, seasonalityAnalyzer, stlDecomposeAnalyzer, strikingDistanceAnalyzer, survivalAnalyzer, trendsAnalyzer, zeroClickAnalyzer };
|