@visactor/vbi 0.4.16 → 0.4.19
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/builder.d.ts +29 -0
- package/dist/builder/features/dimensions/dim-builder.d.ts +10 -10
- package/dist/builder/features/dimensions/dim-node-builder.d.ts +4 -0
- package/dist/builder/features/dimensions/dimension-utils.d.ts +4 -0
- package/dist/builder/features/havingFilter/having-builder.d.ts +3 -3
- package/dist/builder/features/havingFilter/having-node-builder.d.ts +10 -1
- package/dist/builder/features/measures/mea-builder.d.ts +9 -9
- package/dist/builder/features/measures/mea-node-builder.d.ts +4 -0
- package/dist/builder/features/measures/measure-utils.d.ts +4 -0
- package/dist/builder/features/whereFilter/where-builder.d.ts +3 -3
- package/dist/builder/index.d.ts +3 -2
- package/dist/builder/modules/apply-update.d.ts +2 -0
- package/dist/builder/modules/build.d.ts +3 -0
- package/dist/builder/modules/encode-state-as-update.d.ts +2 -0
- package/dist/builder/modules/get-schema.d.ts +5 -0
- package/dist/builder/modules/index.d.ts +5 -0
- package/dist/builder/modules/is-empty.d.ts +2 -0
- package/dist/index.cjs +617 -399
- package/dist/index.d.ts +7 -3
- package/dist/index.js +605 -396
- package/dist/pipeline/vqueryDSL/aggregateMap.d.ts +30 -0
- package/dist/pipeline/vqueryDSL/index.d.ts +2 -3
- package/dist/pipeline/vqueryDSL/types.d.ts +2 -3
- package/dist/types/builder/VBIInterface.d.ts +5 -5
- package/dist/types/builder/adapter.d.ts +21 -0
- package/dist/types/builder/index.d.ts +1 -0
- package/dist/types/dsl/dimensions/dimensions.d.ts +2 -0
- package/dist/types/dsl/havingFilter/having.d.ts +19 -0
- package/dist/types/dsl/index.d.ts +1 -1
- package/dist/types/dsl/measures/aggregate.d.ts +14 -2
- package/dist/types/dsl/measures/measures.d.ts +28 -4
- package/dist/types/dsl/vbi/vbi.d.ts +15 -2
- package/dist/vbi/create-vbi.d.ts +15 -0
- package/dist/vbi/from/from-vbi-dsl-input.d.ts +4 -0
- package/dist/vbi/from/set-base-dsl-fields.d.ts +3 -0
- package/dist/vbi/generate-empty-dsl.d.ts +3 -0
- package/dist/vbi/normalize/ensure-having-group.d.ts +2 -0
- package/dist/vbi/normalize/ensure-where-group.d.ts +2 -0
- package/dist/vbi/normalize/ensure-y-array.d.ts +3 -0
- package/dist/vbi/normalize/to-y-map.d.ts +3 -0
- package/dist/vbi/normalize/types.d.ts +5 -0
- package/dist/vbi.d.ts +1 -0
- package/package.json +5 -5
- package/dist/builder/vbi-builder.d.ts +0 -28
- package/dist/builder/vbi.d.ts +0 -16
package/dist/index.js
CHANGED
|
@@ -1,12 +1,269 @@
|
|
|
1
1
|
import { Array as external_yjs_Array, Doc, Map as external_yjs_Map, UndoManager, applyUpdate, encodeStateAsUpdate } from "yjs";
|
|
2
|
+
import { pipe } from "remeda";
|
|
2
3
|
import { ChartTypeEnum, findTreeNodesBy, preorderTraverse } from "@visactor/vseed";
|
|
3
4
|
import { v4 } from "uuid";
|
|
4
|
-
|
|
5
|
+
const connectorMap = new Map();
|
|
6
|
+
const registerConnector = (id, connector)=>{
|
|
7
|
+
connectorMap.set(id, connector);
|
|
8
|
+
};
|
|
9
|
+
const getConnector = async (id)=>{
|
|
10
|
+
const connector = connectorMap.get(id);
|
|
11
|
+
if (!connector) throw new Error(`connector ${id} not registered`);
|
|
12
|
+
if ('function' == typeof connector) return connector();
|
|
13
|
+
return connector;
|
|
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
|
+
});
|
|
5
259
|
class MeasureNodeBuilder {
|
|
6
260
|
yMap;
|
|
7
261
|
constructor(yMap){
|
|
8
262
|
this.yMap = yMap;
|
|
9
263
|
}
|
|
264
|
+
getId() {
|
|
265
|
+
return this.yMap.get('id');
|
|
266
|
+
}
|
|
10
267
|
getField() {
|
|
11
268
|
return this.yMap.get('field');
|
|
12
269
|
}
|
|
@@ -26,13 +283,46 @@ class MeasureNodeBuilder {
|
|
|
26
283
|
return this.yMap.toJSON();
|
|
27
284
|
}
|
|
28
285
|
}
|
|
286
|
+
const id_id = {
|
|
287
|
+
uuid: ()=>v4()
|
|
288
|
+
};
|
|
289
|
+
function isVBIFilter(clause) {
|
|
290
|
+
return 'field' in clause;
|
|
291
|
+
}
|
|
292
|
+
function isVBIWhereGroup(clause) {
|
|
293
|
+
return 'conditions' in clause;
|
|
294
|
+
}
|
|
295
|
+
function isVBIHavingFilter(clause) {
|
|
296
|
+
return 'field' in clause;
|
|
297
|
+
}
|
|
298
|
+
function isVBIHavingGroup(clause) {
|
|
299
|
+
return 'conditions' in clause;
|
|
300
|
+
}
|
|
301
|
+
const getOrCreateMeasures = (dsl)=>{
|
|
302
|
+
const measures = dsl.get('measures');
|
|
303
|
+
if (measures instanceof external_yjs_Array) return measures;
|
|
304
|
+
const yMeasures = new external_yjs_Array();
|
|
305
|
+
dsl.set('measures', yMeasures);
|
|
306
|
+
return yMeasures;
|
|
307
|
+
};
|
|
308
|
+
const normalizeMeasureNodeIds = (measures)=>{
|
|
309
|
+
measures.toArray().forEach((item)=>{
|
|
310
|
+
if (item instanceof external_yjs_Map && 'string' == typeof item.get('field') && !item.get('id')) item.set('id', id_id.uuid());
|
|
311
|
+
});
|
|
312
|
+
};
|
|
313
|
+
const locateMeasureIndexById = (measures, measureId)=>measures.toArray().findIndex((item)=>item.get('id') === measureId);
|
|
29
314
|
class MeasuresBuilder {
|
|
30
315
|
dsl;
|
|
31
|
-
constructor(
|
|
316
|
+
constructor(doc, dsl){
|
|
32
317
|
this.dsl = dsl;
|
|
318
|
+
doc.transact(()=>{
|
|
319
|
+
const measures = getOrCreateMeasures(this.dsl);
|
|
320
|
+
normalizeMeasureNodeIds(measures);
|
|
321
|
+
});
|
|
33
322
|
}
|
|
34
323
|
add(field, callback) {
|
|
35
324
|
const measure = {
|
|
325
|
+
id: id_id.uuid(),
|
|
36
326
|
alias: field,
|
|
37
327
|
field,
|
|
38
328
|
encoding: 'yAxis',
|
|
@@ -42,45 +332,50 @@ class MeasuresBuilder {
|
|
|
42
332
|
};
|
|
43
333
|
const yMap = new external_yjs_Map();
|
|
44
334
|
for (const [key, value] of Object.entries(measure))yMap.set(key, value);
|
|
45
|
-
this.dsl
|
|
335
|
+
const measures = getOrCreateMeasures(this.dsl);
|
|
336
|
+
measures.push([
|
|
46
337
|
yMap
|
|
47
338
|
]);
|
|
48
339
|
const node = new MeasureNodeBuilder(yMap);
|
|
49
340
|
callback(node);
|
|
50
341
|
return this;
|
|
51
342
|
}
|
|
52
|
-
remove(
|
|
53
|
-
const measures = this.dsl
|
|
54
|
-
const index = measures
|
|
55
|
-
if (-1 !== index)
|
|
343
|
+
remove(id) {
|
|
344
|
+
const measures = getOrCreateMeasures(this.dsl);
|
|
345
|
+
const index = locateMeasureIndexById(measures, id);
|
|
346
|
+
if (-1 !== index) measures.delete(index, 1);
|
|
56
347
|
return this;
|
|
57
348
|
}
|
|
58
|
-
update(
|
|
59
|
-
const measures = this.dsl
|
|
60
|
-
const index = measures
|
|
61
|
-
if (-1 === index) throw new Error(`Measure with
|
|
349
|
+
update(id, callback) {
|
|
350
|
+
const measures = getOrCreateMeasures(this.dsl);
|
|
351
|
+
const index = locateMeasureIndexById(measures, id);
|
|
352
|
+
if (-1 === index) throw new Error(`Measure with id "${id}" not found`);
|
|
62
353
|
const measureYMap = measures.get(index);
|
|
63
354
|
const node = new MeasureNodeBuilder(measureYMap);
|
|
64
355
|
callback(node);
|
|
65
356
|
return this;
|
|
66
357
|
}
|
|
67
|
-
find(
|
|
68
|
-
const measures = this.dsl
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
358
|
+
find(predicate) {
|
|
359
|
+
const measures = getOrCreateMeasures(this.dsl);
|
|
360
|
+
const items = measures.toArray();
|
|
361
|
+
for(let index = 0; index < items.length; index++){
|
|
362
|
+
const node = new MeasureNodeBuilder(items[index]);
|
|
363
|
+
if (predicate(node, index)) return node;
|
|
364
|
+
}
|
|
72
365
|
}
|
|
73
366
|
findAll() {
|
|
74
|
-
const measures = this.dsl
|
|
367
|
+
const measures = getOrCreateMeasures(this.dsl);
|
|
75
368
|
return measures.toArray().map((yMap)=>new MeasureNodeBuilder(yMap));
|
|
76
369
|
}
|
|
77
370
|
toJSON() {
|
|
78
|
-
|
|
371
|
+
const measures = getOrCreateMeasures(this.dsl);
|
|
372
|
+
return measures.toJSON();
|
|
79
373
|
}
|
|
80
374
|
observe(callback) {
|
|
81
|
-
this.dsl
|
|
375
|
+
const measures = getOrCreateMeasures(this.dsl);
|
|
376
|
+
measures.observe(callback);
|
|
82
377
|
return ()=>{
|
|
83
|
-
|
|
378
|
+
measures.unobserve(callback);
|
|
84
379
|
};
|
|
85
380
|
}
|
|
86
381
|
static isMeasureNode(node) {
|
|
@@ -95,6 +390,9 @@ class DimensionNodeBuilder {
|
|
|
95
390
|
constructor(yMap){
|
|
96
391
|
this.yMap = yMap;
|
|
97
392
|
}
|
|
393
|
+
getId() {
|
|
394
|
+
return this.yMap.get('id');
|
|
395
|
+
}
|
|
98
396
|
getField() {
|
|
99
397
|
return this.yMap.get('field');
|
|
100
398
|
}
|
|
@@ -106,57 +404,80 @@ class DimensionNodeBuilder {
|
|
|
106
404
|
return this.yMap.toJSON();
|
|
107
405
|
}
|
|
108
406
|
}
|
|
407
|
+
const getOrCreateDimensions = (dsl)=>{
|
|
408
|
+
const dimensions = dsl.get('dimensions');
|
|
409
|
+
if (dimensions instanceof external_yjs_Array) return dimensions;
|
|
410
|
+
const yDimensions = new external_yjs_Array();
|
|
411
|
+
dsl.set('dimensions', yDimensions);
|
|
412
|
+
return yDimensions;
|
|
413
|
+
};
|
|
414
|
+
const normalizeDimensionNodeIds = (dimensions)=>{
|
|
415
|
+
dimensions.toArray().forEach((item)=>{
|
|
416
|
+
if (item instanceof external_yjs_Map && 'string' == typeof item.get('field') && !item.get('id')) item.set('id', id_id.uuid());
|
|
417
|
+
});
|
|
418
|
+
};
|
|
419
|
+
const locateDimensionIndexById = (dimensions, dimensionId)=>dimensions.toArray().findIndex((item)=>item.get('id') === dimensionId);
|
|
109
420
|
class DimensionsBuilder {
|
|
110
421
|
dsl;
|
|
111
|
-
constructor(
|
|
422
|
+
constructor(doc, dsl){
|
|
112
423
|
this.dsl = dsl;
|
|
424
|
+
doc.transact(()=>{
|
|
425
|
+
const dimensions = getOrCreateDimensions(this.dsl);
|
|
426
|
+
normalizeDimensionNodeIds(dimensions);
|
|
427
|
+
});
|
|
113
428
|
}
|
|
114
429
|
add(field, callback) {
|
|
115
430
|
const dimension = {
|
|
431
|
+
id: id_id.uuid(),
|
|
116
432
|
alias: field,
|
|
117
433
|
field
|
|
118
434
|
};
|
|
119
435
|
const yMap = new external_yjs_Map();
|
|
120
436
|
for (const [key, value] of Object.entries(dimension))yMap.set(key, value);
|
|
121
|
-
this.dsl
|
|
437
|
+
const dimensions = getOrCreateDimensions(this.dsl);
|
|
438
|
+
dimensions.push([
|
|
122
439
|
yMap
|
|
123
440
|
]);
|
|
124
441
|
const node = new DimensionNodeBuilder(yMap);
|
|
125
442
|
callback(node);
|
|
126
443
|
return this;
|
|
127
444
|
}
|
|
128
|
-
remove(
|
|
129
|
-
const dimensions = this.dsl
|
|
130
|
-
const index = dimensions
|
|
131
|
-
if (-1 !== index)
|
|
445
|
+
remove(id) {
|
|
446
|
+
const dimensions = getOrCreateDimensions(this.dsl);
|
|
447
|
+
const index = locateDimensionIndexById(dimensions, id);
|
|
448
|
+
if (-1 !== index) dimensions.delete(index, 1);
|
|
132
449
|
return this;
|
|
133
450
|
}
|
|
134
|
-
update(
|
|
135
|
-
const dimensions = this.dsl
|
|
136
|
-
const index = dimensions
|
|
137
|
-
if (-1 === index) throw new Error(`Dimension with
|
|
451
|
+
update(id, callback) {
|
|
452
|
+
const dimensions = getOrCreateDimensions(this.dsl);
|
|
453
|
+
const index = locateDimensionIndexById(dimensions, id);
|
|
454
|
+
if (-1 === index) throw new Error(`Dimension with id "${id}" not found`);
|
|
138
455
|
const dimensionYMap = dimensions.get(index);
|
|
139
456
|
const node = new DimensionNodeBuilder(dimensionYMap);
|
|
140
457
|
callback(node);
|
|
141
458
|
return this;
|
|
142
459
|
}
|
|
143
|
-
find(
|
|
144
|
-
const dimensions = this.dsl
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
460
|
+
find(predicate) {
|
|
461
|
+
const dimensions = getOrCreateDimensions(this.dsl);
|
|
462
|
+
const items = dimensions.toArray();
|
|
463
|
+
for(let index = 0; index < items.length; index++){
|
|
464
|
+
const node = new DimensionNodeBuilder(items[index]);
|
|
465
|
+
if (predicate(node, index)) return node;
|
|
466
|
+
}
|
|
148
467
|
}
|
|
149
468
|
findAll() {
|
|
150
|
-
const dimensions = this.dsl
|
|
469
|
+
const dimensions = getOrCreateDimensions(this.dsl);
|
|
151
470
|
return dimensions.toArray().map((yMap)=>new DimensionNodeBuilder(yMap));
|
|
152
471
|
}
|
|
153
472
|
toJSON() {
|
|
154
|
-
|
|
473
|
+
const dimensions = getOrCreateDimensions(this.dsl);
|
|
474
|
+
return dimensions.toJSON();
|
|
155
475
|
}
|
|
156
476
|
observe(callback) {
|
|
157
|
-
this.dsl
|
|
477
|
+
const dimensions = getOrCreateDimensions(this.dsl);
|
|
478
|
+
dimensions.observe(callback);
|
|
158
479
|
return ()=>{
|
|
159
|
-
|
|
480
|
+
dimensions.unobserve(callback);
|
|
160
481
|
};
|
|
161
482
|
}
|
|
162
483
|
static isDimensionNode(node) {
|
|
@@ -215,21 +536,6 @@ class ChartTypeBuilder {
|
|
|
215
536
|
];
|
|
216
537
|
}
|
|
217
538
|
}
|
|
218
|
-
const id_id = {
|
|
219
|
-
uuid: ()=>v4()
|
|
220
|
-
};
|
|
221
|
-
function isVBIFilter(clause) {
|
|
222
|
-
return 'field' in clause;
|
|
223
|
-
}
|
|
224
|
-
function isVBIWhereGroup(clause) {
|
|
225
|
-
return 'conditions' in clause;
|
|
226
|
-
}
|
|
227
|
-
function isVBIHavingFilter(clause) {
|
|
228
|
-
return 'field' in clause;
|
|
229
|
-
}
|
|
230
|
-
function isVBIHavingGroup(clause) {
|
|
231
|
-
return 'conditions' in clause;
|
|
232
|
-
}
|
|
233
539
|
function createWhereGroup(op = 'and', groupId = 'root') {
|
|
234
540
|
const yMap = new external_yjs_Map();
|
|
235
541
|
yMap.set('id', groupId);
|
|
@@ -237,7 +543,7 @@ function createWhereGroup(op = 'and', groupId = 'root') {
|
|
|
237
543
|
yMap.set('conditions', new external_yjs_Array());
|
|
238
544
|
return yMap;
|
|
239
545
|
}
|
|
240
|
-
function
|
|
546
|
+
function where_utils_isWhereGroup(yMap) {
|
|
241
547
|
return void 0 !== yMap.get('op') && void 0 !== yMap.get('conditions');
|
|
242
548
|
}
|
|
243
549
|
function findEntry(collection, entryId) {
|
|
@@ -249,7 +555,7 @@ function findEntry(collection, entryId) {
|
|
|
249
555
|
index,
|
|
250
556
|
item
|
|
251
557
|
};
|
|
252
|
-
if (
|
|
558
|
+
if (where_utils_isWhereGroup(item)) {
|
|
253
559
|
const nestedCollection = item.get('conditions');
|
|
254
560
|
const nestedMatch = findEntry(nestedCollection, entryId);
|
|
255
561
|
if (nestedMatch) return nestedMatch;
|
|
@@ -411,13 +717,21 @@ class WhereFilterBuilder {
|
|
|
411
717
|
}
|
|
412
718
|
return this;
|
|
413
719
|
}
|
|
414
|
-
find(
|
|
415
|
-
const
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
720
|
+
find(predicate) {
|
|
721
|
+
const traverse = (collection)=>{
|
|
722
|
+
const items = collection.toArray();
|
|
723
|
+
for(let index = 0; index < items.length; index++){
|
|
724
|
+
const yMap = items[index];
|
|
725
|
+
const entry = WhereFilterBuilder.isGroup(yMap) ? new WhereGroupBuilder(yMap) : new WhereFilterNodeBuilder(yMap);
|
|
726
|
+
if (predicate(entry, index)) return entry;
|
|
727
|
+
if (WhereFilterBuilder.isGroup(yMap)) {
|
|
728
|
+
const nestedCollection = yMap.get('conditions');
|
|
729
|
+
const nestedMatch = traverse(nestedCollection);
|
|
730
|
+
if (nestedMatch) return nestedMatch;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
};
|
|
734
|
+
return traverse(this.getConditions());
|
|
421
735
|
}
|
|
422
736
|
clear() {
|
|
423
737
|
const conditions = this.getConditions();
|
|
@@ -434,7 +748,7 @@ class WhereFilterBuilder {
|
|
|
434
748
|
};
|
|
435
749
|
}
|
|
436
750
|
static isGroup(yMap) {
|
|
437
|
-
return
|
|
751
|
+
return where_utils_isWhereGroup(yMap);
|
|
438
752
|
}
|
|
439
753
|
static isNode(yMap) {
|
|
440
754
|
return void 0 !== yMap.get('field');
|
|
@@ -447,7 +761,7 @@ function createHavingGroup(op = 'and', groupId = 'root') {
|
|
|
447
761
|
yMap.set('conditions', new external_yjs_Array());
|
|
448
762
|
return yMap;
|
|
449
763
|
}
|
|
450
|
-
function
|
|
764
|
+
function having_utils_isHavingGroup(yMap) {
|
|
451
765
|
return void 0 !== yMap.get('op') && void 0 !== yMap.get('conditions');
|
|
452
766
|
}
|
|
453
767
|
function having_utils_findEntry(collection, entryId) {
|
|
@@ -459,7 +773,7 @@ function having_utils_findEntry(collection, entryId) {
|
|
|
459
773
|
index,
|
|
460
774
|
item
|
|
461
775
|
};
|
|
462
|
-
if (
|
|
776
|
+
if (having_utils_isHavingGroup(item)) {
|
|
463
777
|
const nestedCollection = item.get('conditions');
|
|
464
778
|
const nestedMatch = having_utils_findEntry(nestedCollection, entryId);
|
|
465
779
|
if (nestedMatch) return nestedMatch;
|
|
@@ -480,6 +794,9 @@ class HavingFilterNodeBuilder {
|
|
|
480
794
|
getOperator() {
|
|
481
795
|
return this.yMap.get('op');
|
|
482
796
|
}
|
|
797
|
+
getAggregate() {
|
|
798
|
+
return this.yMap.get('aggregate');
|
|
799
|
+
}
|
|
483
800
|
setValue(value) {
|
|
484
801
|
this.yMap.set('value', value);
|
|
485
802
|
return this;
|
|
@@ -488,6 +805,10 @@ class HavingFilterNodeBuilder {
|
|
|
488
805
|
this.yMap.set('op', operator);
|
|
489
806
|
return this;
|
|
490
807
|
}
|
|
808
|
+
setAggregate(aggregate) {
|
|
809
|
+
this.yMap.set('aggregate', aggregate);
|
|
810
|
+
return this;
|
|
811
|
+
}
|
|
491
812
|
toJSON() {
|
|
492
813
|
return this.yMap.toJSON();
|
|
493
814
|
}
|
|
@@ -514,6 +835,9 @@ class HavingGroupBuilder {
|
|
|
514
835
|
const yMap = new external_yjs_Map();
|
|
515
836
|
yMap.set('id', id_id.uuid());
|
|
516
837
|
yMap.set('field', field);
|
|
838
|
+
yMap.set('aggregate', {
|
|
839
|
+
func: 'sum'
|
|
840
|
+
});
|
|
517
841
|
this.getConditions().push([
|
|
518
842
|
yMap
|
|
519
843
|
]);
|
|
@@ -571,6 +895,9 @@ class HavingFilterBuilder {
|
|
|
571
895
|
const yMap = new external_yjs_Map();
|
|
572
896
|
yMap.set('id', id_id.uuid());
|
|
573
897
|
yMap.set('field', field);
|
|
898
|
+
yMap.set('aggregate', {
|
|
899
|
+
func: 'sum'
|
|
900
|
+
});
|
|
574
901
|
this.getConditions().push([
|
|
575
902
|
yMap
|
|
576
903
|
]);
|
|
@@ -617,13 +944,21 @@ class HavingFilterBuilder {
|
|
|
617
944
|
}
|
|
618
945
|
return this;
|
|
619
946
|
}
|
|
620
|
-
find(
|
|
621
|
-
const
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
947
|
+
find(predicate) {
|
|
948
|
+
const traverse = (collection)=>{
|
|
949
|
+
const items = collection.toArray();
|
|
950
|
+
for(let index = 0; index < items.length; index++){
|
|
951
|
+
const yMap = items[index];
|
|
952
|
+
const entry = HavingFilterBuilder.isGroup(yMap) ? new HavingGroupBuilder(yMap) : new HavingFilterNodeBuilder(yMap);
|
|
953
|
+
if (predicate(entry, index)) return entry;
|
|
954
|
+
if (HavingFilterBuilder.isGroup(yMap)) {
|
|
955
|
+
const nestedCollection = yMap.get('conditions');
|
|
956
|
+
const nestedMatch = traverse(nestedCollection);
|
|
957
|
+
if (nestedMatch) return nestedMatch;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
};
|
|
961
|
+
return traverse(this.getConditions());
|
|
627
962
|
}
|
|
628
963
|
clear() {
|
|
629
964
|
const conditions = this.getConditions();
|
|
@@ -640,7 +975,7 @@ class HavingFilterBuilder {
|
|
|
640
975
|
};
|
|
641
976
|
}
|
|
642
977
|
static isGroup(yMap) {
|
|
643
|
-
return
|
|
978
|
+
return having_utils_isHavingGroup(yMap);
|
|
644
979
|
}
|
|
645
980
|
static isNode(yMap) {
|
|
646
981
|
return void 0 !== yMap.get('field');
|
|
@@ -739,202 +1074,30 @@ class undo_manager_UndoManager {
|
|
|
739
1074
|
this.manager.clear(clearUndoStack, clearRedoStack);
|
|
740
1075
|
}
|
|
741
1076
|
}
|
|
742
|
-
const
|
|
743
|
-
|
|
744
|
-
const measures = vbiDSL.measures;
|
|
745
|
-
const dimensions = vbiDSL.dimensions;
|
|
746
|
-
const result = {
|
|
747
|
-
...queryDSL
|
|
748
|
-
};
|
|
749
|
-
const measureNodes = measures.filter((measure)=>MeasuresBuilder.isMeasureNode(measure));
|
|
750
|
-
const measureSelects = measureNodes.map((measure)=>({
|
|
751
|
-
field: measure.field,
|
|
752
|
-
alias: measure.alias,
|
|
753
|
-
aggr: measure.aggregate
|
|
754
|
-
}));
|
|
755
|
-
const dimensionNodes = dimensions.filter((dimension)=>DimensionsBuilder.isDimensionNode(dimension));
|
|
756
|
-
const dimensionSelects = dimensionNodes.map((dimension)=>({
|
|
757
|
-
field: dimension.field,
|
|
758
|
-
alias: dimension.alias
|
|
759
|
-
}));
|
|
760
|
-
result.select = measureSelects.concat(dimensionSelects);
|
|
761
|
-
return result;
|
|
762
|
-
};
|
|
763
|
-
const buildGroupBy = (queryDSL, context)=>{
|
|
764
|
-
const result = {
|
|
765
|
-
...queryDSL
|
|
766
|
-
};
|
|
767
|
-
const { vbiDSL } = context;
|
|
768
|
-
const dimensions = vbiDSL.dimensions;
|
|
769
|
-
const dimensionNodes = dimensions.filter((dimension)=>DimensionsBuilder.isDimensionNode(dimension));
|
|
770
|
-
result.groupBy = dimensionNodes.map((dimension)=>dimension.field);
|
|
771
|
-
return result;
|
|
772
|
-
};
|
|
773
|
-
const buildWhere = (queryDSL, context)=>{
|
|
774
|
-
const { vbiDSL } = context;
|
|
775
|
-
const whereFilter = vbiDSL.whereFilter;
|
|
776
|
-
if (!whereFilter || 0 === whereFilter.conditions.length) return queryDSL;
|
|
777
|
-
const result = {
|
|
778
|
-
...queryDSL
|
|
779
|
-
};
|
|
780
|
-
result.where = mapGroupToCondition(whereFilter);
|
|
781
|
-
return result;
|
|
782
|
-
};
|
|
783
|
-
function buildWhere_isWhereGroup(clause) {
|
|
784
|
-
return 'op' in clause && 'conditions' in clause;
|
|
785
|
-
}
|
|
786
|
-
function mapClauseToCondition(clause) {
|
|
787
|
-
if (buildWhere_isWhereGroup(clause)) return [
|
|
788
|
-
mapGroupToCondition(clause)
|
|
789
|
-
];
|
|
790
|
-
return mapFilterToCondition(clause);
|
|
791
|
-
}
|
|
792
|
-
function mapGroupToCondition(group) {
|
|
793
|
-
return {
|
|
794
|
-
op: group.op,
|
|
795
|
-
conditions: group.conditions.flatMap(mapClauseToCondition)
|
|
796
|
-
};
|
|
797
|
-
}
|
|
798
|
-
function mapFilterToCondition(filter) {
|
|
799
|
-
if ('between' === filter.op || 'not between' === filter.op) return handleBetweenFilter(filter);
|
|
800
|
-
return handleSimpleFilter(filter);
|
|
801
|
-
}
|
|
802
|
-
function handleBetweenFilter(filter) {
|
|
803
|
-
const value = normalizeBetweenValue(filter.value);
|
|
804
|
-
const lowerCondition = void 0 !== value.min && null !== value.min && '' !== value.min ? {
|
|
805
|
-
field: filter.field,
|
|
806
|
-
op: '<' === value.leftOp ? '>' : '>=',
|
|
807
|
-
value: value.min
|
|
808
|
-
} : void 0;
|
|
809
|
-
const upperCondition = void 0 !== value.max && null !== value.max && '' !== value.max ? {
|
|
810
|
-
field: filter.field,
|
|
811
|
-
op: '<' === value.rightOp ? '<' : '<=',
|
|
812
|
-
value: value.max
|
|
813
|
-
} : void 0;
|
|
814
|
-
if ('not between' === filter.op) {
|
|
815
|
-
const outsideConditions = [
|
|
816
|
-
lowerCondition && invertLowerBound(lowerCondition),
|
|
817
|
-
upperCondition && invertUpperBound(upperCondition)
|
|
818
|
-
].filter(Boolean);
|
|
819
|
-
if (outsideConditions.length <= 1) return outsideConditions;
|
|
820
|
-
return [
|
|
821
|
-
{
|
|
822
|
-
op: 'or',
|
|
823
|
-
conditions: outsideConditions
|
|
824
|
-
}
|
|
825
|
-
];
|
|
826
|
-
}
|
|
827
|
-
return [
|
|
828
|
-
lowerCondition,
|
|
829
|
-
upperCondition
|
|
830
|
-
].filter(Boolean);
|
|
831
|
-
}
|
|
832
|
-
function normalizeBetweenValue(value) {
|
|
833
|
-
if (Array.isArray(value)) return {
|
|
834
|
-
min: value[0],
|
|
835
|
-
max: value[1],
|
|
836
|
-
leftOp: '<=',
|
|
837
|
-
rightOp: '<='
|
|
838
|
-
};
|
|
839
|
-
if ('object' == typeof value && null !== value) return value;
|
|
840
|
-
return {};
|
|
841
|
-
}
|
|
842
|
-
function invertLowerBound(condition) {
|
|
843
|
-
return {
|
|
844
|
-
field: condition.field,
|
|
845
|
-
op: '>' === condition.op ? '<=' : '<',
|
|
846
|
-
value: condition.value
|
|
847
|
-
};
|
|
848
|
-
}
|
|
849
|
-
function invertUpperBound(condition) {
|
|
850
|
-
return {
|
|
851
|
-
field: condition.field,
|
|
852
|
-
op: '<' === condition.op ? '>=' : '>',
|
|
853
|
-
value: condition.value
|
|
854
|
-
};
|
|
855
|
-
}
|
|
856
|
-
function handleSimpleFilter(filter) {
|
|
857
|
-
let mappedOp = filter.op ?? '=';
|
|
858
|
-
const value = filter.value;
|
|
859
|
-
if (Array.isArray(value)) {
|
|
860
|
-
if ('=' === mappedOp) mappedOp = 'in';
|
|
861
|
-
if ('!=' === mappedOp) mappedOp = 'not in';
|
|
862
|
-
}
|
|
863
|
-
return [
|
|
864
|
-
{
|
|
865
|
-
field: filter.field,
|
|
866
|
-
op: mappedOp,
|
|
867
|
-
value
|
|
868
|
-
}
|
|
869
|
-
];
|
|
870
|
-
}
|
|
871
|
-
const buildHaving = (queryDSL, context)=>{
|
|
872
|
-
const { vbiDSL } = context;
|
|
873
|
-
const havingFilter = vbiDSL.havingFilter;
|
|
874
|
-
if (!havingFilter || 0 === havingFilter.conditions.length) return queryDSL;
|
|
875
|
-
const result = {
|
|
876
|
-
...queryDSL
|
|
877
|
-
};
|
|
878
|
-
result.having = {
|
|
879
|
-
op: havingFilter.op,
|
|
880
|
-
conditions: havingFilter.conditions.flatMap(buildHaving_mapClauseToCondition)
|
|
881
|
-
};
|
|
882
|
-
return result;
|
|
883
|
-
};
|
|
884
|
-
function buildHaving_isHavingGroup(clause) {
|
|
885
|
-
return 'op' in clause && 'conditions' in clause;
|
|
886
|
-
}
|
|
887
|
-
function buildHaving_mapClauseToCondition(clause) {
|
|
888
|
-
if (buildHaving_isHavingGroup(clause)) return [
|
|
889
|
-
buildHaving_mapGroupToCondition(clause)
|
|
890
|
-
];
|
|
891
|
-
return buildHaving_mapFilterToCondition(clause);
|
|
892
|
-
}
|
|
893
|
-
function buildHaving_mapGroupToCondition(group) {
|
|
894
|
-
return {
|
|
895
|
-
op: group.op,
|
|
896
|
-
conditions: group.conditions.flatMap(buildHaving_mapClauseToCondition)
|
|
897
|
-
};
|
|
898
|
-
}
|
|
899
|
-
function buildHaving_mapFilterToCondition(filter) {
|
|
900
|
-
const mappedOp = filter.op ?? '=';
|
|
901
|
-
return [
|
|
902
|
-
{
|
|
903
|
-
field: filter.field,
|
|
904
|
-
op: mappedOp,
|
|
905
|
-
value: filter.value
|
|
906
|
-
}
|
|
907
|
-
];
|
|
908
|
-
}
|
|
909
|
-
const buildLimit = (queryDSL, context)=>{
|
|
910
|
-
const result = {
|
|
911
|
-
...queryDSL
|
|
912
|
-
};
|
|
913
|
-
const limit = context.vbiDSL.limit ?? 1000;
|
|
914
|
-
result.limit = limit;
|
|
915
|
-
return result;
|
|
1077
|
+
const applyUpdateToDoc = (doc, update, transactionOrigin)=>{
|
|
1078
|
+
applyUpdate(doc, update, transactionOrigin);
|
|
916
1079
|
};
|
|
917
|
-
const
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
return
|
|
1080
|
+
const encodeDocStateAsUpdate = (doc, targetStateVector)=>encodeStateAsUpdate(doc, targetStateVector);
|
|
1081
|
+
const buildVBIDSL = (dsl)=>dsl.toJSON();
|
|
1082
|
+
const getCollectionLength = (value)=>{
|
|
1083
|
+
if (value instanceof external_yjs_Array) return value.length;
|
|
1084
|
+
if (Array.isArray(value)) return value.length;
|
|
1085
|
+
return 0;
|
|
923
1086
|
};
|
|
924
|
-
const
|
|
925
|
-
const
|
|
926
|
-
|
|
1087
|
+
const isEmptyVBIDSL = (dsl)=>{
|
|
1088
|
+
const dimensionsLength = getCollectionLength(dsl.get('dimensions'));
|
|
1089
|
+
const measuresLength = getCollectionLength(dsl.get('measures'));
|
|
1090
|
+
return 0 === dimensionsLength && 0 === measuresLength;
|
|
927
1091
|
};
|
|
928
|
-
const
|
|
929
|
-
const
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
return connector;
|
|
1092
|
+
const getBuilderSchema = async (dsl)=>{
|
|
1093
|
+
const connectorId = dsl.get('connectorId');
|
|
1094
|
+
const connector = await getConnector(connectorId);
|
|
1095
|
+
return connector.discoverSchema();
|
|
933
1096
|
};
|
|
934
1097
|
class VBIBuilder {
|
|
935
1098
|
doc;
|
|
936
1099
|
dsl;
|
|
937
|
-
|
|
1100
|
+
adapters;
|
|
938
1101
|
chartType;
|
|
939
1102
|
measures;
|
|
940
1103
|
dimensions;
|
|
@@ -943,9 +1106,11 @@ class VBIBuilder {
|
|
|
943
1106
|
theme;
|
|
944
1107
|
locale;
|
|
945
1108
|
limit;
|
|
946
|
-
|
|
1109
|
+
undoManager;
|
|
1110
|
+
constructor(doc, options){
|
|
947
1111
|
this.doc = doc;
|
|
948
1112
|
this.dsl = doc.getMap('dsl');
|
|
1113
|
+
this.adapters = resolveVBIBuilderAdapters(options?.adapters);
|
|
949
1114
|
this.undoManager = new undo_manager_UndoManager(this.dsl);
|
|
950
1115
|
this.chartType = new ChartTypeBuilder(doc, this.dsl);
|
|
951
1116
|
this.measures = new MeasuresBuilder(doc, this.dsl);
|
|
@@ -956,149 +1121,193 @@ class VBIBuilder {
|
|
|
956
1121
|
this.locale = new LocaleBuilder(doc, this.dsl);
|
|
957
1122
|
this.limit = new LimitBuilder(doc, this.dsl);
|
|
958
1123
|
}
|
|
959
|
-
applyUpdate(update)
|
|
960
|
-
|
|
961
|
-
}
|
|
962
|
-
encodeStateAsUpdate(targetStateVector) {
|
|
963
|
-
return encodeStateAsUpdate(this.doc, targetStateVector);
|
|
964
|
-
}
|
|
1124
|
+
applyUpdate = (update, transactionOrigin)=>applyUpdateToDoc(this.doc, update, transactionOrigin);
|
|
1125
|
+
encodeStateAsUpdate = (targetStateVector)=>encodeDocStateAsUpdate(this.doc, targetStateVector);
|
|
965
1126
|
buildVSeed = async ()=>{
|
|
966
1127
|
const vbiDSL = this.build();
|
|
967
|
-
const
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
1128
|
+
const queryDSL = this.adapters.buildVQuery({
|
|
1129
|
+
dsl: this.dsl,
|
|
1130
|
+
vbiDSL,
|
|
1131
|
+
builder: this
|
|
1132
|
+
});
|
|
1133
|
+
return this.adapters.buildVSeed({
|
|
1134
|
+
dsl: this.dsl,
|
|
1135
|
+
vbiDSL,
|
|
972
1136
|
queryDSL,
|
|
973
|
-
|
|
974
|
-
connectorId
|
|
1137
|
+
builder: this
|
|
975
1138
|
});
|
|
976
|
-
return {
|
|
977
|
-
chartType: vbiDSL.chartType,
|
|
978
|
-
dataset: queryResult.dataset,
|
|
979
|
-
theme: vbiDSL.theme,
|
|
980
|
-
locale: vbiDSL.locale
|
|
981
|
-
};
|
|
982
1139
|
};
|
|
983
1140
|
buildVQuery = ()=>{
|
|
984
1141
|
const vbiDSL = this.build();
|
|
985
|
-
return buildVQuery(
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
const con = await getConnector(connectorId);
|
|
991
|
-
const result = await con.discoverSchema();
|
|
992
|
-
return result;
|
|
1142
|
+
return this.adapters.buildVQuery({
|
|
1143
|
+
dsl: this.dsl,
|
|
1144
|
+
vbiDSL,
|
|
1145
|
+
builder: this
|
|
1146
|
+
});
|
|
993
1147
|
};
|
|
1148
|
+
build = ()=>buildVBIDSL(this.dsl);
|
|
1149
|
+
isEmpty = ()=>isEmptyVBIDSL(this.dsl);
|
|
1150
|
+
getSchema = async ()=>getBuilderSchema(this.dsl);
|
|
994
1151
|
}
|
|
995
|
-
const
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
conditions: []
|
|
1013
|
-
},
|
|
1014
|
-
theme: 'light',
|
|
1015
|
-
locale: 'zh-CN',
|
|
1016
|
-
version: 0
|
|
1017
|
-
}),
|
|
1018
|
-
from: (vbi)=>{
|
|
1019
|
-
const doc = new Doc();
|
|
1020
|
-
const dsl = doc.getMap('dsl');
|
|
1021
|
-
doc.transact(()=>{
|
|
1022
|
-
if (vbi.connectorId) dsl.set('connectorId', vbi.connectorId);
|
|
1023
|
-
if (vbi.chartType) dsl.set('chartType', vbi.chartType);
|
|
1024
|
-
if (vbi.theme) dsl.set('theme', vbi.theme);
|
|
1025
|
-
if (vbi.limit) dsl.set('limit', vbi.limit);
|
|
1026
|
-
if (vbi.locale) dsl.set('locale', vbi.locale);
|
|
1027
|
-
if (void 0 !== vbi.version) dsl.set('version', vbi.version);
|
|
1028
|
-
const toYMap = (obj, ensureId = false)=>{
|
|
1029
|
-
const yMap = new external_yjs_Map();
|
|
1030
|
-
if (ensureId && !obj.id) yMap.set('id', id_id.uuid());
|
|
1031
|
-
for (const [key, value] of Object.entries(obj))if ('conditions' === key && Array.isArray(value)) {
|
|
1032
|
-
const yArr = new external_yjs_Array();
|
|
1033
|
-
value.forEach((child)=>{
|
|
1034
|
-
if (child instanceof external_yjs_Map) yArr.push([
|
|
1035
|
-
child
|
|
1036
|
-
]);
|
|
1037
|
-
else if ('object' == typeof child && null !== child) yArr.push([
|
|
1038
|
-
toYMap(child, true)
|
|
1039
|
-
]);
|
|
1040
|
-
else yArr.push([
|
|
1041
|
-
child
|
|
1042
|
-
]);
|
|
1043
|
-
});
|
|
1044
|
-
yMap.set(key, yArr);
|
|
1045
|
-
} else yMap.set(key, value);
|
|
1046
|
-
return yMap;
|
|
1047
|
-
};
|
|
1048
|
-
const ensureYArray = (arr, ensureId = false)=>{
|
|
1049
|
-
if (!arr) return new external_yjs_Array();
|
|
1050
|
-
if (arr instanceof external_yjs_Array) return arr;
|
|
1051
|
-
const yArr = new external_yjs_Array();
|
|
1052
|
-
arr.forEach((item)=>{
|
|
1053
|
-
if (item instanceof external_yjs_Map) yArr.push([
|
|
1054
|
-
item
|
|
1055
|
-
]);
|
|
1056
|
-
else if ('object' == typeof item && null !== item) yArr.push([
|
|
1057
|
-
toYMap(item, ensureId)
|
|
1058
|
-
]);
|
|
1059
|
-
else yArr.push([
|
|
1060
|
-
item
|
|
1061
|
-
]);
|
|
1062
|
-
});
|
|
1063
|
-
return yArr;
|
|
1064
|
-
};
|
|
1065
|
-
const whereFilter = vbi.whereFilter ?? {
|
|
1066
|
-
id: 'root',
|
|
1067
|
-
op: 'and',
|
|
1068
|
-
conditions: []
|
|
1069
|
-
};
|
|
1070
|
-
const whereGroup = whereFilter instanceof external_yjs_Map ? whereFilter : createWhereGroup();
|
|
1071
|
-
if (whereFilter instanceof external_yjs_Map) {
|
|
1072
|
-
if (!(whereGroup.get('conditions') instanceof external_yjs_Array)) whereGroup.set('conditions', new external_yjs_Array());
|
|
1073
|
-
if (!whereGroup.get('id')) whereGroup.set('id', 'root');
|
|
1074
|
-
if (!whereGroup.get('op')) whereGroup.set('op', 'and');
|
|
1075
|
-
} else {
|
|
1076
|
-
whereGroup.set('id', whereFilter.id ?? 'root');
|
|
1077
|
-
whereGroup.set('op', whereFilter.op ?? 'and');
|
|
1078
|
-
whereGroup.set('conditions', ensureYArray(whereFilter.conditions, true));
|
|
1152
|
+
const shouldEnsureIdForObject = (obj, ensureId)=>{
|
|
1153
|
+
if (true === ensureId) return true;
|
|
1154
|
+
if ('field' === ensureId) return 'string' == typeof obj.field;
|
|
1155
|
+
return false;
|
|
1156
|
+
};
|
|
1157
|
+
const toYMap = (obj, ensureId = false)=>{
|
|
1158
|
+
const yMap = new external_yjs_Map();
|
|
1159
|
+
if (shouldEnsureIdForObject(obj, ensureId) && !obj.id) yMap.set('id', id_id.uuid());
|
|
1160
|
+
for (const [key, value] of Object.entries(obj)){
|
|
1161
|
+
if (('conditions' === key || 'children' === key) && Array.isArray(value)) {
|
|
1162
|
+
const yArr = new external_yjs_Array();
|
|
1163
|
+
for (const child of value){
|
|
1164
|
+
if (child instanceof external_yjs_Map) {
|
|
1165
|
+
yArr.push([
|
|
1166
|
+
child
|
|
1167
|
+
]);
|
|
1168
|
+
continue;
|
|
1079
1169
|
}
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
};
|
|
1086
|
-
const havingGroup = havingFilter instanceof external_yjs_Map ? havingFilter : createHavingGroup();
|
|
1087
|
-
if (havingFilter instanceof external_yjs_Map) {
|
|
1088
|
-
if (!(havingGroup.get('conditions') instanceof external_yjs_Array)) havingGroup.set('conditions', new external_yjs_Array());
|
|
1089
|
-
if (!havingGroup.get('id')) havingGroup.set('id', 'root');
|
|
1090
|
-
if (!havingGroup.get('op')) havingGroup.set('op', 'and');
|
|
1091
|
-
} else {
|
|
1092
|
-
havingGroup.set('id', havingFilter.id ?? 'root');
|
|
1093
|
-
havingGroup.set('op', havingFilter.op ?? 'and');
|
|
1094
|
-
havingGroup.set('conditions', ensureYArray(havingFilter.conditions, true));
|
|
1170
|
+
if ('object' == typeof child && null !== child) {
|
|
1171
|
+
yArr.push([
|
|
1172
|
+
toYMap(child, ensureId)
|
|
1173
|
+
]);
|
|
1174
|
+
continue;
|
|
1095
1175
|
}
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
}
|
|
1100
|
-
|
|
1176
|
+
yArr.push([
|
|
1177
|
+
child
|
|
1178
|
+
]);
|
|
1179
|
+
}
|
|
1180
|
+
yMap.set(key, yArr);
|
|
1181
|
+
continue;
|
|
1182
|
+
}
|
|
1183
|
+
yMap.set(key, value);
|
|
1184
|
+
}
|
|
1185
|
+
return yMap;
|
|
1186
|
+
};
|
|
1187
|
+
const ensureYArray = (arr, ensureId = false)=>{
|
|
1188
|
+
if (!arr) return new external_yjs_Array();
|
|
1189
|
+
if (arr instanceof external_yjs_Array) return arr;
|
|
1190
|
+
const yArr = new external_yjs_Array();
|
|
1191
|
+
for (const item of arr){
|
|
1192
|
+
if (item instanceof external_yjs_Map) {
|
|
1193
|
+
yArr.push([
|
|
1194
|
+
item
|
|
1195
|
+
]);
|
|
1196
|
+
continue;
|
|
1197
|
+
}
|
|
1198
|
+
if ('object' == typeof item && null !== item) {
|
|
1199
|
+
yArr.push([
|
|
1200
|
+
toYMap(item, ensureId)
|
|
1201
|
+
]);
|
|
1202
|
+
continue;
|
|
1101
1203
|
}
|
|
1204
|
+
yArr.push([
|
|
1205
|
+
item
|
|
1206
|
+
]);
|
|
1207
|
+
}
|
|
1208
|
+
return yArr;
|
|
1209
|
+
};
|
|
1210
|
+
const getDefaultWhereFilter = ()=>({
|
|
1211
|
+
id: 'root',
|
|
1212
|
+
op: 'and',
|
|
1213
|
+
conditions: []
|
|
1214
|
+
});
|
|
1215
|
+
const isFilterGroupInput = (value)=>'object' == typeof value && null !== value;
|
|
1216
|
+
const ensureWhereGroup = (whereFilter)=>{
|
|
1217
|
+
const sourceWhereFilter = whereFilter instanceof external_yjs_Map || isFilterGroupInput(whereFilter) ? whereFilter : getDefaultWhereFilter();
|
|
1218
|
+
const whereGroup = sourceWhereFilter instanceof external_yjs_Map ? sourceWhereFilter : createWhereGroup();
|
|
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));
|
|
1228
|
+
return whereGroup;
|
|
1229
|
+
};
|
|
1230
|
+
const getDefaultHavingFilter = ()=>({
|
|
1231
|
+
id: 'root',
|
|
1232
|
+
op: 'and',
|
|
1233
|
+
conditions: []
|
|
1102
1234
|
});
|
|
1235
|
+
const ensure_having_group_isFilterGroupInput = (value)=>'object' == typeof value && null !== value;
|
|
1236
|
+
const ensureHavingGroup = (havingFilter)=>{
|
|
1237
|
+
const sourceHavingFilter = havingFilter instanceof external_yjs_Map || ensure_having_group_isFilterGroupInput(havingFilter) ? havingFilter : getDefaultHavingFilter();
|
|
1238
|
+
const havingGroup = sourceHavingFilter instanceof external_yjs_Map ? sourceHavingFilter : createHavingGroup();
|
|
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));
|
|
1248
|
+
return havingGroup;
|
|
1249
|
+
};
|
|
1250
|
+
const setBaseDSLFields = (dsl, vbi)=>{
|
|
1251
|
+
if (vbi.connectorId) dsl.set('connectorId', vbi.connectorId);
|
|
1252
|
+
if (vbi.chartType) dsl.set('chartType', vbi.chartType);
|
|
1253
|
+
if (vbi.theme) dsl.set('theme', vbi.theme);
|
|
1254
|
+
if (vbi.limit) dsl.set('limit', vbi.limit);
|
|
1255
|
+
if (vbi.locale) dsl.set('locale', vbi.locale);
|
|
1256
|
+
if (void 0 !== vbi.version) dsl.set('version', vbi.version);
|
|
1257
|
+
};
|
|
1258
|
+
const fromVBIDSLInput = (vbi, options)=>{
|
|
1259
|
+
const doc = new Doc();
|
|
1260
|
+
const dsl = doc.getMap('dsl');
|
|
1261
|
+
doc.transact(()=>{
|
|
1262
|
+
setBaseDSLFields(dsl, vbi);
|
|
1263
|
+
dsl.set('whereFilter', ensureWhereGroup(vbi.whereFilter));
|
|
1264
|
+
dsl.set('havingFilter', ensureHavingGroup(vbi.havingFilter));
|
|
1265
|
+
dsl.set('measures', ensureYArray(vbi.measures, 'field'));
|
|
1266
|
+
dsl.set('dimensions', ensureYArray(vbi.dimensions, 'field'));
|
|
1267
|
+
});
|
|
1268
|
+
return new VBIBuilder(doc, options);
|
|
1269
|
+
};
|
|
1270
|
+
const generateEmptyDSL = (connectorId)=>({
|
|
1271
|
+
connectorId,
|
|
1272
|
+
chartType: 'table',
|
|
1273
|
+
measures: [],
|
|
1274
|
+
dimensions: [],
|
|
1275
|
+
whereFilter: {
|
|
1276
|
+
id: 'root',
|
|
1277
|
+
op: 'and',
|
|
1278
|
+
conditions: []
|
|
1279
|
+
},
|
|
1280
|
+
havingFilter: {
|
|
1281
|
+
id: 'root',
|
|
1282
|
+
op: 'and',
|
|
1283
|
+
conditions: []
|
|
1284
|
+
},
|
|
1285
|
+
theme: 'light',
|
|
1286
|
+
locale: 'zh-CN',
|
|
1287
|
+
version: 0
|
|
1288
|
+
});
|
|
1289
|
+
const mergeBuilderOptions = (base, overrides)=>{
|
|
1290
|
+
if (!base) return overrides;
|
|
1291
|
+
if (!overrides) return base;
|
|
1292
|
+
return {
|
|
1293
|
+
...base,
|
|
1294
|
+
...overrides,
|
|
1295
|
+
adapters: {
|
|
1296
|
+
...base.adapters,
|
|
1297
|
+
...overrides.adapters
|
|
1298
|
+
}
|
|
1299
|
+
};
|
|
1300
|
+
};
|
|
1301
|
+
function createVBI(defaultBuilderOptions) {
|
|
1302
|
+
const from = (vbi, builderOptions)=>fromVBIDSLInput(vbi, mergeBuilderOptions(defaultBuilderOptions, builderOptions));
|
|
1303
|
+
return {
|
|
1304
|
+
connectorMap: connectorMap,
|
|
1305
|
+
registerConnector: registerConnector,
|
|
1306
|
+
getConnector: getConnector,
|
|
1307
|
+
generateEmptyDSL: generateEmptyDSL,
|
|
1308
|
+
from,
|
|
1309
|
+
create: from
|
|
1310
|
+
};
|
|
1311
|
+
}
|
|
1103
1312
|
const VBI = createVBI();
|
|
1104
|
-
export { ChartTypeBuilder, DimensionsBuilder, HavingFilterBuilder, LimitBuilder, LocaleBuilder, MeasuresBuilder, ThemeBuilder, undo_manager_UndoManager as UndoManager, VBI, VBIBuilder, WhereFilterBuilder, buildVQuery, findTreeNodesBy, id_id as id, isVBIFilter, isVBIHavingFilter, isVBIHavingGroup, isVBIWhereGroup, preorderTraverse };
|
|
1313
|
+
export { ChartTypeBuilder, DimensionsBuilder, HavingFilterBuilder, LimitBuilder, LocaleBuilder, MeasuresBuilder, ThemeBuilder, undo_manager_UndoManager as UndoManager, VBI, VBIBuilder, WhereFilterBuilder, buildVQuery, createVBI, defaultVBIBuilderAdapters, findTreeNodesBy, id_id as id, isVBIFilter, isVBIHavingFilter, isVBIHavingGroup, isVBIWhereGroup, preorderTraverse, resolveVBIBuilderAdapters };
|