@webtypen/webframez-core 0.3.104 → 0.3.106

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.
@@ -267,7 +267,7 @@ class Model {
267
267
  type: "aggregation",
268
268
  table: collection ? collection : model ? model.__table : undefined,
269
269
  aggregation: aggregation,
270
- }, model.__connection);
270
+ }, model.__connection, options);
271
271
  });
272
272
  }
273
273
  buildRelationship(model, foreignKey, localKey) {
@@ -37,6 +37,8 @@ export declare class Datatable {
37
37
  autoApplyFilter: "begin" | "end" | null;
38
38
  autoApplySearch: "begin" | "end" | null;
39
39
  logAggregation: boolean;
40
+ deferPresentation: boolean;
41
+ collation: Record<string, any> | ((req: Request) => Record<string, any> | Promise<Record<string, any>>);
40
42
  defaultUnwind?: string | null;
41
43
  disablePerPageConfig: boolean;
42
44
  selectable: boolean;
@@ -77,6 +79,8 @@ export declare class Datatable {
77
79
  }[] | null>;
78
80
  getAggregation(req: Request): Promise<any>;
79
81
  getSubAggregation(req: Request): Promise<any>;
82
+ /** Filtered source for whole-result statistics; presentation and pagination are excluded. */
83
+ getStatisticsAggregation(req: Request): Promise<any[]>;
80
84
  getCollection(req: Request): Promise<any>;
81
85
  getData(req: Request, context?: DatatableColumnContext): Promise<{
82
86
  page: number;
@@ -43,6 +43,8 @@ class Datatable {
43
43
  this.autoApplyFilter = "begin";
44
44
  this.autoApplySearch = null;
45
45
  this.logAggregation = false;
46
+ this.deferPresentation = false;
47
+ this.collation = { locale: "de", strength: 2 };
46
48
  this.defaultUnwind = null;
47
49
  this.disablePerPageConfig = false;
48
50
  this.selectable = false;
@@ -252,10 +254,7 @@ class Datatable {
252
254
  const results = yield DBConnection_1.DBConnection.documentStore(connection)
253
255
  .collection(yield this.getCollection(req))
254
256
  .aggregate(aggregation, {
255
- collation: {
256
- locale: "de",
257
- strength: 2,
258
- },
257
+ collation: typeof this.collation === "function" ? yield this.collation(req) : this.collation,
259
258
  })
260
259
  .toArray();
261
260
  if (!results[0]) {
@@ -353,6 +352,17 @@ class Datatable {
353
352
  return typeof this.subAggregation === "function" ? yield this.subAggregation(req, yield this.getColumnContext(req)) : this.subAggregation;
354
353
  });
355
354
  }
355
+ /** Filtered source for whole-result statistics; presentation and pagination are excluded. */
356
+ getStatisticsAggregation(req) {
357
+ return __awaiter(this, void 0, void 0, function* () {
358
+ return [...(yield this.getAggregation(req)) || [], ...(yield this.getSubAggregation(req)) || []]
359
+ .filter(stage => !stage.skipStats && !stage.$sort && stage.$skip === undefined && stage.$limit === undefined)
360
+ .map((_a) => {
361
+ var { skipStats } = _a, stage = __rest(_a, ["skipStats"]);
362
+ return stage;
363
+ });
364
+ });
365
+ }
356
366
  getCollection(req) {
357
367
  return __awaiter(this, void 0, void 0, function* () {
358
368
  if (this.collection && typeof this.collection === "function") {
@@ -373,16 +383,21 @@ class Datatable {
373
383
  let hasSkip = false;
374
384
  let hasLimit = false;
375
385
  if (aggregationDef && aggregationDef.length > 0) {
376
- for (let entry of aggregationDef) {
386
+ for (const definition of aggregationDef) {
387
+ const { skipStats } = definition, entry = __rest(definition, ["skipStats"]);
377
388
  if (entry.$skip !== undefined) {
378
389
  hasSkip = true;
379
390
  }
380
391
  if (entry.$limit !== undefined) {
381
392
  hasLimit = true;
382
393
  }
383
- if (entry.skipStats) {
384
- delete entry.skipStats;
385
- aggregation.push(entry);
394
+ if (skipStats) {
395
+ if (this.deferPresentation && !entry.$sort && entry.$skip === undefined && entry.$limit === undefined) {
396
+ subAggregation.push(entry);
397
+ }
398
+ else {
399
+ aggregation.push(entry);
400
+ }
386
401
  }
387
402
  else {
388
403
  aggregation.push(entry);
@@ -391,15 +406,15 @@ class Datatable {
391
406
  }
392
407
  }
393
408
  if (subAggregationDef && subAggregationDef.length > 0) {
394
- for (let entry of subAggregationDef) {
409
+ for (const definition of subAggregationDef) {
410
+ const { skipStats } = definition, entry = __rest(definition, ["skipStats"]);
395
411
  if (entry.$skip !== undefined) {
396
412
  hasSkip = true;
397
413
  }
398
414
  if (entry.$limit !== undefined) {
399
415
  hasLimit = true;
400
416
  }
401
- if (entry.skipStats) {
402
- delete entry.skipStats;
417
+ if (skipStats) {
403
418
  subAggregation.push(entry);
404
419
  }
405
420
  else {
@@ -423,10 +438,7 @@ class Datatable {
423
438
  const results = yield DBConnection_1.DBConnection.documentStore(connection)
424
439
  .collection(yield this.getCollection(req))
425
440
  .aggregate(aggr, {
426
- collation: {
427
- locale: "de",
428
- strength: 2,
429
- },
441
+ collation: typeof this.collation === "function" ? yield this.collation(req) : this.collation,
430
442
  })
431
443
  .toArray();
432
444
  const sumsAfter = [];
@@ -511,23 +523,17 @@ class Datatable {
511
523
  }
512
524
  getTotalData(req) {
513
525
  return __awaiter(this, void 0, void 0, function* () {
514
- const aggregation = [];
515
- const aggregationDef = yield this.getAggregation(req);
516
- if (aggregationDef && aggregationDef.length > 0) {
517
- for (let entry of aggregationDef) {
518
- if (entry.skipStats) {
519
- delete entry.skipStats;
520
- aggregation.push(entry);
521
- }
522
- else {
523
- aggregation.push(entry);
524
- }
525
- }
526
- }
526
+ const aggregation = [...(yield this.getAggregation(req)) || [], ...(yield this.getSubAggregation(req)) || []]
527
+ .map((_a) => {
528
+ var { skipStats } = _a, stage = __rest(_a, ["skipStats"]);
529
+ return stage;
530
+ });
527
531
  const connection = yield DBConnection_1.DBConnection.getConnection();
528
532
  return yield DBConnection_1.DBConnection.documentStore(connection)
529
533
  .collection(yield this.getCollection(req))
530
- .aggregate([...(aggregation ? aggregation : [])])
534
+ .aggregate(aggregation, {
535
+ collation: typeof this.collation === "function" ? yield this.collation(req) : this.collation,
536
+ })
531
537
  .toArray();
532
538
  });
533
539
  }
@@ -126,3 +126,26 @@ creates an optional column catalog from nested DataBuilder/DataSchema fields.
126
126
  The `prepare(column, field, path)` callback can attach inline or lazy display
127
127
  options, change the mapping, or return null to omit a field. Plugin installation,
128
128
  tenant checks and relation hydration belong in the host adapter.
129
+
130
+ ## Statistics and collation
131
+
132
+ `getStatisticsAggregation(req)` returns the filtered aggregation and sub-aggregation,
133
+ excluding stages marked `skipStats: true`, sorting and pagination. It does not mutate
134
+ stage definitions. Keep tenant and filter dependencies unmarked; calculate statistic-only
135
+ fields in the statistics service instead of depending on presentation projections.
136
+
137
+ `collation` defaults to `{ locale: "de", strength: 2 }` for compatibility. It can be
138
+ an object or an async request callback. Tables sorting numeric/date fields can return
139
+ `{ locale: "simple" }` to use matching binary indexes. Keep language collation for
140
+ text sorting or filters that depend on it; collation applies to the entire data query.
141
+
142
+ Exports (`getTotalData`) execute both aggregation phases with the same collation as
143
+ the table, without injecting page limits. Explicit limits in the table definition
144
+ remain effective, including bounded notification exports.
145
+
146
+ Tables can opt into `deferPresentation = true` to execute aggregation stages marked
147
+ `skipStats: true` after pagination, before explicit `subAggregation`. Sort/skip/limit
148
+ stages stay in the selection phase. Only mark cardinality-preserving display stages:
149
+ any join or calculation required by a filter, sort, or later selection stage must
150
+ remain unmarked. Exports retain all stages in their original order. The default is
151
+ false, so existing definitions keep their behavior until explicitly reviewed.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webtypen/webframez-core",
3
- "version": "0.3.104",
3
+ "version": "0.3.106",
4
4
  "description": "The core package of the typescript based webframez (web-framework) by webtypen.de",
5
5
  "homepage": "https://webtypen.de/",
6
6
  "license": "MIT",