@visactor/vbi 0.4.15 → 0.4.17
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/{sub-builders → features}/chart-type/chart-type-builder.d.ts +1 -1
- package/dist/builder/{sub-builders → features}/dimensions/dim-builder.d.ts +11 -11
- package/dist/builder/{sub-builders → features}/dimensions/dim-node-builder.d.ts +5 -1
- package/dist/builder/features/dimensions/dimension-utils.d.ts +4 -0
- package/dist/builder/{sub-builders/havingFilters → features/havingFilter}/having-builder.d.ts +15 -15
- package/dist/builder/{sub-builders/havingFilters → features/havingFilter}/having-group-builder.d.ts +4 -3
- package/dist/builder/{sub-builders/havingFilters → features/havingFilter}/having-node-builder.d.ts +2 -2
- package/dist/builder/features/havingFilter/having-utils.d.ts +9 -0
- package/dist/builder/features/havingFilter/index.d.ts +3 -0
- package/dist/builder/features/index.d.ts +9 -0
- package/dist/builder/features/limit/index.d.ts +1 -0
- package/dist/builder/features/limit/limit-builder.d.ts +31 -0
- package/dist/builder/features/locale/index.d.ts +1 -0
- package/dist/builder/features/locale/locale-builder.d.ts +31 -0
- package/dist/builder/{sub-builders → features}/measures/mea-builder.d.ts +10 -10
- package/dist/builder/{sub-builders → features}/measures/mea-node-builder.d.ts +5 -1
- package/dist/builder/features/measures/measure-utils.d.ts +4 -0
- package/dist/builder/features/theme/index.d.ts +1 -0
- package/dist/builder/features/theme/theme-builder.d.ts +31 -0
- package/dist/builder/features/undo-manager/index.d.ts +1 -0
- package/dist/builder/{sub-builders/whereFilters → features/whereFilter}/index.d.ts +1 -1
- package/dist/builder/{sub-builders/whereFilters → features/whereFilter}/where-builder.d.ts +14 -14
- package/dist/builder/{sub-builders/whereFilters → features/whereFilter}/where-group-builder.d.ts +2 -1
- package/dist/builder/{sub-builders/whereFilters → features/whereFilter}/where-node-builder.d.ts +6 -1
- package/dist/builder/features/whereFilter/where-utils.d.ts +9 -0
- package/dist/builder/index.d.ts +2 -3
- package/dist/builder/modules/apply-update.d.ts +2 -0
- package/dist/builder/modules/build-vquery.d.ts +4 -0
- package/dist/builder/modules/build-vseed.d.ts +8 -0
- package/dist/builder/modules/build.d.ts +3 -0
- package/dist/builder/modules/create-builder-features.d.ts +14 -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 +8 -0
- package/dist/builder/modules/is-empty.d.ts +2 -0
- package/dist/builder/vbi-builder.d.ts +13 -17
- package/dist/index.cjs +921 -598
- package/dist/index.d.ts +3 -2
- package/dist/index.js +904 -599
- package/dist/types/builder/VBIInterface.d.ts +7 -5
- package/dist/types/dsl/dimensions/dimensions.d.ts +2 -0
- package/dist/types/dsl/{havingFilters → havingFilter}/having.d.ts +10 -6
- package/dist/types/dsl/index.d.ts +4 -5
- package/dist/types/dsl/measures/measures.d.ts +18 -2
- package/dist/types/dsl/vbi/vbi.d.ts +42 -4
- package/dist/types/dsl/{whereFilters → whereFilter}/filters.d.ts +10 -6
- package/dist/utils/filter-guards.d.ts +5 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/vbi/create-vbi.d.ts +14 -0
- package/dist/vbi/from/from-vbi-dsl-input.d.ts +3 -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 +14 -0
- package/package.json +5 -5
- package/dist/builder/sub-builders/havingFilters/index.d.ts +0 -3
- package/dist/builder/sub-builders/index.d.ts +0 -5
- package/dist/builder/vbi.d.ts +0 -16
- /package/dist/builder/{sub-builders → features}/chart-type/index.d.ts +0 -0
- /package/dist/builder/{sub-builders → features}/dimensions/index.d.ts +0 -0
- /package/dist/builder/{sub-builders → features}/measures/index.d.ts +0 -0
- /package/dist/builder/{undo-manager.d.ts → features/undo-manager/undo-manager.d.ts} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,89 +1,247 @@
|
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
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 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;
|
|
21
63
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
findAll() {
|
|
63
|
-
const dimensions = this.dsl.get('dimensions');
|
|
64
|
-
return dimensions.toArray().map((yMap)=>new DimensionNodeBuilder(yMap));
|
|
65
|
-
}
|
|
66
|
-
toJson() {
|
|
67
|
-
return this.dsl.get('dimensions').toJSON();
|
|
68
|
-
}
|
|
69
|
-
observe(callback) {
|
|
70
|
-
this.dsl.get('dimensions').observe(callback);
|
|
71
|
-
return ()=>{
|
|
72
|
-
this.dsl.get('dimensions').unobserve(callback);
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
static isDimensionNode(node) {
|
|
76
|
-
return 'alias' in node && !('children' in node);
|
|
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
|
+
];
|
|
77
104
|
}
|
|
78
|
-
|
|
79
|
-
|
|
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';
|
|
80
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
|
+
];
|
|
81
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
|
+
};
|
|
82
237
|
class MeasureNodeBuilder {
|
|
83
238
|
yMap;
|
|
84
239
|
constructor(yMap){
|
|
85
240
|
this.yMap = yMap;
|
|
86
241
|
}
|
|
242
|
+
getId() {
|
|
243
|
+
return this.yMap.get('id');
|
|
244
|
+
}
|
|
87
245
|
getField() {
|
|
88
246
|
return this.yMap.get('field');
|
|
89
247
|
}
|
|
@@ -99,17 +257,50 @@ class MeasureNodeBuilder {
|
|
|
99
257
|
this.yMap.set('aggregate', aggregate);
|
|
100
258
|
return this;
|
|
101
259
|
}
|
|
102
|
-
|
|
260
|
+
toJSON() {
|
|
103
261
|
return this.yMap.toJSON();
|
|
104
262
|
}
|
|
105
263
|
}
|
|
264
|
+
const id_id = {
|
|
265
|
+
uuid: ()=>v4()
|
|
266
|
+
};
|
|
267
|
+
function isVBIFilter(clause) {
|
|
268
|
+
return 'field' in clause;
|
|
269
|
+
}
|
|
270
|
+
function isVBIWhereGroup(clause) {
|
|
271
|
+
return 'conditions' in clause;
|
|
272
|
+
}
|
|
273
|
+
function isVBIHavingFilter(clause) {
|
|
274
|
+
return 'field' in clause;
|
|
275
|
+
}
|
|
276
|
+
function isVBIHavingGroup(clause) {
|
|
277
|
+
return 'conditions' in clause;
|
|
278
|
+
}
|
|
279
|
+
const getOrCreateMeasures = (dsl)=>{
|
|
280
|
+
const measures = dsl.get('measures');
|
|
281
|
+
if (measures instanceof external_yjs_Array) return measures;
|
|
282
|
+
const yMeasures = new external_yjs_Array();
|
|
283
|
+
dsl.set('measures', yMeasures);
|
|
284
|
+
return yMeasures;
|
|
285
|
+
};
|
|
286
|
+
const normalizeMeasureNodeIds = (measures)=>{
|
|
287
|
+
measures.toArray().forEach((item)=>{
|
|
288
|
+
if (item instanceof external_yjs_Map && 'string' == typeof item.get('field') && !item.get('id')) item.set('id', id_id.uuid());
|
|
289
|
+
});
|
|
290
|
+
};
|
|
291
|
+
const locateMeasureIndexById = (measures, measureId)=>measures.toArray().findIndex((item)=>item.get('id') === measureId);
|
|
106
292
|
class MeasuresBuilder {
|
|
107
293
|
dsl;
|
|
108
|
-
constructor(
|
|
294
|
+
constructor(doc, dsl){
|
|
109
295
|
this.dsl = dsl;
|
|
296
|
+
doc.transact(()=>{
|
|
297
|
+
const measures = getOrCreateMeasures(this.dsl);
|
|
298
|
+
normalizeMeasureNodeIds(measures);
|
|
299
|
+
});
|
|
110
300
|
}
|
|
111
301
|
add(field, callback) {
|
|
112
302
|
const measure = {
|
|
303
|
+
id: id_id.uuid(),
|
|
113
304
|
alias: field,
|
|
114
305
|
field,
|
|
115
306
|
encoding: 'yAxis',
|
|
@@ -119,45 +310,50 @@ class MeasuresBuilder {
|
|
|
119
310
|
};
|
|
120
311
|
const yMap = new external_yjs_Map();
|
|
121
312
|
for (const [key, value] of Object.entries(measure))yMap.set(key, value);
|
|
122
|
-
this.dsl
|
|
313
|
+
const measures = getOrCreateMeasures(this.dsl);
|
|
314
|
+
measures.push([
|
|
123
315
|
yMap
|
|
124
316
|
]);
|
|
125
317
|
const node = new MeasureNodeBuilder(yMap);
|
|
126
318
|
callback(node);
|
|
127
319
|
return this;
|
|
128
320
|
}
|
|
129
|
-
remove(
|
|
130
|
-
const measures = this.dsl
|
|
131
|
-
const index = measures
|
|
132
|
-
if (-1 !== index)
|
|
321
|
+
remove(id) {
|
|
322
|
+
const measures = getOrCreateMeasures(this.dsl);
|
|
323
|
+
const index = locateMeasureIndexById(measures, id);
|
|
324
|
+
if (-1 !== index) measures.delete(index, 1);
|
|
133
325
|
return this;
|
|
134
326
|
}
|
|
135
|
-
update(
|
|
136
|
-
const measures = this.dsl
|
|
137
|
-
const index = measures
|
|
138
|
-
if (-1 === index) throw new Error(`Measure with
|
|
327
|
+
update(id, callback) {
|
|
328
|
+
const measures = getOrCreateMeasures(this.dsl);
|
|
329
|
+
const index = locateMeasureIndexById(measures, id);
|
|
330
|
+
if (-1 === index) throw new Error(`Measure with id "${id}" not found`);
|
|
139
331
|
const measureYMap = measures.get(index);
|
|
140
332
|
const node = new MeasureNodeBuilder(measureYMap);
|
|
141
333
|
callback(node);
|
|
142
334
|
return this;
|
|
143
335
|
}
|
|
144
|
-
find(
|
|
145
|
-
const measures = this.dsl
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
336
|
+
find(predicate) {
|
|
337
|
+
const measures = getOrCreateMeasures(this.dsl);
|
|
338
|
+
const items = measures.toArray();
|
|
339
|
+
for(let index = 0; index < items.length; index++){
|
|
340
|
+
const node = new MeasureNodeBuilder(items[index]);
|
|
341
|
+
if (predicate(node, index)) return node;
|
|
342
|
+
}
|
|
149
343
|
}
|
|
150
344
|
findAll() {
|
|
151
|
-
const measures = this.dsl
|
|
345
|
+
const measures = getOrCreateMeasures(this.dsl);
|
|
152
346
|
return measures.toArray().map((yMap)=>new MeasureNodeBuilder(yMap));
|
|
153
347
|
}
|
|
154
|
-
|
|
155
|
-
|
|
348
|
+
toJSON() {
|
|
349
|
+
const measures = getOrCreateMeasures(this.dsl);
|
|
350
|
+
return measures.toJSON();
|
|
156
351
|
}
|
|
157
352
|
observe(callback) {
|
|
158
|
-
this.dsl
|
|
353
|
+
const measures = getOrCreateMeasures(this.dsl);
|
|
354
|
+
measures.observe(callback);
|
|
159
355
|
return ()=>{
|
|
160
|
-
|
|
356
|
+
measures.unobserve(callback);
|
|
161
357
|
};
|
|
162
358
|
}
|
|
163
359
|
static isMeasureNode(node) {
|
|
@@ -167,10 +363,7 @@ class MeasuresBuilder {
|
|
|
167
363
|
return 'children' in node;
|
|
168
364
|
}
|
|
169
365
|
}
|
|
170
|
-
|
|
171
|
-
uuid: ()=>v4()
|
|
172
|
-
};
|
|
173
|
-
class HavingFiltersNodeBuilder {
|
|
366
|
+
class DimensionNodeBuilder {
|
|
174
367
|
yMap;
|
|
175
368
|
constructor(yMap){
|
|
176
369
|
this.yMap = yMap;
|
|
@@ -181,26 +374,210 @@ class HavingFiltersNodeBuilder {
|
|
|
181
374
|
getField() {
|
|
182
375
|
return this.yMap.get('field');
|
|
183
376
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
187
|
-
setValue(value) {
|
|
188
|
-
this.yMap.set('value', value);
|
|
189
|
-
return this;
|
|
190
|
-
}
|
|
191
|
-
setOperator(operator) {
|
|
192
|
-
this.yMap.set('op', operator);
|
|
377
|
+
setAlias(alias) {
|
|
378
|
+
this.yMap.set('alias', alias);
|
|
193
379
|
return this;
|
|
194
380
|
}
|
|
195
|
-
|
|
381
|
+
toJSON() {
|
|
196
382
|
return this.yMap.toJSON();
|
|
197
383
|
}
|
|
198
384
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
385
|
+
const getOrCreateDimensions = (dsl)=>{
|
|
386
|
+
const dimensions = dsl.get('dimensions');
|
|
387
|
+
if (dimensions instanceof external_yjs_Array) return dimensions;
|
|
388
|
+
const yDimensions = new external_yjs_Array();
|
|
389
|
+
dsl.set('dimensions', yDimensions);
|
|
390
|
+
return yDimensions;
|
|
391
|
+
};
|
|
392
|
+
const normalizeDimensionNodeIds = (dimensions)=>{
|
|
393
|
+
dimensions.toArray().forEach((item)=>{
|
|
394
|
+
if (item instanceof external_yjs_Map && 'string' == typeof item.get('field') && !item.get('id')) item.set('id', id_id.uuid());
|
|
395
|
+
});
|
|
396
|
+
};
|
|
397
|
+
const locateDimensionIndexById = (dimensions, dimensionId)=>dimensions.toArray().findIndex((item)=>item.get('id') === dimensionId);
|
|
398
|
+
class DimensionsBuilder {
|
|
399
|
+
dsl;
|
|
400
|
+
constructor(doc, dsl){
|
|
401
|
+
this.dsl = dsl;
|
|
402
|
+
doc.transact(()=>{
|
|
403
|
+
const dimensions = getOrCreateDimensions(this.dsl);
|
|
404
|
+
normalizeDimensionNodeIds(dimensions);
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
add(field, callback) {
|
|
408
|
+
const dimension = {
|
|
409
|
+
id: id_id.uuid(),
|
|
410
|
+
alias: field,
|
|
411
|
+
field
|
|
412
|
+
};
|
|
413
|
+
const yMap = new external_yjs_Map();
|
|
414
|
+
for (const [key, value] of Object.entries(dimension))yMap.set(key, value);
|
|
415
|
+
const dimensions = getOrCreateDimensions(this.dsl);
|
|
416
|
+
dimensions.push([
|
|
417
|
+
yMap
|
|
418
|
+
]);
|
|
419
|
+
const node = new DimensionNodeBuilder(yMap);
|
|
420
|
+
callback(node);
|
|
421
|
+
return this;
|
|
422
|
+
}
|
|
423
|
+
remove(id) {
|
|
424
|
+
const dimensions = getOrCreateDimensions(this.dsl);
|
|
425
|
+
const index = locateDimensionIndexById(dimensions, id);
|
|
426
|
+
if (-1 !== index) dimensions.delete(index, 1);
|
|
427
|
+
return this;
|
|
428
|
+
}
|
|
429
|
+
update(id, callback) {
|
|
430
|
+
const dimensions = getOrCreateDimensions(this.dsl);
|
|
431
|
+
const index = locateDimensionIndexById(dimensions, id);
|
|
432
|
+
if (-1 === index) throw new Error(`Dimension with id "${id}" not found`);
|
|
433
|
+
const dimensionYMap = dimensions.get(index);
|
|
434
|
+
const node = new DimensionNodeBuilder(dimensionYMap);
|
|
435
|
+
callback(node);
|
|
436
|
+
return this;
|
|
437
|
+
}
|
|
438
|
+
find(predicate) {
|
|
439
|
+
const dimensions = getOrCreateDimensions(this.dsl);
|
|
440
|
+
const items = dimensions.toArray();
|
|
441
|
+
for(let index = 0; index < items.length; index++){
|
|
442
|
+
const node = new DimensionNodeBuilder(items[index]);
|
|
443
|
+
if (predicate(node, index)) return node;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
findAll() {
|
|
447
|
+
const dimensions = getOrCreateDimensions(this.dsl);
|
|
448
|
+
return dimensions.toArray().map((yMap)=>new DimensionNodeBuilder(yMap));
|
|
449
|
+
}
|
|
450
|
+
toJSON() {
|
|
451
|
+
const dimensions = getOrCreateDimensions(this.dsl);
|
|
452
|
+
return dimensions.toJSON();
|
|
453
|
+
}
|
|
454
|
+
observe(callback) {
|
|
455
|
+
const dimensions = getOrCreateDimensions(this.dsl);
|
|
456
|
+
dimensions.observe(callback);
|
|
457
|
+
return ()=>{
|
|
458
|
+
dimensions.unobserve(callback);
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
static isDimensionNode(node) {
|
|
462
|
+
return 'alias' in node && !('children' in node);
|
|
463
|
+
}
|
|
464
|
+
static isDimensionGroup(node) {
|
|
465
|
+
return 'children' in node;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
class ChartTypeBuilder {
|
|
469
|
+
dsl;
|
|
470
|
+
constructor(_doc, dsl){
|
|
471
|
+
this.dsl = dsl;
|
|
472
|
+
}
|
|
473
|
+
observe(callback) {
|
|
474
|
+
const wrapper = (e, trans)=>{
|
|
475
|
+
if (e.keysChanged.has('chartType')) callback(e, trans);
|
|
476
|
+
};
|
|
477
|
+
this.dsl.observe(wrapper);
|
|
478
|
+
return ()=>{
|
|
479
|
+
this.dsl.unobserve(wrapper);
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
changeChartType(chartType) {
|
|
483
|
+
this.dsl.set('chartType', chartType);
|
|
484
|
+
}
|
|
485
|
+
getChartType() {
|
|
486
|
+
return this.dsl.get('chartType') || 'table';
|
|
487
|
+
}
|
|
488
|
+
toJSON() {
|
|
489
|
+
return this.dsl.get('chartType') || 'table';
|
|
490
|
+
}
|
|
491
|
+
getAvailableChartTypes() {
|
|
492
|
+
return [
|
|
493
|
+
ChartTypeEnum.Table,
|
|
494
|
+
ChartTypeEnum.PivotTable,
|
|
495
|
+
ChartTypeEnum.Line,
|
|
496
|
+
ChartTypeEnum.Column,
|
|
497
|
+
ChartTypeEnum.ColumnPercent,
|
|
498
|
+
ChartTypeEnum.ColumnParallel,
|
|
499
|
+
ChartTypeEnum.BarPercent,
|
|
500
|
+
ChartTypeEnum.BarParallel,
|
|
501
|
+
ChartTypeEnum.Area,
|
|
502
|
+
ChartTypeEnum.AreaPercent,
|
|
503
|
+
ChartTypeEnum.DualAxis,
|
|
504
|
+
ChartTypeEnum.Scatter,
|
|
505
|
+
ChartTypeEnum.Rose,
|
|
506
|
+
ChartTypeEnum.RoseParallel,
|
|
507
|
+
ChartTypeEnum.Pie,
|
|
508
|
+
ChartTypeEnum.Donut,
|
|
509
|
+
ChartTypeEnum.Radar,
|
|
510
|
+
ChartTypeEnum.Funnel,
|
|
511
|
+
ChartTypeEnum.Heatmap,
|
|
512
|
+
ChartTypeEnum.Boxplot,
|
|
513
|
+
ChartTypeEnum.Histogram
|
|
514
|
+
];
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
function createWhereGroup(op = 'and', groupId = 'root') {
|
|
518
|
+
const yMap = new external_yjs_Map();
|
|
519
|
+
yMap.set('id', groupId);
|
|
520
|
+
yMap.set('op', op);
|
|
521
|
+
yMap.set('conditions', new external_yjs_Array());
|
|
522
|
+
return yMap;
|
|
523
|
+
}
|
|
524
|
+
function where_utils_isWhereGroup(yMap) {
|
|
525
|
+
return void 0 !== yMap.get('op') && void 0 !== yMap.get('conditions');
|
|
526
|
+
}
|
|
527
|
+
function findEntry(collection, entryId) {
|
|
528
|
+
const items = collection.toArray();
|
|
529
|
+
for(let index = 0; index < items.length; index++){
|
|
530
|
+
const item = items[index];
|
|
531
|
+
if (item.get('id') === entryId) return {
|
|
532
|
+
collection,
|
|
533
|
+
index,
|
|
534
|
+
item
|
|
535
|
+
};
|
|
536
|
+
if (where_utils_isWhereGroup(item)) {
|
|
537
|
+
const nestedCollection = item.get('conditions');
|
|
538
|
+
const nestedMatch = findEntry(nestedCollection, entryId);
|
|
539
|
+
if (nestedMatch) return nestedMatch;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
class WhereFilterNodeBuilder {
|
|
544
|
+
yMap;
|
|
545
|
+
constructor(yMap){
|
|
546
|
+
this.yMap = yMap;
|
|
547
|
+
}
|
|
548
|
+
getId() {
|
|
549
|
+
return this.yMap.get('id');
|
|
550
|
+
}
|
|
551
|
+
getField() {
|
|
552
|
+
return this.yMap.get('field');
|
|
553
|
+
}
|
|
554
|
+
setField(field) {
|
|
555
|
+
this.yMap.set('field', field);
|
|
556
|
+
return this;
|
|
557
|
+
}
|
|
558
|
+
getOperator() {
|
|
559
|
+
return this.yMap.get('op');
|
|
560
|
+
}
|
|
561
|
+
setOperator(operator) {
|
|
562
|
+
this.yMap.set('op', operator);
|
|
563
|
+
return this;
|
|
564
|
+
}
|
|
565
|
+
setValue(value) {
|
|
566
|
+
this.yMap.set('value', value);
|
|
567
|
+
return this;
|
|
568
|
+
}
|
|
569
|
+
toJSON() {
|
|
570
|
+
return this.yMap.toJSON();
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
class WhereGroupBuilder {
|
|
574
|
+
yMap;
|
|
575
|
+
constructor(yMap){
|
|
576
|
+
this.yMap = yMap;
|
|
577
|
+
}
|
|
578
|
+
getConditions() {
|
|
579
|
+
return this.yMap.get('conditions');
|
|
580
|
+
}
|
|
204
581
|
getId() {
|
|
205
582
|
return this.yMap.get('id');
|
|
206
583
|
}
|
|
@@ -215,29 +592,24 @@ class HavingGroupBuilder {
|
|
|
215
592
|
const yMap = new external_yjs_Map();
|
|
216
593
|
yMap.set('id', id_id.uuid());
|
|
217
594
|
yMap.set('field', field);
|
|
218
|
-
|
|
219
|
-
conditions.push([
|
|
595
|
+
this.getConditions().push([
|
|
220
596
|
yMap
|
|
221
597
|
]);
|
|
222
|
-
const node = new
|
|
598
|
+
const node = new WhereFilterNodeBuilder(yMap);
|
|
223
599
|
callback(node);
|
|
224
600
|
return this;
|
|
225
601
|
}
|
|
226
602
|
addGroup(op, callback) {
|
|
227
|
-
const yMap =
|
|
228
|
-
|
|
229
|
-
yMap.set('op', op);
|
|
230
|
-
yMap.set('conditions', new external_yjs_Array());
|
|
231
|
-
const conditions = this.yMap.get('conditions');
|
|
232
|
-
conditions.push([
|
|
603
|
+
const yMap = createWhereGroup(op, id_id.uuid());
|
|
604
|
+
this.getConditions().push([
|
|
233
605
|
yMap
|
|
234
606
|
]);
|
|
235
|
-
const group = new
|
|
607
|
+
const group = new WhereGroupBuilder(yMap);
|
|
236
608
|
callback(group);
|
|
237
609
|
return this;
|
|
238
610
|
}
|
|
239
611
|
remove(idOrIndex) {
|
|
240
|
-
const conditions = this.
|
|
612
|
+
const conditions = this.getConditions();
|
|
241
613
|
if ('number' == typeof idOrIndex) {
|
|
242
614
|
if (idOrIndex >= 0 && idOrIndex < conditions.length) conditions.delete(idOrIndex, 1);
|
|
243
615
|
} else {
|
|
@@ -247,154 +619,146 @@ class HavingGroupBuilder {
|
|
|
247
619
|
return this;
|
|
248
620
|
}
|
|
249
621
|
clear() {
|
|
250
|
-
const conditions = this.
|
|
622
|
+
const conditions = this.getConditions();
|
|
251
623
|
conditions.delete(0, conditions.length);
|
|
252
624
|
return this;
|
|
253
625
|
}
|
|
254
|
-
|
|
626
|
+
toJSON() {
|
|
255
627
|
return this.yMap.toJSON();
|
|
256
628
|
}
|
|
257
629
|
}
|
|
258
|
-
class
|
|
259
|
-
|
|
260
|
-
doc;
|
|
630
|
+
class WhereFilterBuilder {
|
|
631
|
+
whereFilter;
|
|
261
632
|
constructor(doc, dsl){
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
633
|
+
doc.transact(()=>{
|
|
634
|
+
const existingWhereFilter = dsl.get('whereFilter');
|
|
635
|
+
if (existingWhereFilter instanceof external_yjs_Map) this.whereFilter = existingWhereFilter;
|
|
636
|
+
else {
|
|
637
|
+
this.whereFilter = createWhereGroup();
|
|
638
|
+
dsl.set('whereFilter', this.whereFilter);
|
|
639
|
+
}
|
|
640
|
+
if (!(this.whereFilter.get('conditions') instanceof external_yjs_Array)) this.whereFilter.set('conditions', new external_yjs_Array());
|
|
641
|
+
if (!this.whereFilter.get('id')) this.whereFilter.set('id', 'root');
|
|
642
|
+
if (!this.whereFilter.get('op')) this.whereFilter.set('op', 'and');
|
|
266
643
|
});
|
|
267
644
|
}
|
|
645
|
+
getConditions() {
|
|
646
|
+
return this.whereFilter.get('conditions');
|
|
647
|
+
}
|
|
268
648
|
add(field, callback) {
|
|
269
649
|
const yMap = new external_yjs_Map();
|
|
270
650
|
yMap.set('id', id_id.uuid());
|
|
271
651
|
yMap.set('field', field);
|
|
272
|
-
this.
|
|
652
|
+
this.getConditions().push([
|
|
273
653
|
yMap
|
|
274
654
|
]);
|
|
275
|
-
const node = new
|
|
655
|
+
const node = new WhereFilterNodeBuilder(yMap);
|
|
276
656
|
callback(node);
|
|
277
657
|
return this;
|
|
278
658
|
}
|
|
279
659
|
addGroup(op, callback) {
|
|
280
|
-
const yMap =
|
|
281
|
-
|
|
282
|
-
yMap.set('op', op);
|
|
283
|
-
yMap.set('conditions', new external_yjs_Array());
|
|
284
|
-
this.dsl.get('havingFilters').push([
|
|
660
|
+
const yMap = createWhereGroup(op, id_id.uuid());
|
|
661
|
+
this.getConditions().push([
|
|
285
662
|
yMap
|
|
286
663
|
]);
|
|
287
|
-
const group = new
|
|
664
|
+
const group = new WhereGroupBuilder(yMap);
|
|
288
665
|
callback(group);
|
|
289
666
|
return this;
|
|
290
667
|
}
|
|
291
668
|
update(id, callback) {
|
|
292
|
-
const
|
|
293
|
-
const
|
|
294
|
-
if (
|
|
295
|
-
|
|
296
|
-
const
|
|
669
|
+
const conditions = this.getConditions();
|
|
670
|
+
const match = findEntry(conditions, id);
|
|
671
|
+
if (!match) throw new Error(`Where filter with id ${id} not found`);
|
|
672
|
+
if (!WhereFilterBuilder.isNode(match.item)) throw new Error(`Item with id ${id} is not a filter`);
|
|
673
|
+
const filterYMap = match.item;
|
|
674
|
+
const node = new WhereFilterNodeBuilder(filterYMap);
|
|
297
675
|
callback(node);
|
|
298
676
|
return this;
|
|
299
677
|
}
|
|
300
678
|
updateGroup(id, callback) {
|
|
301
|
-
const
|
|
302
|
-
const
|
|
303
|
-
if (
|
|
304
|
-
const yMap =
|
|
305
|
-
if (!
|
|
306
|
-
const group = new
|
|
679
|
+
const conditions = this.getConditions();
|
|
680
|
+
const match = findEntry(conditions, id);
|
|
681
|
+
if (!match) throw new Error(`Where group with id ${id} not found`);
|
|
682
|
+
const yMap = match.item;
|
|
683
|
+
if (!WhereFilterBuilder.isGroup(yMap)) throw new Error(`Item with id ${id} is not a group`);
|
|
684
|
+
const group = new WhereGroupBuilder(yMap);
|
|
307
685
|
callback(group);
|
|
308
686
|
return this;
|
|
309
687
|
}
|
|
310
688
|
remove(idOrIndex) {
|
|
311
|
-
const
|
|
689
|
+
const conditions = this.getConditions();
|
|
312
690
|
if ('number' == typeof idOrIndex) {
|
|
313
|
-
if (idOrIndex >= 0 && idOrIndex <
|
|
691
|
+
if (idOrIndex >= 0 && idOrIndex < conditions.length) conditions.delete(idOrIndex, 1);
|
|
314
692
|
} else {
|
|
315
|
-
const
|
|
316
|
-
if (
|
|
693
|
+
const match = findEntry(conditions, idOrIndex);
|
|
694
|
+
if (match) match.collection.delete(match.index, 1);
|
|
317
695
|
}
|
|
318
696
|
return this;
|
|
319
697
|
}
|
|
320
|
-
find(
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
698
|
+
find(predicate) {
|
|
699
|
+
const traverse = (collection)=>{
|
|
700
|
+
const items = collection.toArray();
|
|
701
|
+
for(let index = 0; index < items.length; index++){
|
|
702
|
+
const yMap = items[index];
|
|
703
|
+
const entry = WhereFilterBuilder.isGroup(yMap) ? new WhereGroupBuilder(yMap) : new WhereFilterNodeBuilder(yMap);
|
|
704
|
+
if (predicate(entry, index)) return entry;
|
|
705
|
+
if (WhereFilterBuilder.isGroup(yMap)) {
|
|
706
|
+
const nestedCollection = yMap.get('conditions');
|
|
707
|
+
const nestedMatch = traverse(nestedCollection);
|
|
708
|
+
if (nestedMatch) return nestedMatch;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
return traverse(this.getConditions());
|
|
326
713
|
}
|
|
327
714
|
clear() {
|
|
328
|
-
const
|
|
329
|
-
|
|
715
|
+
const conditions = this.getConditions();
|
|
716
|
+
conditions.delete(0, conditions.length);
|
|
330
717
|
return this;
|
|
331
718
|
}
|
|
332
|
-
|
|
333
|
-
return this.
|
|
719
|
+
toJSON() {
|
|
720
|
+
return this.whereFilter.toJSON();
|
|
334
721
|
}
|
|
335
722
|
observe(callback) {
|
|
336
|
-
this.
|
|
723
|
+
this.whereFilter.observeDeep(callback);
|
|
337
724
|
return ()=>{
|
|
338
|
-
this.
|
|
725
|
+
this.whereFilter.unobserveDeep(callback);
|
|
339
726
|
};
|
|
340
727
|
}
|
|
341
728
|
static isGroup(yMap) {
|
|
342
|
-
return
|
|
729
|
+
return where_utils_isWhereGroup(yMap);
|
|
343
730
|
}
|
|
344
731
|
static isNode(yMap) {
|
|
345
732
|
return void 0 !== yMap.get('field');
|
|
346
733
|
}
|
|
347
734
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
735
|
+
function createHavingGroup(op = 'and', groupId = 'root') {
|
|
736
|
+
const yMap = new external_yjs_Map();
|
|
737
|
+
yMap.set('id', groupId);
|
|
738
|
+
yMap.set('op', op);
|
|
739
|
+
yMap.set('conditions', new external_yjs_Array());
|
|
740
|
+
return yMap;
|
|
741
|
+
}
|
|
742
|
+
function having_utils_isHavingGroup(yMap) {
|
|
743
|
+
return void 0 !== yMap.get('op') && void 0 !== yMap.get('conditions');
|
|
744
|
+
}
|
|
745
|
+
function having_utils_findEntry(collection, entryId) {
|
|
746
|
+
const items = collection.toArray();
|
|
747
|
+
for(let index = 0; index < items.length; index++){
|
|
748
|
+
const item = items[index];
|
|
749
|
+
if (item.get('id') === entryId) return {
|
|
750
|
+
collection,
|
|
751
|
+
index,
|
|
752
|
+
item
|
|
360
753
|
};
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
return this.dsl.get('chartType') || 'table';
|
|
367
|
-
}
|
|
368
|
-
toJson() {
|
|
369
|
-
return this.dsl.get('chartType') || 'table';
|
|
370
|
-
}
|
|
371
|
-
getAvailableChartTypes() {
|
|
372
|
-
return [
|
|
373
|
-
ChartTypeEnum.Table,
|
|
374
|
-
ChartTypeEnum.PivotTable,
|
|
375
|
-
ChartTypeEnum.Line,
|
|
376
|
-
ChartTypeEnum.Column,
|
|
377
|
-
ChartTypeEnum.ColumnPercent,
|
|
378
|
-
ChartTypeEnum.ColumnParallel,
|
|
379
|
-
ChartTypeEnum.BarPercent,
|
|
380
|
-
ChartTypeEnum.BarParallel,
|
|
381
|
-
ChartTypeEnum.Area,
|
|
382
|
-
ChartTypeEnum.AreaPercent,
|
|
383
|
-
ChartTypeEnum.DualAxis,
|
|
384
|
-
ChartTypeEnum.Scatter,
|
|
385
|
-
ChartTypeEnum.Rose,
|
|
386
|
-
ChartTypeEnum.RoseParallel,
|
|
387
|
-
ChartTypeEnum.Pie,
|
|
388
|
-
ChartTypeEnum.Donut,
|
|
389
|
-
ChartTypeEnum.Radar,
|
|
390
|
-
ChartTypeEnum.Funnel,
|
|
391
|
-
ChartTypeEnum.Heatmap,
|
|
392
|
-
ChartTypeEnum.Boxplot,
|
|
393
|
-
ChartTypeEnum.Histogram
|
|
394
|
-
];
|
|
754
|
+
if (having_utils_isHavingGroup(item)) {
|
|
755
|
+
const nestedCollection = item.get('conditions');
|
|
756
|
+
const nestedMatch = having_utils_findEntry(nestedCollection, entryId);
|
|
757
|
+
if (nestedMatch) return nestedMatch;
|
|
758
|
+
}
|
|
395
759
|
}
|
|
396
760
|
}
|
|
397
|
-
class
|
|
761
|
+
class HavingFilterNodeBuilder {
|
|
398
762
|
yMap;
|
|
399
763
|
constructor(yMap){
|
|
400
764
|
this.yMap = yMap;
|
|
@@ -408,23 +772,26 @@ class WhereFilterNodeBuilder {
|
|
|
408
772
|
getOperator() {
|
|
409
773
|
return this.yMap.get('op');
|
|
410
774
|
}
|
|
411
|
-
setOperator(operator) {
|
|
412
|
-
this.yMap.set('op', operator);
|
|
413
|
-
return this;
|
|
414
|
-
}
|
|
415
775
|
setValue(value) {
|
|
416
776
|
this.yMap.set('value', value);
|
|
417
777
|
return this;
|
|
418
778
|
}
|
|
419
|
-
|
|
779
|
+
setOperator(operator) {
|
|
780
|
+
this.yMap.set('op', operator);
|
|
781
|
+
return this;
|
|
782
|
+
}
|
|
783
|
+
toJSON() {
|
|
420
784
|
return this.yMap.toJSON();
|
|
421
785
|
}
|
|
422
786
|
}
|
|
423
|
-
class
|
|
787
|
+
class HavingGroupBuilder {
|
|
424
788
|
yMap;
|
|
425
789
|
constructor(yMap){
|
|
426
790
|
this.yMap = yMap;
|
|
427
791
|
}
|
|
792
|
+
getConditions() {
|
|
793
|
+
return this.yMap.get('conditions');
|
|
794
|
+
}
|
|
428
795
|
getId() {
|
|
429
796
|
return this.yMap.get('id');
|
|
430
797
|
}
|
|
@@ -439,29 +806,24 @@ class WhereGroupBuilder {
|
|
|
439
806
|
const yMap = new external_yjs_Map();
|
|
440
807
|
yMap.set('id', id_id.uuid());
|
|
441
808
|
yMap.set('field', field);
|
|
442
|
-
|
|
443
|
-
conditions.push([
|
|
809
|
+
this.getConditions().push([
|
|
444
810
|
yMap
|
|
445
811
|
]);
|
|
446
|
-
const node = new
|
|
812
|
+
const node = new HavingFilterNodeBuilder(yMap);
|
|
447
813
|
callback(node);
|
|
448
814
|
return this;
|
|
449
815
|
}
|
|
450
816
|
addGroup(op, callback) {
|
|
451
|
-
const yMap =
|
|
452
|
-
|
|
453
|
-
yMap.set('op', op);
|
|
454
|
-
yMap.set('conditions', new external_yjs_Array());
|
|
455
|
-
const conditions = this.yMap.get('conditions');
|
|
456
|
-
conditions.push([
|
|
817
|
+
const yMap = createHavingGroup(op, id_id.uuid());
|
|
818
|
+
this.getConditions().push([
|
|
457
819
|
yMap
|
|
458
820
|
]);
|
|
459
|
-
const group = new
|
|
821
|
+
const group = new HavingGroupBuilder(yMap);
|
|
460
822
|
callback(group);
|
|
461
823
|
return this;
|
|
462
824
|
}
|
|
463
825
|
remove(idOrIndex) {
|
|
464
|
-
const conditions = this.
|
|
826
|
+
const conditions = this.getConditions();
|
|
465
827
|
if ('number' == typeof idOrIndex) {
|
|
466
828
|
if (idOrIndex >= 0 && idOrIndex < conditions.length) conditions.delete(idOrIndex, 1);
|
|
467
829
|
} else {
|
|
@@ -471,104 +833,191 @@ class WhereGroupBuilder {
|
|
|
471
833
|
return this;
|
|
472
834
|
}
|
|
473
835
|
clear() {
|
|
474
|
-
const conditions = this.
|
|
836
|
+
const conditions = this.getConditions();
|
|
475
837
|
conditions.delete(0, conditions.length);
|
|
476
838
|
return this;
|
|
477
839
|
}
|
|
478
|
-
|
|
840
|
+
toJSON() {
|
|
479
841
|
return this.yMap.toJSON();
|
|
480
842
|
}
|
|
481
843
|
}
|
|
482
|
-
class
|
|
483
|
-
|
|
484
|
-
doc;
|
|
844
|
+
class HavingFilterBuilder {
|
|
845
|
+
havingFilter;
|
|
485
846
|
constructor(doc, dsl){
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
847
|
+
doc.transact(()=>{
|
|
848
|
+
const existingHavingFilter = dsl.get('havingFilter');
|
|
849
|
+
if (existingHavingFilter instanceof external_yjs_Map) this.havingFilter = existingHavingFilter;
|
|
850
|
+
else {
|
|
851
|
+
this.havingFilter = createHavingGroup();
|
|
852
|
+
dsl.set('havingFilter', this.havingFilter);
|
|
853
|
+
}
|
|
854
|
+
if (!(this.havingFilter.get('conditions') instanceof external_yjs_Array)) this.havingFilter.set('conditions', new external_yjs_Array());
|
|
855
|
+
if (!this.havingFilter.get('id')) this.havingFilter.set('id', 'root');
|
|
856
|
+
if (!this.havingFilter.get('op')) this.havingFilter.set('op', 'and');
|
|
490
857
|
});
|
|
491
858
|
}
|
|
859
|
+
getConditions() {
|
|
860
|
+
return this.havingFilter.get('conditions');
|
|
861
|
+
}
|
|
492
862
|
add(field, callback) {
|
|
493
863
|
const yMap = new external_yjs_Map();
|
|
494
864
|
yMap.set('id', id_id.uuid());
|
|
495
865
|
yMap.set('field', field);
|
|
496
|
-
this.
|
|
866
|
+
this.getConditions().push([
|
|
497
867
|
yMap
|
|
498
868
|
]);
|
|
499
|
-
const node = new
|
|
869
|
+
const node = new HavingFilterNodeBuilder(yMap);
|
|
500
870
|
callback(node);
|
|
501
871
|
return this;
|
|
502
872
|
}
|
|
503
873
|
addGroup(op, callback) {
|
|
504
|
-
const yMap =
|
|
505
|
-
|
|
506
|
-
yMap.set('op', op);
|
|
507
|
-
yMap.set('conditions', new external_yjs_Array());
|
|
508
|
-
this.dsl.get('whereFilters').push([
|
|
874
|
+
const yMap = createHavingGroup(op, id_id.uuid());
|
|
875
|
+
this.getConditions().push([
|
|
509
876
|
yMap
|
|
510
877
|
]);
|
|
511
|
-
const group = new
|
|
878
|
+
const group = new HavingGroupBuilder(yMap);
|
|
512
879
|
callback(group);
|
|
513
880
|
return this;
|
|
514
881
|
}
|
|
515
882
|
update(id, callback) {
|
|
516
|
-
const
|
|
517
|
-
const
|
|
518
|
-
if (
|
|
519
|
-
|
|
520
|
-
const
|
|
883
|
+
const conditions = this.getConditions();
|
|
884
|
+
const match = having_utils_findEntry(conditions, id);
|
|
885
|
+
if (!match) throw new Error(`Having filter with id ${id} not found`);
|
|
886
|
+
if (!HavingFilterBuilder.isNode(match.item)) throw new Error(`Item with id ${id} is not a filter`);
|
|
887
|
+
const filterYMap = match.item;
|
|
888
|
+
const node = new HavingFilterNodeBuilder(filterYMap);
|
|
521
889
|
callback(node);
|
|
522
890
|
return this;
|
|
523
891
|
}
|
|
524
892
|
updateGroup(id, callback) {
|
|
525
|
-
const
|
|
526
|
-
const
|
|
527
|
-
if (
|
|
528
|
-
const yMap =
|
|
529
|
-
if (!
|
|
530
|
-
const group = new
|
|
893
|
+
const conditions = this.getConditions();
|
|
894
|
+
const match = having_utils_findEntry(conditions, id);
|
|
895
|
+
if (!match) throw new Error(`Having group with id ${id} not found`);
|
|
896
|
+
const yMap = match.item;
|
|
897
|
+
if (!HavingFilterBuilder.isGroup(yMap)) throw new Error(`Item with id ${id} is not a group`);
|
|
898
|
+
const group = new HavingGroupBuilder(yMap);
|
|
531
899
|
callback(group);
|
|
532
900
|
return this;
|
|
533
901
|
}
|
|
534
902
|
remove(idOrIndex) {
|
|
535
|
-
const
|
|
903
|
+
const conditions = this.getConditions();
|
|
536
904
|
if ('number' == typeof idOrIndex) {
|
|
537
|
-
if (idOrIndex >= 0 && idOrIndex <
|
|
905
|
+
if (idOrIndex >= 0 && idOrIndex < conditions.length) conditions.delete(idOrIndex, 1);
|
|
538
906
|
} else {
|
|
539
|
-
const
|
|
540
|
-
if (
|
|
907
|
+
const match = having_utils_findEntry(conditions, idOrIndex);
|
|
908
|
+
if (match) match.collection.delete(match.index, 1);
|
|
541
909
|
}
|
|
542
910
|
return this;
|
|
543
911
|
}
|
|
544
|
-
find(
|
|
545
|
-
const
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
912
|
+
find(predicate) {
|
|
913
|
+
const traverse = (collection)=>{
|
|
914
|
+
const items = collection.toArray();
|
|
915
|
+
for(let index = 0; index < items.length; index++){
|
|
916
|
+
const yMap = items[index];
|
|
917
|
+
const entry = HavingFilterBuilder.isGroup(yMap) ? new HavingGroupBuilder(yMap) : new HavingFilterNodeBuilder(yMap);
|
|
918
|
+
if (predicate(entry, index)) return entry;
|
|
919
|
+
if (HavingFilterBuilder.isGroup(yMap)) {
|
|
920
|
+
const nestedCollection = yMap.get('conditions');
|
|
921
|
+
const nestedMatch = traverse(nestedCollection);
|
|
922
|
+
if (nestedMatch) return nestedMatch;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
};
|
|
926
|
+
return traverse(this.getConditions());
|
|
550
927
|
}
|
|
551
928
|
clear() {
|
|
552
|
-
const
|
|
553
|
-
|
|
929
|
+
const conditions = this.getConditions();
|
|
930
|
+
conditions.delete(0, conditions.length);
|
|
554
931
|
return this;
|
|
555
932
|
}
|
|
556
|
-
|
|
557
|
-
return this.
|
|
933
|
+
toJSON() {
|
|
934
|
+
return this.havingFilter.toJSON();
|
|
558
935
|
}
|
|
559
936
|
observe(callback) {
|
|
560
|
-
this.
|
|
937
|
+
this.havingFilter.observeDeep(callback);
|
|
561
938
|
return ()=>{
|
|
562
|
-
this.
|
|
939
|
+
this.havingFilter.unobserveDeep(callback);
|
|
563
940
|
};
|
|
564
941
|
}
|
|
565
942
|
static isGroup(yMap) {
|
|
566
|
-
return
|
|
943
|
+
return having_utils_isHavingGroup(yMap);
|
|
567
944
|
}
|
|
568
945
|
static isNode(yMap) {
|
|
569
946
|
return void 0 !== yMap.get('field');
|
|
570
947
|
}
|
|
571
948
|
}
|
|
949
|
+
class ThemeBuilder {
|
|
950
|
+
dsl;
|
|
951
|
+
constructor(_doc, dsl){
|
|
952
|
+
this.dsl = dsl;
|
|
953
|
+
}
|
|
954
|
+
observe(callback) {
|
|
955
|
+
const wrapper = (e, trans)=>{
|
|
956
|
+
if (e.keysChanged.has('theme')) callback(e, trans);
|
|
957
|
+
};
|
|
958
|
+
this.dsl.observe(wrapper);
|
|
959
|
+
return ()=>{
|
|
960
|
+
this.dsl.unobserve(wrapper);
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
setTheme(theme) {
|
|
964
|
+
this.dsl.set('theme', theme);
|
|
965
|
+
}
|
|
966
|
+
getTheme() {
|
|
967
|
+
return this.dsl.get('theme') || 'light';
|
|
968
|
+
}
|
|
969
|
+
toJSON() {
|
|
970
|
+
return this.getTheme();
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
class LocaleBuilder {
|
|
974
|
+
dsl;
|
|
975
|
+
constructor(_doc, dsl){
|
|
976
|
+
this.dsl = dsl;
|
|
977
|
+
}
|
|
978
|
+
observe(callback) {
|
|
979
|
+
const wrapper = (e, trans)=>{
|
|
980
|
+
if (e.keysChanged.has('locale')) callback(e, trans);
|
|
981
|
+
};
|
|
982
|
+
this.dsl.observe(wrapper);
|
|
983
|
+
return ()=>{
|
|
984
|
+
this.dsl.unobserve(wrapper);
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
setLocale(locale) {
|
|
988
|
+
this.dsl.set('locale', locale);
|
|
989
|
+
}
|
|
990
|
+
getLocale() {
|
|
991
|
+
return this.dsl.get('locale') || 'zh-CN';
|
|
992
|
+
}
|
|
993
|
+
toJSON() {
|
|
994
|
+
return this.getLocale();
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
class LimitBuilder {
|
|
998
|
+
dsl;
|
|
999
|
+
constructor(_doc, dsl){
|
|
1000
|
+
this.dsl = dsl;
|
|
1001
|
+
}
|
|
1002
|
+
observe(callback) {
|
|
1003
|
+
const wrapper = (e, trans)=>{
|
|
1004
|
+
if (e.keysChanged.has('limit')) callback(e, trans);
|
|
1005
|
+
};
|
|
1006
|
+
this.dsl.observe(wrapper);
|
|
1007
|
+
return ()=>{
|
|
1008
|
+
this.dsl.unobserve(wrapper);
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
1011
|
+
setLimit(limit) {
|
|
1012
|
+
this.dsl.set('limit', limit);
|
|
1013
|
+
}
|
|
1014
|
+
getLimit() {
|
|
1015
|
+
return this.dsl.get('limit');
|
|
1016
|
+
}
|
|
1017
|
+
toJSON() {
|
|
1018
|
+
return this.getLimit();
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
572
1021
|
class undo_manager_UndoManager {
|
|
573
1022
|
manager;
|
|
574
1023
|
constructor(scope){
|
|
@@ -590,346 +1039,202 @@ class undo_manager_UndoManager {
|
|
|
590
1039
|
this.manager.clear(clearUndoStack, clearRedoStack);
|
|
591
1040
|
}
|
|
592
1041
|
}
|
|
593
|
-
const
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
alias: measure.alias,
|
|
604
|
-
aggr: measure.aggregate
|
|
605
|
-
}));
|
|
606
|
-
const dimensionNodes = dimensions.filter((dimension)=>DimensionsBuilder.isDimensionNode(dimension));
|
|
607
|
-
const dimensionSelects = dimensionNodes.map((dimension)=>({
|
|
608
|
-
field: dimension.field,
|
|
609
|
-
alias: dimension.alias
|
|
610
|
-
}));
|
|
611
|
-
result.select = measureSelects.concat(dimensionSelects);
|
|
612
|
-
return result;
|
|
613
|
-
};
|
|
614
|
-
const buildGroupBy = (queryDSL, context)=>{
|
|
615
|
-
const result = {
|
|
616
|
-
...queryDSL
|
|
617
|
-
};
|
|
618
|
-
const { vbiDSL } = context;
|
|
619
|
-
const dimensions = vbiDSL.dimensions;
|
|
620
|
-
const dimensionNodes = dimensions.filter((dimension)=>DimensionsBuilder.isDimensionNode(dimension));
|
|
621
|
-
result.groupBy = dimensionNodes.map((dimension)=>dimension.field);
|
|
622
|
-
return result;
|
|
623
|
-
};
|
|
624
|
-
const buildWhere = (queryDSL, context)=>{
|
|
625
|
-
const { vbiDSL } = context;
|
|
626
|
-
const whereFilters = vbiDSL.whereFilters || [];
|
|
627
|
-
if (0 === whereFilters.length) return queryDSL;
|
|
628
|
-
const result = {
|
|
629
|
-
...queryDSL
|
|
630
|
-
};
|
|
631
|
-
result.where = {
|
|
632
|
-
op: 'and',
|
|
633
|
-
conditions: whereFilters.flatMap(mapClauseToCondition)
|
|
634
|
-
};
|
|
635
|
-
return result;
|
|
636
|
-
};
|
|
637
|
-
function isWhereGroup(clause) {
|
|
638
|
-
return 'op' in clause && 'conditions' in clause;
|
|
639
|
-
}
|
|
640
|
-
function mapClauseToCondition(clause) {
|
|
641
|
-
if (isWhereGroup(clause)) return [
|
|
642
|
-
mapGroupToCondition(clause)
|
|
643
|
-
];
|
|
644
|
-
return mapFilterToCondition(clause);
|
|
645
|
-
}
|
|
646
|
-
function mapGroupToCondition(group) {
|
|
647
|
-
return {
|
|
648
|
-
op: group.op,
|
|
649
|
-
conditions: group.conditions.flatMap(mapClauseToCondition)
|
|
650
|
-
};
|
|
651
|
-
}
|
|
652
|
-
function mapFilterToCondition(filter) {
|
|
653
|
-
if ('between' === filter.op) return handleBetweenFilter(filter);
|
|
654
|
-
return handleSimpleFilter(filter);
|
|
655
|
-
}
|
|
656
|
-
function handleBetweenFilter(filter) {
|
|
657
|
-
const conditions = [];
|
|
658
|
-
const value = filter.value;
|
|
659
|
-
if (void 0 !== value.min && null !== value.min && '' !== value.min) conditions.push({
|
|
660
|
-
field: filter.field,
|
|
661
|
-
op: '<' === value.leftOp ? '>' : '>=',
|
|
662
|
-
value: value.min
|
|
663
|
-
});
|
|
664
|
-
if (void 0 !== value.max && null !== value.max && '' !== value.max) conditions.push({
|
|
665
|
-
field: filter.field,
|
|
666
|
-
op: '<' === value.rightOp ? '<' : '<=',
|
|
667
|
-
value: value.max
|
|
1042
|
+
const createBuilderFeatures = (doc, dsl)=>({
|
|
1043
|
+
undoManager: new undo_manager_UndoManager(dsl),
|
|
1044
|
+
chartType: new ChartTypeBuilder(doc, dsl),
|
|
1045
|
+
measures: new MeasuresBuilder(doc, dsl),
|
|
1046
|
+
dimensions: new DimensionsBuilder(doc, dsl),
|
|
1047
|
+
havingFilter: new HavingFilterBuilder(doc, dsl),
|
|
1048
|
+
whereFilter: new WhereFilterBuilder(doc, dsl),
|
|
1049
|
+
theme: new ThemeBuilder(doc, dsl),
|
|
1050
|
+
locale: new LocaleBuilder(doc, dsl),
|
|
1051
|
+
limit: new LimitBuilder(doc, dsl)
|
|
668
1052
|
});
|
|
669
|
-
return conditions;
|
|
670
|
-
}
|
|
671
|
-
function handleSimpleFilter(filter) {
|
|
672
|
-
let mappedOp = filter.op ?? '=';
|
|
673
|
-
const value = filter.value;
|
|
674
|
-
if (Array.isArray(value)) {
|
|
675
|
-
if ('=' === mappedOp) mappedOp = 'in';
|
|
676
|
-
if ('!=' === mappedOp) mappedOp = 'not in';
|
|
677
|
-
}
|
|
678
|
-
return [
|
|
679
|
-
{
|
|
680
|
-
field: filter.field,
|
|
681
|
-
op: mappedOp,
|
|
682
|
-
value
|
|
683
|
-
}
|
|
684
|
-
];
|
|
685
|
-
}
|
|
686
|
-
const buildHaving = (queryDSL, context)=>{
|
|
687
|
-
const { vbiDSL } = context;
|
|
688
|
-
const havingFilters = vbiDSL.havingFilters || [];
|
|
689
|
-
if (0 === havingFilters.length) return queryDSL;
|
|
690
|
-
const result = {
|
|
691
|
-
...queryDSL
|
|
692
|
-
};
|
|
693
|
-
result.having = {
|
|
694
|
-
op: 'and',
|
|
695
|
-
conditions: havingFilters.flatMap(buildHaving_mapClauseToCondition)
|
|
696
|
-
};
|
|
697
|
-
return result;
|
|
698
|
-
};
|
|
699
|
-
function isHavingGroup(clause) {
|
|
700
|
-
return 'op' in clause && 'conditions' in clause;
|
|
701
|
-
}
|
|
702
|
-
function buildHaving_mapClauseToCondition(clause) {
|
|
703
|
-
if (isHavingGroup(clause)) return [
|
|
704
|
-
buildHaving_mapGroupToCondition(clause)
|
|
705
|
-
];
|
|
706
|
-
return buildHaving_mapFilterToCondition(clause);
|
|
707
|
-
}
|
|
708
|
-
function buildHaving_mapGroupToCondition(group) {
|
|
709
|
-
return {
|
|
710
|
-
op: group.op,
|
|
711
|
-
conditions: group.conditions.flatMap(buildHaving_mapClauseToCondition)
|
|
712
|
-
};
|
|
713
|
-
}
|
|
714
|
-
function buildHaving_mapFilterToCondition(filter) {
|
|
715
|
-
const mappedOp = filter.op ?? '=';
|
|
716
|
-
return [
|
|
717
|
-
{
|
|
718
|
-
field: filter.field,
|
|
719
|
-
op: mappedOp,
|
|
720
|
-
value: filter.value
|
|
721
|
-
}
|
|
722
|
-
];
|
|
723
|
-
}
|
|
724
|
-
const buildLimit = (queryDSL, context)=>{
|
|
725
|
-
const result = {
|
|
726
|
-
...queryDSL
|
|
727
|
-
};
|
|
728
|
-
const limit = context.vbiDSL.limit ?? 1000;
|
|
729
|
-
result.limit = limit;
|
|
730
|
-
return result;
|
|
731
|
-
};
|
|
732
|
-
const buildVQuery = (vbiDSL, builder)=>{
|
|
733
|
-
const wrapper = (processor)=>(queryDSL)=>processor(queryDSL, {
|
|
734
|
-
vbiDSL,
|
|
735
|
-
builder
|
|
736
|
-
});
|
|
737
|
-
return pipe({}, wrapper(buildSelect), wrapper(buildGroupBy), wrapper(buildWhere), wrapper(buildHaving), wrapper(buildLimit));
|
|
738
|
-
};
|
|
739
|
-
const connectorMap = new Map();
|
|
740
|
-
const registerConnector = (id, connector)=>{
|
|
741
|
-
connectorMap.set(id, connector);
|
|
742
|
-
};
|
|
743
|
-
const getConnector = async (id)=>{
|
|
744
|
-
const connector = connectorMap.get(id);
|
|
745
|
-
if (!connector) throw new Error(`connector ${id} not registered`);
|
|
746
|
-
if ('function' == typeof connector) return connector();
|
|
747
|
-
return connector;
|
|
748
|
-
};
|
|
749
1053
|
class VBIBuilder {
|
|
750
1054
|
doc;
|
|
751
1055
|
dsl;
|
|
752
|
-
undoManager;
|
|
753
1056
|
chartType;
|
|
754
1057
|
measures;
|
|
755
1058
|
dimensions;
|
|
756
|
-
|
|
757
|
-
|
|
1059
|
+
havingFilter;
|
|
1060
|
+
whereFilter;
|
|
1061
|
+
theme;
|
|
1062
|
+
locale;
|
|
1063
|
+
limit;
|
|
1064
|
+
undoManager;
|
|
758
1065
|
constructor(doc){
|
|
759
1066
|
this.doc = doc;
|
|
760
1067
|
this.dsl = doc.getMap('dsl');
|
|
761
|
-
|
|
762
|
-
this.
|
|
763
|
-
this.
|
|
764
|
-
this.
|
|
765
|
-
this.
|
|
766
|
-
this.
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
1068
|
+
const features = createBuilderFeatures(doc, this.dsl);
|
|
1069
|
+
this.undoManager = features.undoManager;
|
|
1070
|
+
this.chartType = features.chartType;
|
|
1071
|
+
this.measures = features.measures;
|
|
1072
|
+
this.dimensions = features.dimensions;
|
|
1073
|
+
this.havingFilter = features.havingFilter;
|
|
1074
|
+
this.whereFilter = features.whereFilter;
|
|
1075
|
+
this.theme = features.theme;
|
|
1076
|
+
this.locale = features.locale;
|
|
1077
|
+
this.limit = features.limit;
|
|
1078
|
+
}
|
|
1079
|
+
applyUpdate = (update, transactionOrigin)=>applyUpdateToDoc(this.doc, update, transactionOrigin);
|
|
1080
|
+
encodeStateAsUpdate = (targetStateVector)=>encodeDocStateAsUpdate(this.doc, targetStateVector);
|
|
774
1081
|
buildVSeed = async ()=>{
|
|
775
1082
|
const vbiDSL = this.build();
|
|
776
|
-
const connectorId = vbiDSL.connectorId;
|
|
777
|
-
const connector = await getConnector(vbiDSL.connectorId);
|
|
778
1083
|
const queryDSL = this.buildVQuery();
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
queryDSL
|
|
782
|
-
schema,
|
|
783
|
-
connectorId
|
|
1084
|
+
return buildVSeedDSL({
|
|
1085
|
+
vbiDSL,
|
|
1086
|
+
queryDSL
|
|
784
1087
|
});
|
|
785
|
-
return {
|
|
786
|
-
chartType: vbiDSL.chartType,
|
|
787
|
-
dataset: queryResult.dataset,
|
|
788
|
-
theme: vbiDSL.theme,
|
|
789
|
-
locale: vbiDSL.locale
|
|
790
|
-
};
|
|
791
|
-
};
|
|
792
|
-
buildVQuery = ()=>{
|
|
793
|
-
const vbiDSL = this.build();
|
|
794
|
-
return buildVQuery(vbiDSL, this);
|
|
795
1088
|
};
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
1089
|
+
buildVQuery = ()=>buildVQueryDSL(this.dsl, this);
|
|
1090
|
+
build = ()=>buildVBIDSL(this.dsl);
|
|
1091
|
+
isEmpty = ()=>isEmptyVBIDSL(this.dsl);
|
|
1092
|
+
getSchema = async ()=>getBuilderSchema(this.dsl);
|
|
1093
|
+
}
|
|
1094
|
+
const shouldEnsureIdForObject = (obj, ensureId)=>{
|
|
1095
|
+
if (true === ensureId) return true;
|
|
1096
|
+
if ('field' === ensureId) return 'string' == typeof obj.field;
|
|
1097
|
+
return false;
|
|
1098
|
+
};
|
|
1099
|
+
const toYMap = (obj, ensureId = false)=>{
|
|
1100
|
+
const yMap = new external_yjs_Map();
|
|
1101
|
+
if (shouldEnsureIdForObject(obj, ensureId) && !obj.id) yMap.set('id', id_id.uuid());
|
|
1102
|
+
for (const [key, value] of Object.entries(obj)){
|
|
1103
|
+
if (('conditions' === key || 'children' === key) && Array.isArray(value)) {
|
|
1104
|
+
const yArr = new external_yjs_Array();
|
|
1105
|
+
for (const child of value){
|
|
1106
|
+
if (child instanceof external_yjs_Map) {
|
|
1107
|
+
yArr.push([
|
|
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
|
+
}
|
|
1122
|
+
yMap.set(key, yArr);
|
|
1123
|
+
continue;
|
|
1124
|
+
}
|
|
1125
|
+
yMap.set(key, value);
|
|
810
1126
|
}
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
1127
|
+
return yMap;
|
|
1128
|
+
};
|
|
1129
|
+
const ensureYArray = (arr, ensureId = false)=>{
|
|
1130
|
+
if (!arr) return new external_yjs_Array();
|
|
1131
|
+
if (arr instanceof external_yjs_Array) return arr;
|
|
1132
|
+
const yArr = new external_yjs_Array();
|
|
1133
|
+
for (const item of arr){
|
|
1134
|
+
if (item instanceof external_yjs_Map) {
|
|
1135
|
+
yArr.push([
|
|
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
|
+
]);
|
|
814
1149
|
}
|
|
815
|
-
|
|
1150
|
+
return yArr;
|
|
1151
|
+
};
|
|
1152
|
+
const getDefaultWhereFilter = ()=>({
|
|
1153
|
+
id: 'root',
|
|
1154
|
+
op: 'and',
|
|
1155
|
+
conditions: []
|
|
1156
|
+
});
|
|
1157
|
+
const isFilterGroupInput = (value)=>'object' == typeof value && null !== value;
|
|
1158
|
+
const ensureWhereGroup = (whereFilter)=>{
|
|
1159
|
+
const sourceWhereFilter = whereFilter instanceof external_yjs_Map || isFilterGroupInput(whereFilter) ? whereFilter : getDefaultWhereFilter();
|
|
1160
|
+
const whereGroup = sourceWhereFilter instanceof external_yjs_Map ? sourceWhereFilter : createWhereGroup();
|
|
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));
|
|
1170
|
+
return whereGroup;
|
|
1171
|
+
};
|
|
1172
|
+
const getDefaultHavingFilter = ()=>({
|
|
1173
|
+
id: 'root',
|
|
1174
|
+
op: 'and',
|
|
1175
|
+
conditions: []
|
|
1176
|
+
});
|
|
1177
|
+
const ensure_having_group_isFilterGroupInput = (value)=>'object' == typeof value && null !== value;
|
|
1178
|
+
const ensureHavingGroup = (havingFilter)=>{
|
|
1179
|
+
const sourceHavingFilter = havingFilter instanceof external_yjs_Map || ensure_having_group_isFilterGroupInput(havingFilter) ? havingFilter : getDefaultHavingFilter();
|
|
1180
|
+
const havingGroup = sourceHavingFilter instanceof external_yjs_Map ? sourceHavingFilter : createHavingGroup();
|
|
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));
|
|
1190
|
+
return havingGroup;
|
|
1191
|
+
};
|
|
1192
|
+
const setBaseDSLFields = (dsl, vbi)=>{
|
|
1193
|
+
if (vbi.connectorId) dsl.set('connectorId', vbi.connectorId);
|
|
1194
|
+
if (vbi.chartType) dsl.set('chartType', vbi.chartType);
|
|
1195
|
+
if (vbi.theme) dsl.set('theme', vbi.theme);
|
|
1196
|
+
if (vbi.limit) dsl.set('limit', vbi.limit);
|
|
1197
|
+
if (vbi.locale) dsl.set('locale', vbi.locale);
|
|
1198
|
+
if (void 0 !== vbi.version) dsl.set('version', vbi.version);
|
|
1199
|
+
};
|
|
1200
|
+
const fromVBIDSLInput = (vbi)=>{
|
|
1201
|
+
const doc = new Doc();
|
|
1202
|
+
const dsl = doc.getMap('dsl');
|
|
1203
|
+
doc.transact(()=>{
|
|
1204
|
+
setBaseDSLFields(dsl, vbi);
|
|
1205
|
+
dsl.set('whereFilter', ensureWhereGroup(vbi.whereFilter));
|
|
1206
|
+
dsl.set('havingFilter', ensureHavingGroup(vbi.havingFilter));
|
|
1207
|
+
dsl.set('measures', ensureYArray(vbi.measures, 'field'));
|
|
1208
|
+
dsl.set('dimensions', ensureYArray(vbi.dimensions, 'field'));
|
|
1209
|
+
});
|
|
1210
|
+
return new VBIBuilder(doc);
|
|
1211
|
+
};
|
|
1212
|
+
const generateEmptyDSL = (connectorId)=>({
|
|
1213
|
+
connectorId,
|
|
1214
|
+
chartType: 'table',
|
|
1215
|
+
measures: [],
|
|
1216
|
+
dimensions: [],
|
|
1217
|
+
whereFilter: {
|
|
1218
|
+
id: 'root',
|
|
1219
|
+
op: 'and',
|
|
1220
|
+
conditions: []
|
|
1221
|
+
},
|
|
1222
|
+
havingFilter: {
|
|
1223
|
+
id: 'root',
|
|
1224
|
+
op: 'and',
|
|
1225
|
+
conditions: []
|
|
1226
|
+
},
|
|
1227
|
+
theme: 'light',
|
|
1228
|
+
locale: 'zh-CN',
|
|
1229
|
+
version: 0
|
|
1230
|
+
});
|
|
816
1231
|
const createVBI = ()=>({
|
|
817
1232
|
connectorMap: connectorMap,
|
|
818
1233
|
registerConnector: registerConnector,
|
|
819
1234
|
getConnector: getConnector,
|
|
820
|
-
generateEmptyDSL:
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
measures: [],
|
|
824
|
-
dimensions: [],
|
|
825
|
-
whereFilters: [],
|
|
826
|
-
havingFilters: [],
|
|
827
|
-
theme: 'light',
|
|
828
|
-
locale: 'zh-CN',
|
|
829
|
-
version: 0
|
|
830
|
-
}),
|
|
831
|
-
from: (vbi)=>{
|
|
832
|
-
const doc = new Doc();
|
|
833
|
-
const dsl = doc.getMap('dsl');
|
|
834
|
-
doc.transact(()=>{
|
|
835
|
-
if (vbi.connectorId) dsl.set('connectorId', vbi.connectorId);
|
|
836
|
-
if (vbi.chartType) dsl.set('chartType', vbi.chartType);
|
|
837
|
-
if (vbi.theme) dsl.set('theme', vbi.theme);
|
|
838
|
-
if (vbi.limit) dsl.set('limit', vbi.limit);
|
|
839
|
-
if (vbi.locale) dsl.set('locale', vbi.locale);
|
|
840
|
-
if (vbi.version) dsl.set('version', vbi.version);
|
|
841
|
-
const toYMap = (obj, ensureId = false)=>{
|
|
842
|
-
const yMap = new external_yjs_Map();
|
|
843
|
-
if (ensureId && !obj.id) yMap.set('id', id_id.uuid());
|
|
844
|
-
for (const [key, value] of Object.entries(obj))if ('conditions' === key && Array.isArray(value)) {
|
|
845
|
-
const yArr = new external_yjs_Array();
|
|
846
|
-
value.forEach((child)=>{
|
|
847
|
-
if (child instanceof external_yjs_Map) yArr.push([
|
|
848
|
-
child
|
|
849
|
-
]);
|
|
850
|
-
else if ('object' == typeof child && null !== child) yArr.push([
|
|
851
|
-
toYMap(child, true)
|
|
852
|
-
]);
|
|
853
|
-
else yArr.push([
|
|
854
|
-
child
|
|
855
|
-
]);
|
|
856
|
-
});
|
|
857
|
-
yMap.set(key, yArr);
|
|
858
|
-
} else yMap.set(key, value);
|
|
859
|
-
return yMap;
|
|
860
|
-
};
|
|
861
|
-
const ensureYArray = (arr, ensureId = false)=>{
|
|
862
|
-
if (!arr) return new external_yjs_Array();
|
|
863
|
-
if (arr instanceof external_yjs_Array) return arr;
|
|
864
|
-
const yArr = new external_yjs_Array();
|
|
865
|
-
arr.forEach((item)=>{
|
|
866
|
-
if (item instanceof external_yjs_Map) yArr.push([
|
|
867
|
-
item
|
|
868
|
-
]);
|
|
869
|
-
else if ('object' == typeof item && null !== item) yArr.push([
|
|
870
|
-
toYMap(item, ensureId)
|
|
871
|
-
]);
|
|
872
|
-
else yArr.push([
|
|
873
|
-
item
|
|
874
|
-
]);
|
|
875
|
-
});
|
|
876
|
-
return yArr;
|
|
877
|
-
};
|
|
878
|
-
dsl.set('whereFilters', ensureYArray(vbi.whereFilters, true));
|
|
879
|
-
dsl.set('havingFilters', ensureYArray(vbi.havingFilters, true));
|
|
880
|
-
dsl.set('measures', ensureYArray(vbi.measures));
|
|
881
|
-
dsl.set('dimensions', ensureYArray(vbi.dimensions));
|
|
882
|
-
});
|
|
883
|
-
return new VBIBuilder(doc);
|
|
884
|
-
}
|
|
1235
|
+
generateEmptyDSL: generateEmptyDSL,
|
|
1236
|
+
from: fromVBIDSLInput,
|
|
1237
|
+
create: fromVBIDSLInput
|
|
885
1238
|
});
|
|
886
1239
|
const VBI = createVBI();
|
|
887
|
-
|
|
888
|
-
id: z.string(),
|
|
889
|
-
field: z.string(),
|
|
890
|
-
op: z.string().optional(),
|
|
891
|
-
value: z.any().optional()
|
|
892
|
-
});
|
|
893
|
-
const zVBIWhereGroup = z.lazy(()=>z.object({
|
|
894
|
-
id: z.string(),
|
|
895
|
-
op: z["enum"]([
|
|
896
|
-
'and',
|
|
897
|
-
'or'
|
|
898
|
-
]),
|
|
899
|
-
conditions: z.array(zVBIWhereClause)
|
|
900
|
-
}));
|
|
901
|
-
const zVBIWhereClause = z.lazy(()=>z.union([
|
|
902
|
-
zVBIFilter,
|
|
903
|
-
zVBIWhereGroup
|
|
904
|
-
]));
|
|
905
|
-
function isVBIFilter(clause) {
|
|
906
|
-
return 'field' in clause;
|
|
907
|
-
}
|
|
908
|
-
function isVBIWhereGroup(clause) {
|
|
909
|
-
return 'conditions' in clause;
|
|
910
|
-
}
|
|
911
|
-
const zVBIHavingFilter = z.object({
|
|
912
|
-
id: z.string(),
|
|
913
|
-
field: z.string(),
|
|
914
|
-
op: z.string().optional(),
|
|
915
|
-
value: z.any().optional()
|
|
916
|
-
});
|
|
917
|
-
const zVBIHavingGroup = z.lazy(()=>z.object({
|
|
918
|
-
id: z.string(),
|
|
919
|
-
op: z["enum"]([
|
|
920
|
-
'and',
|
|
921
|
-
'or'
|
|
922
|
-
]),
|
|
923
|
-
conditions: z.array(zVBIHavingClause)
|
|
924
|
-
}));
|
|
925
|
-
const zVBIHavingClause = z.lazy(()=>z.union([
|
|
926
|
-
zVBIHavingFilter,
|
|
927
|
-
zVBIHavingGroup
|
|
928
|
-
]));
|
|
929
|
-
function isVBIHavingFilter(clause) {
|
|
930
|
-
return 'field' in clause;
|
|
931
|
-
}
|
|
932
|
-
function isVBIHavingGroup(clause) {
|
|
933
|
-
return 'conditions' in clause;
|
|
934
|
-
}
|
|
935
|
-
export { ChartTypeBuilder, DimensionsBuilder, MeasuresBuilder, VBI, VBIBuilder, buildVQuery, findTreeNodesBy, id_id as id, isVBIFilter, isVBIHavingFilter, isVBIHavingGroup, isVBIWhereGroup, preorderTraverse };
|
|
1240
|
+
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 };
|