@visactor/vbi 0.4.12 → 0.4.14

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.
Files changed (32) hide show
  1. package/dist/builder/index.d.ts +1 -0
  2. package/dist/builder/sub-builders/havingFilters/having-builder.d.ts +33 -20
  3. package/dist/builder/sub-builders/havingFilters/having-group-builder.d.ts +48 -0
  4. package/dist/builder/sub-builders/havingFilters/having-node-builder.d.ts +8 -0
  5. package/dist/builder/sub-builders/havingFilters/index.d.ts +1 -0
  6. package/dist/builder/sub-builders/index.d.ts +1 -1
  7. package/dist/builder/sub-builders/whereFilters/index.d.ts +1 -0
  8. package/dist/builder/sub-builders/whereFilters/where-builder.d.ts +30 -17
  9. package/dist/builder/sub-builders/whereFilters/where-group-builder.d.ts +48 -0
  10. package/dist/builder/sub-builders/whereFilters/where-node-builder.d.ts +8 -0
  11. package/dist/builder/undo-manager.d.ts +37 -0
  12. package/dist/builder/vbi-builder.d.ts +2 -1
  13. package/dist/index.cjs +446 -130
  14. package/dist/index.js +428 -121
  15. package/dist/pipeline/index.d.ts +1 -1
  16. package/dist/pipeline/vqueryDSL/buildGroupBy.d.ts +2 -0
  17. package/dist/pipeline/vqueryDSL/buildHaving.d.ts +2 -0
  18. package/dist/pipeline/vqueryDSL/buildLimit.d.ts +2 -0
  19. package/dist/pipeline/vqueryDSL/buildSelect.d.ts +2 -0
  20. package/dist/pipeline/vqueryDSL/buildWhere.d.ts +2 -0
  21. package/dist/pipeline/vqueryDSL/index.d.ts +4 -1
  22. package/dist/pipeline/vqueryDSL/types.d.ts +7 -0
  23. package/dist/types/builder/VBIInterface.d.ts +2 -1
  24. package/dist/types/dsl/havingFilters/having.d.ts +12 -7
  25. package/dist/types/dsl/index.d.ts +4 -2
  26. package/dist/types/dsl/vbi/vbi.d.ts +2 -10
  27. package/dist/types/dsl/whereFilters/filters.d.ts +12 -1
  28. package/dist/types/index.d.ts +0 -1
  29. package/dist/utils/id.d.ts +3 -0
  30. package/dist/utils/index.d.ts +1 -0
  31. package/package.json +12 -10
  32. package/dist/pipeline/vqueryDSL/buildVQuery.d.ts +0 -4
package/dist/index.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { Array as external_yjs_Array, Doc, Map as external_yjs_Map, UndoManager, applyUpdate, encodeStateAsUpdate } from "yjs";
2
2
  import { ChartTypeEnum, findTreeNodesBy, preorderTraverse } from "@visactor/vseed";
3
- import { z } from "zod";
3
+ import { v4 } from "uuid";
4
4
  import { pipe } from "remeda";
5
+ import { z } from "zod";
5
6
  class DimensionNodeBuilder {
6
7
  yMap;
7
8
  constructor(yMap){
@@ -166,20 +167,88 @@ class MeasuresBuilder {
166
167
  return 'children' in node;
167
168
  }
168
169
  }
170
+ const id_id = {
171
+ uuid: ()=>v4()
172
+ };
169
173
  class HavingFiltersNodeBuilder {
170
174
  yMap;
171
175
  constructor(yMap){
172
176
  this.yMap = yMap;
173
177
  }
178
+ getId() {
179
+ return this.yMap.get('id');
180
+ }
174
181
  getField() {
175
182
  return this.yMap.get('field');
176
183
  }
184
+ getOperator() {
185
+ return this.yMap.get('op');
186
+ }
177
187
  setValue(value) {
178
188
  this.yMap.set('value', value);
179
189
  return this;
180
190
  }
181
191
  setOperator(operator) {
182
- this.yMap.set('operator', operator);
192
+ this.yMap.set('op', operator);
193
+ return this;
194
+ }
195
+ toJson() {
196
+ return this.yMap.toJSON();
197
+ }
198
+ }
199
+ class HavingGroupBuilder {
200
+ yMap;
201
+ constructor(yMap){
202
+ this.yMap = yMap;
203
+ }
204
+ getId() {
205
+ return this.yMap.get('id');
206
+ }
207
+ getOperator() {
208
+ return this.yMap.get('op');
209
+ }
210
+ setOperator(op) {
211
+ this.yMap.set('op', op);
212
+ return this;
213
+ }
214
+ add(field, callback) {
215
+ const yMap = new external_yjs_Map();
216
+ yMap.set('id', id_id.uuid());
217
+ yMap.set('field', field);
218
+ const conditions = this.yMap.get('conditions');
219
+ conditions.push([
220
+ yMap
221
+ ]);
222
+ const node = new HavingFiltersNodeBuilder(yMap);
223
+ callback(node);
224
+ return this;
225
+ }
226
+ addGroup(op, callback) {
227
+ const yMap = new external_yjs_Map();
228
+ yMap.set('id', id_id.uuid());
229
+ yMap.set('op', op);
230
+ yMap.set('conditions', new external_yjs_Array());
231
+ const conditions = this.yMap.get('conditions');
232
+ conditions.push([
233
+ yMap
234
+ ]);
235
+ const group = new HavingGroupBuilder(yMap);
236
+ callback(group);
237
+ return this;
238
+ }
239
+ remove(idOrIndex) {
240
+ const conditions = this.yMap.get('conditions');
241
+ if ('number' == typeof idOrIndex) {
242
+ if (idOrIndex >= 0 && idOrIndex < conditions.length) conditions.delete(idOrIndex, 1);
243
+ } else {
244
+ const index = conditions.toArray().findIndex((item)=>item.get('id') === idOrIndex);
245
+ if (-1 !== index) conditions.delete(index, 1);
246
+ }
247
+ return this;
248
+ }
249
+ clear() {
250
+ const conditions = this.yMap.get('conditions');
251
+ conditions.delete(0, conditions.length);
183
252
  return this;
184
253
  }
185
254
  toJson() {
@@ -188,54 +257,76 @@ class HavingFiltersNodeBuilder {
188
257
  }
189
258
  class HavingFiltersBuilder {
190
259
  dsl;
191
- constructor(_doc, dsl){
260
+ doc;
261
+ constructor(doc, dsl){
262
+ this.doc = doc;
192
263
  this.dsl = dsl;
264
+ if (!this.dsl.get('havingFilters')) this.doc.transact(()=>{
265
+ this.dsl.set('havingFilters', new external_yjs_Array());
266
+ });
193
267
  }
194
268
  add(field, callback) {
195
- if (!field || 'string' != typeof field) throw new Error('Field is required and must be a string');
196
- const defaultFilter = {
197
- field,
198
- operator: 'eq',
199
- value: null
200
- };
201
269
  const yMap = new external_yjs_Map();
202
- for (const [key, value] of Object.entries(defaultFilter))yMap.set(key, value);
270
+ yMap.set('id', id_id.uuid());
271
+ yMap.set('field', field);
203
272
  this.dsl.get('havingFilters').push([
204
273
  yMap
205
274
  ]);
206
- const filterNode = new HavingFiltersNodeBuilder(yMap);
207
- callback(filterNode);
275
+ const node = new HavingFiltersNodeBuilder(yMap);
276
+ callback(node);
208
277
  return this;
209
278
  }
210
- update(field, callback) {
279
+ addGroup(op, callback) {
280
+ const yMap = new external_yjs_Map();
281
+ yMap.set('id', id_id.uuid());
282
+ yMap.set('op', op);
283
+ yMap.set('conditions', new external_yjs_Array());
284
+ this.dsl.get('havingFilters').push([
285
+ yMap
286
+ ]);
287
+ const group = new HavingGroupBuilder(yMap);
288
+ callback(group);
289
+ return this;
290
+ }
291
+ update(id, callback) {
211
292
  const havingFilters = this.dsl.get('havingFilters');
212
- const index = havingFilters.toArray().findIndex((item)=>item.get('field') === field);
213
- if (-1 === index) throw new Error(`Having filter with field "${field}" not found`);
293
+ const index = havingFilters.toArray().findIndex((item)=>item.get('id') === id);
294
+ if (-1 === index) throw new Error(`Having filter with id ${id} not found`);
214
295
  const filterYMap = havingFilters.get(index);
215
296
  const node = new HavingFiltersNodeBuilder(filterYMap);
216
297
  callback(node);
217
298
  return this;
218
299
  }
219
- remove(field) {
220
- if (!field || 'string' != typeof field) console.error('[HavingFiltersBuilder] Invalid field name:', field);
300
+ updateGroup(id, callback) {
221
301
  const havingFilters = this.dsl.get('havingFilters');
222
- const index = havingFilters.toArray().findIndex((item)=>item.get('field') === field);
223
- if (-1 !== index) this.dsl.get('havingFilters').delete(index, 1);
302
+ const index = havingFilters.toArray().findIndex((item)=>item.get('id') === id);
303
+ if (-1 === index) throw new Error(`Having group with id ${id} not found`);
304
+ const yMap = havingFilters.get(index);
305
+ if (!HavingFiltersBuilder.isGroup(yMap)) throw new Error(`Item with id ${id} is not a group`);
306
+ const group = new HavingGroupBuilder(yMap);
307
+ callback(group);
224
308
  return this;
225
309
  }
226
- find(field) {
310
+ remove(idOrIndex) {
227
311
  const havingFilters = this.dsl.get('havingFilters');
228
- const index = havingFilters.toArray().findIndex((item)=>item.get('field') === field);
229
- if (-1 === index) return;
230
- return new HavingFiltersNodeBuilder(havingFilters.get(index));
312
+ if ('number' == typeof idOrIndex) {
313
+ if (idOrIndex >= 0 && idOrIndex < havingFilters.length) havingFilters.delete(idOrIndex, 1);
314
+ } else {
315
+ const index = havingFilters.toArray().findIndex((item)=>item.get('id') === idOrIndex);
316
+ if (-1 !== index) havingFilters.delete(index, 1);
317
+ }
318
+ return this;
231
319
  }
232
- findAll() {
320
+ find(id) {
233
321
  const havingFilters = this.dsl.get('havingFilters');
234
- return havingFilters.toArray().map((yMap)=>new HavingFiltersNodeBuilder(yMap));
322
+ const yMap = havingFilters.toArray().find((item)=>item.get('id') === id);
323
+ if (!yMap) return;
324
+ if (HavingFiltersBuilder.isGroup(yMap)) return new HavingGroupBuilder(yMap);
325
+ return new HavingFiltersNodeBuilder(yMap);
235
326
  }
236
327
  clear() {
237
328
  const havingFilters = this.dsl.get('havingFilters');
238
- if (havingFilters.length > 0) havingFilters.delete(0, havingFilters.length);
329
+ havingFilters.delete(0, havingFilters.length);
239
330
  return this;
240
331
  }
241
332
  toJson() {
@@ -247,6 +338,12 @@ class HavingFiltersBuilder {
247
338
  this.dsl.get('havingFilters').unobserve(callback);
248
339
  };
249
340
  }
341
+ static isGroup(yMap) {
342
+ return void 0 !== yMap.get('op') && void 0 !== yMap.get('conditions');
343
+ }
344
+ static isNode(yMap) {
345
+ return void 0 !== yMap.get('field');
346
+ }
250
347
  }
251
348
  class ChartTypeBuilder {
252
349
  dsl;
@@ -302,11 +399,17 @@ class WhereFilterNodeBuilder {
302
399
  constructor(yMap){
303
400
  this.yMap = yMap;
304
401
  }
402
+ getId() {
403
+ return this.yMap.get('id');
404
+ }
305
405
  getField() {
306
406
  return this.yMap.get('field');
307
407
  }
408
+ getOperator() {
409
+ return this.yMap.get('op');
410
+ }
308
411
  setOperator(operator) {
309
- this.yMap.set('operator', operator);
412
+ this.yMap.set('op', operator);
310
413
  return this;
311
414
  }
312
415
  setValue(value) {
@@ -317,6 +420,65 @@ class WhereFilterNodeBuilder {
317
420
  return this.yMap.toJSON();
318
421
  }
319
422
  }
423
+ class WhereGroupBuilder {
424
+ yMap;
425
+ constructor(yMap){
426
+ this.yMap = yMap;
427
+ }
428
+ getId() {
429
+ return this.yMap.get('id');
430
+ }
431
+ getOperator() {
432
+ return this.yMap.get('op');
433
+ }
434
+ setOperator(op) {
435
+ this.yMap.set('op', op);
436
+ return this;
437
+ }
438
+ add(field, callback) {
439
+ const yMap = new external_yjs_Map();
440
+ yMap.set('id', id_id.uuid());
441
+ yMap.set('field', field);
442
+ const conditions = this.yMap.get('conditions');
443
+ conditions.push([
444
+ yMap
445
+ ]);
446
+ const node = new WhereFilterNodeBuilder(yMap);
447
+ callback(node);
448
+ return this;
449
+ }
450
+ addGroup(op, callback) {
451
+ const yMap = new external_yjs_Map();
452
+ yMap.set('id', id_id.uuid());
453
+ yMap.set('op', op);
454
+ yMap.set('conditions', new external_yjs_Array());
455
+ const conditions = this.yMap.get('conditions');
456
+ conditions.push([
457
+ yMap
458
+ ]);
459
+ const group = new WhereGroupBuilder(yMap);
460
+ callback(group);
461
+ return this;
462
+ }
463
+ remove(idOrIndex) {
464
+ const conditions = this.yMap.get('conditions');
465
+ if ('number' == typeof idOrIndex) {
466
+ if (idOrIndex >= 0 && idOrIndex < conditions.length) conditions.delete(idOrIndex, 1);
467
+ } else {
468
+ const index = conditions.toArray().findIndex((item)=>item.get('id') === idOrIndex);
469
+ if (-1 !== index) conditions.delete(index, 1);
470
+ }
471
+ return this;
472
+ }
473
+ clear() {
474
+ const conditions = this.yMap.get('conditions');
475
+ conditions.delete(0, conditions.length);
476
+ return this;
477
+ }
478
+ toJson() {
479
+ return this.yMap.toJSON();
480
+ }
481
+ }
320
482
  class WhereFiltersBuilder {
321
483
  dsl;
322
484
  doc;
@@ -328,11 +490,9 @@ class WhereFiltersBuilder {
328
490
  });
329
491
  }
330
492
  add(field, callback) {
331
- const filter = {
332
- field
333
- };
334
493
  const yMap = new external_yjs_Map();
335
- for (const [key, value] of Object.entries(filter))yMap.set(key, value);
494
+ yMap.set('id', id_id.uuid());
495
+ yMap.set('field', field);
336
496
  this.dsl.get('whereFilters').push([
337
497
  yMap
338
498
  ]);
@@ -340,31 +500,53 @@ class WhereFiltersBuilder {
340
500
  callback(node);
341
501
  return this;
342
502
  }
343
- update(field, callback) {
503
+ addGroup(op, callback) {
504
+ const yMap = new external_yjs_Map();
505
+ yMap.set('id', id_id.uuid());
506
+ yMap.set('op', op);
507
+ yMap.set('conditions', new external_yjs_Array());
508
+ this.dsl.get('whereFilters').push([
509
+ yMap
510
+ ]);
511
+ const group = new WhereGroupBuilder(yMap);
512
+ callback(group);
513
+ return this;
514
+ }
515
+ update(id, callback) {
344
516
  const whereFilters = this.dsl.get('whereFilters');
345
- const index = whereFilters.toArray().findIndex((item)=>item.get('field') === field);
346
- if (-1 === index) throw new Error(`Where filter with field ${field} not found`);
517
+ const index = whereFilters.toArray().findIndex((item)=>item.get('id') === id);
518
+ if (-1 === index) throw new Error(`Where filter with id ${id} not found`);
347
519
  const filterYMap = whereFilters.get(index);
348
520
  const node = new WhereFilterNodeBuilder(filterYMap);
349
521
  callback(node);
350
522
  return this;
351
523
  }
352
- remove(field) {
524
+ updateGroup(id, callback) {
353
525
  const whereFilters = this.dsl.get('whereFilters');
354
- const index = whereFilters.toArray().findIndex((item)=>item.get('field') === field);
355
- if (-1 === index) return this;
356
- whereFilters.delete(index, 1);
526
+ const index = whereFilters.toArray().findIndex((item)=>item.get('id') === id);
527
+ if (-1 === index) throw new Error(`Where group with id ${id} not found`);
528
+ const yMap = whereFilters.get(index);
529
+ if (!WhereFiltersBuilder.isGroup(yMap)) throw new Error(`Item with id ${id} is not a group`);
530
+ const group = new WhereGroupBuilder(yMap);
531
+ callback(group);
357
532
  return this;
358
533
  }
359
- find(field) {
534
+ remove(idOrIndex) {
360
535
  const whereFilters = this.dsl.get('whereFilters');
361
- const index = whereFilters.toArray().findIndex((item)=>item.get('field') === field);
362
- if (-1 === index) return;
363
- return new WhereFilterNodeBuilder(whereFilters.get(index));
536
+ if ('number' == typeof idOrIndex) {
537
+ if (idOrIndex >= 0 && idOrIndex < whereFilters.length) whereFilters.delete(idOrIndex, 1);
538
+ } else {
539
+ const index = whereFilters.toArray().findIndex((item)=>item.get('id') === idOrIndex);
540
+ if (-1 !== index) whereFilters.delete(index, 1);
541
+ }
542
+ return this;
364
543
  }
365
- findAll() {
544
+ find(id) {
366
545
  const whereFilters = this.dsl.get('whereFilters');
367
- return whereFilters.toArray().map((yMap)=>new WhereFilterNodeBuilder(yMap));
546
+ const yMap = whereFilters.toArray().find((item)=>item.get('id') === id);
547
+ if (!yMap) return;
548
+ if (WhereFiltersBuilder.isGroup(yMap)) return new WhereGroupBuilder(yMap);
549
+ return new WhereFilterNodeBuilder(yMap);
368
550
  }
369
551
  clear() {
370
552
  const whereFilters = this.dsl.get('whereFilters');
@@ -380,61 +562,34 @@ class WhereFiltersBuilder {
380
562
  this.dsl.get('whereFilters').unobserve(callback);
381
563
  };
382
564
  }
565
+ static isGroup(yMap) {
566
+ return void 0 !== yMap.get('op') && void 0 !== yMap.get('conditions');
567
+ }
568
+ static isNode(yMap) {
569
+ return void 0 !== yMap.get('field');
570
+ }
571
+ }
572
+ class undo_manager_UndoManager {
573
+ manager;
574
+ constructor(scope){
575
+ this.manager = new UndoManager(scope);
576
+ }
577
+ undo() {
578
+ return null !== this.manager.undo();
579
+ }
580
+ redo() {
581
+ return null !== this.manager.redo();
582
+ }
583
+ canUndo() {
584
+ return this.manager.canUndo();
585
+ }
586
+ canRedo() {
587
+ return this.manager.canRedo();
588
+ }
589
+ clear(clearUndoStack, clearRedoStack) {
590
+ this.manager.clear(clearUndoStack, clearRedoStack);
591
+ }
383
592
  }
384
- const zVBIHavingFilter = z.object({
385
- field: z.string(),
386
- operator: z.string().optional(),
387
- value: z.any().optional()
388
- });
389
- const zVBIHavingArray = z.array(zVBIHavingFilter);
390
- const buildVQuery = (vbiDSL, builder)=>{
391
- const wrapper = (processor)=>(queryDSL)=>processor(queryDSL, {
392
- vbiDSL,
393
- builder
394
- });
395
- return pipe({}, wrapper(buildSelect), wrapper(buildGroupBy), wrapper(buildWhere), wrapper(buildHaving), wrapper(buildOrderBy), wrapper(buildLimit));
396
- };
397
- const buildWhere = (queryDSL, context)=>{
398
- const { vbiDSL } = context;
399
- const whereFilters = vbiDSL.whereFilters || [];
400
- if (0 === whereFilters.length) return queryDSL;
401
- const result = {
402
- ...queryDSL
403
- };
404
- result.where = {
405
- op: 'and',
406
- conditions: whereFilters.flatMap((filter)=>{
407
- if ('between' === filter.operator && filter.value && 'object' == typeof filter.value && !Array.isArray(filter.value)) {
408
- const conditions = [];
409
- if (void 0 !== filter.value.min && null !== filter.value.min && '' !== filter.value.min) conditions.push({
410
- field: filter.field,
411
- op: '<' === filter.value.leftOp ? '>' : '>=',
412
- value: filter.value.min
413
- });
414
- if (void 0 !== filter.value.max && null !== filter.value.max && '' !== filter.value.max) conditions.push({
415
- field: filter.field,
416
- op: '<' === filter.value.rightOp ? '<' : '<=',
417
- value: filter.value.max
418
- });
419
- return conditions;
420
- }
421
- let mappedOp = filter.operator ?? '=';
422
- if (Array.isArray(filter.value)) {
423
- if ('=' === mappedOp) mappedOp = 'in';
424
- if ('!=' === mappedOp) mappedOp = 'not in';
425
- }
426
- return [
427
- {
428
- field: filter.field,
429
- op: mappedOp,
430
- value: filter.value
431
- }
432
- ];
433
- })
434
- };
435
- return result;
436
- };
437
- const buildOrderBy = (queryDSL, context)=>queryDSL;
438
593
  const buildSelect = (queryDSL, context)=>{
439
594
  const { vbiDSL } = context;
440
595
  const measures = vbiDSL.measures;
@@ -466,14 +621,68 @@ const buildGroupBy = (queryDSL, context)=>{
466
621
  result.groupBy = dimensionNodes.map((dimension)=>dimension.field);
467
622
  return result;
468
623
  };
469
- const buildLimit = (queryDSL, context)=>{
624
+ const buildWhere = (queryDSL, context)=>{
625
+ const { vbiDSL } = context;
626
+ const whereFilters = vbiDSL.whereFilters || [];
627
+ if (0 === whereFilters.length) return queryDSL;
470
628
  const result = {
471
629
  ...queryDSL
472
630
  };
473
- const limit = context.vbiDSL.limit ?? 1000;
474
- result.limit = limit;
631
+ result.where = {
632
+ op: 'and',
633
+ conditions: whereFilters.flatMap(mapClauseToCondition)
634
+ };
475
635
  return result;
476
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
668
+ });
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
+ }
477
686
  const buildHaving = (queryDSL, context)=>{
478
687
  const { vbiDSL } = context;
479
688
  const havingFilters = vbiDSL.havingFilters || [];
@@ -483,17 +692,50 @@ const buildHaving = (queryDSL, context)=>{
483
692
  };
484
693
  result.having = {
485
694
  op: 'and',
486
- conditions: havingFilters.map((filter)=>{
487
- const mappedOp = filter.operator ?? '=';
488
- return {
489
- field: filter.field,
490
- op: mappedOp,
491
- value: filter.value
492
- };
493
- })
695
+ conditions: havingFilters.flatMap(buildHaving_mapClauseToCondition)
494
696
  };
495
697
  return result;
496
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
+ };
497
739
  const connectorMap = new Map();
498
740
  const registerConnector = (id, connector)=>{
499
741
  connectorMap.set(id, connector);
@@ -516,7 +758,7 @@ class VBIBuilder {
516
758
  constructor(doc){
517
759
  this.doc = doc;
518
760
  this.dsl = doc.getMap('dsl');
519
- this.undoManager = new UndoManager(this.dsl);
761
+ this.undoManager = new undo_manager_UndoManager(this.dsl);
520
762
  this.chartType = new ChartTypeBuilder(doc, this.dsl);
521
763
  this.measures = new MeasuresBuilder(doc, this.dsl);
522
764
  this.dimensions = new DimensionsBuilder(doc, this.dsl);
@@ -596,7 +838,27 @@ const createVBI = ()=>({
596
838
  if (vbi.limit) dsl.set('limit', vbi.limit);
597
839
  if (vbi.locale) dsl.set('locale', vbi.locale);
598
840
  if (vbi.version) dsl.set('version', vbi.version);
599
- const ensureYArray = (arr)=>{
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)=>{
600
862
  if (!arr) return new external_yjs_Array();
601
863
  if (arr instanceof external_yjs_Array) return arr;
602
864
  const yArr = new external_yjs_Array();
@@ -604,20 +866,17 @@ const createVBI = ()=>({
604
866
  if (item instanceof external_yjs_Map) yArr.push([
605
867
  item
606
868
  ]);
607
- else if ('object' == typeof item && null !== item) {
608
- const yMap = new external_yjs_Map();
609
- for (const [key, value] of Object.entries(item))yMap.set(key, value);
610
- yArr.push([
611
- yMap
612
- ]);
613
- } else yArr.push([
869
+ else if ('object' == typeof item && null !== item) yArr.push([
870
+ toYMap(item, ensureId)
871
+ ]);
872
+ else yArr.push([
614
873
  item
615
874
  ]);
616
875
  });
617
876
  return yArr;
618
877
  };
619
- dsl.set('whereFilters', ensureYArray(vbi.whereFilters));
620
- dsl.set('havingFilters', ensureYArray(vbi.havingFilters));
878
+ dsl.set('whereFilters', ensureYArray(vbi.whereFilters, true));
879
+ dsl.set('havingFilters', ensureYArray(vbi.havingFilters, true));
621
880
  dsl.set('measures', ensureYArray(vbi.measures));
622
881
  dsl.set('dimensions', ensureYArray(vbi.dimensions));
623
882
  });
@@ -625,4 +884,52 @@ const createVBI = ()=>({
625
884
  }
626
885
  });
627
886
  const VBI = createVBI();
628
- export { ChartTypeBuilder, DimensionsBuilder, MeasuresBuilder, VBI, VBIBuilder, buildVQuery, findTreeNodesBy, preorderTraverse, zVBIHavingArray, zVBIHavingFilter };
887
+ const zVBIFilter = z.object({
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 };