@visactor/vbi 0.4.19 → 0.4.20
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/builder/adapters/vquery-vseed/build-vquery.d.ts +2 -2
- package/dist/builder/adapters/vquery-vseed/build-vseed.d.ts +2 -2
- package/dist/builder/adapters/vquery-vseed/index.d.ts +3 -3
- package/dist/builder/builder.d.ts +6 -6
- package/dist/builder/features/chart-type/chart-type-builder.d.ts +20 -1
- package/dist/builder/features/chart-type/dimension-encoding.d.ts +4 -0
- package/dist/builder/features/chart-type/measure-encoding.d.ts +4 -0
- package/dist/builder/features/chart-type/reapply-dimension-encodings.d.ts +2 -0
- package/dist/builder/features/chart-type/reapply-measure-encodings.d.ts +2 -0
- package/dist/builder/features/dimensions/dim-builder.d.ts +3 -2
- package/dist/builder/features/dimensions/dim-node-builder.d.ts +32 -1
- package/dist/builder/features/havingFilter/having-builder.d.ts +2 -2
- package/dist/builder/features/measures/mea-builder.d.ts +3 -2
- package/dist/builder/features/measures/mea-node-builder.d.ts +33 -3
- package/dist/builder/features/whereFilter/where-builder.d.ts +2 -2
- package/dist/builder/features/whereFilter/where-node-builder.d.ts +11 -2
- package/dist/builder/index.d.ts +1 -1
- package/dist/builder/modules/build.d.ts +2 -2
- package/dist/builder/modules/index.d.ts +2 -2
- package/dist/builder/modules/is-empty.d.ts +1 -1
- package/dist/index.cjs +1497 -387
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1489 -379
- package/dist/pipeline/vqueryDSL/aggregateMap.d.ts +23 -3
- package/dist/pipeline/vqueryDSL/buildOrderBy.d.ts +2 -0
- package/dist/pipeline/vqueryDSL/index.d.ts +2 -2
- package/dist/pipeline/vqueryDSL/resolveDatePredicate.d.ts +7 -0
- package/dist/pipeline/vqueryDSL/types.d.ts +6 -5
- package/dist/types/builder/VBIInterface.d.ts +5 -4
- package/dist/types/builder/adapter.d.ts +15 -13
- package/dist/types/builder/build-vseed.d.ts +3 -0
- package/dist/types/builder/context.d.ts +2 -2
- package/dist/types/builder/index.d.ts +4 -3
- package/dist/types/builder/observe.d.ts +2 -1
- package/dist/types/connector/query.d.ts +1 -0
- package/dist/types/dsl/dimensions/aggregate.d.ts +15 -0
- package/dist/types/dsl/dimensions/dimensions.d.ts +62 -0
- package/dist/types/dsl/encoding.d.ts +2 -2
- package/dist/types/dsl/havingFilter/having.d.ts +3 -3
- package/dist/types/dsl/index.d.ts +5 -3
- package/dist/types/dsl/measures/measures.d.ts +151 -4
- package/dist/types/dsl/sort.d.ts +13 -0
- package/dist/types/dsl/vbi/vbi.d.ts +90 -5
- package/dist/types/dsl/whereFilter/date.d.ts +95 -0
- package/dist/types/dsl/whereFilter/filters.d.ts +142 -5
- package/dist/utils/filter-guards.d.ts +2 -2
- package/dist/vbi/create-vbi.d.ts +6 -7
- package/dist/vbi/from/from-vbi-dsl-input.d.ts +3 -3
- package/dist/vbi/from/set-base-dsl-fields.d.ts +2 -2
- package/dist/vbi/generate-empty-dsl.d.ts +2 -2
- package/dist/vbi/normalize/types.d.ts +3 -3
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -12,250 +12,6 @@ const getConnector = async (id)=>{
|
|
|
12
12
|
if ('function' == typeof connector) return connector();
|
|
13
13
|
return connector;
|
|
14
14
|
};
|
|
15
|
-
const VBI_TO_VQUERY_AGGR_FUNC_MAP = {
|
|
16
|
-
count: 'count',
|
|
17
|
-
countDistinct: 'count_distinct',
|
|
18
|
-
sum: 'sum',
|
|
19
|
-
avg: 'avg',
|
|
20
|
-
min: 'min',
|
|
21
|
-
max: 'max',
|
|
22
|
-
variance: 'variance',
|
|
23
|
-
variancePop: 'variance_pop',
|
|
24
|
-
stddev: 'stddev',
|
|
25
|
-
median: 'median',
|
|
26
|
-
quantile: 'quantile'
|
|
27
|
-
};
|
|
28
|
-
const mapAggregateForVQuery = (aggregate)=>{
|
|
29
|
-
if (!aggregate) return aggregate;
|
|
30
|
-
const mappedFunc = VBI_TO_VQUERY_AGGR_FUNC_MAP[aggregate.func] ?? aggregate.func;
|
|
31
|
-
return {
|
|
32
|
-
...aggregate,
|
|
33
|
-
func: mappedFunc
|
|
34
|
-
};
|
|
35
|
-
};
|
|
36
|
-
const buildSelect = (queryDSL, context)=>{
|
|
37
|
-
const { vbiDSL } = context;
|
|
38
|
-
const measures = vbiDSL.measures;
|
|
39
|
-
const dimensions = vbiDSL.dimensions;
|
|
40
|
-
const result = {
|
|
41
|
-
...queryDSL
|
|
42
|
-
};
|
|
43
|
-
const measureNodes = measures.filter((measure)=>MeasuresBuilder.isMeasureNode(measure));
|
|
44
|
-
const measureSelects = measureNodes.map((measure)=>({
|
|
45
|
-
field: measure.field,
|
|
46
|
-
alias: measure.alias,
|
|
47
|
-
aggr: mapAggregateForVQuery(measure.aggregate)
|
|
48
|
-
}));
|
|
49
|
-
const dimensionNodes = dimensions.filter((dimension)=>DimensionsBuilder.isDimensionNode(dimension));
|
|
50
|
-
const dimensionSelects = dimensionNodes.map((dimension)=>({
|
|
51
|
-
field: dimension.field,
|
|
52
|
-
alias: dimension.alias
|
|
53
|
-
}));
|
|
54
|
-
result.select = [
|
|
55
|
-
...measureSelects,
|
|
56
|
-
...dimensionSelects
|
|
57
|
-
];
|
|
58
|
-
return result;
|
|
59
|
-
};
|
|
60
|
-
const buildGroupBy = (queryDSL, context)=>{
|
|
61
|
-
const result = {
|
|
62
|
-
...queryDSL
|
|
63
|
-
};
|
|
64
|
-
const { vbiDSL } = context;
|
|
65
|
-
const dimensions = vbiDSL.dimensions;
|
|
66
|
-
const dimensionNodes = dimensions.filter((dimension)=>DimensionsBuilder.isDimensionNode(dimension));
|
|
67
|
-
result.groupBy = dimensionNodes.map((dimension)=>dimension.field);
|
|
68
|
-
return result;
|
|
69
|
-
};
|
|
70
|
-
const buildWhere = (queryDSL, context)=>{
|
|
71
|
-
const { vbiDSL } = context;
|
|
72
|
-
const whereFilter = vbiDSL.whereFilter;
|
|
73
|
-
if (!whereFilter || 0 === whereFilter.conditions.length) return queryDSL;
|
|
74
|
-
const result = {
|
|
75
|
-
...queryDSL
|
|
76
|
-
};
|
|
77
|
-
result.where = mapGroupToCondition(whereFilter);
|
|
78
|
-
return result;
|
|
79
|
-
};
|
|
80
|
-
function isWhereGroup(clause) {
|
|
81
|
-
return 'op' in clause && 'conditions' in clause;
|
|
82
|
-
}
|
|
83
|
-
function mapClauseToCondition(clause) {
|
|
84
|
-
if (isWhereGroup(clause)) return [
|
|
85
|
-
mapGroupToCondition(clause)
|
|
86
|
-
];
|
|
87
|
-
return mapFilterToCondition(clause);
|
|
88
|
-
}
|
|
89
|
-
function mapGroupToCondition(group) {
|
|
90
|
-
return {
|
|
91
|
-
op: group.op,
|
|
92
|
-
conditions: group.conditions.flatMap(mapClauseToCondition)
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
function mapFilterToCondition(filter) {
|
|
96
|
-
if ('between' === filter.op || 'not between' === filter.op) return handleBetweenFilter(filter);
|
|
97
|
-
return handleSimpleFilter(filter);
|
|
98
|
-
}
|
|
99
|
-
function handleBetweenFilter(filter) {
|
|
100
|
-
const value = normalizeBetweenValue(filter.value);
|
|
101
|
-
const lowerCondition = void 0 !== value.min && null !== value.min && '' !== value.min ? {
|
|
102
|
-
field: filter.field,
|
|
103
|
-
op: '<' === value.leftOp ? '>' : '>=',
|
|
104
|
-
value: value.min
|
|
105
|
-
} : void 0;
|
|
106
|
-
const upperCondition = void 0 !== value.max && null !== value.max && '' !== value.max ? {
|
|
107
|
-
field: filter.field,
|
|
108
|
-
op: '<' === value.rightOp ? '<' : '<=',
|
|
109
|
-
value: value.max
|
|
110
|
-
} : void 0;
|
|
111
|
-
if ('not between' === filter.op) {
|
|
112
|
-
const outsideConditions = [
|
|
113
|
-
lowerCondition && invertLowerBound(lowerCondition),
|
|
114
|
-
upperCondition && invertUpperBound(upperCondition)
|
|
115
|
-
].filter(Boolean);
|
|
116
|
-
if (outsideConditions.length <= 1) return outsideConditions;
|
|
117
|
-
return [
|
|
118
|
-
{
|
|
119
|
-
op: 'or',
|
|
120
|
-
conditions: outsideConditions
|
|
121
|
-
}
|
|
122
|
-
];
|
|
123
|
-
}
|
|
124
|
-
return [
|
|
125
|
-
lowerCondition,
|
|
126
|
-
upperCondition
|
|
127
|
-
].filter(Boolean);
|
|
128
|
-
}
|
|
129
|
-
function normalizeBetweenValue(value) {
|
|
130
|
-
if (Array.isArray(value)) return {
|
|
131
|
-
min: value[0],
|
|
132
|
-
max: value[1],
|
|
133
|
-
leftOp: '<=',
|
|
134
|
-
rightOp: '<='
|
|
135
|
-
};
|
|
136
|
-
if ('object' == typeof value && null !== value) return value;
|
|
137
|
-
return {};
|
|
138
|
-
}
|
|
139
|
-
function invertLowerBound(condition) {
|
|
140
|
-
return {
|
|
141
|
-
field: condition.field,
|
|
142
|
-
op: '>' === condition.op ? '<=' : '<',
|
|
143
|
-
value: condition.value
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
function invertUpperBound(condition) {
|
|
147
|
-
return {
|
|
148
|
-
field: condition.field,
|
|
149
|
-
op: '<' === condition.op ? '>=' : '>',
|
|
150
|
-
value: condition.value
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
function handleSimpleFilter(filter) {
|
|
154
|
-
let mappedOp = filter.op ?? '=';
|
|
155
|
-
const value = filter.value;
|
|
156
|
-
if (Array.isArray(value)) {
|
|
157
|
-
if ('=' === mappedOp) mappedOp = 'in';
|
|
158
|
-
if ('!=' === mappedOp) mappedOp = 'not in';
|
|
159
|
-
}
|
|
160
|
-
return [
|
|
161
|
-
{
|
|
162
|
-
field: filter.field,
|
|
163
|
-
op: mappedOp,
|
|
164
|
-
value
|
|
165
|
-
}
|
|
166
|
-
];
|
|
167
|
-
}
|
|
168
|
-
const DEFAULT_HAVING_AGGREGATE = {
|
|
169
|
-
func: 'sum'
|
|
170
|
-
};
|
|
171
|
-
const buildHaving = (queryDSL, context)=>{
|
|
172
|
-
const { vbiDSL } = context;
|
|
173
|
-
const havingFilter = vbiDSL.havingFilter;
|
|
174
|
-
if (!havingFilter || 0 === havingFilter.conditions.length) return queryDSL;
|
|
175
|
-
const result = {
|
|
176
|
-
...queryDSL
|
|
177
|
-
};
|
|
178
|
-
result.having = {
|
|
179
|
-
op: havingFilter.op,
|
|
180
|
-
conditions: havingFilter.conditions.flatMap(buildHaving_mapClauseToCondition)
|
|
181
|
-
};
|
|
182
|
-
return result;
|
|
183
|
-
};
|
|
184
|
-
function isHavingGroup(clause) {
|
|
185
|
-
return 'op' in clause && 'conditions' in clause;
|
|
186
|
-
}
|
|
187
|
-
function buildHaving_mapClauseToCondition(clause) {
|
|
188
|
-
if (isHavingGroup(clause)) return [
|
|
189
|
-
buildHaving_mapGroupToCondition(clause)
|
|
190
|
-
];
|
|
191
|
-
return buildHaving_mapFilterToCondition(clause);
|
|
192
|
-
}
|
|
193
|
-
function buildHaving_mapGroupToCondition(group) {
|
|
194
|
-
return {
|
|
195
|
-
op: group.op,
|
|
196
|
-
conditions: group.conditions.flatMap(buildHaving_mapClauseToCondition)
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
function buildHaving_mapFilterToCondition(filter) {
|
|
200
|
-
const mappedOp = normalizeOperator(filter.op, filter.value);
|
|
201
|
-
const aggregate = mapAggregateForVQuery(filter.aggregate ?? DEFAULT_HAVING_AGGREGATE);
|
|
202
|
-
return [
|
|
203
|
-
{
|
|
204
|
-
field: filter.field,
|
|
205
|
-
aggr: aggregate,
|
|
206
|
-
op: mappedOp,
|
|
207
|
-
value: filter.value
|
|
208
|
-
}
|
|
209
|
-
];
|
|
210
|
-
}
|
|
211
|
-
function normalizeOperator(op, value) {
|
|
212
|
-
let mappedOp = op ?? '=';
|
|
213
|
-
if (Array.isArray(value)) {
|
|
214
|
-
if ('=' === mappedOp) mappedOp = 'in';
|
|
215
|
-
if ('!=' === mappedOp) mappedOp = 'not in';
|
|
216
|
-
}
|
|
217
|
-
return mappedOp;
|
|
218
|
-
}
|
|
219
|
-
const buildLimit = (queryDSL, context)=>{
|
|
220
|
-
const result = {
|
|
221
|
-
...queryDSL
|
|
222
|
-
};
|
|
223
|
-
const limit = context.vbiDSL.limit ?? 1000;
|
|
224
|
-
result.limit = limit;
|
|
225
|
-
return result;
|
|
226
|
-
};
|
|
227
|
-
const buildVQuery = (vbiDSL, builder)=>{
|
|
228
|
-
const wrapper = (processor)=>(queryDSL)=>processor(queryDSL, {
|
|
229
|
-
vbiDSL,
|
|
230
|
-
builder
|
|
231
|
-
});
|
|
232
|
-
return pipe({}, wrapper(buildSelect), wrapper(buildGroupBy), wrapper(buildWhere), wrapper(buildHaving), wrapper(buildLimit));
|
|
233
|
-
};
|
|
234
|
-
const buildVQueryDSL = ({ vbiDSL, builder })=>buildVQuery(vbiDSL, builder);
|
|
235
|
-
const buildVSeedDSL = async ({ vbiDSL, queryDSL })=>{
|
|
236
|
-
const connectorId = vbiDSL.connectorId;
|
|
237
|
-
const connector = await getConnector(connectorId);
|
|
238
|
-
const schema = await connector.discoverSchema();
|
|
239
|
-
const queryResult = await connector.query({
|
|
240
|
-
queryDSL,
|
|
241
|
-
schema,
|
|
242
|
-
connectorId
|
|
243
|
-
});
|
|
244
|
-
return {
|
|
245
|
-
chartType: vbiDSL.chartType,
|
|
246
|
-
dataset: queryResult.dataset,
|
|
247
|
-
theme: vbiDSL.theme,
|
|
248
|
-
locale: vbiDSL.locale
|
|
249
|
-
};
|
|
250
|
-
};
|
|
251
|
-
const defaultVBIBuilderAdapters = {
|
|
252
|
-
buildVQuery: buildVQueryDSL,
|
|
253
|
-
buildVSeed: buildVSeedDSL
|
|
254
|
-
};
|
|
255
|
-
const resolveVBIBuilderAdapters = (adapters)=>({
|
|
256
|
-
buildVQuery: adapters?.buildVQuery ?? defaultVBIBuilderAdapters.buildVQuery,
|
|
257
|
-
buildVSeed: adapters?.buildVSeed ?? defaultVBIBuilderAdapters.buildVSeed
|
|
258
|
-
});
|
|
259
15
|
class MeasureNodeBuilder {
|
|
260
16
|
yMap;
|
|
261
17
|
constructor(yMap){
|
|
@@ -267,6 +23,12 @@ class MeasureNodeBuilder {
|
|
|
267
23
|
getField() {
|
|
268
24
|
return this.yMap.get('field');
|
|
269
25
|
}
|
|
26
|
+
getEncoding() {
|
|
27
|
+
return this.yMap.get('encoding');
|
|
28
|
+
}
|
|
29
|
+
getSort() {
|
|
30
|
+
return this.yMap.get('sort');
|
|
31
|
+
}
|
|
270
32
|
setAlias(alias) {
|
|
271
33
|
this.yMap.set('alias', alias);
|
|
272
34
|
return this;
|
|
@@ -275,10 +37,29 @@ class MeasureNodeBuilder {
|
|
|
275
37
|
this.yMap.set('encoding', encoding);
|
|
276
38
|
return this;
|
|
277
39
|
}
|
|
40
|
+
setSort(sort) {
|
|
41
|
+
this.yMap.set('sort', sort);
|
|
42
|
+
return this;
|
|
43
|
+
}
|
|
278
44
|
setAggregate(aggregate) {
|
|
279
45
|
this.yMap.set('aggregate', aggregate);
|
|
280
46
|
return this;
|
|
281
47
|
}
|
|
48
|
+
setFormat(format) {
|
|
49
|
+
this.yMap.set('format', format);
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
getFormat() {
|
|
53
|
+
return this.yMap.get('format');
|
|
54
|
+
}
|
|
55
|
+
clearFormat() {
|
|
56
|
+
this.yMap.delete('format');
|
|
57
|
+
return this;
|
|
58
|
+
}
|
|
59
|
+
clearSort() {
|
|
60
|
+
this.yMap.delete('sort');
|
|
61
|
+
return this;
|
|
62
|
+
}
|
|
282
63
|
toJSON() {
|
|
283
64
|
return this.yMap.toJSON();
|
|
284
65
|
}
|
|
@@ -311,9 +92,369 @@ const normalizeMeasureNodeIds = (measures)=>{
|
|
|
311
92
|
});
|
|
312
93
|
};
|
|
313
94
|
const locateMeasureIndexById = (measures, measureId)=>measures.toArray().findIndex((item)=>item.get('id') === measureId);
|
|
95
|
+
const MEASURE_ENCODING_SUPPORT = {
|
|
96
|
+
table: [
|
|
97
|
+
'column'
|
|
98
|
+
],
|
|
99
|
+
pivotTable: [
|
|
100
|
+
'detail'
|
|
101
|
+
],
|
|
102
|
+
column: [
|
|
103
|
+
'yAxis',
|
|
104
|
+
'detail',
|
|
105
|
+
'color',
|
|
106
|
+
'label',
|
|
107
|
+
'tooltip'
|
|
108
|
+
],
|
|
109
|
+
columnParallel: [
|
|
110
|
+
'yAxis',
|
|
111
|
+
'detail',
|
|
112
|
+
'color',
|
|
113
|
+
'label',
|
|
114
|
+
'tooltip'
|
|
115
|
+
],
|
|
116
|
+
columnPercent: [
|
|
117
|
+
'yAxis',
|
|
118
|
+
'detail',
|
|
119
|
+
'color',
|
|
120
|
+
'label',
|
|
121
|
+
'tooltip'
|
|
122
|
+
],
|
|
123
|
+
line: [
|
|
124
|
+
'yAxis',
|
|
125
|
+
'detail',
|
|
126
|
+
'color',
|
|
127
|
+
'label',
|
|
128
|
+
'tooltip'
|
|
129
|
+
],
|
|
130
|
+
area: [
|
|
131
|
+
'yAxis',
|
|
132
|
+
'detail',
|
|
133
|
+
'color',
|
|
134
|
+
'label',
|
|
135
|
+
'tooltip'
|
|
136
|
+
],
|
|
137
|
+
areaPercent: [
|
|
138
|
+
'yAxis',
|
|
139
|
+
'detail',
|
|
140
|
+
'color',
|
|
141
|
+
'label',
|
|
142
|
+
'tooltip'
|
|
143
|
+
],
|
|
144
|
+
bar: [
|
|
145
|
+
'xAxis',
|
|
146
|
+
'detail',
|
|
147
|
+
'color',
|
|
148
|
+
'label',
|
|
149
|
+
'tooltip'
|
|
150
|
+
],
|
|
151
|
+
barParallel: [
|
|
152
|
+
'xAxis',
|
|
153
|
+
'detail',
|
|
154
|
+
'color',
|
|
155
|
+
'label',
|
|
156
|
+
'tooltip'
|
|
157
|
+
],
|
|
158
|
+
barPercent: [
|
|
159
|
+
'xAxis',
|
|
160
|
+
'detail',
|
|
161
|
+
'color',
|
|
162
|
+
'label',
|
|
163
|
+
'tooltip'
|
|
164
|
+
],
|
|
165
|
+
dualAxis: [
|
|
166
|
+
'primaryYAxis',
|
|
167
|
+
'secondaryYAxis',
|
|
168
|
+
'color',
|
|
169
|
+
'label',
|
|
170
|
+
'tooltip'
|
|
171
|
+
],
|
|
172
|
+
scatter: [
|
|
173
|
+
'xAxis',
|
|
174
|
+
'yAxis',
|
|
175
|
+
'size',
|
|
176
|
+
'color',
|
|
177
|
+
'label',
|
|
178
|
+
'tooltip'
|
|
179
|
+
],
|
|
180
|
+
pie: [
|
|
181
|
+
'angle',
|
|
182
|
+
'detail',
|
|
183
|
+
'color',
|
|
184
|
+
'label',
|
|
185
|
+
'tooltip'
|
|
186
|
+
],
|
|
187
|
+
donut: [
|
|
188
|
+
'angle',
|
|
189
|
+
'detail',
|
|
190
|
+
'color',
|
|
191
|
+
'label',
|
|
192
|
+
'tooltip'
|
|
193
|
+
],
|
|
194
|
+
rose: [
|
|
195
|
+
'radius',
|
|
196
|
+
'detail',
|
|
197
|
+
'color',
|
|
198
|
+
'label',
|
|
199
|
+
'tooltip'
|
|
200
|
+
],
|
|
201
|
+
roseParallel: [
|
|
202
|
+
'radius',
|
|
203
|
+
'detail',
|
|
204
|
+
'color',
|
|
205
|
+
'label',
|
|
206
|
+
'tooltip'
|
|
207
|
+
],
|
|
208
|
+
radar: [
|
|
209
|
+
'radius',
|
|
210
|
+
'detail',
|
|
211
|
+
'color',
|
|
212
|
+
'label',
|
|
213
|
+
'tooltip'
|
|
214
|
+
],
|
|
215
|
+
funnel: [
|
|
216
|
+
'size',
|
|
217
|
+
'detail',
|
|
218
|
+
'color',
|
|
219
|
+
'label',
|
|
220
|
+
'tooltip'
|
|
221
|
+
],
|
|
222
|
+
heatmap: [
|
|
223
|
+
'color',
|
|
224
|
+
'detail',
|
|
225
|
+
'label',
|
|
226
|
+
'tooltip'
|
|
227
|
+
],
|
|
228
|
+
histogram: [
|
|
229
|
+
'value',
|
|
230
|
+
'x0',
|
|
231
|
+
'x1',
|
|
232
|
+
'yAxis',
|
|
233
|
+
'detail',
|
|
234
|
+
'color',
|
|
235
|
+
'label',
|
|
236
|
+
'tooltip'
|
|
237
|
+
],
|
|
238
|
+
boxplot: [
|
|
239
|
+
'value',
|
|
240
|
+
'q1',
|
|
241
|
+
'q3',
|
|
242
|
+
'min',
|
|
243
|
+
'max',
|
|
244
|
+
'median',
|
|
245
|
+
'outliers',
|
|
246
|
+
'color',
|
|
247
|
+
'label',
|
|
248
|
+
'tooltip'
|
|
249
|
+
],
|
|
250
|
+
treeMap: [
|
|
251
|
+
'size',
|
|
252
|
+
'detail',
|
|
253
|
+
'color',
|
|
254
|
+
'label',
|
|
255
|
+
'tooltip'
|
|
256
|
+
],
|
|
257
|
+
sunburst: [
|
|
258
|
+
'size',
|
|
259
|
+
'detail',
|
|
260
|
+
'color',
|
|
261
|
+
'label',
|
|
262
|
+
'tooltip'
|
|
263
|
+
],
|
|
264
|
+
circlePacking: [
|
|
265
|
+
'size',
|
|
266
|
+
'detail',
|
|
267
|
+
'color',
|
|
268
|
+
'label',
|
|
269
|
+
'tooltip'
|
|
270
|
+
],
|
|
271
|
+
raceBar: [
|
|
272
|
+
'xAxis',
|
|
273
|
+
'detail',
|
|
274
|
+
'color',
|
|
275
|
+
'label',
|
|
276
|
+
'tooltip'
|
|
277
|
+
],
|
|
278
|
+
raceColumn: [
|
|
279
|
+
'yAxis',
|
|
280
|
+
'detail',
|
|
281
|
+
'color',
|
|
282
|
+
'label',
|
|
283
|
+
'tooltip'
|
|
284
|
+
],
|
|
285
|
+
raceLine: [
|
|
286
|
+
'yAxis',
|
|
287
|
+
'detail',
|
|
288
|
+
'color',
|
|
289
|
+
'label',
|
|
290
|
+
'tooltip'
|
|
291
|
+
],
|
|
292
|
+
raceScatter: [
|
|
293
|
+
'xAxis',
|
|
294
|
+
'yAxis',
|
|
295
|
+
'size',
|
|
296
|
+
'color',
|
|
297
|
+
'label',
|
|
298
|
+
'tooltip'
|
|
299
|
+
],
|
|
300
|
+
racePie: [
|
|
301
|
+
'angle',
|
|
302
|
+
'detail',
|
|
303
|
+
'color',
|
|
304
|
+
'label',
|
|
305
|
+
'tooltip'
|
|
306
|
+
],
|
|
307
|
+
raceDonut: [
|
|
308
|
+
'angle',
|
|
309
|
+
'detail',
|
|
310
|
+
'color',
|
|
311
|
+
'label',
|
|
312
|
+
'tooltip'
|
|
313
|
+
]
|
|
314
|
+
};
|
|
315
|
+
const repeatEncoding = (first, rest = first)=>(index)=>0 === index ? first : rest;
|
|
316
|
+
const STRATEGY_BY_CHART_TYPE = {
|
|
317
|
+
[ChartTypeEnum.Table]: {
|
|
318
|
+
supported: MEASURE_ENCODING_SUPPORT.table,
|
|
319
|
+
recommend: repeatEncoding('column')
|
|
320
|
+
},
|
|
321
|
+
[ChartTypeEnum.PivotTable]: {
|
|
322
|
+
supported: MEASURE_ENCODING_SUPPORT.pivotTable,
|
|
323
|
+
recommend: repeatEncoding('detail')
|
|
324
|
+
},
|
|
325
|
+
[ChartTypeEnum.Column]: {
|
|
326
|
+
supported: MEASURE_ENCODING_SUPPORT.column,
|
|
327
|
+
recommend: repeatEncoding('yAxis')
|
|
328
|
+
},
|
|
329
|
+
[ChartTypeEnum.ColumnParallel]: {
|
|
330
|
+
supported: MEASURE_ENCODING_SUPPORT.columnParallel,
|
|
331
|
+
recommend: repeatEncoding('yAxis')
|
|
332
|
+
},
|
|
333
|
+
[ChartTypeEnum.ColumnPercent]: {
|
|
334
|
+
supported: MEASURE_ENCODING_SUPPORT.columnPercent,
|
|
335
|
+
recommend: repeatEncoding('yAxis')
|
|
336
|
+
},
|
|
337
|
+
[ChartTypeEnum.Line]: {
|
|
338
|
+
supported: MEASURE_ENCODING_SUPPORT.line,
|
|
339
|
+
recommend: repeatEncoding('yAxis')
|
|
340
|
+
},
|
|
341
|
+
[ChartTypeEnum.Area]: {
|
|
342
|
+
supported: MEASURE_ENCODING_SUPPORT.area,
|
|
343
|
+
recommend: repeatEncoding('yAxis')
|
|
344
|
+
},
|
|
345
|
+
[ChartTypeEnum.AreaPercent]: {
|
|
346
|
+
supported: MEASURE_ENCODING_SUPPORT.areaPercent,
|
|
347
|
+
recommend: repeatEncoding('yAxis')
|
|
348
|
+
},
|
|
349
|
+
[ChartTypeEnum.Bar]: {
|
|
350
|
+
supported: MEASURE_ENCODING_SUPPORT.bar,
|
|
351
|
+
recommend: repeatEncoding('xAxis')
|
|
352
|
+
},
|
|
353
|
+
[ChartTypeEnum.BarParallel]: {
|
|
354
|
+
supported: MEASURE_ENCODING_SUPPORT.barParallel,
|
|
355
|
+
recommend: repeatEncoding('xAxis')
|
|
356
|
+
},
|
|
357
|
+
[ChartTypeEnum.BarPercent]: {
|
|
358
|
+
supported: MEASURE_ENCODING_SUPPORT.barPercent,
|
|
359
|
+
recommend: repeatEncoding('xAxis')
|
|
360
|
+
},
|
|
361
|
+
[ChartTypeEnum.DualAxis]: {
|
|
362
|
+
supported: MEASURE_ENCODING_SUPPORT.dualAxis,
|
|
363
|
+
recommend: (index)=>0 === index ? 'primaryYAxis' : 'secondaryYAxis'
|
|
364
|
+
},
|
|
365
|
+
[ChartTypeEnum.Scatter]: {
|
|
366
|
+
supported: MEASURE_ENCODING_SUPPORT.scatter,
|
|
367
|
+
recommend: (index)=>0 === index ? 'xAxis' : 'yAxis'
|
|
368
|
+
},
|
|
369
|
+
[ChartTypeEnum.Pie]: {
|
|
370
|
+
supported: MEASURE_ENCODING_SUPPORT.pie,
|
|
371
|
+
recommend: repeatEncoding('angle')
|
|
372
|
+
},
|
|
373
|
+
[ChartTypeEnum.Donut]: {
|
|
374
|
+
supported: MEASURE_ENCODING_SUPPORT.donut,
|
|
375
|
+
recommend: repeatEncoding('angle')
|
|
376
|
+
},
|
|
377
|
+
[ChartTypeEnum.Rose]: {
|
|
378
|
+
supported: MEASURE_ENCODING_SUPPORT.rose,
|
|
379
|
+
recommend: repeatEncoding('radius')
|
|
380
|
+
},
|
|
381
|
+
[ChartTypeEnum.RoseParallel]: {
|
|
382
|
+
supported: MEASURE_ENCODING_SUPPORT.roseParallel,
|
|
383
|
+
recommend: repeatEncoding('radius')
|
|
384
|
+
},
|
|
385
|
+
[ChartTypeEnum.Radar]: {
|
|
386
|
+
supported: MEASURE_ENCODING_SUPPORT.radar,
|
|
387
|
+
recommend: repeatEncoding('radius')
|
|
388
|
+
},
|
|
389
|
+
[ChartTypeEnum.Funnel]: {
|
|
390
|
+
supported: MEASURE_ENCODING_SUPPORT.funnel,
|
|
391
|
+
recommend: repeatEncoding('size')
|
|
392
|
+
},
|
|
393
|
+
[ChartTypeEnum.Heatmap]: {
|
|
394
|
+
supported: MEASURE_ENCODING_SUPPORT.heatmap,
|
|
395
|
+
recommend: repeatEncoding('color')
|
|
396
|
+
},
|
|
397
|
+
[ChartTypeEnum.Histogram]: {
|
|
398
|
+
supported: MEASURE_ENCODING_SUPPORT.histogram,
|
|
399
|
+
recommend: repeatEncoding('value')
|
|
400
|
+
},
|
|
401
|
+
[ChartTypeEnum.Boxplot]: {
|
|
402
|
+
supported: MEASURE_ENCODING_SUPPORT.boxplot,
|
|
403
|
+
recommend: repeatEncoding('value')
|
|
404
|
+
},
|
|
405
|
+
[ChartTypeEnum.TreeMap]: {
|
|
406
|
+
supported: MEASURE_ENCODING_SUPPORT.treeMap,
|
|
407
|
+
recommend: repeatEncoding('size')
|
|
408
|
+
},
|
|
409
|
+
[ChartTypeEnum.Sunburst]: {
|
|
410
|
+
supported: MEASURE_ENCODING_SUPPORT.sunburst,
|
|
411
|
+
recommend: repeatEncoding('size')
|
|
412
|
+
},
|
|
413
|
+
[ChartTypeEnum.CirclePacking]: {
|
|
414
|
+
supported: MEASURE_ENCODING_SUPPORT.circlePacking,
|
|
415
|
+
recommend: repeatEncoding('size')
|
|
416
|
+
},
|
|
417
|
+
[ChartTypeEnum.RaceBar]: {
|
|
418
|
+
supported: MEASURE_ENCODING_SUPPORT.raceBar,
|
|
419
|
+
recommend: repeatEncoding('xAxis')
|
|
420
|
+
},
|
|
421
|
+
[ChartTypeEnum.RaceColumn]: {
|
|
422
|
+
supported: MEASURE_ENCODING_SUPPORT.raceColumn,
|
|
423
|
+
recommend: repeatEncoding('yAxis')
|
|
424
|
+
},
|
|
425
|
+
[ChartTypeEnum.RaceLine]: {
|
|
426
|
+
supported: MEASURE_ENCODING_SUPPORT.raceLine,
|
|
427
|
+
recommend: repeatEncoding('yAxis')
|
|
428
|
+
},
|
|
429
|
+
[ChartTypeEnum.RaceScatter]: {
|
|
430
|
+
supported: MEASURE_ENCODING_SUPPORT.raceScatter,
|
|
431
|
+
recommend: (index)=>0 === index ? 'xAxis' : 'yAxis'
|
|
432
|
+
},
|
|
433
|
+
[ChartTypeEnum.RacePie]: {
|
|
434
|
+
supported: MEASURE_ENCODING_SUPPORT.racePie,
|
|
435
|
+
recommend: repeatEncoding('angle')
|
|
436
|
+
},
|
|
437
|
+
[ChartTypeEnum.RaceDonut]: {
|
|
438
|
+
supported: MEASURE_ENCODING_SUPPORT.raceDonut,
|
|
439
|
+
recommend: repeatEncoding('angle')
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
const DEFAULT_STRATEGY = STRATEGY_BY_CHART_TYPE[ChartTypeEnum.Table];
|
|
443
|
+
const getSupportedMeasureEncodingsForChartType = (chartType)=>[
|
|
444
|
+
...(STRATEGY_BY_CHART_TYPE[chartType] || DEFAULT_STRATEGY).supported
|
|
445
|
+
];
|
|
446
|
+
const getRecommendedMeasureEncodingsForChartType = (chartType, measureCount)=>{
|
|
447
|
+
const strategy = STRATEGY_BY_CHART_TYPE[chartType] || DEFAULT_STRATEGY;
|
|
448
|
+
if (measureCount <= 0) return [];
|
|
449
|
+
return Array.from({
|
|
450
|
+
length: measureCount
|
|
451
|
+
}, (_, index)=>strategy.recommend(index));
|
|
452
|
+
};
|
|
314
453
|
class MeasuresBuilder {
|
|
454
|
+
doc;
|
|
315
455
|
dsl;
|
|
316
456
|
constructor(doc, dsl){
|
|
457
|
+
this.doc = doc;
|
|
317
458
|
this.dsl = dsl;
|
|
318
459
|
doc.transact(()=>{
|
|
319
460
|
const measures = getOrCreateMeasures(this.dsl);
|
|
@@ -321,38 +462,47 @@ class MeasuresBuilder {
|
|
|
321
462
|
});
|
|
322
463
|
}
|
|
323
464
|
add(field, callback) {
|
|
465
|
+
const measures = getOrCreateMeasures(this.dsl);
|
|
466
|
+
const chartType = this.dsl.get('chartType') || 'table';
|
|
467
|
+
const [encoding] = getRecommendedMeasureEncodingsForChartType(chartType, measures.length + 1).slice(-1);
|
|
324
468
|
const measure = {
|
|
325
469
|
id: id_id.uuid(),
|
|
326
470
|
alias: field,
|
|
327
471
|
field,
|
|
328
|
-
encoding
|
|
472
|
+
encoding,
|
|
329
473
|
aggregate: {
|
|
330
474
|
func: 'sum'
|
|
331
475
|
}
|
|
332
476
|
};
|
|
333
477
|
const yMap = new external_yjs_Map();
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
478
|
+
this.doc.transact(()=>{
|
|
479
|
+
for (const [key, value] of Object.entries(measure))yMap.set(key, value);
|
|
480
|
+
const measures = getOrCreateMeasures(this.dsl);
|
|
481
|
+
measures.push([
|
|
482
|
+
yMap
|
|
483
|
+
]);
|
|
484
|
+
const node = new MeasureNodeBuilder(yMap);
|
|
485
|
+
callback(node);
|
|
486
|
+
});
|
|
341
487
|
return this;
|
|
342
488
|
}
|
|
343
489
|
remove(id) {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
490
|
+
this.doc.transact(()=>{
|
|
491
|
+
const measures = getOrCreateMeasures(this.dsl);
|
|
492
|
+
const index = locateMeasureIndexById(measures, id);
|
|
493
|
+
if (-1 !== index) measures.delete(index, 1);
|
|
494
|
+
});
|
|
347
495
|
return this;
|
|
348
496
|
}
|
|
349
497
|
update(id, callback) {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
498
|
+
this.doc.transact(()=>{
|
|
499
|
+
const measures = getOrCreateMeasures(this.dsl);
|
|
500
|
+
const index = locateMeasureIndexById(measures, id);
|
|
501
|
+
if (-1 === index) throw new Error(`Measure with id "${id}" not found`);
|
|
502
|
+
const measureYMap = measures.get(index);
|
|
503
|
+
const node = new MeasureNodeBuilder(measureYMap);
|
|
504
|
+
callback(node);
|
|
505
|
+
});
|
|
356
506
|
return this;
|
|
357
507
|
}
|
|
358
508
|
find(predicate) {
|
|
@@ -373,9 +523,9 @@ class MeasuresBuilder {
|
|
|
373
523
|
}
|
|
374
524
|
observe(callback) {
|
|
375
525
|
const measures = getOrCreateMeasures(this.dsl);
|
|
376
|
-
measures.
|
|
526
|
+
measures.observeDeep(callback);
|
|
377
527
|
return ()=>{
|
|
378
|
-
measures.
|
|
528
|
+
measures.unobserveDeep(callback);
|
|
379
529
|
};
|
|
380
530
|
}
|
|
381
531
|
static isMeasureNode(node) {
|
|
@@ -396,10 +546,36 @@ class DimensionNodeBuilder {
|
|
|
396
546
|
getField() {
|
|
397
547
|
return this.yMap.get('field');
|
|
398
548
|
}
|
|
399
|
-
|
|
549
|
+
getEncoding() {
|
|
550
|
+
return this.yMap.get('encoding');
|
|
551
|
+
}
|
|
552
|
+
getSort() {
|
|
553
|
+
return this.yMap.get('sort');
|
|
554
|
+
}
|
|
555
|
+
setAlias(alias) {
|
|
400
556
|
this.yMap.set('alias', alias);
|
|
401
557
|
return this;
|
|
402
558
|
}
|
|
559
|
+
setEncoding(encoding) {
|
|
560
|
+
this.yMap.set('encoding', encoding);
|
|
561
|
+
return this;
|
|
562
|
+
}
|
|
563
|
+
setSort(sort) {
|
|
564
|
+
this.yMap.set('sort', sort);
|
|
565
|
+
return this;
|
|
566
|
+
}
|
|
567
|
+
setAggregate(aggregate) {
|
|
568
|
+
this.yMap.set('aggregate', aggregate);
|
|
569
|
+
return this;
|
|
570
|
+
}
|
|
571
|
+
clearAggregate() {
|
|
572
|
+
this.yMap.delete('aggregate');
|
|
573
|
+
return this;
|
|
574
|
+
}
|
|
575
|
+
clearSort() {
|
|
576
|
+
this.yMap.delete('sort');
|
|
577
|
+
return this;
|
|
578
|
+
}
|
|
403
579
|
toJSON() {
|
|
404
580
|
return this.yMap.toJSON();
|
|
405
581
|
}
|
|
@@ -417,9 +593,423 @@ const normalizeDimensionNodeIds = (dimensions)=>{
|
|
|
417
593
|
});
|
|
418
594
|
};
|
|
419
595
|
const locateDimensionIndexById = (dimensions, dimensionId)=>dimensions.toArray().findIndex((item)=>item.get('id') === dimensionId);
|
|
596
|
+
const DIMENSION_ENCODING_SUPPORT = {
|
|
597
|
+
table: [
|
|
598
|
+
'column'
|
|
599
|
+
],
|
|
600
|
+
pivotTable: [
|
|
601
|
+
'row',
|
|
602
|
+
'column'
|
|
603
|
+
],
|
|
604
|
+
column: [
|
|
605
|
+
'xAxis',
|
|
606
|
+
'color',
|
|
607
|
+
'detail',
|
|
608
|
+
'tooltip',
|
|
609
|
+
'label',
|
|
610
|
+
'row',
|
|
611
|
+
'column'
|
|
612
|
+
],
|
|
613
|
+
columnParallel: [
|
|
614
|
+
'xAxis',
|
|
615
|
+
'color',
|
|
616
|
+
'detail',
|
|
617
|
+
'tooltip',
|
|
618
|
+
'label',
|
|
619
|
+
'row',
|
|
620
|
+
'column'
|
|
621
|
+
],
|
|
622
|
+
columnPercent: [
|
|
623
|
+
'xAxis',
|
|
624
|
+
'color',
|
|
625
|
+
'detail',
|
|
626
|
+
'tooltip',
|
|
627
|
+
'label',
|
|
628
|
+
'row',
|
|
629
|
+
'column'
|
|
630
|
+
],
|
|
631
|
+
line: [
|
|
632
|
+
'xAxis',
|
|
633
|
+
'color',
|
|
634
|
+
'detail',
|
|
635
|
+
'tooltip',
|
|
636
|
+
'label',
|
|
637
|
+
'row',
|
|
638
|
+
'column'
|
|
639
|
+
],
|
|
640
|
+
area: [
|
|
641
|
+
'xAxis',
|
|
642
|
+
'color',
|
|
643
|
+
'detail',
|
|
644
|
+
'tooltip',
|
|
645
|
+
'label',
|
|
646
|
+
'row',
|
|
647
|
+
'column'
|
|
648
|
+
],
|
|
649
|
+
areaPercent: [
|
|
650
|
+
'xAxis',
|
|
651
|
+
'color',
|
|
652
|
+
'detail',
|
|
653
|
+
'tooltip',
|
|
654
|
+
'label',
|
|
655
|
+
'row',
|
|
656
|
+
'column'
|
|
657
|
+
],
|
|
658
|
+
dualAxis: [
|
|
659
|
+
'xAxis',
|
|
660
|
+
'color',
|
|
661
|
+
'detail',
|
|
662
|
+
'tooltip',
|
|
663
|
+
'label',
|
|
664
|
+
'row',
|
|
665
|
+
'column'
|
|
666
|
+
],
|
|
667
|
+
bar: [
|
|
668
|
+
'yAxis',
|
|
669
|
+
'color',
|
|
670
|
+
'detail',
|
|
671
|
+
'tooltip',
|
|
672
|
+
'label',
|
|
673
|
+
'row',
|
|
674
|
+
'column'
|
|
675
|
+
],
|
|
676
|
+
barParallel: [
|
|
677
|
+
'yAxis',
|
|
678
|
+
'color',
|
|
679
|
+
'detail',
|
|
680
|
+
'tooltip',
|
|
681
|
+
'label',
|
|
682
|
+
'row',
|
|
683
|
+
'column'
|
|
684
|
+
],
|
|
685
|
+
barPercent: [
|
|
686
|
+
'yAxis',
|
|
687
|
+
'color',
|
|
688
|
+
'detail',
|
|
689
|
+
'tooltip',
|
|
690
|
+
'label',
|
|
691
|
+
'row',
|
|
692
|
+
'column'
|
|
693
|
+
],
|
|
694
|
+
pie: [
|
|
695
|
+
'color',
|
|
696
|
+
'detail',
|
|
697
|
+
'tooltip',
|
|
698
|
+
'label',
|
|
699
|
+
'row',
|
|
700
|
+
'column'
|
|
701
|
+
],
|
|
702
|
+
donut: [
|
|
703
|
+
'color',
|
|
704
|
+
'detail',
|
|
705
|
+
'tooltip',
|
|
706
|
+
'label',
|
|
707
|
+
'row',
|
|
708
|
+
'column'
|
|
709
|
+
],
|
|
710
|
+
funnel: [
|
|
711
|
+
'color',
|
|
712
|
+
'detail',
|
|
713
|
+
'tooltip',
|
|
714
|
+
'label',
|
|
715
|
+
'row',
|
|
716
|
+
'column'
|
|
717
|
+
],
|
|
718
|
+
scatter: [
|
|
719
|
+
'color',
|
|
720
|
+
'detail',
|
|
721
|
+
'tooltip',
|
|
722
|
+
'label',
|
|
723
|
+
'row',
|
|
724
|
+
'column'
|
|
725
|
+
],
|
|
726
|
+
rose: [
|
|
727
|
+
'angle',
|
|
728
|
+
'color',
|
|
729
|
+
'detail',
|
|
730
|
+
'tooltip',
|
|
731
|
+
'label',
|
|
732
|
+
'row',
|
|
733
|
+
'column'
|
|
734
|
+
],
|
|
735
|
+
roseParallel: [
|
|
736
|
+
'angle',
|
|
737
|
+
'color',
|
|
738
|
+
'detail',
|
|
739
|
+
'tooltip',
|
|
740
|
+
'label',
|
|
741
|
+
'row',
|
|
742
|
+
'column'
|
|
743
|
+
],
|
|
744
|
+
radar: [
|
|
745
|
+
'angle',
|
|
746
|
+
'color',
|
|
747
|
+
'detail',
|
|
748
|
+
'tooltip',
|
|
749
|
+
'label',
|
|
750
|
+
'row',
|
|
751
|
+
'column'
|
|
752
|
+
],
|
|
753
|
+
heatmap: [
|
|
754
|
+
'xAxis',
|
|
755
|
+
'yAxis',
|
|
756
|
+
'color',
|
|
757
|
+
'detail',
|
|
758
|
+
'tooltip',
|
|
759
|
+
'label',
|
|
760
|
+
'row',
|
|
761
|
+
'column'
|
|
762
|
+
],
|
|
763
|
+
boxplot: [
|
|
764
|
+
'xAxis',
|
|
765
|
+
'color',
|
|
766
|
+
'tooltip',
|
|
767
|
+
'label',
|
|
768
|
+
'row',
|
|
769
|
+
'column'
|
|
770
|
+
],
|
|
771
|
+
histogram: [
|
|
772
|
+
'color',
|
|
773
|
+
'detail',
|
|
774
|
+
'tooltip',
|
|
775
|
+
'label',
|
|
776
|
+
'row',
|
|
777
|
+
'column'
|
|
778
|
+
],
|
|
779
|
+
treeMap: [
|
|
780
|
+
'hierarchy',
|
|
781
|
+
'detail',
|
|
782
|
+
'tooltip',
|
|
783
|
+
'label',
|
|
784
|
+
'row',
|
|
785
|
+
'column'
|
|
786
|
+
],
|
|
787
|
+
sunburst: [
|
|
788
|
+
'hierarchy',
|
|
789
|
+
'detail',
|
|
790
|
+
'tooltip',
|
|
791
|
+
'label',
|
|
792
|
+
'row',
|
|
793
|
+
'column'
|
|
794
|
+
],
|
|
795
|
+
circlePacking: [
|
|
796
|
+
'hierarchy',
|
|
797
|
+
'detail',
|
|
798
|
+
'tooltip',
|
|
799
|
+
'label',
|
|
800
|
+
'row',
|
|
801
|
+
'column'
|
|
802
|
+
],
|
|
803
|
+
raceBar: [
|
|
804
|
+
'player',
|
|
805
|
+
'yAxis',
|
|
806
|
+
'color',
|
|
807
|
+
'detail',
|
|
808
|
+
'tooltip',
|
|
809
|
+
'label',
|
|
810
|
+
'row',
|
|
811
|
+
'column'
|
|
812
|
+
],
|
|
813
|
+
raceColumn: [
|
|
814
|
+
'player',
|
|
815
|
+
'xAxis',
|
|
816
|
+
'color',
|
|
817
|
+
'detail',
|
|
818
|
+
'tooltip',
|
|
819
|
+
'label',
|
|
820
|
+
'row',
|
|
821
|
+
'column'
|
|
822
|
+
],
|
|
823
|
+
raceLine: [
|
|
824
|
+
'player',
|
|
825
|
+
'xAxis',
|
|
826
|
+
'color',
|
|
827
|
+
'detail',
|
|
828
|
+
'tooltip',
|
|
829
|
+
'label',
|
|
830
|
+
'row',
|
|
831
|
+
'column'
|
|
832
|
+
],
|
|
833
|
+
raceScatter: [
|
|
834
|
+
'player',
|
|
835
|
+
'color',
|
|
836
|
+
'detail',
|
|
837
|
+
'tooltip',
|
|
838
|
+
'label',
|
|
839
|
+
'row',
|
|
840
|
+
'column'
|
|
841
|
+
],
|
|
842
|
+
racePie: [
|
|
843
|
+
'player',
|
|
844
|
+
'color',
|
|
845
|
+
'detail',
|
|
846
|
+
'tooltip',
|
|
847
|
+
'label',
|
|
848
|
+
'row',
|
|
849
|
+
'column'
|
|
850
|
+
],
|
|
851
|
+
raceDonut: [
|
|
852
|
+
'player',
|
|
853
|
+
'color',
|
|
854
|
+
'detail',
|
|
855
|
+
'tooltip',
|
|
856
|
+
'label',
|
|
857
|
+
'row',
|
|
858
|
+
'column'
|
|
859
|
+
]
|
|
860
|
+
};
|
|
861
|
+
const dimension_encoding_repeatEncoding = (first, rest = first)=>(index)=>0 === index ? first : rest;
|
|
862
|
+
const alternateEncoding = (a, b)=>(index)=>index % 2 === 0 ? a : b;
|
|
863
|
+
const dimension_encoding_STRATEGY_BY_CHART_TYPE = {
|
|
864
|
+
[ChartTypeEnum.Table]: {
|
|
865
|
+
supported: DIMENSION_ENCODING_SUPPORT.table,
|
|
866
|
+
recommend: dimension_encoding_repeatEncoding('column')
|
|
867
|
+
},
|
|
868
|
+
[ChartTypeEnum.PivotTable]: {
|
|
869
|
+
supported: DIMENSION_ENCODING_SUPPORT.pivotTable,
|
|
870
|
+
recommend: alternateEncoding('column', 'row')
|
|
871
|
+
},
|
|
872
|
+
[ChartTypeEnum.Column]: {
|
|
873
|
+
supported: DIMENSION_ENCODING_SUPPORT.column,
|
|
874
|
+
recommend: dimension_encoding_repeatEncoding('xAxis', 'color')
|
|
875
|
+
},
|
|
876
|
+
[ChartTypeEnum.ColumnParallel]: {
|
|
877
|
+
supported: DIMENSION_ENCODING_SUPPORT.columnParallel,
|
|
878
|
+
recommend: dimension_encoding_repeatEncoding('xAxis', 'color')
|
|
879
|
+
},
|
|
880
|
+
[ChartTypeEnum.ColumnPercent]: {
|
|
881
|
+
supported: DIMENSION_ENCODING_SUPPORT.columnPercent,
|
|
882
|
+
recommend: dimension_encoding_repeatEncoding('xAxis', 'color')
|
|
883
|
+
},
|
|
884
|
+
[ChartTypeEnum.Line]: {
|
|
885
|
+
supported: DIMENSION_ENCODING_SUPPORT.line,
|
|
886
|
+
recommend: dimension_encoding_repeatEncoding('xAxis', 'color')
|
|
887
|
+
},
|
|
888
|
+
[ChartTypeEnum.Area]: {
|
|
889
|
+
supported: DIMENSION_ENCODING_SUPPORT.area,
|
|
890
|
+
recommend: dimension_encoding_repeatEncoding('xAxis', 'color')
|
|
891
|
+
},
|
|
892
|
+
[ChartTypeEnum.AreaPercent]: {
|
|
893
|
+
supported: DIMENSION_ENCODING_SUPPORT.areaPercent,
|
|
894
|
+
recommend: dimension_encoding_repeatEncoding('xAxis', 'color')
|
|
895
|
+
},
|
|
896
|
+
[ChartTypeEnum.DualAxis]: {
|
|
897
|
+
supported: DIMENSION_ENCODING_SUPPORT.dualAxis,
|
|
898
|
+
recommend: dimension_encoding_repeatEncoding('xAxis', 'color')
|
|
899
|
+
},
|
|
900
|
+
[ChartTypeEnum.Bar]: {
|
|
901
|
+
supported: DIMENSION_ENCODING_SUPPORT.bar,
|
|
902
|
+
recommend: dimension_encoding_repeatEncoding('yAxis', 'color')
|
|
903
|
+
},
|
|
904
|
+
[ChartTypeEnum.BarParallel]: {
|
|
905
|
+
supported: DIMENSION_ENCODING_SUPPORT.barParallel,
|
|
906
|
+
recommend: dimension_encoding_repeatEncoding('yAxis', 'color')
|
|
907
|
+
},
|
|
908
|
+
[ChartTypeEnum.BarPercent]: {
|
|
909
|
+
supported: DIMENSION_ENCODING_SUPPORT.barPercent,
|
|
910
|
+
recommend: dimension_encoding_repeatEncoding('yAxis', 'color')
|
|
911
|
+
},
|
|
912
|
+
[ChartTypeEnum.Pie]: {
|
|
913
|
+
supported: DIMENSION_ENCODING_SUPPORT.pie,
|
|
914
|
+
recommend: dimension_encoding_repeatEncoding('color')
|
|
915
|
+
},
|
|
916
|
+
[ChartTypeEnum.Donut]: {
|
|
917
|
+
supported: DIMENSION_ENCODING_SUPPORT.donut,
|
|
918
|
+
recommend: dimension_encoding_repeatEncoding('color')
|
|
919
|
+
},
|
|
920
|
+
[ChartTypeEnum.Funnel]: {
|
|
921
|
+
supported: DIMENSION_ENCODING_SUPPORT.funnel,
|
|
922
|
+
recommend: dimension_encoding_repeatEncoding('color')
|
|
923
|
+
},
|
|
924
|
+
[ChartTypeEnum.Scatter]: {
|
|
925
|
+
supported: DIMENSION_ENCODING_SUPPORT.scatter,
|
|
926
|
+
recommend: dimension_encoding_repeatEncoding('color')
|
|
927
|
+
},
|
|
928
|
+
[ChartTypeEnum.Rose]: {
|
|
929
|
+
supported: DIMENSION_ENCODING_SUPPORT.rose,
|
|
930
|
+
recommend: dimension_encoding_repeatEncoding('angle', 'color')
|
|
931
|
+
},
|
|
932
|
+
[ChartTypeEnum.RoseParallel]: {
|
|
933
|
+
supported: DIMENSION_ENCODING_SUPPORT.roseParallel,
|
|
934
|
+
recommend: dimension_encoding_repeatEncoding('angle', 'color')
|
|
935
|
+
},
|
|
936
|
+
[ChartTypeEnum.Radar]: {
|
|
937
|
+
supported: DIMENSION_ENCODING_SUPPORT.radar,
|
|
938
|
+
recommend: dimension_encoding_repeatEncoding('angle', 'color')
|
|
939
|
+
},
|
|
940
|
+
[ChartTypeEnum.Heatmap]: {
|
|
941
|
+
supported: DIMENSION_ENCODING_SUPPORT.heatmap,
|
|
942
|
+
recommend: dimension_encoding_repeatEncoding('xAxis', 'yAxis')
|
|
943
|
+
},
|
|
944
|
+
[ChartTypeEnum.Boxplot]: {
|
|
945
|
+
supported: DIMENSION_ENCODING_SUPPORT.boxplot,
|
|
946
|
+
recommend: dimension_encoding_repeatEncoding('xAxis', 'color')
|
|
947
|
+
},
|
|
948
|
+
[ChartTypeEnum.Histogram]: {
|
|
949
|
+
supported: DIMENSION_ENCODING_SUPPORT.histogram,
|
|
950
|
+
recommend: dimension_encoding_repeatEncoding('color')
|
|
951
|
+
},
|
|
952
|
+
[ChartTypeEnum.TreeMap]: {
|
|
953
|
+
supported: DIMENSION_ENCODING_SUPPORT.treeMap,
|
|
954
|
+
recommend: dimension_encoding_repeatEncoding('hierarchy')
|
|
955
|
+
},
|
|
956
|
+
[ChartTypeEnum.Sunburst]: {
|
|
957
|
+
supported: DIMENSION_ENCODING_SUPPORT.sunburst,
|
|
958
|
+
recommend: dimension_encoding_repeatEncoding('hierarchy')
|
|
959
|
+
},
|
|
960
|
+
[ChartTypeEnum.CirclePacking]: {
|
|
961
|
+
supported: DIMENSION_ENCODING_SUPPORT.circlePacking,
|
|
962
|
+
recommend: dimension_encoding_repeatEncoding('hierarchy')
|
|
963
|
+
},
|
|
964
|
+
[ChartTypeEnum.RaceBar]: {
|
|
965
|
+
supported: DIMENSION_ENCODING_SUPPORT.raceBar,
|
|
966
|
+
recommend: (index)=>{
|
|
967
|
+
if (0 === index) return 'player';
|
|
968
|
+
if (1 === index) return 'yAxis';
|
|
969
|
+
return 'color';
|
|
970
|
+
}
|
|
971
|
+
},
|
|
972
|
+
[ChartTypeEnum.RaceColumn]: {
|
|
973
|
+
supported: DIMENSION_ENCODING_SUPPORT.raceColumn,
|
|
974
|
+
recommend: (index)=>{
|
|
975
|
+
if (0 === index) return 'player';
|
|
976
|
+
if (1 === index) return 'xAxis';
|
|
977
|
+
return 'color';
|
|
978
|
+
}
|
|
979
|
+
},
|
|
980
|
+
[ChartTypeEnum.RaceLine]: {
|
|
981
|
+
supported: DIMENSION_ENCODING_SUPPORT.raceLine,
|
|
982
|
+
recommend: (index)=>0 === index ? 'player' : 'color'
|
|
983
|
+
},
|
|
984
|
+
[ChartTypeEnum.RaceScatter]: {
|
|
985
|
+
supported: DIMENSION_ENCODING_SUPPORT.raceScatter,
|
|
986
|
+
recommend: (index)=>0 === index ? 'player' : 'color'
|
|
987
|
+
},
|
|
988
|
+
[ChartTypeEnum.RacePie]: {
|
|
989
|
+
supported: DIMENSION_ENCODING_SUPPORT.racePie,
|
|
990
|
+
recommend: (index)=>0 === index ? 'player' : 'color'
|
|
991
|
+
},
|
|
992
|
+
[ChartTypeEnum.RaceDonut]: {
|
|
993
|
+
supported: DIMENSION_ENCODING_SUPPORT.raceDonut,
|
|
994
|
+
recommend: (index)=>0 === index ? 'player' : 'color'
|
|
995
|
+
}
|
|
996
|
+
};
|
|
997
|
+
const dimension_encoding_DEFAULT_STRATEGY = dimension_encoding_STRATEGY_BY_CHART_TYPE[ChartTypeEnum.Table];
|
|
998
|
+
const getSupportedDimensionEncodingsForChartType = (chartType)=>[
|
|
999
|
+
...(dimension_encoding_STRATEGY_BY_CHART_TYPE[chartType] || dimension_encoding_DEFAULT_STRATEGY).supported
|
|
1000
|
+
];
|
|
1001
|
+
const getRecommendedDimensionEncodingsForChartType = (chartType, dimensionCount)=>{
|
|
1002
|
+
const strategy = dimension_encoding_STRATEGY_BY_CHART_TYPE[chartType] || dimension_encoding_DEFAULT_STRATEGY;
|
|
1003
|
+
if (dimensionCount <= 0) return [];
|
|
1004
|
+
return Array.from({
|
|
1005
|
+
length: dimensionCount
|
|
1006
|
+
}, (_, index)=>strategy.recommend(index));
|
|
1007
|
+
};
|
|
420
1008
|
class DimensionsBuilder {
|
|
1009
|
+
doc;
|
|
421
1010
|
dsl;
|
|
422
1011
|
constructor(doc, dsl){
|
|
1012
|
+
this.doc = doc;
|
|
423
1013
|
this.dsl = dsl;
|
|
424
1014
|
doc.transact(()=>{
|
|
425
1015
|
const dimensions = getOrCreateDimensions(this.dsl);
|
|
@@ -427,34 +1017,44 @@ class DimensionsBuilder {
|
|
|
427
1017
|
});
|
|
428
1018
|
}
|
|
429
1019
|
add(field, callback) {
|
|
1020
|
+
const dimensions = getOrCreateDimensions(this.dsl);
|
|
1021
|
+
const chartType = this.dsl.get('chartType') || 'table';
|
|
1022
|
+
const [encoding] = getRecommendedDimensionEncodingsForChartType(chartType, dimensions.length + 1).slice(-1);
|
|
430
1023
|
const dimension = {
|
|
431
1024
|
id: id_id.uuid(),
|
|
432
1025
|
alias: field,
|
|
433
|
-
field
|
|
1026
|
+
field,
|
|
1027
|
+
encoding
|
|
434
1028
|
};
|
|
435
1029
|
const yMap = new external_yjs_Map();
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
1030
|
+
this.doc.transact(()=>{
|
|
1031
|
+
for (const [key, value] of Object.entries(dimension))yMap.set(key, value);
|
|
1032
|
+
const dimensions = getOrCreateDimensions(this.dsl);
|
|
1033
|
+
dimensions.push([
|
|
1034
|
+
yMap
|
|
1035
|
+
]);
|
|
1036
|
+
const node = new DimensionNodeBuilder(yMap);
|
|
1037
|
+
callback(node);
|
|
1038
|
+
});
|
|
443
1039
|
return this;
|
|
444
1040
|
}
|
|
445
1041
|
remove(id) {
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
1042
|
+
this.doc.transact(()=>{
|
|
1043
|
+
const dimensions = getOrCreateDimensions(this.dsl);
|
|
1044
|
+
const index = locateDimensionIndexById(dimensions, id);
|
|
1045
|
+
if (-1 !== index) dimensions.delete(index, 1);
|
|
1046
|
+
});
|
|
449
1047
|
return this;
|
|
450
1048
|
}
|
|
451
1049
|
update(id, callback) {
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
1050
|
+
this.doc.transact(()=>{
|
|
1051
|
+
const dimensions = getOrCreateDimensions(this.dsl);
|
|
1052
|
+
const index = locateDimensionIndexById(dimensions, id);
|
|
1053
|
+
if (-1 === index) throw new Error(`Dimension with id "${id}" not found`);
|
|
1054
|
+
const dimensionYMap = dimensions.get(index);
|
|
1055
|
+
const node = new DimensionNodeBuilder(dimensionYMap);
|
|
1056
|
+
callback(node);
|
|
1057
|
+
});
|
|
458
1058
|
return this;
|
|
459
1059
|
}
|
|
460
1060
|
find(predicate) {
|
|
@@ -475,9 +1075,9 @@ class DimensionsBuilder {
|
|
|
475
1075
|
}
|
|
476
1076
|
observe(callback) {
|
|
477
1077
|
const dimensions = getOrCreateDimensions(this.dsl);
|
|
478
|
-
dimensions.
|
|
1078
|
+
dimensions.observeDeep(callback);
|
|
479
1079
|
return ()=>{
|
|
480
|
-
dimensions.
|
|
1080
|
+
dimensions.unobserveDeep(callback);
|
|
481
1081
|
};
|
|
482
1082
|
}
|
|
483
1083
|
static isDimensionNode(node) {
|
|
@@ -487,9 +1087,27 @@ class DimensionsBuilder {
|
|
|
487
1087
|
return 'children' in node;
|
|
488
1088
|
}
|
|
489
1089
|
}
|
|
1090
|
+
const reapplyDimensionEncodings = (dsl, chartType)=>{
|
|
1091
|
+
const dimensions = getOrCreateDimensions(dsl);
|
|
1092
|
+
const nodes = dimensions.toArray().filter((item)=>item instanceof external_yjs_Map && 'string' == typeof item.get('field'));
|
|
1093
|
+
const encodings = getRecommendedDimensionEncodingsForChartType(chartType, nodes.length);
|
|
1094
|
+
nodes.forEach((node, index)=>{
|
|
1095
|
+
node.set('encoding', encodings[index]);
|
|
1096
|
+
});
|
|
1097
|
+
};
|
|
1098
|
+
const reapplyMeasureEncodings = (dsl, chartType)=>{
|
|
1099
|
+
const measures = getOrCreateMeasures(dsl);
|
|
1100
|
+
const nodes = measures.toArray().filter((item)=>item instanceof external_yjs_Map && 'string' == typeof item.get('field'));
|
|
1101
|
+
const encodings = getRecommendedMeasureEncodingsForChartType(chartType, nodes.length);
|
|
1102
|
+
nodes.forEach((node, index)=>{
|
|
1103
|
+
node.set('encoding', encodings[index]);
|
|
1104
|
+
});
|
|
1105
|
+
};
|
|
490
1106
|
class ChartTypeBuilder {
|
|
1107
|
+
doc;
|
|
491
1108
|
dsl;
|
|
492
|
-
constructor(
|
|
1109
|
+
constructor(doc, dsl){
|
|
1110
|
+
this.doc = doc;
|
|
493
1111
|
this.dsl = dsl;
|
|
494
1112
|
}
|
|
495
1113
|
observe(callback) {
|
|
@@ -502,11 +1120,29 @@ class ChartTypeBuilder {
|
|
|
502
1120
|
};
|
|
503
1121
|
}
|
|
504
1122
|
changeChartType(chartType) {
|
|
505
|
-
this.
|
|
1123
|
+
this.doc.transact(()=>{
|
|
1124
|
+
this.dsl.set('chartType', chartType);
|
|
1125
|
+
reapplyDimensionEncodings(this.dsl, chartType);
|
|
1126
|
+
reapplyMeasureEncodings(this.dsl, chartType);
|
|
1127
|
+
});
|
|
506
1128
|
}
|
|
507
1129
|
getChartType() {
|
|
508
1130
|
return this.dsl.get('chartType') || 'table';
|
|
509
1131
|
}
|
|
1132
|
+
getSupportedDimensionEncodings() {
|
|
1133
|
+
return getSupportedDimensionEncodingsForChartType(this.getChartType());
|
|
1134
|
+
}
|
|
1135
|
+
getRecommendedDimensionEncodings(dimensionCount) {
|
|
1136
|
+
const resolvedCount = dimensionCount ?? this.dsl.get('dimensions')?.length;
|
|
1137
|
+
return getRecommendedDimensionEncodingsForChartType(this.getChartType(), resolvedCount);
|
|
1138
|
+
}
|
|
1139
|
+
getSupportedMeasureEncodings() {
|
|
1140
|
+
return getSupportedMeasureEncodingsForChartType(this.getChartType());
|
|
1141
|
+
}
|
|
1142
|
+
getRecommendedMeasureEncodings(measureCount) {
|
|
1143
|
+
const resolvedCount = measureCount ?? this.dsl.get('measures')?.length;
|
|
1144
|
+
return getRecommendedMeasureEncodingsForChartType(this.getChartType(), resolvedCount);
|
|
1145
|
+
}
|
|
510
1146
|
toJSON() {
|
|
511
1147
|
return this.dsl.get('chartType') || 'table';
|
|
512
1148
|
}
|
|
@@ -514,25 +1150,35 @@ class ChartTypeBuilder {
|
|
|
514
1150
|
return [
|
|
515
1151
|
ChartTypeEnum.Table,
|
|
516
1152
|
ChartTypeEnum.PivotTable,
|
|
517
|
-
ChartTypeEnum.Line,
|
|
518
1153
|
ChartTypeEnum.Column,
|
|
519
|
-
ChartTypeEnum.ColumnPercent,
|
|
520
1154
|
ChartTypeEnum.ColumnParallel,
|
|
521
|
-
ChartTypeEnum.
|
|
1155
|
+
ChartTypeEnum.ColumnPercent,
|
|
1156
|
+
ChartTypeEnum.Bar,
|
|
522
1157
|
ChartTypeEnum.BarParallel,
|
|
1158
|
+
ChartTypeEnum.BarPercent,
|
|
1159
|
+
ChartTypeEnum.Line,
|
|
523
1160
|
ChartTypeEnum.Area,
|
|
524
1161
|
ChartTypeEnum.AreaPercent,
|
|
525
1162
|
ChartTypeEnum.DualAxis,
|
|
526
1163
|
ChartTypeEnum.Scatter,
|
|
527
|
-
ChartTypeEnum.Rose,
|
|
528
|
-
ChartTypeEnum.RoseParallel,
|
|
529
1164
|
ChartTypeEnum.Pie,
|
|
530
1165
|
ChartTypeEnum.Donut,
|
|
1166
|
+
ChartTypeEnum.Rose,
|
|
1167
|
+
ChartTypeEnum.RoseParallel,
|
|
531
1168
|
ChartTypeEnum.Radar,
|
|
532
1169
|
ChartTypeEnum.Funnel,
|
|
533
1170
|
ChartTypeEnum.Heatmap,
|
|
534
1171
|
ChartTypeEnum.Boxplot,
|
|
535
|
-
ChartTypeEnum.Histogram
|
|
1172
|
+
ChartTypeEnum.Histogram,
|
|
1173
|
+
ChartTypeEnum.TreeMap,
|
|
1174
|
+
ChartTypeEnum.Sunburst,
|
|
1175
|
+
ChartTypeEnum.CirclePacking,
|
|
1176
|
+
ChartTypeEnum.RaceBar,
|
|
1177
|
+
ChartTypeEnum.RaceColumn,
|
|
1178
|
+
ChartTypeEnum.RaceLine,
|
|
1179
|
+
ChartTypeEnum.RaceScatter,
|
|
1180
|
+
ChartTypeEnum.RacePie,
|
|
1181
|
+
ChartTypeEnum.RaceDonut
|
|
536
1182
|
];
|
|
537
1183
|
}
|
|
538
1184
|
}
|
|
@@ -543,7 +1189,7 @@ function createWhereGroup(op = 'and', groupId = 'root') {
|
|
|
543
1189
|
yMap.set('conditions', new external_yjs_Array());
|
|
544
1190
|
return yMap;
|
|
545
1191
|
}
|
|
546
|
-
function
|
|
1192
|
+
function isWhereGroup(yMap) {
|
|
547
1193
|
return void 0 !== yMap.get('op') && void 0 !== yMap.get('conditions');
|
|
548
1194
|
}
|
|
549
1195
|
function findEntry(collection, entryId) {
|
|
@@ -555,7 +1201,7 @@ function findEntry(collection, entryId) {
|
|
|
555
1201
|
index,
|
|
556
1202
|
item
|
|
557
1203
|
};
|
|
558
|
-
if (
|
|
1204
|
+
if (isWhereGroup(item)) {
|
|
559
1205
|
const nestedCollection = item.get('conditions');
|
|
560
1206
|
const nestedMatch = findEntry(nestedCollection, entryId);
|
|
561
1207
|
if (nestedMatch) return nestedMatch;
|
|
@@ -588,6 +1234,15 @@ class WhereFilterNodeBuilder {
|
|
|
588
1234
|
this.yMap.set('value', value);
|
|
589
1235
|
return this;
|
|
590
1236
|
}
|
|
1237
|
+
setDate(predicate) {
|
|
1238
|
+
this.yMap.set('op', 'date');
|
|
1239
|
+
this.yMap.set('value', predicate);
|
|
1240
|
+
return this;
|
|
1241
|
+
}
|
|
1242
|
+
getDate() {
|
|
1243
|
+
if ('date' !== this.yMap.get('op')) return;
|
|
1244
|
+
return this.yMap.get('value');
|
|
1245
|
+
}
|
|
591
1246
|
toJSON() {
|
|
592
1247
|
return this.yMap.toJSON();
|
|
593
1248
|
}
|
|
@@ -748,7 +1403,7 @@ class WhereFilterBuilder {
|
|
|
748
1403
|
};
|
|
749
1404
|
}
|
|
750
1405
|
static isGroup(yMap) {
|
|
751
|
-
return
|
|
1406
|
+
return isWhereGroup(yMap);
|
|
752
1407
|
}
|
|
753
1408
|
static isNode(yMap) {
|
|
754
1409
|
return void 0 !== yMap.get('field');
|
|
@@ -761,7 +1416,7 @@ function createHavingGroup(op = 'and', groupId = 'root') {
|
|
|
761
1416
|
yMap.set('conditions', new external_yjs_Array());
|
|
762
1417
|
return yMap;
|
|
763
1418
|
}
|
|
764
|
-
function
|
|
1419
|
+
function isHavingGroup(yMap) {
|
|
765
1420
|
return void 0 !== yMap.get('op') && void 0 !== yMap.get('conditions');
|
|
766
1421
|
}
|
|
767
1422
|
function having_utils_findEntry(collection, entryId) {
|
|
@@ -773,7 +1428,7 @@ function having_utils_findEntry(collection, entryId) {
|
|
|
773
1428
|
index,
|
|
774
1429
|
item
|
|
775
1430
|
};
|
|
776
|
-
if (
|
|
1431
|
+
if (isHavingGroup(item)) {
|
|
777
1432
|
const nestedCollection = item.get('conditions');
|
|
778
1433
|
const nestedMatch = having_utils_findEntry(nestedCollection, entryId);
|
|
779
1434
|
if (nestedMatch) return nestedMatch;
|
|
@@ -975,7 +1630,7 @@ class HavingFilterBuilder {
|
|
|
975
1630
|
};
|
|
976
1631
|
}
|
|
977
1632
|
static isGroup(yMap) {
|
|
978
|
-
return
|
|
1633
|
+
return isHavingGroup(yMap);
|
|
979
1634
|
}
|
|
980
1635
|
static isNode(yMap) {
|
|
981
1636
|
return void 0 !== yMap.get('field');
|
|
@@ -1074,17 +1729,518 @@ class undo_manager_UndoManager {
|
|
|
1074
1729
|
this.manager.clear(clearUndoStack, clearRedoStack);
|
|
1075
1730
|
}
|
|
1076
1731
|
}
|
|
1732
|
+
const VBI_TO_VQUERY_MEASURE_AGGR_FUNC_MAP = {
|
|
1733
|
+
count: 'count',
|
|
1734
|
+
countDistinct: 'count_distinct',
|
|
1735
|
+
sum: 'sum',
|
|
1736
|
+
avg: 'avg',
|
|
1737
|
+
min: 'min',
|
|
1738
|
+
max: 'max',
|
|
1739
|
+
variance: 'variance',
|
|
1740
|
+
variancePop: 'variance_pop',
|
|
1741
|
+
stddev: 'stddev',
|
|
1742
|
+
median: 'median',
|
|
1743
|
+
quantile: 'quantile'
|
|
1744
|
+
};
|
|
1745
|
+
const VBI_TO_VQUERY_DIMENSION_AGGR_FUNC_MAP = {
|
|
1746
|
+
toYear: 'to_year',
|
|
1747
|
+
toQuarter: 'to_quarter',
|
|
1748
|
+
toMonth: 'to_month',
|
|
1749
|
+
toWeek: 'to_week',
|
|
1750
|
+
toDay: 'to_day',
|
|
1751
|
+
toHour: 'to_hour',
|
|
1752
|
+
toMinute: 'to_minute',
|
|
1753
|
+
toSecond: 'to_second'
|
|
1754
|
+
};
|
|
1755
|
+
const mapAggregateForVQuery = (aggregate)=>{
|
|
1756
|
+
if (!aggregate) return aggregate;
|
|
1757
|
+
const mappedFunc = VBI_TO_VQUERY_MEASURE_AGGR_FUNC_MAP[aggregate.func] ?? aggregate.func;
|
|
1758
|
+
return {
|
|
1759
|
+
...aggregate,
|
|
1760
|
+
func: mappedFunc
|
|
1761
|
+
};
|
|
1762
|
+
};
|
|
1763
|
+
const mapDimensionAggregateForVQuery = (aggregate)=>{
|
|
1764
|
+
if (!aggregate) return aggregate;
|
|
1765
|
+
const mappedFunc = VBI_TO_VQUERY_DIMENSION_AGGR_FUNC_MAP[aggregate.func] ?? aggregate.func;
|
|
1766
|
+
return {
|
|
1767
|
+
...aggregate,
|
|
1768
|
+
func: mappedFunc
|
|
1769
|
+
};
|
|
1770
|
+
};
|
|
1771
|
+
const buildSelect = (queryDSL, context)=>{
|
|
1772
|
+
const { vbiDSL } = context;
|
|
1773
|
+
const measures = vbiDSL.measures;
|
|
1774
|
+
const dimensions = vbiDSL.dimensions;
|
|
1775
|
+
const result = {
|
|
1776
|
+
...queryDSL
|
|
1777
|
+
};
|
|
1778
|
+
const measureNodes = measures.filter((measure)=>MeasuresBuilder.isMeasureNode(measure));
|
|
1779
|
+
const measureSelects = measureNodes.map((measure)=>({
|
|
1780
|
+
field: measure.field,
|
|
1781
|
+
alias: measure.id,
|
|
1782
|
+
aggr: mapAggregateForVQuery(measure.aggregate)
|
|
1783
|
+
}));
|
|
1784
|
+
const dimensionNodes = dimensions.filter((dimension)=>DimensionsBuilder.isDimensionNode(dimension));
|
|
1785
|
+
const dimensionSelects = dimensionNodes.map((dimension)=>{
|
|
1786
|
+
const aggregate = mapDimensionAggregateForVQuery(dimension.aggregate);
|
|
1787
|
+
if (!aggregate) return {
|
|
1788
|
+
field: dimension.field,
|
|
1789
|
+
alias: dimension.id
|
|
1790
|
+
};
|
|
1791
|
+
return {
|
|
1792
|
+
field: dimension.field,
|
|
1793
|
+
alias: dimension.id,
|
|
1794
|
+
aggr: aggregate
|
|
1795
|
+
};
|
|
1796
|
+
});
|
|
1797
|
+
result.select = [
|
|
1798
|
+
...measureSelects,
|
|
1799
|
+
...dimensionSelects
|
|
1800
|
+
];
|
|
1801
|
+
return result;
|
|
1802
|
+
};
|
|
1803
|
+
const buildGroupBy = (queryDSL, context)=>{
|
|
1804
|
+
const result = {
|
|
1805
|
+
...queryDSL
|
|
1806
|
+
};
|
|
1807
|
+
const { vbiDSL } = context;
|
|
1808
|
+
const dimensions = vbiDSL.dimensions;
|
|
1809
|
+
const dimensionNodes = dimensions.filter((dimension)=>DimensionsBuilder.isDimensionNode(dimension));
|
|
1810
|
+
result.groupBy = dimensionNodes.map((dimension)=>dimension.aggregate ? dimension.id : dimension.field);
|
|
1811
|
+
return result;
|
|
1812
|
+
};
|
|
1813
|
+
function resolveDatePredicate(predicate, now) {
|
|
1814
|
+
switch(predicate.type){
|
|
1815
|
+
case 'range':
|
|
1816
|
+
return resolveRange(predicate);
|
|
1817
|
+
case 'period':
|
|
1818
|
+
return resolvePeriod(predicate);
|
|
1819
|
+
case 'relative':
|
|
1820
|
+
return resolveRelative(predicate, now ?? new Date());
|
|
1821
|
+
case 'current':
|
|
1822
|
+
return resolveCurrent(predicate, now ?? new Date());
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
function resolveRange(p) {
|
|
1826
|
+
return {
|
|
1827
|
+
start: toDateString(p.start),
|
|
1828
|
+
end: toDateString(p.end),
|
|
1829
|
+
bounds: p.bounds ?? '[)'
|
|
1830
|
+
};
|
|
1831
|
+
}
|
|
1832
|
+
function resolvePeriod(p) {
|
|
1833
|
+
switch(p.unit){
|
|
1834
|
+
case 'year':
|
|
1835
|
+
return {
|
|
1836
|
+
start: `${p.year}-01-01`,
|
|
1837
|
+
end: `${p.year + 1}-01-01`,
|
|
1838
|
+
bounds: '[)'
|
|
1839
|
+
};
|
|
1840
|
+
case 'quarter':
|
|
1841
|
+
return resolveQuarter(p.year, p.quarter);
|
|
1842
|
+
case 'month':
|
|
1843
|
+
return resolveMonth(p.year, p.month);
|
|
1844
|
+
case 'week':
|
|
1845
|
+
return resolveISOWeek(p.year, p.week);
|
|
1846
|
+
case 'day':
|
|
1847
|
+
return resolveDay(p.date);
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
function resolveQuarter(year, quarter) {
|
|
1851
|
+
const startMonth = (quarter - 1) * 3 + 1;
|
|
1852
|
+
const start = formatDate(year, startMonth, 1);
|
|
1853
|
+
const endDate = new Date(Date.UTC(year, startMonth - 1 + 3, 1));
|
|
1854
|
+
return {
|
|
1855
|
+
start,
|
|
1856
|
+
end: utcToDateString(endDate),
|
|
1857
|
+
bounds: '[)'
|
|
1858
|
+
};
|
|
1859
|
+
}
|
|
1860
|
+
function resolveMonth(year, month) {
|
|
1861
|
+
const start = formatDate(year, month, 1);
|
|
1862
|
+
const endDate = new Date(Date.UTC(year, month, 1));
|
|
1863
|
+
return {
|
|
1864
|
+
start,
|
|
1865
|
+
end: utcToDateString(endDate),
|
|
1866
|
+
bounds: '[)'
|
|
1867
|
+
};
|
|
1868
|
+
}
|
|
1869
|
+
function resolveISOWeek(year, week) {
|
|
1870
|
+
const jan4 = new Date(Date.UTC(year, 0, 4));
|
|
1871
|
+
const dayOfWeek = jan4.getUTCDay() || 7;
|
|
1872
|
+
const monday = new Date(jan4.getTime() - (dayOfWeek - 1) * 86400000);
|
|
1873
|
+
const weekStart = new Date(monday.getTime() + (week - 1) * 604800000);
|
|
1874
|
+
const weekEnd = new Date(weekStart.getTime() + 604800000);
|
|
1875
|
+
return {
|
|
1876
|
+
start: utcToDateString(weekStart),
|
|
1877
|
+
end: utcToDateString(weekEnd),
|
|
1878
|
+
bounds: '[)'
|
|
1879
|
+
};
|
|
1880
|
+
}
|
|
1881
|
+
function resolveDay(date) {
|
|
1882
|
+
const d = 'string' == typeof date ? new Date(date + 'T00:00:00Z') : date;
|
|
1883
|
+
const next = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate() + 1));
|
|
1884
|
+
return {
|
|
1885
|
+
start: utcToDateString(d),
|
|
1886
|
+
end: utcToDateString(next),
|
|
1887
|
+
bounds: '[)'
|
|
1888
|
+
};
|
|
1889
|
+
}
|
|
1890
|
+
function resolveRelative(p, now) {
|
|
1891
|
+
const today = truncateToDay(now);
|
|
1892
|
+
const offset = 'last' === p.mode ? -p.amount : p.amount;
|
|
1893
|
+
const shifted = shiftDate(today, offset, p.unit);
|
|
1894
|
+
if ('last' === p.mode) return {
|
|
1895
|
+
start: utcToDateString(shifted),
|
|
1896
|
+
end: utcToDateString(today),
|
|
1897
|
+
bounds: '[)'
|
|
1898
|
+
};
|
|
1899
|
+
return {
|
|
1900
|
+
start: utcToDateString(today),
|
|
1901
|
+
end: utcToDateString(shifted),
|
|
1902
|
+
bounds: '[)'
|
|
1903
|
+
};
|
|
1904
|
+
}
|
|
1905
|
+
function resolveCurrent(p, now) {
|
|
1906
|
+
const offset = p.offset ?? 0;
|
|
1907
|
+
const base = truncateToDay(now);
|
|
1908
|
+
const periodStart = getPeriodStart(base, p.unit, offset);
|
|
1909
|
+
const periodEnd = shiftDate(periodStart, 1, p.unit);
|
|
1910
|
+
return {
|
|
1911
|
+
start: utcToDateString(periodStart),
|
|
1912
|
+
end: utcToDateString(periodEnd),
|
|
1913
|
+
bounds: '[)'
|
|
1914
|
+
};
|
|
1915
|
+
}
|
|
1916
|
+
function getPeriodStart(date, unit, offset) {
|
|
1917
|
+
const y = date.getUTCFullYear();
|
|
1918
|
+
const m = date.getUTCMonth();
|
|
1919
|
+
switch(unit){
|
|
1920
|
+
case 'year':
|
|
1921
|
+
return new Date(Date.UTC(y + offset, 0, 1));
|
|
1922
|
+
case 'quarter':
|
|
1923
|
+
{
|
|
1924
|
+
const q = Math.floor(m / 3);
|
|
1925
|
+
return new Date(Date.UTC(y, (q + offset) * 3, 1));
|
|
1926
|
+
}
|
|
1927
|
+
case 'month':
|
|
1928
|
+
return new Date(Date.UTC(y, m + offset, 1));
|
|
1929
|
+
case 'week':
|
|
1930
|
+
{
|
|
1931
|
+
const dow = date.getUTCDay() || 7;
|
|
1932
|
+
const monday = new Date(date.getTime() - (dow - 1) * 86400000);
|
|
1933
|
+
return new Date(monday.getTime() + 7 * offset * 86400000);
|
|
1934
|
+
}
|
|
1935
|
+
case 'day':
|
|
1936
|
+
return new Date(Date.UTC(y, m, date.getUTCDate() + offset));
|
|
1937
|
+
default:
|
|
1938
|
+
return date;
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
function shiftDate(date, amount, unit) {
|
|
1942
|
+
const y = date.getUTCFullYear();
|
|
1943
|
+
const m = date.getUTCMonth();
|
|
1944
|
+
const d = date.getUTCDate();
|
|
1945
|
+
switch(unit){
|
|
1946
|
+
case 'year':
|
|
1947
|
+
return new Date(Date.UTC(y + amount, m, d));
|
|
1948
|
+
case 'quarter':
|
|
1949
|
+
return new Date(Date.UTC(y, m + 3 * amount, d));
|
|
1950
|
+
case 'month':
|
|
1951
|
+
return new Date(Date.UTC(y, m + amount, d));
|
|
1952
|
+
case 'week':
|
|
1953
|
+
return new Date(date.getTime() + 7 * amount * 86400000);
|
|
1954
|
+
case 'day':
|
|
1955
|
+
return new Date(Date.UTC(y, m, d + amount));
|
|
1956
|
+
default:
|
|
1957
|
+
return date;
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
function truncateToDay(date) {
|
|
1961
|
+
return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
|
|
1962
|
+
}
|
|
1963
|
+
function toDateString(input) {
|
|
1964
|
+
if ('string' == typeof input) return input;
|
|
1965
|
+
return utcToDateString(input);
|
|
1966
|
+
}
|
|
1967
|
+
function utcToDateString(date) {
|
|
1968
|
+
return formatDate(date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate());
|
|
1969
|
+
}
|
|
1970
|
+
function formatDate(year, month, day) {
|
|
1971
|
+
const y = String(year);
|
|
1972
|
+
const m = String(month).padStart(2, '0');
|
|
1973
|
+
const d = String(day).padStart(2, '0');
|
|
1974
|
+
return `${y}-${m}-${d}`;
|
|
1975
|
+
}
|
|
1976
|
+
const buildWhere = (queryDSL, context)=>{
|
|
1977
|
+
const { vbiDSL } = context;
|
|
1978
|
+
const whereFilter = vbiDSL.whereFilter;
|
|
1979
|
+
if (!whereFilter || 0 === whereFilter.conditions.length) return queryDSL;
|
|
1980
|
+
const result = {
|
|
1981
|
+
...queryDSL
|
|
1982
|
+
};
|
|
1983
|
+
result.where = mapGroupToCondition(whereFilter);
|
|
1984
|
+
return result;
|
|
1985
|
+
};
|
|
1986
|
+
function buildWhere_isWhereGroup(clause) {
|
|
1987
|
+
return 'op' in clause && 'conditions' in clause;
|
|
1988
|
+
}
|
|
1989
|
+
function mapClauseToCondition(clause) {
|
|
1990
|
+
if (buildWhere_isWhereGroup(clause)) return [
|
|
1991
|
+
mapGroupToCondition(clause)
|
|
1992
|
+
];
|
|
1993
|
+
return mapFilterToCondition(clause);
|
|
1994
|
+
}
|
|
1995
|
+
function mapGroupToCondition(group) {
|
|
1996
|
+
return {
|
|
1997
|
+
op: group.op,
|
|
1998
|
+
conditions: group.conditions.flatMap((c)=>mapClauseToCondition(c))
|
|
1999
|
+
};
|
|
2000
|
+
}
|
|
2001
|
+
function mapFilterToCondition(filter) {
|
|
2002
|
+
if ('date' === filter.op) return handleDateFilter(filter.field, filter.value);
|
|
2003
|
+
if ('between' === filter.op || 'not between' === filter.op) return handleBetweenFilter(filter);
|
|
2004
|
+
return handleSimpleFilter(filter);
|
|
2005
|
+
}
|
|
2006
|
+
function handleDateFilter(field, predicate) {
|
|
2007
|
+
const range = resolveDatePredicate(predicate);
|
|
2008
|
+
const startOp = '>=';
|
|
2009
|
+
const endOp = '[]' === range.bounds ? '<=' : '<';
|
|
2010
|
+
return [
|
|
2011
|
+
{
|
|
2012
|
+
field,
|
|
2013
|
+
op: startOp,
|
|
2014
|
+
value: range.start
|
|
2015
|
+
},
|
|
2016
|
+
{
|
|
2017
|
+
field,
|
|
2018
|
+
op: endOp,
|
|
2019
|
+
value: range.end
|
|
2020
|
+
}
|
|
2021
|
+
];
|
|
2022
|
+
}
|
|
2023
|
+
function handleBetweenFilter(filter) {
|
|
2024
|
+
const value = normalizeBetweenValue(filter.value);
|
|
2025
|
+
const lowerCondition = void 0 !== value.min && null !== value.min && '' !== value.min ? {
|
|
2026
|
+
field: filter.field,
|
|
2027
|
+
op: '<' === value.leftOp ? '>' : '>=',
|
|
2028
|
+
value: value.min
|
|
2029
|
+
} : void 0;
|
|
2030
|
+
const upperCondition = void 0 !== value.max && null !== value.max && '' !== value.max ? {
|
|
2031
|
+
field: filter.field,
|
|
2032
|
+
op: '<' === value.rightOp ? '<' : '<=',
|
|
2033
|
+
value: value.max
|
|
2034
|
+
} : void 0;
|
|
2035
|
+
if ('not between' === filter.op) {
|
|
2036
|
+
const outsideConditions = [
|
|
2037
|
+
lowerCondition && invertLowerBound(lowerCondition),
|
|
2038
|
+
upperCondition && invertUpperBound(upperCondition)
|
|
2039
|
+
].filter(Boolean);
|
|
2040
|
+
if (outsideConditions.length <= 1) return outsideConditions;
|
|
2041
|
+
return [
|
|
2042
|
+
{
|
|
2043
|
+
op: 'or',
|
|
2044
|
+
conditions: outsideConditions
|
|
2045
|
+
}
|
|
2046
|
+
];
|
|
2047
|
+
}
|
|
2048
|
+
return [
|
|
2049
|
+
lowerCondition,
|
|
2050
|
+
upperCondition
|
|
2051
|
+
].filter(Boolean);
|
|
2052
|
+
}
|
|
2053
|
+
function normalizeBetweenValue(value) {
|
|
2054
|
+
if (Array.isArray(value)) return {
|
|
2055
|
+
min: value[0],
|
|
2056
|
+
max: value[1],
|
|
2057
|
+
leftOp: '<=',
|
|
2058
|
+
rightOp: '<='
|
|
2059
|
+
};
|
|
2060
|
+
if ('object' == typeof value && null !== value) return value;
|
|
2061
|
+
return {};
|
|
2062
|
+
}
|
|
2063
|
+
function invertLowerBound(condition) {
|
|
2064
|
+
return {
|
|
2065
|
+
field: condition.field,
|
|
2066
|
+
op: '>' === condition.op ? '<=' : '<',
|
|
2067
|
+
value: condition.value
|
|
2068
|
+
};
|
|
2069
|
+
}
|
|
2070
|
+
function invertUpperBound(condition) {
|
|
2071
|
+
return {
|
|
2072
|
+
field: condition.field,
|
|
2073
|
+
op: '<' === condition.op ? '>=' : '>',
|
|
2074
|
+
value: condition.value
|
|
2075
|
+
};
|
|
2076
|
+
}
|
|
2077
|
+
function handleSimpleFilter(filter) {
|
|
2078
|
+
let mappedOp = filter.op;
|
|
2079
|
+
const value = filter.value;
|
|
2080
|
+
if (Array.isArray(value)) {
|
|
2081
|
+
if ('=' === mappedOp) mappedOp = 'in';
|
|
2082
|
+
if ('!=' === mappedOp) mappedOp = 'not in';
|
|
2083
|
+
}
|
|
2084
|
+
return [
|
|
2085
|
+
{
|
|
2086
|
+
field: filter.field,
|
|
2087
|
+
op: mappedOp,
|
|
2088
|
+
value
|
|
2089
|
+
}
|
|
2090
|
+
];
|
|
2091
|
+
}
|
|
2092
|
+
const buildHaving = (queryDSL, context)=>{
|
|
2093
|
+
const { vbiDSL } = context;
|
|
2094
|
+
const havingFilter = vbiDSL.havingFilter;
|
|
2095
|
+
if (!havingFilter || 0 === havingFilter.conditions.length) return queryDSL;
|
|
2096
|
+
const result = {
|
|
2097
|
+
...queryDSL
|
|
2098
|
+
};
|
|
2099
|
+
result.having = {
|
|
2100
|
+
op: havingFilter.op,
|
|
2101
|
+
conditions: havingFilter.conditions.flatMap(buildHaving_mapClauseToCondition)
|
|
2102
|
+
};
|
|
2103
|
+
return result;
|
|
2104
|
+
};
|
|
2105
|
+
function buildHaving_isHavingGroup(clause) {
|
|
2106
|
+
return 'op' in clause && 'conditions' in clause;
|
|
2107
|
+
}
|
|
2108
|
+
function buildHaving_mapClauseToCondition(clause) {
|
|
2109
|
+
if (buildHaving_isHavingGroup(clause)) return [
|
|
2110
|
+
buildHaving_mapGroupToCondition(clause)
|
|
2111
|
+
];
|
|
2112
|
+
return buildHaving_mapFilterToCondition(clause);
|
|
2113
|
+
}
|
|
2114
|
+
function buildHaving_mapGroupToCondition(group) {
|
|
2115
|
+
return {
|
|
2116
|
+
op: group.op,
|
|
2117
|
+
conditions: group.conditions.flatMap(buildHaving_mapClauseToCondition)
|
|
2118
|
+
};
|
|
2119
|
+
}
|
|
2120
|
+
function buildHaving_mapFilterToCondition(filter) {
|
|
2121
|
+
const mappedOp = normalizeOperator(filter.op, filter.value);
|
|
2122
|
+
const aggregate = mapAggregateForVQuery(filter.aggregate);
|
|
2123
|
+
return [
|
|
2124
|
+
{
|
|
2125
|
+
field: filter.field,
|
|
2126
|
+
aggr: aggregate,
|
|
2127
|
+
op: mappedOp,
|
|
2128
|
+
value: filter.value
|
|
2129
|
+
}
|
|
2130
|
+
];
|
|
2131
|
+
}
|
|
2132
|
+
function normalizeOperator(op, value) {
|
|
2133
|
+
let mappedOp = op;
|
|
2134
|
+
if (Array.isArray(value)) {
|
|
2135
|
+
if ('=' === mappedOp) mappedOp = 'in';
|
|
2136
|
+
if ('!=' === mappedOp) mappedOp = 'not in';
|
|
2137
|
+
}
|
|
2138
|
+
return mappedOp;
|
|
2139
|
+
}
|
|
2140
|
+
const toOrderItem = (node)=>({
|
|
2141
|
+
field: node.id,
|
|
2142
|
+
order: node.sort?.order
|
|
2143
|
+
});
|
|
2144
|
+
const buildOrderBy = (queryDSL, context)=>{
|
|
2145
|
+
const result = {
|
|
2146
|
+
...queryDSL
|
|
2147
|
+
};
|
|
2148
|
+
const dimensions = context.vbiDSL.dimensions.filter(DimensionsBuilder.isDimensionNode);
|
|
2149
|
+
const measures = context.vbiDSL.measures.filter(MeasuresBuilder.isMeasureNode);
|
|
2150
|
+
const sortedDimensions = dimensions.filter((node)=>node.sort);
|
|
2151
|
+
const sortedMeasures = measures.filter((node)=>node.sort);
|
|
2152
|
+
const sortedNodes = [
|
|
2153
|
+
...sortedDimensions,
|
|
2154
|
+
...sortedMeasures
|
|
2155
|
+
];
|
|
2156
|
+
if (sortedNodes.length > 0) {
|
|
2157
|
+
result.orderBy = sortedNodes.map(toOrderItem);
|
|
2158
|
+
return result;
|
|
2159
|
+
}
|
|
2160
|
+
const firstDimension = dimensions[0];
|
|
2161
|
+
if (firstDimension) result.orderBy = [
|
|
2162
|
+
{
|
|
2163
|
+
field: firstDimension.id,
|
|
2164
|
+
order: 'asc'
|
|
2165
|
+
}
|
|
2166
|
+
];
|
|
2167
|
+
return result;
|
|
2168
|
+
};
|
|
2169
|
+
const buildLimit = (queryDSL, context)=>{
|
|
2170
|
+
const result = {
|
|
2171
|
+
...queryDSL
|
|
2172
|
+
};
|
|
2173
|
+
const limit = context.vbiDSL.limit ?? 1000;
|
|
2174
|
+
result.limit = limit;
|
|
2175
|
+
return result;
|
|
2176
|
+
};
|
|
2177
|
+
const buildVQuery = (vbiDSL, builder)=>{
|
|
2178
|
+
const wrapper = (processor)=>(queryDSL)=>processor(queryDSL, {
|
|
2179
|
+
vbiDSL,
|
|
2180
|
+
builder
|
|
2181
|
+
});
|
|
2182
|
+
return pipe({}, wrapper(buildSelect), wrapper(buildGroupBy), wrapper(buildWhere), wrapper(buildHaving), wrapper(buildOrderBy), wrapper(buildLimit));
|
|
2183
|
+
};
|
|
2184
|
+
const buildVQueryDSL = ({ vbiDSL, builder })=>buildVQuery(vbiDSL, builder);
|
|
2185
|
+
const buildVSeedDSL = async ({ vbiDSL, queryDSL, options })=>{
|
|
2186
|
+
const connectorId = vbiDSL.connectorId;
|
|
2187
|
+
const connector = await getConnector(connectorId);
|
|
2188
|
+
const schema = await connector.discoverSchema();
|
|
2189
|
+
const queryResult = await connector.query({
|
|
2190
|
+
queryDSL,
|
|
2191
|
+
schema,
|
|
2192
|
+
connectorId,
|
|
2193
|
+
signal: options.signal
|
|
2194
|
+
});
|
|
2195
|
+
const measures = vbiDSL.measures.filter((measure)=>MeasuresBuilder.isMeasureNode(measure)).map((measure)=>{
|
|
2196
|
+
const nextMeasure = {
|
|
2197
|
+
id: measure.id,
|
|
2198
|
+
alias: measure.alias
|
|
2199
|
+
};
|
|
2200
|
+
if (measure.encoding) nextMeasure.encoding = measure.encoding;
|
|
2201
|
+
if (measure.format) if ('autoFormat' in measure.format && true === measure.format.autoFormat) nextMeasure.autoFormat = true;
|
|
2202
|
+
else {
|
|
2203
|
+
nextMeasure.autoFormat = false;
|
|
2204
|
+
nextMeasure.numFormat = Object.fromEntries(Object.entries(measure.format).filter(([key])=>'autoFormat' !== key));
|
|
2205
|
+
}
|
|
2206
|
+
return nextMeasure;
|
|
2207
|
+
});
|
|
2208
|
+
const dimensions = vbiDSL.dimensions.filter((dimension)=>DimensionsBuilder.isDimensionNode(dimension)).map((dimension)=>{
|
|
2209
|
+
const nextDimension = {
|
|
2210
|
+
id: dimension.id,
|
|
2211
|
+
alias: dimension.alias
|
|
2212
|
+
};
|
|
2213
|
+
if (dimension.encoding) nextDimension.encoding = dimension.encoding;
|
|
2214
|
+
return nextDimension;
|
|
2215
|
+
});
|
|
2216
|
+
return {
|
|
2217
|
+
chartType: vbiDSL.chartType,
|
|
2218
|
+
dataset: queryResult.dataset,
|
|
2219
|
+
dimensions,
|
|
2220
|
+
measures,
|
|
2221
|
+
theme: vbiDSL.theme,
|
|
2222
|
+
locale: vbiDSL.locale
|
|
2223
|
+
};
|
|
2224
|
+
};
|
|
2225
|
+
const defaultVBIChartBuilderAdapters = {
|
|
2226
|
+
buildVQuery: buildVQueryDSL,
|
|
2227
|
+
buildVSeed: buildVSeedDSL
|
|
2228
|
+
};
|
|
2229
|
+
const resolveVBIChartBuilderAdapters = (adapters)=>({
|
|
2230
|
+
buildVQuery: adapters?.buildVQuery ?? defaultVBIChartBuilderAdapters.buildVQuery,
|
|
2231
|
+
buildVSeed: adapters?.buildVSeed ?? defaultVBIChartBuilderAdapters.buildVSeed
|
|
2232
|
+
});
|
|
1077
2233
|
const applyUpdateToDoc = (doc, update, transactionOrigin)=>{
|
|
1078
2234
|
applyUpdate(doc, update, transactionOrigin);
|
|
1079
2235
|
};
|
|
1080
2236
|
const encodeDocStateAsUpdate = (doc, targetStateVector)=>encodeStateAsUpdate(doc, targetStateVector);
|
|
1081
|
-
const
|
|
2237
|
+
const buildVBIChartDSL = (dsl)=>dsl.toJSON();
|
|
1082
2238
|
const getCollectionLength = (value)=>{
|
|
1083
2239
|
if (value instanceof external_yjs_Array) return value.length;
|
|
1084
2240
|
if (Array.isArray(value)) return value.length;
|
|
1085
2241
|
return 0;
|
|
1086
2242
|
};
|
|
1087
|
-
const
|
|
2243
|
+
const isEmptyVBIChartDSL = (dsl)=>{
|
|
1088
2244
|
const dimensionsLength = getCollectionLength(dsl.get('dimensions'));
|
|
1089
2245
|
const measuresLength = getCollectionLength(dsl.get('measures'));
|
|
1090
2246
|
return 0 === dimensionsLength && 0 === measuresLength;
|
|
@@ -1094,7 +2250,7 @@ const getBuilderSchema = async (dsl)=>{
|
|
|
1094
2250
|
const connector = await getConnector(connectorId);
|
|
1095
2251
|
return connector.discoverSchema();
|
|
1096
2252
|
};
|
|
1097
|
-
class
|
|
2253
|
+
class VBIChartBuilder {
|
|
1098
2254
|
doc;
|
|
1099
2255
|
dsl;
|
|
1100
2256
|
adapters;
|
|
@@ -1110,7 +2266,7 @@ class VBIBuilder {
|
|
|
1110
2266
|
constructor(doc, options){
|
|
1111
2267
|
this.doc = doc;
|
|
1112
2268
|
this.dsl = doc.getMap('dsl');
|
|
1113
|
-
this.adapters =
|
|
2269
|
+
this.adapters = resolveVBIChartBuilderAdapters(options?.adapters);
|
|
1114
2270
|
this.undoManager = new undo_manager_UndoManager(this.dsl);
|
|
1115
2271
|
this.chartType = new ChartTypeBuilder(doc, this.dsl);
|
|
1116
2272
|
this.measures = new MeasuresBuilder(doc, this.dsl);
|
|
@@ -1123,7 +2279,7 @@ class VBIBuilder {
|
|
|
1123
2279
|
}
|
|
1124
2280
|
applyUpdate = (update, transactionOrigin)=>applyUpdateToDoc(this.doc, update, transactionOrigin);
|
|
1125
2281
|
encodeStateAsUpdate = (targetStateVector)=>encodeDocStateAsUpdate(this.doc, targetStateVector);
|
|
1126
|
-
buildVSeed = async ()=>{
|
|
2282
|
+
buildVSeed = async (options = {})=>{
|
|
1127
2283
|
const vbiDSL = this.build();
|
|
1128
2284
|
const queryDSL = this.adapters.buildVQuery({
|
|
1129
2285
|
dsl: this.dsl,
|
|
@@ -1134,6 +2290,7 @@ class VBIBuilder {
|
|
|
1134
2290
|
dsl: this.dsl,
|
|
1135
2291
|
vbiDSL,
|
|
1136
2292
|
queryDSL,
|
|
2293
|
+
options,
|
|
1137
2294
|
builder: this
|
|
1138
2295
|
});
|
|
1139
2296
|
};
|
|
@@ -1145,14 +2302,13 @@ class VBIBuilder {
|
|
|
1145
2302
|
builder: this
|
|
1146
2303
|
});
|
|
1147
2304
|
};
|
|
1148
|
-
build = ()=>
|
|
1149
|
-
isEmpty = ()=>
|
|
2305
|
+
build = ()=>buildVBIChartDSL(this.dsl);
|
|
2306
|
+
isEmpty = ()=>isEmptyVBIChartDSL(this.dsl);
|
|
1150
2307
|
getSchema = async ()=>getBuilderSchema(this.dsl);
|
|
1151
2308
|
}
|
|
1152
2309
|
const shouldEnsureIdForObject = (obj, ensureId)=>{
|
|
1153
2310
|
if (true === ensureId) return true;
|
|
1154
|
-
|
|
1155
|
-
return false;
|
|
2311
|
+
return 'field' === ensureId && 'string' == typeof obj.field;
|
|
1156
2312
|
};
|
|
1157
2313
|
const toYMap = (obj, ensureId = false)=>{
|
|
1158
2314
|
const yMap = new external_yjs_Map();
|
|
@@ -1160,23 +2316,9 @@ const toYMap = (obj, ensureId = false)=>{
|
|
|
1160
2316
|
for (const [key, value] of Object.entries(obj)){
|
|
1161
2317
|
if (('conditions' === key || 'children' === key) && Array.isArray(value)) {
|
|
1162
2318
|
const yArr = new external_yjs_Array();
|
|
1163
|
-
for (const child of value)
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
child
|
|
1167
|
-
]);
|
|
1168
|
-
continue;
|
|
1169
|
-
}
|
|
1170
|
-
if ('object' == typeof child && null !== child) {
|
|
1171
|
-
yArr.push([
|
|
1172
|
-
toYMap(child, ensureId)
|
|
1173
|
-
]);
|
|
1174
|
-
continue;
|
|
1175
|
-
}
|
|
1176
|
-
yArr.push([
|
|
1177
|
-
child
|
|
1178
|
-
]);
|
|
1179
|
-
}
|
|
2319
|
+
for (const child of value)yArr.push([
|
|
2320
|
+
toYMap(child, ensureId)
|
|
2321
|
+
]);
|
|
1180
2322
|
yMap.set(key, yArr);
|
|
1181
2323
|
continue;
|
|
1182
2324
|
}
|
|
@@ -1186,65 +2328,34 @@ const toYMap = (obj, ensureId = false)=>{
|
|
|
1186
2328
|
};
|
|
1187
2329
|
const ensureYArray = (arr, ensureId = false)=>{
|
|
1188
2330
|
if (!arr) return new external_yjs_Array();
|
|
1189
|
-
if (arr instanceof external_yjs_Array) return arr;
|
|
1190
2331
|
const yArr = new external_yjs_Array();
|
|
1191
|
-
for (const item of arr)
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
item
|
|
1195
|
-
]);
|
|
1196
|
-
continue;
|
|
1197
|
-
}
|
|
1198
|
-
if ('object' == typeof item && null !== item) {
|
|
1199
|
-
yArr.push([
|
|
1200
|
-
toYMap(item, ensureId)
|
|
1201
|
-
]);
|
|
1202
|
-
continue;
|
|
1203
|
-
}
|
|
1204
|
-
yArr.push([
|
|
1205
|
-
item
|
|
1206
|
-
]);
|
|
1207
|
-
}
|
|
2332
|
+
for (const item of arr)yArr.push([
|
|
2333
|
+
toYMap(item, ensureId)
|
|
2334
|
+
]);
|
|
1208
2335
|
return yArr;
|
|
1209
2336
|
};
|
|
1210
|
-
const
|
|
2337
|
+
const ensureWhereGroup = (whereFilter)=>{
|
|
2338
|
+
const source = whereFilter ?? {
|
|
1211
2339
|
id: 'root',
|
|
1212
2340
|
op: 'and',
|
|
1213
2341
|
conditions: []
|
|
1214
|
-
}
|
|
1215
|
-
const
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
if (sourceWhereFilter instanceof external_yjs_Map) {
|
|
1220
|
-
if (!(whereGroup.get('conditions') instanceof external_yjs_Array)) whereGroup.set('conditions', new external_yjs_Array());
|
|
1221
|
-
if (!whereGroup.get('id')) whereGroup.set('id', 'root');
|
|
1222
|
-
if (!whereGroup.get('op')) whereGroup.set('op', 'and');
|
|
1223
|
-
return whereGroup;
|
|
1224
|
-
}
|
|
1225
|
-
whereGroup.set('id', sourceWhereFilter.id ?? 'root');
|
|
1226
|
-
whereGroup.set('op', sourceWhereFilter.op ?? 'and');
|
|
1227
|
-
whereGroup.set('conditions', ensureYArray(sourceWhereFilter.conditions, true));
|
|
2342
|
+
};
|
|
2343
|
+
const whereGroup = createWhereGroup();
|
|
2344
|
+
whereGroup.set('id', source.id);
|
|
2345
|
+
whereGroup.set('op', source.op);
|
|
2346
|
+
whereGroup.set('conditions', ensureYArray(source.conditions, true));
|
|
1228
2347
|
return whereGroup;
|
|
1229
2348
|
};
|
|
1230
|
-
const
|
|
2349
|
+
const ensureHavingGroup = (havingFilter)=>{
|
|
2350
|
+
const source = havingFilter ?? {
|
|
1231
2351
|
id: 'root',
|
|
1232
2352
|
op: 'and',
|
|
1233
2353
|
conditions: []
|
|
1234
|
-
}
|
|
1235
|
-
const
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
if (sourceHavingFilter instanceof external_yjs_Map) {
|
|
1240
|
-
if (!(havingGroup.get('conditions') instanceof external_yjs_Array)) havingGroup.set('conditions', new external_yjs_Array());
|
|
1241
|
-
if (!havingGroup.get('id')) havingGroup.set('id', 'root');
|
|
1242
|
-
if (!havingGroup.get('op')) havingGroup.set('op', 'and');
|
|
1243
|
-
return havingGroup;
|
|
1244
|
-
}
|
|
1245
|
-
havingGroup.set('id', sourceHavingFilter.id ?? 'root');
|
|
1246
|
-
havingGroup.set('op', sourceHavingFilter.op ?? 'and');
|
|
1247
|
-
havingGroup.set('conditions', ensureYArray(sourceHavingFilter.conditions, true));
|
|
2354
|
+
};
|
|
2355
|
+
const havingGroup = createHavingGroup();
|
|
2356
|
+
havingGroup.set('id', source.id);
|
|
2357
|
+
havingGroup.set('op', source.op);
|
|
2358
|
+
havingGroup.set('conditions', ensureYArray(source.conditions, true));
|
|
1248
2359
|
return havingGroup;
|
|
1249
2360
|
};
|
|
1250
2361
|
const setBaseDSLFields = (dsl, vbi)=>{
|
|
@@ -1255,7 +2366,7 @@ const setBaseDSLFields = (dsl, vbi)=>{
|
|
|
1255
2366
|
if (vbi.locale) dsl.set('locale', vbi.locale);
|
|
1256
2367
|
if (void 0 !== vbi.version) dsl.set('version', vbi.version);
|
|
1257
2368
|
};
|
|
1258
|
-
const
|
|
2369
|
+
const createChartBuilderFromVBIChartDSLInput = (vbi, options)=>{
|
|
1259
2370
|
const doc = new Doc();
|
|
1260
2371
|
const dsl = doc.getMap('dsl');
|
|
1261
2372
|
doc.transact(()=>{
|
|
@@ -1265,9 +2376,9 @@ const fromVBIDSLInput = (vbi, options)=>{
|
|
|
1265
2376
|
dsl.set('measures', ensureYArray(vbi.measures, 'field'));
|
|
1266
2377
|
dsl.set('dimensions', ensureYArray(vbi.dimensions, 'field'));
|
|
1267
2378
|
});
|
|
1268
|
-
return new
|
|
2379
|
+
return new VBIChartBuilder(doc, options);
|
|
1269
2380
|
};
|
|
1270
|
-
const
|
|
2381
|
+
const generateEmptyChartDSL = (connectorId)=>({
|
|
1271
2382
|
connectorId,
|
|
1272
2383
|
chartType: 'table',
|
|
1273
2384
|
measures: [],
|
|
@@ -1299,15 +2410,14 @@ const mergeBuilderOptions = (base, overrides)=>{
|
|
|
1299
2410
|
};
|
|
1300
2411
|
};
|
|
1301
2412
|
function createVBI(defaultBuilderOptions) {
|
|
1302
|
-
const
|
|
2413
|
+
const createChart = (vbi, builderOptions)=>createChartBuilderFromVBIChartDSLInput(vbi, mergeBuilderOptions(defaultBuilderOptions, builderOptions));
|
|
1303
2414
|
return {
|
|
1304
2415
|
connectorMap: connectorMap,
|
|
1305
2416
|
registerConnector: registerConnector,
|
|
1306
2417
|
getConnector: getConnector,
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
create: from
|
|
2418
|
+
generateEmptyChartDSL: generateEmptyChartDSL,
|
|
2419
|
+
createChart
|
|
1310
2420
|
};
|
|
1311
2421
|
}
|
|
1312
2422
|
const VBI = createVBI();
|
|
1313
|
-
export { ChartTypeBuilder, DimensionsBuilder, HavingFilterBuilder, LimitBuilder, LocaleBuilder, MeasuresBuilder, ThemeBuilder, undo_manager_UndoManager as UndoManager, VBI,
|
|
2423
|
+
export { ChartTypeBuilder, DimensionsBuilder, HavingFilterBuilder, LimitBuilder, LocaleBuilder, MeasuresBuilder, ThemeBuilder, undo_manager_UndoManager as UndoManager, VBI, VBIChartBuilder, WhereFilterBuilder, buildVQuery, createVBI, defaultVBIChartBuilderAdapters, findTreeNodesBy, id_id as id, isVBIFilter, isVBIHavingFilter, isVBIHavingGroup, isVBIWhereGroup, preorderTraverse, resolveVBIChartBuilderAdapters };
|