@visactor/vbi 0.4.17 → 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/index.d.ts +1 -0
- package/dist/builder/adapters/vquery-vseed/build-vquery.d.ts +3 -0
- package/dist/builder/adapters/vquery-vseed/build-vseed.d.ts +3 -0
- package/dist/builder/adapters/vquery-vseed/index.d.ts +7 -0
- package/dist/builder/adapters/vquery-vseed/types.d.ts +4 -0
- package/dist/builder/{vbi-builder.d.ts → builder.d.ts} +8 -8
- 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/havingFilter/having-node-builder.d.ts +10 -1
- 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 +2 -1
- package/dist/builder/modules/build.d.ts +2 -2
- package/dist/builder/modules/index.d.ts +2 -5
- package/dist/builder/modules/is-empty.d.ts +1 -1
- package/dist/index.cjs +1578 -386
- package/dist/index.d.ts +6 -3
- package/dist/index.js +1567 -384
- package/dist/pipeline/vqueryDSL/aggregateMap.d.ts +50 -0
- package/dist/pipeline/vqueryDSL/buildOrderBy.d.ts +2 -0
- package/dist/pipeline/vqueryDSL/index.d.ts +2 -3
- package/dist/pipeline/vqueryDSL/resolveDatePredicate.d.ts +7 -0
- package/dist/pipeline/vqueryDSL/types.d.ts +6 -6
- package/dist/types/builder/VBIInterface.d.ts +7 -7
- package/dist/types/builder/adapter.d.ts +23 -0
- 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 -2
- 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 +20 -1
- package/dist/types/dsl/index.d.ts +6 -4
- package/dist/types/dsl/measures/aggregate.d.ts +14 -2
- package/dist/types/dsl/measures/measures.d.ts +177 -8
- package/dist/types/dsl/sort.d.ts +13 -0
- package/dist/types/dsl/vbi/vbi.d.ts +103 -7
- 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 +14 -14
- package/dist/vbi/from/from-vbi-dsl-input.d.ts +4 -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/dist/vbi.d.ts +1 -14
- package/package.json +5 -5
- package/dist/builder/modules/build-vquery.d.ts +0 -4
- package/dist/builder/modules/build-vseed.d.ts +0 -8
- package/dist/builder/modules/create-builder-features.d.ts +0 -14
package/dist/index.js
CHANGED
|
@@ -12,228 +12,6 @@ const getConnector = async (id)=>{
|
|
|
12
12
|
if ('function' == typeof connector) return connector();
|
|
13
13
|
return connector;
|
|
14
14
|
};
|
|
15
|
-
const applyUpdateToDoc = (doc, update, transactionOrigin)=>{
|
|
16
|
-
applyUpdate(doc, update, transactionOrigin);
|
|
17
|
-
};
|
|
18
|
-
const encodeDocStateAsUpdate = (doc, targetStateVector)=>encodeStateAsUpdate(doc, targetStateVector);
|
|
19
|
-
const buildVBIDSL = (dsl)=>dsl.toJSON();
|
|
20
|
-
const buildSelect = (queryDSL, context)=>{
|
|
21
|
-
const { vbiDSL } = context;
|
|
22
|
-
const measures = vbiDSL.measures;
|
|
23
|
-
const dimensions = vbiDSL.dimensions;
|
|
24
|
-
const result = {
|
|
25
|
-
...queryDSL
|
|
26
|
-
};
|
|
27
|
-
const measureNodes = measures.filter((measure)=>MeasuresBuilder.isMeasureNode(measure));
|
|
28
|
-
const measureSelects = measureNodes.map((measure)=>({
|
|
29
|
-
field: measure.field,
|
|
30
|
-
alias: measure.alias,
|
|
31
|
-
aggr: measure.aggregate
|
|
32
|
-
}));
|
|
33
|
-
const dimensionNodes = dimensions.filter((dimension)=>DimensionsBuilder.isDimensionNode(dimension));
|
|
34
|
-
const dimensionSelects = dimensionNodes.map((dimension)=>({
|
|
35
|
-
field: dimension.field,
|
|
36
|
-
alias: dimension.alias
|
|
37
|
-
}));
|
|
38
|
-
result.select = measureSelects.concat(dimensionSelects);
|
|
39
|
-
return result;
|
|
40
|
-
};
|
|
41
|
-
const buildGroupBy = (queryDSL, context)=>{
|
|
42
|
-
const result = {
|
|
43
|
-
...queryDSL
|
|
44
|
-
};
|
|
45
|
-
const { vbiDSL } = context;
|
|
46
|
-
const dimensions = vbiDSL.dimensions;
|
|
47
|
-
const dimensionNodes = dimensions.filter((dimension)=>DimensionsBuilder.isDimensionNode(dimension));
|
|
48
|
-
result.groupBy = dimensionNodes.map((dimension)=>dimension.field);
|
|
49
|
-
return result;
|
|
50
|
-
};
|
|
51
|
-
const buildWhere = (queryDSL, context)=>{
|
|
52
|
-
const { vbiDSL } = context;
|
|
53
|
-
const whereFilter = vbiDSL.whereFilter;
|
|
54
|
-
if (!whereFilter || 0 === whereFilter.conditions.length) return queryDSL;
|
|
55
|
-
const result = {
|
|
56
|
-
...queryDSL
|
|
57
|
-
};
|
|
58
|
-
result.where = mapGroupToCondition(whereFilter);
|
|
59
|
-
return result;
|
|
60
|
-
};
|
|
61
|
-
function isWhereGroup(clause) {
|
|
62
|
-
return 'op' in clause && 'conditions' in clause;
|
|
63
|
-
}
|
|
64
|
-
function mapClauseToCondition(clause) {
|
|
65
|
-
if (isWhereGroup(clause)) return [
|
|
66
|
-
mapGroupToCondition(clause)
|
|
67
|
-
];
|
|
68
|
-
return mapFilterToCondition(clause);
|
|
69
|
-
}
|
|
70
|
-
function mapGroupToCondition(group) {
|
|
71
|
-
return {
|
|
72
|
-
op: group.op,
|
|
73
|
-
conditions: group.conditions.flatMap(mapClauseToCondition)
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
function mapFilterToCondition(filter) {
|
|
77
|
-
if ('between' === filter.op || 'not between' === filter.op) return handleBetweenFilter(filter);
|
|
78
|
-
return handleSimpleFilter(filter);
|
|
79
|
-
}
|
|
80
|
-
function handleBetweenFilter(filter) {
|
|
81
|
-
const value = normalizeBetweenValue(filter.value);
|
|
82
|
-
const lowerCondition = void 0 !== value.min && null !== value.min && '' !== value.min ? {
|
|
83
|
-
field: filter.field,
|
|
84
|
-
op: '<' === value.leftOp ? '>' : '>=',
|
|
85
|
-
value: value.min
|
|
86
|
-
} : void 0;
|
|
87
|
-
const upperCondition = void 0 !== value.max && null !== value.max && '' !== value.max ? {
|
|
88
|
-
field: filter.field,
|
|
89
|
-
op: '<' === value.rightOp ? '<' : '<=',
|
|
90
|
-
value: value.max
|
|
91
|
-
} : void 0;
|
|
92
|
-
if ('not between' === filter.op) {
|
|
93
|
-
const outsideConditions = [
|
|
94
|
-
lowerCondition && invertLowerBound(lowerCondition),
|
|
95
|
-
upperCondition && invertUpperBound(upperCondition)
|
|
96
|
-
].filter(Boolean);
|
|
97
|
-
if (outsideConditions.length <= 1) return outsideConditions;
|
|
98
|
-
return [
|
|
99
|
-
{
|
|
100
|
-
op: 'or',
|
|
101
|
-
conditions: outsideConditions
|
|
102
|
-
}
|
|
103
|
-
];
|
|
104
|
-
}
|
|
105
|
-
return [
|
|
106
|
-
lowerCondition,
|
|
107
|
-
upperCondition
|
|
108
|
-
].filter(Boolean);
|
|
109
|
-
}
|
|
110
|
-
function normalizeBetweenValue(value) {
|
|
111
|
-
if (Array.isArray(value)) return {
|
|
112
|
-
min: value[0],
|
|
113
|
-
max: value[1],
|
|
114
|
-
leftOp: '<=',
|
|
115
|
-
rightOp: '<='
|
|
116
|
-
};
|
|
117
|
-
if ('object' == typeof value && null !== value) return value;
|
|
118
|
-
return {};
|
|
119
|
-
}
|
|
120
|
-
function invertLowerBound(condition) {
|
|
121
|
-
return {
|
|
122
|
-
field: condition.field,
|
|
123
|
-
op: '>' === condition.op ? '<=' : '<',
|
|
124
|
-
value: condition.value
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
function invertUpperBound(condition) {
|
|
128
|
-
return {
|
|
129
|
-
field: condition.field,
|
|
130
|
-
op: '<' === condition.op ? '>=' : '>',
|
|
131
|
-
value: condition.value
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
function handleSimpleFilter(filter) {
|
|
135
|
-
let mappedOp = filter.op ?? '=';
|
|
136
|
-
const value = filter.value;
|
|
137
|
-
if (Array.isArray(value)) {
|
|
138
|
-
if ('=' === mappedOp) mappedOp = 'in';
|
|
139
|
-
if ('!=' === mappedOp) mappedOp = 'not in';
|
|
140
|
-
}
|
|
141
|
-
return [
|
|
142
|
-
{
|
|
143
|
-
field: filter.field,
|
|
144
|
-
op: mappedOp,
|
|
145
|
-
value
|
|
146
|
-
}
|
|
147
|
-
];
|
|
148
|
-
}
|
|
149
|
-
const buildHaving = (queryDSL, context)=>{
|
|
150
|
-
const { vbiDSL } = context;
|
|
151
|
-
const havingFilter = vbiDSL.havingFilter;
|
|
152
|
-
if (!havingFilter || 0 === havingFilter.conditions.length) return queryDSL;
|
|
153
|
-
const result = {
|
|
154
|
-
...queryDSL
|
|
155
|
-
};
|
|
156
|
-
result.having = {
|
|
157
|
-
op: havingFilter.op,
|
|
158
|
-
conditions: havingFilter.conditions.flatMap(buildHaving_mapClauseToCondition)
|
|
159
|
-
};
|
|
160
|
-
return result;
|
|
161
|
-
};
|
|
162
|
-
function isHavingGroup(clause) {
|
|
163
|
-
return 'op' in clause && 'conditions' in clause;
|
|
164
|
-
}
|
|
165
|
-
function buildHaving_mapClauseToCondition(clause) {
|
|
166
|
-
if (isHavingGroup(clause)) return [
|
|
167
|
-
buildHaving_mapGroupToCondition(clause)
|
|
168
|
-
];
|
|
169
|
-
return buildHaving_mapFilterToCondition(clause);
|
|
170
|
-
}
|
|
171
|
-
function buildHaving_mapGroupToCondition(group) {
|
|
172
|
-
return {
|
|
173
|
-
op: group.op,
|
|
174
|
-
conditions: group.conditions.flatMap(buildHaving_mapClauseToCondition)
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
function buildHaving_mapFilterToCondition(filter) {
|
|
178
|
-
const mappedOp = filter.op ?? '=';
|
|
179
|
-
return [
|
|
180
|
-
{
|
|
181
|
-
field: filter.field,
|
|
182
|
-
op: mappedOp,
|
|
183
|
-
value: filter.value
|
|
184
|
-
}
|
|
185
|
-
];
|
|
186
|
-
}
|
|
187
|
-
const buildLimit = (queryDSL, context)=>{
|
|
188
|
-
const result = {
|
|
189
|
-
...queryDSL
|
|
190
|
-
};
|
|
191
|
-
const limit = context.vbiDSL.limit ?? 1000;
|
|
192
|
-
result.limit = limit;
|
|
193
|
-
return result;
|
|
194
|
-
};
|
|
195
|
-
const buildVQuery = (vbiDSL, builder)=>{
|
|
196
|
-
const wrapper = (processor)=>(queryDSL)=>processor(queryDSL, {
|
|
197
|
-
vbiDSL,
|
|
198
|
-
builder
|
|
199
|
-
});
|
|
200
|
-
return pipe({}, wrapper(buildSelect), wrapper(buildGroupBy), wrapper(buildWhere), wrapper(buildHaving), wrapper(buildLimit));
|
|
201
|
-
};
|
|
202
|
-
const buildVQueryDSL = (dsl, builder)=>{
|
|
203
|
-
const vbiDSL = buildVBIDSL(dsl);
|
|
204
|
-
return buildVQuery(vbiDSL, builder);
|
|
205
|
-
};
|
|
206
|
-
const buildVSeedDSL = async ({ vbiDSL, queryDSL })=>{
|
|
207
|
-
const connectorId = vbiDSL.connectorId;
|
|
208
|
-
const connector = await getConnector(connectorId);
|
|
209
|
-
const schema = await connector.discoverSchema();
|
|
210
|
-
const queryResult = await connector.query({
|
|
211
|
-
queryDSL,
|
|
212
|
-
schema,
|
|
213
|
-
connectorId
|
|
214
|
-
});
|
|
215
|
-
return {
|
|
216
|
-
chartType: vbiDSL.chartType,
|
|
217
|
-
dataset: queryResult.dataset,
|
|
218
|
-
theme: vbiDSL.theme,
|
|
219
|
-
locale: vbiDSL.locale
|
|
220
|
-
};
|
|
221
|
-
};
|
|
222
|
-
const getCollectionLength = (value)=>{
|
|
223
|
-
if (value instanceof external_yjs_Array) return value.length;
|
|
224
|
-
if (Array.isArray(value)) return value.length;
|
|
225
|
-
return 0;
|
|
226
|
-
};
|
|
227
|
-
const isEmptyVBIDSL = (dsl)=>{
|
|
228
|
-
const dimensionsLength = getCollectionLength(dsl.get('dimensions'));
|
|
229
|
-
const measuresLength = getCollectionLength(dsl.get('measures'));
|
|
230
|
-
return 0 === dimensionsLength && 0 === measuresLength;
|
|
231
|
-
};
|
|
232
|
-
const getBuilderSchema = async (dsl)=>{
|
|
233
|
-
const connectorId = dsl.get('connectorId');
|
|
234
|
-
const connector = await getConnector(connectorId);
|
|
235
|
-
return connector.discoverSchema();
|
|
236
|
-
};
|
|
237
15
|
class MeasureNodeBuilder {
|
|
238
16
|
yMap;
|
|
239
17
|
constructor(yMap){
|
|
@@ -245,6 +23,12 @@ class MeasureNodeBuilder {
|
|
|
245
23
|
getField() {
|
|
246
24
|
return this.yMap.get('field');
|
|
247
25
|
}
|
|
26
|
+
getEncoding() {
|
|
27
|
+
return this.yMap.get('encoding');
|
|
28
|
+
}
|
|
29
|
+
getSort() {
|
|
30
|
+
return this.yMap.get('sort');
|
|
31
|
+
}
|
|
248
32
|
setAlias(alias) {
|
|
249
33
|
this.yMap.set('alias', alias);
|
|
250
34
|
return this;
|
|
@@ -253,10 +37,29 @@ class MeasureNodeBuilder {
|
|
|
253
37
|
this.yMap.set('encoding', encoding);
|
|
254
38
|
return this;
|
|
255
39
|
}
|
|
40
|
+
setSort(sort) {
|
|
41
|
+
this.yMap.set('sort', sort);
|
|
42
|
+
return this;
|
|
43
|
+
}
|
|
256
44
|
setAggregate(aggregate) {
|
|
257
45
|
this.yMap.set('aggregate', aggregate);
|
|
258
46
|
return this;
|
|
259
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
|
+
}
|
|
260
63
|
toJSON() {
|
|
261
64
|
return this.yMap.toJSON();
|
|
262
65
|
}
|
|
@@ -289,9 +92,369 @@ const normalizeMeasureNodeIds = (measures)=>{
|
|
|
289
92
|
});
|
|
290
93
|
};
|
|
291
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
|
+
};
|
|
292
453
|
class MeasuresBuilder {
|
|
454
|
+
doc;
|
|
293
455
|
dsl;
|
|
294
456
|
constructor(doc, dsl){
|
|
457
|
+
this.doc = doc;
|
|
295
458
|
this.dsl = dsl;
|
|
296
459
|
doc.transact(()=>{
|
|
297
460
|
const measures = getOrCreateMeasures(this.dsl);
|
|
@@ -299,38 +462,47 @@ class MeasuresBuilder {
|
|
|
299
462
|
});
|
|
300
463
|
}
|
|
301
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);
|
|
302
468
|
const measure = {
|
|
303
469
|
id: id_id.uuid(),
|
|
304
470
|
alias: field,
|
|
305
471
|
field,
|
|
306
|
-
encoding
|
|
472
|
+
encoding,
|
|
307
473
|
aggregate: {
|
|
308
474
|
func: 'sum'
|
|
309
475
|
}
|
|
310
476
|
};
|
|
311
477
|
const yMap = new external_yjs_Map();
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
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
|
+
});
|
|
319
487
|
return this;
|
|
320
488
|
}
|
|
321
489
|
remove(id) {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
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
|
+
});
|
|
325
495
|
return this;
|
|
326
496
|
}
|
|
327
497
|
update(id, callback) {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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
|
+
});
|
|
334
506
|
return this;
|
|
335
507
|
}
|
|
336
508
|
find(predicate) {
|
|
@@ -351,9 +523,9 @@ class MeasuresBuilder {
|
|
|
351
523
|
}
|
|
352
524
|
observe(callback) {
|
|
353
525
|
const measures = getOrCreateMeasures(this.dsl);
|
|
354
|
-
measures.
|
|
526
|
+
measures.observeDeep(callback);
|
|
355
527
|
return ()=>{
|
|
356
|
-
measures.
|
|
528
|
+
measures.unobserveDeep(callback);
|
|
357
529
|
};
|
|
358
530
|
}
|
|
359
531
|
static isMeasureNode(node) {
|
|
@@ -374,10 +546,36 @@ class DimensionNodeBuilder {
|
|
|
374
546
|
getField() {
|
|
375
547
|
return this.yMap.get('field');
|
|
376
548
|
}
|
|
549
|
+
getEncoding() {
|
|
550
|
+
return this.yMap.get('encoding');
|
|
551
|
+
}
|
|
552
|
+
getSort() {
|
|
553
|
+
return this.yMap.get('sort');
|
|
554
|
+
}
|
|
377
555
|
setAlias(alias) {
|
|
378
556
|
this.yMap.set('alias', alias);
|
|
379
557
|
return this;
|
|
380
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
|
+
}
|
|
381
579
|
toJSON() {
|
|
382
580
|
return this.yMap.toJSON();
|
|
383
581
|
}
|
|
@@ -395,44 +593,468 @@ const normalizeDimensionNodeIds = (dimensions)=>{
|
|
|
395
593
|
});
|
|
396
594
|
};
|
|
397
595
|
const locateDimensionIndexById = (dimensions, dimensionId)=>dimensions.toArray().findIndex((item)=>item.get('id') === dimensionId);
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
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
|
+
};
|
|
1008
|
+
class DimensionsBuilder {
|
|
1009
|
+
doc;
|
|
1010
|
+
dsl;
|
|
1011
|
+
constructor(doc, dsl){
|
|
1012
|
+
this.doc = doc;
|
|
1013
|
+
this.dsl = dsl;
|
|
1014
|
+
doc.transact(()=>{
|
|
403
1015
|
const dimensions = getOrCreateDimensions(this.dsl);
|
|
404
1016
|
normalizeDimensionNodeIds(dimensions);
|
|
405
1017
|
});
|
|
406
1018
|
}
|
|
407
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);
|
|
408
1023
|
const dimension = {
|
|
409
1024
|
id: id_id.uuid(),
|
|
410
1025
|
alias: field,
|
|
411
|
-
field
|
|
1026
|
+
field,
|
|
1027
|
+
encoding
|
|
412
1028
|
};
|
|
413
1029
|
const yMap = new external_yjs_Map();
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
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
|
+
});
|
|
421
1039
|
return this;
|
|
422
1040
|
}
|
|
423
1041
|
remove(id) {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
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
|
+
});
|
|
427
1047
|
return this;
|
|
428
1048
|
}
|
|
429
1049
|
update(id, callback) {
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
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
|
+
});
|
|
436
1058
|
return this;
|
|
437
1059
|
}
|
|
438
1060
|
find(predicate) {
|
|
@@ -453,9 +1075,9 @@ class DimensionsBuilder {
|
|
|
453
1075
|
}
|
|
454
1076
|
observe(callback) {
|
|
455
1077
|
const dimensions = getOrCreateDimensions(this.dsl);
|
|
456
|
-
dimensions.
|
|
1078
|
+
dimensions.observeDeep(callback);
|
|
457
1079
|
return ()=>{
|
|
458
|
-
dimensions.
|
|
1080
|
+
dimensions.unobserveDeep(callback);
|
|
459
1081
|
};
|
|
460
1082
|
}
|
|
461
1083
|
static isDimensionNode(node) {
|
|
@@ -465,9 +1087,27 @@ class DimensionsBuilder {
|
|
|
465
1087
|
return 'children' in node;
|
|
466
1088
|
}
|
|
467
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
|
+
};
|
|
468
1106
|
class ChartTypeBuilder {
|
|
1107
|
+
doc;
|
|
469
1108
|
dsl;
|
|
470
|
-
constructor(
|
|
1109
|
+
constructor(doc, dsl){
|
|
1110
|
+
this.doc = doc;
|
|
471
1111
|
this.dsl = dsl;
|
|
472
1112
|
}
|
|
473
1113
|
observe(callback) {
|
|
@@ -480,11 +1120,29 @@ class ChartTypeBuilder {
|
|
|
480
1120
|
};
|
|
481
1121
|
}
|
|
482
1122
|
changeChartType(chartType) {
|
|
483
|
-
this.
|
|
1123
|
+
this.doc.transact(()=>{
|
|
1124
|
+
this.dsl.set('chartType', chartType);
|
|
1125
|
+
reapplyDimensionEncodings(this.dsl, chartType);
|
|
1126
|
+
reapplyMeasureEncodings(this.dsl, chartType);
|
|
1127
|
+
});
|
|
484
1128
|
}
|
|
485
1129
|
getChartType() {
|
|
486
1130
|
return this.dsl.get('chartType') || 'table';
|
|
487
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
|
+
}
|
|
488
1146
|
toJSON() {
|
|
489
1147
|
return this.dsl.get('chartType') || 'table';
|
|
490
1148
|
}
|
|
@@ -492,25 +1150,35 @@ class ChartTypeBuilder {
|
|
|
492
1150
|
return [
|
|
493
1151
|
ChartTypeEnum.Table,
|
|
494
1152
|
ChartTypeEnum.PivotTable,
|
|
495
|
-
ChartTypeEnum.Line,
|
|
496
1153
|
ChartTypeEnum.Column,
|
|
497
|
-
ChartTypeEnum.ColumnPercent,
|
|
498
1154
|
ChartTypeEnum.ColumnParallel,
|
|
499
|
-
ChartTypeEnum.
|
|
1155
|
+
ChartTypeEnum.ColumnPercent,
|
|
1156
|
+
ChartTypeEnum.Bar,
|
|
500
1157
|
ChartTypeEnum.BarParallel,
|
|
1158
|
+
ChartTypeEnum.BarPercent,
|
|
1159
|
+
ChartTypeEnum.Line,
|
|
501
1160
|
ChartTypeEnum.Area,
|
|
502
1161
|
ChartTypeEnum.AreaPercent,
|
|
503
1162
|
ChartTypeEnum.DualAxis,
|
|
504
1163
|
ChartTypeEnum.Scatter,
|
|
505
|
-
ChartTypeEnum.Rose,
|
|
506
|
-
ChartTypeEnum.RoseParallel,
|
|
507
1164
|
ChartTypeEnum.Pie,
|
|
508
1165
|
ChartTypeEnum.Donut,
|
|
1166
|
+
ChartTypeEnum.Rose,
|
|
1167
|
+
ChartTypeEnum.RoseParallel,
|
|
509
1168
|
ChartTypeEnum.Radar,
|
|
510
1169
|
ChartTypeEnum.Funnel,
|
|
511
1170
|
ChartTypeEnum.Heatmap,
|
|
512
1171
|
ChartTypeEnum.Boxplot,
|
|
513
|
-
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
|
|
514
1182
|
];
|
|
515
1183
|
}
|
|
516
1184
|
}
|
|
@@ -521,7 +1189,7 @@ function createWhereGroup(op = 'and', groupId = 'root') {
|
|
|
521
1189
|
yMap.set('conditions', new external_yjs_Array());
|
|
522
1190
|
return yMap;
|
|
523
1191
|
}
|
|
524
|
-
function
|
|
1192
|
+
function isWhereGroup(yMap) {
|
|
525
1193
|
return void 0 !== yMap.get('op') && void 0 !== yMap.get('conditions');
|
|
526
1194
|
}
|
|
527
1195
|
function findEntry(collection, entryId) {
|
|
@@ -533,7 +1201,7 @@ function findEntry(collection, entryId) {
|
|
|
533
1201
|
index,
|
|
534
1202
|
item
|
|
535
1203
|
};
|
|
536
|
-
if (
|
|
1204
|
+
if (isWhereGroup(item)) {
|
|
537
1205
|
const nestedCollection = item.get('conditions');
|
|
538
1206
|
const nestedMatch = findEntry(nestedCollection, entryId);
|
|
539
1207
|
if (nestedMatch) return nestedMatch;
|
|
@@ -566,6 +1234,15 @@ class WhereFilterNodeBuilder {
|
|
|
566
1234
|
this.yMap.set('value', value);
|
|
567
1235
|
return this;
|
|
568
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
|
+
}
|
|
569
1246
|
toJSON() {
|
|
570
1247
|
return this.yMap.toJSON();
|
|
571
1248
|
}
|
|
@@ -726,7 +1403,7 @@ class WhereFilterBuilder {
|
|
|
726
1403
|
};
|
|
727
1404
|
}
|
|
728
1405
|
static isGroup(yMap) {
|
|
729
|
-
return
|
|
1406
|
+
return isWhereGroup(yMap);
|
|
730
1407
|
}
|
|
731
1408
|
static isNode(yMap) {
|
|
732
1409
|
return void 0 !== yMap.get('field');
|
|
@@ -739,7 +1416,7 @@ function createHavingGroup(op = 'and', groupId = 'root') {
|
|
|
739
1416
|
yMap.set('conditions', new external_yjs_Array());
|
|
740
1417
|
return yMap;
|
|
741
1418
|
}
|
|
742
|
-
function
|
|
1419
|
+
function isHavingGroup(yMap) {
|
|
743
1420
|
return void 0 !== yMap.get('op') && void 0 !== yMap.get('conditions');
|
|
744
1421
|
}
|
|
745
1422
|
function having_utils_findEntry(collection, entryId) {
|
|
@@ -751,7 +1428,7 @@ function having_utils_findEntry(collection, entryId) {
|
|
|
751
1428
|
index,
|
|
752
1429
|
item
|
|
753
1430
|
};
|
|
754
|
-
if (
|
|
1431
|
+
if (isHavingGroup(item)) {
|
|
755
1432
|
const nestedCollection = item.get('conditions');
|
|
756
1433
|
const nestedMatch = having_utils_findEntry(nestedCollection, entryId);
|
|
757
1434
|
if (nestedMatch) return nestedMatch;
|
|
@@ -772,6 +1449,9 @@ class HavingFilterNodeBuilder {
|
|
|
772
1449
|
getOperator() {
|
|
773
1450
|
return this.yMap.get('op');
|
|
774
1451
|
}
|
|
1452
|
+
getAggregate() {
|
|
1453
|
+
return this.yMap.get('aggregate');
|
|
1454
|
+
}
|
|
775
1455
|
setValue(value) {
|
|
776
1456
|
this.yMap.set('value', value);
|
|
777
1457
|
return this;
|
|
@@ -780,6 +1460,10 @@ class HavingFilterNodeBuilder {
|
|
|
780
1460
|
this.yMap.set('op', operator);
|
|
781
1461
|
return this;
|
|
782
1462
|
}
|
|
1463
|
+
setAggregate(aggregate) {
|
|
1464
|
+
this.yMap.set('aggregate', aggregate);
|
|
1465
|
+
return this;
|
|
1466
|
+
}
|
|
783
1467
|
toJSON() {
|
|
784
1468
|
return this.yMap.toJSON();
|
|
785
1469
|
}
|
|
@@ -806,6 +1490,9 @@ class HavingGroupBuilder {
|
|
|
806
1490
|
const yMap = new external_yjs_Map();
|
|
807
1491
|
yMap.set('id', id_id.uuid());
|
|
808
1492
|
yMap.set('field', field);
|
|
1493
|
+
yMap.set('aggregate', {
|
|
1494
|
+
func: 'sum'
|
|
1495
|
+
});
|
|
809
1496
|
this.getConditions().push([
|
|
810
1497
|
yMap
|
|
811
1498
|
]);
|
|
@@ -863,6 +1550,9 @@ class HavingFilterBuilder {
|
|
|
863
1550
|
const yMap = new external_yjs_Map();
|
|
864
1551
|
yMap.set('id', id_id.uuid());
|
|
865
1552
|
yMap.set('field', field);
|
|
1553
|
+
yMap.set('aggregate', {
|
|
1554
|
+
func: 'sum'
|
|
1555
|
+
});
|
|
866
1556
|
this.getConditions().push([
|
|
867
1557
|
yMap
|
|
868
1558
|
]);
|
|
@@ -940,7 +1630,7 @@ class HavingFilterBuilder {
|
|
|
940
1630
|
};
|
|
941
1631
|
}
|
|
942
1632
|
static isGroup(yMap) {
|
|
943
|
-
return
|
|
1633
|
+
return isHavingGroup(yMap);
|
|
944
1634
|
}
|
|
945
1635
|
static isNode(yMap) {
|
|
946
1636
|
return void 0 !== yMap.get('field');
|
|
@@ -1039,20 +1729,531 @@ class undo_manager_UndoManager {
|
|
|
1039
1729
|
this.manager.clear(clearUndoStack, clearRedoStack);
|
|
1040
1730
|
}
|
|
1041
1731
|
}
|
|
1042
|
-
const
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
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
|
+
};
|
|
1052
1796
|
});
|
|
1053
|
-
|
|
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
|
+
});
|
|
2233
|
+
const applyUpdateToDoc = (doc, update, transactionOrigin)=>{
|
|
2234
|
+
applyUpdate(doc, update, transactionOrigin);
|
|
2235
|
+
};
|
|
2236
|
+
const encodeDocStateAsUpdate = (doc, targetStateVector)=>encodeStateAsUpdate(doc, targetStateVector);
|
|
2237
|
+
const buildVBIChartDSL = (dsl)=>dsl.toJSON();
|
|
2238
|
+
const getCollectionLength = (value)=>{
|
|
2239
|
+
if (value instanceof external_yjs_Array) return value.length;
|
|
2240
|
+
if (Array.isArray(value)) return value.length;
|
|
2241
|
+
return 0;
|
|
2242
|
+
};
|
|
2243
|
+
const isEmptyVBIChartDSL = (dsl)=>{
|
|
2244
|
+
const dimensionsLength = getCollectionLength(dsl.get('dimensions'));
|
|
2245
|
+
const measuresLength = getCollectionLength(dsl.get('measures'));
|
|
2246
|
+
return 0 === dimensionsLength && 0 === measuresLength;
|
|
2247
|
+
};
|
|
2248
|
+
const getBuilderSchema = async (dsl)=>{
|
|
2249
|
+
const connectorId = dsl.get('connectorId');
|
|
2250
|
+
const connector = await getConnector(connectorId);
|
|
2251
|
+
return connector.discoverSchema();
|
|
2252
|
+
};
|
|
2253
|
+
class VBIChartBuilder {
|
|
1054
2254
|
doc;
|
|
1055
2255
|
dsl;
|
|
2256
|
+
adapters;
|
|
1056
2257
|
chartType;
|
|
1057
2258
|
measures;
|
|
1058
2259
|
dimensions;
|
|
@@ -1062,39 +2263,52 @@ class VBIBuilder {
|
|
|
1062
2263
|
locale;
|
|
1063
2264
|
limit;
|
|
1064
2265
|
undoManager;
|
|
1065
|
-
constructor(doc){
|
|
2266
|
+
constructor(doc, options){
|
|
1066
2267
|
this.doc = doc;
|
|
1067
2268
|
this.dsl = doc.getMap('dsl');
|
|
1068
|
-
|
|
1069
|
-
this.undoManager =
|
|
1070
|
-
this.chartType =
|
|
1071
|
-
this.measures =
|
|
1072
|
-
this.dimensions =
|
|
1073
|
-
this.havingFilter =
|
|
1074
|
-
this.whereFilter =
|
|
1075
|
-
this.theme =
|
|
1076
|
-
this.locale =
|
|
1077
|
-
this.limit =
|
|
2269
|
+
this.adapters = resolveVBIChartBuilderAdapters(options?.adapters);
|
|
2270
|
+
this.undoManager = new undo_manager_UndoManager(this.dsl);
|
|
2271
|
+
this.chartType = new ChartTypeBuilder(doc, this.dsl);
|
|
2272
|
+
this.measures = new MeasuresBuilder(doc, this.dsl);
|
|
2273
|
+
this.dimensions = new DimensionsBuilder(doc, this.dsl);
|
|
2274
|
+
this.havingFilter = new HavingFilterBuilder(doc, this.dsl);
|
|
2275
|
+
this.whereFilter = new WhereFilterBuilder(doc, this.dsl);
|
|
2276
|
+
this.theme = new ThemeBuilder(doc, this.dsl);
|
|
2277
|
+
this.locale = new LocaleBuilder(doc, this.dsl);
|
|
2278
|
+
this.limit = new LimitBuilder(doc, this.dsl);
|
|
1078
2279
|
}
|
|
1079
2280
|
applyUpdate = (update, transactionOrigin)=>applyUpdateToDoc(this.doc, update, transactionOrigin);
|
|
1080
2281
|
encodeStateAsUpdate = (targetStateVector)=>encodeDocStateAsUpdate(this.doc, targetStateVector);
|
|
1081
|
-
buildVSeed = async ()=>{
|
|
2282
|
+
buildVSeed = async (options = {})=>{
|
|
1082
2283
|
const vbiDSL = this.build();
|
|
1083
|
-
const queryDSL = this.buildVQuery(
|
|
1084
|
-
|
|
2284
|
+
const queryDSL = this.adapters.buildVQuery({
|
|
2285
|
+
dsl: this.dsl,
|
|
1085
2286
|
vbiDSL,
|
|
1086
|
-
|
|
2287
|
+
builder: this
|
|
2288
|
+
});
|
|
2289
|
+
return this.adapters.buildVSeed({
|
|
2290
|
+
dsl: this.dsl,
|
|
2291
|
+
vbiDSL,
|
|
2292
|
+
queryDSL,
|
|
2293
|
+
options,
|
|
2294
|
+
builder: this
|
|
2295
|
+
});
|
|
2296
|
+
};
|
|
2297
|
+
buildVQuery = ()=>{
|
|
2298
|
+
const vbiDSL = this.build();
|
|
2299
|
+
return this.adapters.buildVQuery({
|
|
2300
|
+
dsl: this.dsl,
|
|
2301
|
+
vbiDSL,
|
|
2302
|
+
builder: this
|
|
1087
2303
|
});
|
|
1088
2304
|
};
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
isEmpty = ()=>isEmptyVBIDSL(this.dsl);
|
|
2305
|
+
build = ()=>buildVBIChartDSL(this.dsl);
|
|
2306
|
+
isEmpty = ()=>isEmptyVBIChartDSL(this.dsl);
|
|
1092
2307
|
getSchema = async ()=>getBuilderSchema(this.dsl);
|
|
1093
2308
|
}
|
|
1094
2309
|
const shouldEnsureIdForObject = (obj, ensureId)=>{
|
|
1095
2310
|
if (true === ensureId) return true;
|
|
1096
|
-
|
|
1097
|
-
return false;
|
|
2311
|
+
return 'field' === ensureId && 'string' == typeof obj.field;
|
|
1098
2312
|
};
|
|
1099
2313
|
const toYMap = (obj, ensureId = false)=>{
|
|
1100
2314
|
const yMap = new external_yjs_Map();
|
|
@@ -1102,23 +2316,9 @@ const toYMap = (obj, ensureId = false)=>{
|
|
|
1102
2316
|
for (const [key, value] of Object.entries(obj)){
|
|
1103
2317
|
if (('conditions' === key || 'children' === key) && Array.isArray(value)) {
|
|
1104
2318
|
const yArr = new external_yjs_Array();
|
|
1105
|
-
for (const child of value)
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
child
|
|
1109
|
-
]);
|
|
1110
|
-
continue;
|
|
1111
|
-
}
|
|
1112
|
-
if ('object' == typeof child && null !== child) {
|
|
1113
|
-
yArr.push([
|
|
1114
|
-
toYMap(child, ensureId)
|
|
1115
|
-
]);
|
|
1116
|
-
continue;
|
|
1117
|
-
}
|
|
1118
|
-
yArr.push([
|
|
1119
|
-
child
|
|
1120
|
-
]);
|
|
1121
|
-
}
|
|
2319
|
+
for (const child of value)yArr.push([
|
|
2320
|
+
toYMap(child, ensureId)
|
|
2321
|
+
]);
|
|
1122
2322
|
yMap.set(key, yArr);
|
|
1123
2323
|
continue;
|
|
1124
2324
|
}
|
|
@@ -1128,65 +2328,34 @@ const toYMap = (obj, ensureId = false)=>{
|
|
|
1128
2328
|
};
|
|
1129
2329
|
const ensureYArray = (arr, ensureId = false)=>{
|
|
1130
2330
|
if (!arr) return new external_yjs_Array();
|
|
1131
|
-
if (arr instanceof external_yjs_Array) return arr;
|
|
1132
2331
|
const yArr = new external_yjs_Array();
|
|
1133
|
-
for (const item of arr)
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
item
|
|
1137
|
-
]);
|
|
1138
|
-
continue;
|
|
1139
|
-
}
|
|
1140
|
-
if ('object' == typeof item && null !== item) {
|
|
1141
|
-
yArr.push([
|
|
1142
|
-
toYMap(item, ensureId)
|
|
1143
|
-
]);
|
|
1144
|
-
continue;
|
|
1145
|
-
}
|
|
1146
|
-
yArr.push([
|
|
1147
|
-
item
|
|
1148
|
-
]);
|
|
1149
|
-
}
|
|
2332
|
+
for (const item of arr)yArr.push([
|
|
2333
|
+
toYMap(item, ensureId)
|
|
2334
|
+
]);
|
|
1150
2335
|
return yArr;
|
|
1151
2336
|
};
|
|
1152
|
-
const
|
|
2337
|
+
const ensureWhereGroup = (whereFilter)=>{
|
|
2338
|
+
const source = whereFilter ?? {
|
|
1153
2339
|
id: 'root',
|
|
1154
2340
|
op: 'and',
|
|
1155
2341
|
conditions: []
|
|
1156
|
-
}
|
|
1157
|
-
const
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
if (sourceWhereFilter instanceof external_yjs_Map) {
|
|
1162
|
-
if (!(whereGroup.get('conditions') instanceof external_yjs_Array)) whereGroup.set('conditions', new external_yjs_Array());
|
|
1163
|
-
if (!whereGroup.get('id')) whereGroup.set('id', 'root');
|
|
1164
|
-
if (!whereGroup.get('op')) whereGroup.set('op', 'and');
|
|
1165
|
-
return whereGroup;
|
|
1166
|
-
}
|
|
1167
|
-
whereGroup.set('id', sourceWhereFilter.id ?? 'root');
|
|
1168
|
-
whereGroup.set('op', sourceWhereFilter.op ?? 'and');
|
|
1169
|
-
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));
|
|
1170
2347
|
return whereGroup;
|
|
1171
2348
|
};
|
|
1172
|
-
const
|
|
2349
|
+
const ensureHavingGroup = (havingFilter)=>{
|
|
2350
|
+
const source = havingFilter ?? {
|
|
1173
2351
|
id: 'root',
|
|
1174
2352
|
op: 'and',
|
|
1175
2353
|
conditions: []
|
|
1176
|
-
}
|
|
1177
|
-
const
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
if (sourceHavingFilter instanceof external_yjs_Map) {
|
|
1182
|
-
if (!(havingGroup.get('conditions') instanceof external_yjs_Array)) havingGroup.set('conditions', new external_yjs_Array());
|
|
1183
|
-
if (!havingGroup.get('id')) havingGroup.set('id', 'root');
|
|
1184
|
-
if (!havingGroup.get('op')) havingGroup.set('op', 'and');
|
|
1185
|
-
return havingGroup;
|
|
1186
|
-
}
|
|
1187
|
-
havingGroup.set('id', sourceHavingFilter.id ?? 'root');
|
|
1188
|
-
havingGroup.set('op', sourceHavingFilter.op ?? 'and');
|
|
1189
|
-
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));
|
|
1190
2359
|
return havingGroup;
|
|
1191
2360
|
};
|
|
1192
2361
|
const setBaseDSLFields = (dsl, vbi)=>{
|
|
@@ -1197,7 +2366,7 @@ const setBaseDSLFields = (dsl, vbi)=>{
|
|
|
1197
2366
|
if (vbi.locale) dsl.set('locale', vbi.locale);
|
|
1198
2367
|
if (void 0 !== vbi.version) dsl.set('version', vbi.version);
|
|
1199
2368
|
};
|
|
1200
|
-
const
|
|
2369
|
+
const createChartBuilderFromVBIChartDSLInput = (vbi, options)=>{
|
|
1201
2370
|
const doc = new Doc();
|
|
1202
2371
|
const dsl = doc.getMap('dsl');
|
|
1203
2372
|
doc.transact(()=>{
|
|
@@ -1207,9 +2376,9 @@ const fromVBIDSLInput = (vbi)=>{
|
|
|
1207
2376
|
dsl.set('measures', ensureYArray(vbi.measures, 'field'));
|
|
1208
2377
|
dsl.set('dimensions', ensureYArray(vbi.dimensions, 'field'));
|
|
1209
2378
|
});
|
|
1210
|
-
return new
|
|
2379
|
+
return new VBIChartBuilder(doc, options);
|
|
1211
2380
|
};
|
|
1212
|
-
const
|
|
2381
|
+
const generateEmptyChartDSL = (connectorId)=>({
|
|
1213
2382
|
connectorId,
|
|
1214
2383
|
chartType: 'table',
|
|
1215
2384
|
measures: [],
|
|
@@ -1228,13 +2397,27 @@ const generateEmptyDSL = (connectorId)=>({
|
|
|
1228
2397
|
locale: 'zh-CN',
|
|
1229
2398
|
version: 0
|
|
1230
2399
|
});
|
|
1231
|
-
const
|
|
2400
|
+
const mergeBuilderOptions = (base, overrides)=>{
|
|
2401
|
+
if (!base) return overrides;
|
|
2402
|
+
if (!overrides) return base;
|
|
2403
|
+
return {
|
|
2404
|
+
...base,
|
|
2405
|
+
...overrides,
|
|
2406
|
+
adapters: {
|
|
2407
|
+
...base.adapters,
|
|
2408
|
+
...overrides.adapters
|
|
2409
|
+
}
|
|
2410
|
+
};
|
|
2411
|
+
};
|
|
2412
|
+
function createVBI(defaultBuilderOptions) {
|
|
2413
|
+
const createChart = (vbi, builderOptions)=>createChartBuilderFromVBIChartDSLInput(vbi, mergeBuilderOptions(defaultBuilderOptions, builderOptions));
|
|
2414
|
+
return {
|
|
1232
2415
|
connectorMap: connectorMap,
|
|
1233
2416
|
registerConnector: registerConnector,
|
|
1234
2417
|
getConnector: getConnector,
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
2418
|
+
generateEmptyChartDSL: generateEmptyChartDSL,
|
|
2419
|
+
createChart
|
|
2420
|
+
};
|
|
2421
|
+
}
|
|
1239
2422
|
const VBI = createVBI();
|
|
1240
|
-
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 };
|