@lightdash/common 0.1396.3 → 0.1397.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -56,7 +56,7 @@ export type CatalogTable = Pick<TableBase, 'name' | 'label' | 'groupLabel' | 'de
56
56
  icon: CatalogItemIcon | null;
57
57
  };
58
58
  export type CatalogItem = CatalogField | CatalogTable;
59
- export type CatalogMetricsTreeNode = Pick<CatalogField, 'name' | 'tableName'>;
59
+ export type CatalogMetricsTreeNode = Pick<CatalogField, 'catalogSearchUuid' | 'name' | 'tableName'>;
60
60
  export type CatalogMetricsTreeEdge = {
61
61
  source: CatalogMetricsTreeNode;
62
62
  target: CatalogMetricsTreeNode;
@@ -82,8 +82,8 @@ export type ApiGetMetricsTree = {
82
82
  };
83
83
  };
84
84
  export type ApiMetricsTreeEdgePayload = {
85
- sourceMetricId: string;
86
- targetMetricId: string;
85
+ sourceCatalogSearchUuid: string;
86
+ targetCatalogSearchUuid: string;
87
87
  };
88
88
  export type CatalogMetadata = {
89
89
  name: string;
@@ -5,7 +5,8 @@ export declare enum MetricExplorerComparison {
5
5
  PREVIOUS_PERIOD = "previous_period",
6
6
  DIFFERENT_METRIC = "different_metric"
7
7
  }
8
- export type MetricExplorerDateRange = [Date | null, Date | null];
8
+ export type MetricExplorerPartialDateRange = [Date | null, Date | null];
9
+ export type MetricExplorerDateRange = [Date, Date];
9
10
  export type MetricExplorerComparisonType = {
10
11
  type: MetricExplorerComparison.NONE;
11
12
  } | {
@@ -14,6 +15,11 @@ export type MetricExplorerComparisonType = {
14
15
  type: MetricExplorerComparison.DIFFERENT_METRIC;
15
16
  metricName: string;
16
17
  };
18
+ export type MetricExploreDataPoint = {
19
+ date: Date;
20
+ metric: unknown;
21
+ compareMetric: unknown;
22
+ };
17
23
  export type MetricsExplorerQueryResults = {
18
24
  rows: ResultRow[];
19
25
  comparisonRows: ResultRow[] | undefined;
@@ -1,4 +1 @@
1
- import type { CatalogMetricsTreeNode } from '../types/catalog';
2
1
  export declare const MAX_METRICS_TREE_NODE_COUNT = 30;
3
- export declare function getMetricsTreeNodeId(field: CatalogMetricsTreeNode): string;
4
- export declare function parseMetricsTreeNodeId(id: string): CatalogMetricsTreeNode;
@@ -1,19 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseMetricsTreeNodeId = exports.getMetricsTreeNodeId = exports.MAX_METRICS_TREE_NODE_COUNT = void 0;
3
+ exports.MAX_METRICS_TREE_NODE_COUNT = void 0;
4
4
  exports.MAX_METRICS_TREE_NODE_COUNT = 30;
5
- function getMetricsTreeNodeId(field) {
6
- return `${field.tableName}::${field.name}`;
7
- }
8
- exports.getMetricsTreeNodeId = getMetricsTreeNodeId;
9
- function parseMetricsTreeNodeId(id) {
10
- const [tableName, name] = id.split('::');
11
- if (!tableName || !name) {
12
- throw new Error('Invalid metrics tree node id');
13
- }
14
- return {
15
- name,
16
- tableName,
17
- };
18
- }
19
- exports.parseMetricsTreeNodeId = parseMetricsTreeNodeId;
@@ -1,10 +1,27 @@
1
+ import type { MetricWithAssociatedTimeDimension } from '../types/catalog';
1
2
  import { ConditionalOperator } from '../types/conditionalRule';
3
+ import type { Dimension } from '../types/field';
2
4
  import { type DateFilterSettings, type FieldTarget, type FilterRule } from '../types/filter';
3
- import type { MetricExplorerDateRange } from '../types/metricsExplorer';
5
+ import type { MetricExploreDataPoint, MetricExplorerDateRange } from '../types/metricsExplorer';
6
+ import type { ResultRow } from '../types/results';
4
7
  import { TimeFrames } from '../types/timeFrames';
5
8
  type DateFilter = FilterRule<ConditionalOperator, FieldTarget, unknown, DateFilterSettings>;
9
+ type ImpelemntedTimeframe = TimeFrames.DAY | TimeFrames.WEEK | TimeFrames.MONTH | TimeFrames.YEAR;
6
10
  export declare const getFieldIdForDateDimension: (fieldId: string, timeframe: TimeFrames) => string;
7
- export declare const getMetricExplorerDefaultGrainFilters: (exploreName: string, dimensionName: string, timeInterval: TimeFrames | undefined) => DateFilter[];
8
- export declare const getMetricExplorerDimensionPreviousFilters: (exploreName: string, dimensionName: string, timeInterval: TimeFrames | undefined) => DateFilter[];
9
- export declare const getMetricExplorerDateRangeFilters: (exploreName: string, dimensionName: string, [startDate, endDate]: MetricExplorerDateRange) => DateFilter[];
11
+ export declare const oneYearBack: (date: Date) => Date;
12
+ export declare const oneYearForward: (date: Date) => Date;
13
+ export declare const getGrainForDateRange: (dateRange: [Date, Date]) => ImpelemntedTimeframe;
14
+ export declare const getMetricExplorerDateRangeFilters: (exploreName: string, dimensionName: string, dateRange: MetricExplorerDateRange) => DateFilter[];
15
+ export declare const getMetricExplorerDataPoints: (dimension: Dimension, metric: MetricWithAssociatedTimeDimension, metricRows: ResultRow[]) => Array<MetricExploreDataPoint>;
16
+ export declare const getMetricExplorerDataPointsWithCompare: (dimension: Dimension, metric: MetricWithAssociatedTimeDimension, metricRows: ResultRow[], compareMetricRows: ResultRow[], offsetFunction?: (date: Date) => Date) => Array<MetricExploreDataPoint>;
17
+ /**
18
+ * Get the date range for a given time interval, based on the current date and the time interval
19
+ * Time grain Year: -> past 5 years (i.e. 5 completed years + this uncompleted year)
20
+ * Time grain Month -> past 12 months (i.e. 12 completed months + this uncompleted month)
21
+ * Time grain Week -> past 12 weeks (i.e. 12 completed weeks + this uncompleted week)
22
+ * Time grain Day -> past 30 days (i.e. 30 completed days + this uncompleted day)
23
+ * @param timeInterval - The time interval
24
+ * @returns The date range
25
+ */
26
+ export declare const getDefaultDateRangeFromInterval: (timeInterval: TimeFrames) => MetricExplorerDateRange;
10
27
  export {};
@@ -1,10 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getMetricExplorerDateRangeFilters = exports.getMetricExplorerDimensionPreviousFilters = exports.getMetricExplorerDefaultGrainFilters = exports.getFieldIdForDateDimension = void 0;
3
+ exports.getDefaultDateRangeFromInterval = exports.getMetricExplorerDataPointsWithCompare = exports.getMetricExplorerDataPoints = exports.getMetricExplorerDateRangeFilters = exports.getGrainForDateRange = exports.oneYearForward = exports.oneYearBack = exports.getFieldIdForDateDimension = void 0;
4
4
  const tslib_1 = require("tslib");
5
+ const dayjs_1 = tslib_1.__importDefault(require("dayjs"));
6
+ const lodash_1 = require("lodash");
5
7
  const uuid_1 = require("uuid");
6
8
  const conditionalRule_1 = require("../types/conditionalRule");
7
- const filter_1 = require("../types/filter");
8
9
  const timeFrames_1 = require("../types/timeFrames");
9
10
  const assertUnreachable_1 = tslib_1.__importDefault(require("./assertUnreachable"));
10
11
  const item_1 = require("./item");
@@ -48,241 +49,37 @@ const getFieldIdForDateDimension = (fieldId, timeframe) => {
48
49
  }
49
50
  };
50
51
  exports.getFieldIdForDateDimension = getFieldIdForDateDimension;
51
- // Time grain Year: -> past 5 years (i.e. 5 completed years + this uncompleted year)
52
- // Time grain Month -> past 12 months (i.e. 12 completed months + this uncompleted month)
53
- // Time grain Week -> past 12 weeks (i.e. 12 completed weeks + this uncompleted week)
54
- // Time grain Day -> past 30 days (i.e. 30 completed days + this uncompleted day)
55
- const getMetricExplorerDefaultGrainFilters = (exploreName, dimensionName, timeInterval) => {
56
- const fieldWithGrain = timeInterval
57
- ? (0, exports.getFieldIdForDateDimension)(dimensionName, timeInterval)
58
- : dimensionName;
59
- const targetFieldId = (0, item_1.getItemId)({
60
- table: exploreName,
61
- name: fieldWithGrain,
62
- });
63
- if (!timeInterval) {
64
- throw new Error('Time interval is required to get relevant filter');
65
- }
66
- switch (timeInterval) {
67
- case timeFrames_1.TimeFrames.DAY:
68
- return [
69
- {
70
- id: (0, uuid_1.v4)(),
71
- target: { fieldId: targetFieldId },
72
- operator: conditionalRule_1.ConditionalOperator.IN_THE_PAST,
73
- values: [29],
74
- settings: {
75
- unitOfTime: filter_1.UnitOfTime.days,
76
- completed: true,
77
- },
78
- },
79
- {
80
- id: (0, uuid_1.v4)(),
81
- target: { fieldId: targetFieldId },
82
- operator: conditionalRule_1.ConditionalOperator.IN_THE_CURRENT,
83
- values: [],
84
- settings: { unitOfTime: filter_1.UnitOfTime.days },
85
- },
86
- ];
87
- case timeFrames_1.TimeFrames.WEEK:
88
- return [
89
- {
90
- id: (0, uuid_1.v4)(),
91
- target: { fieldId: targetFieldId },
92
- operator: conditionalRule_1.ConditionalOperator.IN_THE_PAST,
93
- values: [11],
94
- settings: {
95
- unitOfTime: filter_1.UnitOfTime.weeks,
96
- completed: true,
97
- },
98
- },
99
- {
100
- id: (0, uuid_1.v4)(),
101
- target: { fieldId: targetFieldId },
102
- operator: conditionalRule_1.ConditionalOperator.IN_THE_CURRENT,
103
- values: [],
104
- settings: { unitOfTime: filter_1.UnitOfTime.weeks },
105
- },
106
- ];
107
- case timeFrames_1.TimeFrames.MONTH:
108
- return [
109
- {
110
- id: (0, uuid_1.v4)(),
111
- target: { fieldId: targetFieldId },
112
- operator: conditionalRule_1.ConditionalOperator.IN_THE_PAST,
113
- values: [11],
114
- settings: {
115
- unitOfTime: filter_1.UnitOfTime.months,
116
- completed: true,
117
- },
118
- },
119
- {
120
- id: (0, uuid_1.v4)(),
121
- target: { fieldId: targetFieldId },
122
- operator: conditionalRule_1.ConditionalOperator.IN_THE_CURRENT,
123
- values: [],
124
- settings: { unitOfTime: filter_1.UnitOfTime.months },
125
- },
126
- ];
127
- case timeFrames_1.TimeFrames.YEAR:
128
- return [
129
- {
130
- id: (0, uuid_1.v4)(),
131
- target: { fieldId: targetFieldId },
132
- operator: conditionalRule_1.ConditionalOperator.IN_THE_PAST,
133
- values: [4],
134
- settings: { unitOfTime: filter_1.UnitOfTime.years, completed: true },
135
- },
136
- {
137
- id: (0, uuid_1.v4)(),
138
- target: { fieldId: targetFieldId },
139
- operator: conditionalRule_1.ConditionalOperator.IN_THE_CURRENT,
140
- values: [],
141
- settings: { unitOfTime: filter_1.UnitOfTime.years },
142
- },
143
- ];
144
- default:
145
- return assertUnimplementedTimeframe(timeInterval);
146
- }
147
- };
148
- exports.getMetricExplorerDefaultGrainFilters = getMetricExplorerDefaultGrainFilters;
149
- // Time grain Year: -> 10 years ago - 5 years ago
150
- // Time grain Month -> 24 months ago - 12 months ago
151
- // Time grain Week -> 24 weeks ago - 12 weeks ago
152
- // Time grain Day -> 60 days ago - 30 days ago
153
- const getMetricExplorerDimensionPreviousFilters = (exploreName, dimensionName, timeInterval) => {
154
- const targetFieldId = (0, item_1.getItemId)({
155
- table: exploreName,
156
- name: dimensionName,
157
- });
158
- if (!timeInterval) {
159
- throw new Error('Time interval is required to get relevant filter');
160
- }
161
- switch (timeInterval) {
162
- case timeFrames_1.TimeFrames.DAY:
163
- // 60 days ago - 30 days ago
164
- return [
165
- {
166
- id: (0, uuid_1.v4)(),
167
- target: { fieldId: targetFieldId },
168
- operator: conditionalRule_1.ConditionalOperator.IN_THE_PAST,
169
- values: [60],
170
- settings: {
171
- unitOfTime: filter_1.UnitOfTime.days,
172
- completed: true,
173
- },
174
- },
175
- {
176
- id: (0, uuid_1.v4)(),
177
- target: { fieldId: targetFieldId },
178
- operator: conditionalRule_1.ConditionalOperator.NOT_IN_THE_PAST,
179
- values: [30],
180
- settings: {
181
- unitOfTime: filter_1.UnitOfTime.days,
182
- completed: true,
183
- },
184
- },
185
- ];
186
- case timeFrames_1.TimeFrames.WEEK:
187
- // 24 weeks ago - 12 weeks ago
188
- return [
189
- {
190
- id: (0, uuid_1.v4)(),
191
- target: { fieldId: targetFieldId },
192
- operator: conditionalRule_1.ConditionalOperator.IN_THE_PAST,
193
- values: [24],
194
- settings: {
195
- unitOfTime: filter_1.UnitOfTime.weeks,
196
- completed: true,
197
- },
198
- },
199
- {
200
- id: (0, uuid_1.v4)(),
201
- target: { fieldId: targetFieldId },
202
- operator: conditionalRule_1.ConditionalOperator.NOT_IN_THE_PAST,
203
- values: [12],
204
- settings: {
205
- unitOfTime: filter_1.UnitOfTime.weeks,
206
- completed: true,
207
- },
208
- },
209
- ];
210
- case timeFrames_1.TimeFrames.MONTH:
211
- // 24 months ago - 12 months ago
212
- return [
213
- {
214
- id: (0, uuid_1.v4)(),
215
- target: { fieldId: targetFieldId },
216
- operator: conditionalRule_1.ConditionalOperator.IN_THE_PAST,
217
- values: [24],
218
- settings: {
219
- unitOfTime: filter_1.UnitOfTime.months,
220
- completed: true,
221
- },
222
- },
223
- {
224
- id: (0, uuid_1.v4)(),
225
- target: { fieldId: targetFieldId },
226
- operator: conditionalRule_1.ConditionalOperator.NOT_IN_THE_PAST,
227
- values: [12],
228
- settings: {
229
- unitOfTime: filter_1.UnitOfTime.months,
230
- completed: true,
231
- },
232
- },
233
- ];
234
- case timeFrames_1.TimeFrames.YEAR:
235
- // 10 years ago - 5 years ago
236
- return [
237
- {
238
- id: (0, uuid_1.v4)(),
239
- target: { fieldId: targetFieldId },
240
- operator: conditionalRule_1.ConditionalOperator.IN_THE_PAST,
241
- values: [10],
242
- settings: {
243
- unitOfTime: filter_1.UnitOfTime.years,
244
- completed: true,
245
- },
246
- },
247
- {
248
- id: (0, uuid_1.v4)(),
249
- target: { fieldId: targetFieldId },
250
- operator: conditionalRule_1.ConditionalOperator.NOT_IN_THE_PAST,
251
- values: [5],
252
- settings: {
253
- unitOfTime: filter_1.UnitOfTime.years,
254
- completed: true,
255
- },
256
- },
257
- ];
258
- default:
259
- return assertUnimplementedTimeframe(timeInterval);
260
- }
261
- };
262
- exports.getMetricExplorerDimensionPreviousFilters = getMetricExplorerDimensionPreviousFilters;
263
- // Time grain Year: -> past 5 years (i.e. 5 completed years + this uncompleted year)
264
- // Time grain Month -> past 12 months (i.e. 12 completed months + this uncompleted month)
265
- // Time grain Week -> past 12 weeks (i.e. 12 completed weeks + this uncompleted week)
266
- // Time grain Day -> past 30 days (i.e. 30 completed days + this uncompleted day)
52
+ const oneYearBack = (date) => (0, dayjs_1.default)(date)
53
+ .set('year', (0, dayjs_1.default)(date).get('year') - 1)
54
+ .toDate();
55
+ exports.oneYearBack = oneYearBack;
56
+ const oneYearForward = (date) => (0, dayjs_1.default)(date)
57
+ .set('year', (0, dayjs_1.default)(date).get('year') + 1)
58
+ .toDate();
59
+ exports.oneYearForward = oneYearForward;
60
+ // TODO: refine the time grain for each time frame
61
+ // Time grain Year: -> past 5 years (i.e. 5 completed years + this uncompleted year)
62
+ // Time grain Month -> past 12 months (i.e. 12 completed months + this uncompleted month)
63
+ // Time grain Week -> past 12 weeks (i.e. 12 completed weeks + this uncompleted week)
64
+ // Time grain Day -> past 30 days (i.e. 30 completed days + this uncompleted day)
65
+ // above sample is taken from the spec but the implementation is different for practical reasons for visualization
267
66
  const getGrainForDateRange = (dateRange) => {
268
67
  const diff = dateRange[1].getTime() - dateRange[0].getTime();
269
68
  const days = diff / (1000 * 60 * 60 * 24);
270
- if (days <= 31) {
69
+ if (days <= 31 * 3) {
271
70
  return timeFrames_1.TimeFrames.DAY;
272
71
  }
273
72
  if (days <= 7 * 12) {
274
73
  return timeFrames_1.TimeFrames.WEEK;
275
74
  }
276
- if (days <= 366) {
75
+ if (days <= 31 * 12) {
277
76
  return timeFrames_1.TimeFrames.MONTH;
278
77
  }
279
78
  return timeFrames_1.TimeFrames.YEAR;
280
79
  };
281
- const getMetricExplorerDateRangeFilters = (exploreName, dimensionName, [startDate, endDate]) => {
282
- if (!startDate || !endDate) {
283
- return [];
284
- }
285
- const defaultGrain = getGrainForDateRange([startDate, endDate]);
80
+ exports.getGrainForDateRange = getGrainForDateRange;
81
+ const getMetricExplorerDateRangeFilters = (exploreName, dimensionName, dateRange) => {
82
+ const defaultGrain = (0, exports.getGrainForDateRange)(dateRange);
286
83
  const targetFieldId = (0, item_1.getItemId)({
287
84
  table: exploreName,
288
85
  name: (0, exports.getFieldIdForDateDimension)(dimensionName, defaultGrain),
@@ -292,8 +89,75 @@ const getMetricExplorerDateRangeFilters = (exploreName, dimensionName, [startDat
292
89
  id: (0, uuid_1.v4)(),
293
90
  target: { fieldId: targetFieldId },
294
91
  operator: conditionalRule_1.ConditionalOperator.IN_BETWEEN,
295
- values: [startDate, endDate],
92
+ values: dateRange,
296
93
  },
297
94
  ];
298
95
  };
299
96
  exports.getMetricExplorerDateRangeFilters = getMetricExplorerDateRangeFilters;
97
+ const getMetricExplorerDataPoints = (dimension, metric, metricRows) => {
98
+ const dimensionId = (0, item_1.getItemId)(dimension);
99
+ const metricId = (0, item_1.getItemId)(metric);
100
+ const groupByMetricRows = (0, lodash_1.groupBy)(metricRows, (row) => new Date(String(row[dimensionId].value.raw)).toString());
101
+ return Object.keys(groupByMetricRows).map((date) => ({
102
+ date: new Date(date),
103
+ metric: groupByMetricRows[date]?.[0]?.[metricId]?.value.raw ?? null,
104
+ compareMetric: null,
105
+ }));
106
+ };
107
+ exports.getMetricExplorerDataPoints = getMetricExplorerDataPoints;
108
+ const getMetricExplorerDataPointsWithCompare = (dimension, metric, metricRows, compareMetricRows, offsetFunction = exports.oneYearForward) => {
109
+ const dimensionId = (0, item_1.getItemId)(dimension);
110
+ const metricId = (0, item_1.getItemId)(metric);
111
+ const mapDateField = (row) => new Date(String(row[dimensionId].value.raw)).toString();
112
+ const groupByMetricRows = (0, lodash_1.groupBy)(metricRows, (row) => mapDateField(row));
113
+ const groupByCompareMetricRows = (0, lodash_1.groupBy)(compareMetricRows, (row) => mapDateField(row));
114
+ const offsetGroupByCompareMetricRows = (0, lodash_1.mapKeys)(groupByCompareMetricRows, (_, date) => offsetFunction(new Date(date)).toString());
115
+ const dates = new Set([
116
+ ...Object.keys(groupByMetricRows),
117
+ ...Object.keys(offsetGroupByCompareMetricRows),
118
+ ]);
119
+ return Array.from(dates).map((date) => ({
120
+ date: new Date(date),
121
+ metric: groupByMetricRows[date]?.[0]?.[metricId]?.value.raw ?? null,
122
+ compareMetric: offsetGroupByCompareMetricRows[date]?.[0]?.[metricId]?.value.raw ??
123
+ null,
124
+ }));
125
+ };
126
+ exports.getMetricExplorerDataPointsWithCompare = getMetricExplorerDataPointsWithCompare;
127
+ /**
128
+ * Get the date range for a given time interval, based on the current date and the time interval
129
+ * Time grain Year: -> past 5 years (i.e. 5 completed years + this uncompleted year)
130
+ * Time grain Month -> past 12 months (i.e. 12 completed months + this uncompleted month)
131
+ * Time grain Week -> past 12 weeks (i.e. 12 completed weeks + this uncompleted week)
132
+ * Time grain Day -> past 30 days (i.e. 30 completed days + this uncompleted day)
133
+ * @param timeInterval - The time interval
134
+ * @returns The date range
135
+ */
136
+ const getDefaultDateRangeFromInterval = (timeInterval) => {
137
+ const now = (0, dayjs_1.default)();
138
+ switch (timeInterval) {
139
+ case timeFrames_1.TimeFrames.DAY:
140
+ return [
141
+ now.subtract(29, 'day').startOf('day').toDate(),
142
+ now.toDate(),
143
+ ];
144
+ case timeFrames_1.TimeFrames.WEEK:
145
+ return [
146
+ now.subtract(11, 'week').startOf('week').toDate(),
147
+ now.toDate(),
148
+ ];
149
+ case timeFrames_1.TimeFrames.MONTH:
150
+ return [
151
+ now.subtract(11, 'month').startOf('month').toDate(),
152
+ now.toDate(),
153
+ ];
154
+ case timeFrames_1.TimeFrames.YEAR:
155
+ return [
156
+ now.subtract(4, 'year').startOf('year').toDate(),
157
+ now.toDate(),
158
+ ];
159
+ default:
160
+ return assertUnimplementedTimeframe(timeInterval);
161
+ }
162
+ };
163
+ exports.getDefaultDateRangeFromInterval = getDefaultDateRangeFromInterval;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightdash/common",
3
- "version": "0.1396.3",
3
+ "version": "0.1397.1",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [