@sisense/sdk-data 2.21.0 → 2.23.0

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 (36) hide show
  1. package/dist/cjs/dimensional-model/attributes.d.ts +10 -2
  2. package/dist/cjs/dimensional-model/attributes.js +11 -9
  3. package/dist/cjs/dimensional-model/dimensions/dimensions.d.ts +1 -1
  4. package/dist/cjs/dimensional-model/dimensions/dimensions.js +20 -17
  5. package/dist/cjs/dimensional-model/dimensions/utils.js +4 -0
  6. package/dist/cjs/dimensional-model/filters/factory.d.ts +8 -8
  7. package/dist/cjs/dimensional-model/filters/factory.js +30 -7
  8. package/dist/cjs/dimensional-model/filters/filter-relations.d.ts +16 -1
  9. package/dist/cjs/dimensional-model/filters/filter-relations.js +50 -13
  10. package/dist/cjs/dimensional-model/filters/filters.js +2 -2
  11. package/dist/cjs/dimensional-model/interfaces.d.ts +12 -0
  12. package/dist/cjs/dimensional-model/measures/measures.d.ts +2 -2
  13. package/dist/cjs/dimensional-model/measures/measures.js +4 -1
  14. package/dist/cjs/dimensional-model/types.d.ts +15 -9
  15. package/dist/cjs/dimensional-model/types.js +1 -0
  16. package/dist/cjs/utils.d.ts +16 -3
  17. package/dist/cjs/utils.js +20 -8
  18. package/dist/dimensional-model/attributes.d.ts +10 -2
  19. package/dist/dimensional-model/attributes.js +11 -9
  20. package/dist/dimensional-model/dimensions/dimensions.d.ts +1 -1
  21. package/dist/dimensional-model/dimensions/dimensions.js +20 -17
  22. package/dist/dimensional-model/dimensions/utils.js +4 -0
  23. package/dist/dimensional-model/filters/factory.d.ts +8 -8
  24. package/dist/dimensional-model/filters/factory.js +30 -7
  25. package/dist/dimensional-model/filters/filter-relations.d.ts +16 -1
  26. package/dist/dimensional-model/filters/filter-relations.js +50 -13
  27. package/dist/dimensional-model/filters/filters.js +3 -3
  28. package/dist/dimensional-model/interfaces.d.ts +12 -0
  29. package/dist/dimensional-model/measures/measures.d.ts +2 -2
  30. package/dist/dimensional-model/measures/measures.js +4 -1
  31. package/dist/dimensional-model/types.d.ts +15 -9
  32. package/dist/dimensional-model/types.js +1 -0
  33. package/dist/tsconfig.prod.cjs.tsbuildinfo +1 -1
  34. package/dist/utils.d.ts +16 -3
  35. package/dist/utils.js +17 -6
  36. package/package.json +2 -3
@@ -113,14 +113,21 @@ export function isTrivialSingleNodeRelations(relations) {
113
113
  /**
114
114
  * Calculates new relations based on the changes in filters.
115
115
  *
116
+ * @param prevFilters - The previous array of filters that the relations were built for.
117
+ * @param prevRelations - The previous relation rules.
118
+ * @param newFilters - The updated array of filters to build new relations for.
119
+ * @param options - Optional flags controlling how the diff is computed:
120
+ * - `shouldReplaceSameAttributeFilters`: when `true`, a new filter on the same attribute as
121
+ * an existing one (but with a different GUID) is treated as an in-place replacement rather
122
+ * than a remove + add pair, preserving the original relation tree structure.
116
123
  * @internal
117
124
  */
118
- export function calculateNewRelations(prevFilters, prevRelations, newFilters) {
125
+ export function calculateNewRelations(prevFilters, prevRelations, newFilters, options = {}) {
119
126
  // If there are no previous relations - no need to recalculate them
120
127
  if (prevRelations === null) {
121
128
  return null;
122
129
  }
123
- const performedActions = diffFilters(prevFilters, newFilters);
130
+ const performedActions = diffFilters(prevFilters, newFilters, options.shouldReplaceSameAttributeFilters);
124
131
  if (performedActions.length === 0) {
125
132
  return prevRelations;
126
133
  }
@@ -130,6 +137,8 @@ export function calculateNewRelations(prevFilters, prevRelations, newFilters) {
130
137
  return addFilterToRelations(action.payload, relations);
131
138
  case 'remove':
132
139
  return removeFilterFromRelations(action.payload, relations);
140
+ case 'replace':
141
+ return getRelationsWithReplacedFilter(relations, action.payload.prevFilter, action.payload.newFilter);
133
142
  }
134
143
  }, prevRelations);
135
144
  }
@@ -170,26 +179,54 @@ export function getRelationsWithReplacedFilter(relations, filterToReplace, newFi
170
179
  * Compares two arrays of Filter objects and determines the actions needed
171
180
  * to transform prevFilters into newFilters.
172
181
  *
182
+ * When `shouldReplaceSameAttributeFilters` is enabled, a new filter whose attribute matches
183
+ * an existing one (same `getFilterCompareId`) but has a different GUID is emitted as a single
184
+ * `replace` action instead of a `remove` + `add` pair. This preserves the relation tree node
185
+ * at the original position rather than appending the new filter with AND at the root.
186
+ * The disabled existing filters are excluded from the same-attribute replacement check
187
+ * and will therefore never be replaced in-place.
188
+ *
173
189
  * @param prevFilters - The original array of filters.
174
190
  * @param newFilters - The updated array of filters.
175
- * @param isEqualFilters - A function to determine if two filters are equal.
191
+ * @param shouldReplaceSameAttributeFilters - Optional flag to enable same-attribute filter replacement.
176
192
  * @returns An array of FilterAction objects representing the changes.
177
193
  */
178
- function diffFilters(prevFilters, newFilters) {
194
+ function diffFilters(prevFilters, newFilters, shouldReplaceSameAttributeFilters = false) {
179
195
  const actions = [];
180
- // Clone the arrays to avoid mutating the original data
181
- const prevFiltersCopy = [...prevFilters];
182
- const newFiltersCopy = [...newFilters];
183
- // Determine removals
184
- prevFiltersCopy.forEach((prevFilter) => {
185
- const existsInNew = newFiltersCopy.some((newFilter) => areFiltersEqualForRelations(prevFilter, newFilter));
196
+ // Track which filters from each side have already been matched to avoid double-counting.
197
+ const matchedPrevFilters = new Set();
198
+ const matchedNewFilters = new Set();
199
+ if (shouldReplaceSameAttributeFilters) {
200
+ prevFilters.forEach((prevFilter, prevIdx) => {
201
+ // Disabled filters are never eligible for in-place replacement.
202
+ if (prevFilter.config.disabled) {
203
+ return;
204
+ }
205
+ // Look for a new filter on the same attribute with a different GUID.
206
+ const newIdx = newFilters.findIndex((newFilter, idx) => !matchedNewFilters.has(idx) &&
207
+ !areFiltersEqualForRelations(prevFilter, newFilter) &&
208
+ getFilterCompareId(prevFilter) === getFilterCompareId(newFilter));
209
+ if (newIdx !== -1) {
210
+ actions.push({ type: 'replace', payload: { prevFilter, newFilter: newFilters[newIdx] } });
211
+ matchedPrevFilters.add(prevIdx);
212
+ matchedNewFilters.add(newIdx);
213
+ }
214
+ });
215
+ }
216
+ // Removals: prev filters not matched by GUID in new filters (and not already handled as replace).
217
+ prevFilters.forEach((prevFilter, prevIdx) => {
218
+ if (matchedPrevFilters.has(prevIdx))
219
+ return;
220
+ const existsInNew = newFilters.some((newFilter) => areFiltersEqualForRelations(prevFilter, newFilter));
186
221
  if (!existsInNew) {
187
222
  actions.push({ type: 'remove', payload: prevFilter });
188
223
  }
189
224
  });
190
- // Determine additions
191
- newFiltersCopy.forEach((newFilter) => {
192
- const existsInPrev = prevFiltersCopy.some((prevFilter) => areFiltersEqualForRelations(newFilter, prevFilter));
225
+ // Additions: new filters not matched by GUID in prev filters (and not already handled as replace).
226
+ newFilters.forEach((newFilter, newIdx) => {
227
+ if (matchedNewFilters.has(newIdx))
228
+ return;
229
+ const existsInPrev = prevFilters.some((prevFilter) => areFiltersEqualForRelations(newFilter, prevFilter));
193
230
  if (!existsInPrev) {
194
231
  actions.push({ type: 'add', payload: newFilter });
195
232
  }
@@ -5,7 +5,7 @@ import omit from 'lodash-es/omit.js';
5
5
  import { TranslatableError } from '../../translation/translatable-error.js';
6
6
  import { DimensionalElement } from '../base.js';
7
7
  import { create } from '../factory.js';
8
- import { isDimensionalBaseMeasure } from '../measures/measures.js';
8
+ import { isDimensionalBaseMeasure, isDimensionalCalculatedMeasure } from '../measures/measures.js';
9
9
  import { DateLevels, MetadataTypes } from '../types.js';
10
10
  import { getDefaultBaseFilterConfig, getDefaultMembersFilterConfig, } from './filter-config-utils.js';
11
11
  /**
@@ -470,7 +470,7 @@ export class MeasureFilter extends DoubleOperatorFilter {
470
470
  return AbstractFilter.disabledJaql(nested);
471
471
  }
472
472
  const result = super.jaql(nested);
473
- if (isDimensionalBaseMeasure(this.measure)) {
473
+ if (isDimensionalBaseMeasure(this.measure) || isDimensionalCalculatedMeasure(this.measure)) {
474
474
  Object.entries(this.measure.jaql().jaql).forEach(([key, value]) => {
475
475
  result.jaql[key] = value;
476
476
  });
@@ -561,7 +561,7 @@ export class MeasureRankingFilter extends AbstractFilter {
561
561
  return AbstractFilter.disabledJaql(nested);
562
562
  }
563
563
  const result = super.jaql(nested);
564
- if (isDimensionalBaseMeasure(this.measure)) {
564
+ if (isDimensionalBaseMeasure(this.measure) || isDimensionalCalculatedMeasure(this.measure)) {
565
565
  Object.entries(this.measure.jaql().jaql).forEach(([key, value]) => {
566
566
  result.jaql[key] = value;
567
567
  });
@@ -82,6 +82,18 @@ export interface Element {
82
82
  * @internal
83
83
  */
84
84
  composeCode?: string;
85
+ /**
86
+ * Merged from DataSourceField. It is used for the aggregation filtering
87
+ *
88
+ * @internal
89
+ */
90
+ readonly merged?: boolean;
91
+ /**
92
+ * Indexed from DataSourceField. It is used for the aggregation filtering
93
+ *
94
+ * @internal
95
+ */
96
+ readonly indexed?: boolean;
85
97
  }
86
98
  /**
87
99
  * Base interface for measure, which is typically numeric aggregation over {@link Attribute}(s).
@@ -1,6 +1,6 @@
1
1
  import { DimensionalElement } from '../base.js';
2
2
  import { Attribute, BaseMeasure, CalculatedMeasure, Measure, MeasureContext, MeasureTemplate } from '../interfaces.js';
3
- import { AnyObject, JaqlDataSource, JSONObject, Sort } from '../types.js';
3
+ import { AggregationType, AnyObject, JaqlDataSource, JSONObject, Sort } from '../types.js';
4
4
  /**
5
5
  * @internal
6
6
  */
@@ -57,7 +57,7 @@ export declare class DimensionalBaseMeasure extends AbstractMeasure implements B
57
57
  * @internal
58
58
  */
59
59
  readonly __serializable: string;
60
- static aggregationFromJAQL(agg: string): string;
60
+ static aggregationFromJAQL(agg: string): AggregationType;
61
61
  static aggregationToJAQL(agg: string): string;
62
62
  constructor(name: string, attribute: Attribute, agg: string, format?: string, desc?: string, sort?: Sort, dataSource?: JaqlDataSource, composeCode?: string);
63
63
  /**
@@ -235,7 +235,10 @@ export class DimensionalCalculatedMeasure extends AbstractMeasure {
235
235
  };
236
236
  const context = {};
237
237
  const keys = Object.getOwnPropertyNames(this.context);
238
- keys.forEach((k) => (context[k] = this.context[k].jaql(true)));
238
+ keys.forEach((k) => {
239
+ const v = this.context[k];
240
+ context[k] = v && typeof v.jaql === 'function' ? v.jaql(true) : v;
241
+ });
239
242
  r.jaql.context = context;
240
243
  if (this._format) {
241
244
  r.format = { number: this._format };
@@ -9,24 +9,29 @@ export type { JaqlDataSource, JaqlDataSourceForDto };
9
9
  */
10
10
  export declare const AggregationTypes: {
11
11
  /** Sum aggregation type */
12
- Sum: string;
12
+ readonly Sum: "sum";
13
13
  /** Average aggregation type */
14
- Average: string;
14
+ readonly Average: "avg";
15
15
  /** Min aggregation type */
16
- Min: string;
16
+ readonly Min: "min";
17
17
  /** Max aggregation type */
18
- Max: string;
18
+ readonly Max: "max";
19
19
  /** Count aggregation type */
20
- Count: string;
20
+ readonly Count: "count";
21
21
  /** Count distinct aggregation type */
22
- CountDistinct: string;
22
+ readonly CountDistinct: "countDistinct";
23
23
  /** Median aggregation type */
24
- Median: string;
24
+ readonly Median: "median";
25
25
  /** Variance aggregation type */
26
- Variance: string;
26
+ readonly Variance: "var";
27
27
  /** Standard deviation aggregation type */
28
- StandardDeviation: string;
28
+ readonly StandardDeviation: "stdev";
29
29
  };
30
+ declare const aggTypes: ("sum" | "avg" | "min" | "max" | "count" | "countDistinct" | "median" | "var" | "stdev")[];
31
+ /**
32
+ * @internal
33
+ */
34
+ export declare type AggregationType = (typeof aggTypes)[number];
30
35
  /**
31
36
  * Different sort types.
32
37
  */
@@ -202,6 +207,7 @@ export declare type BaseJaql = {
202
207
  jaql: FilterJaql;
203
208
  };
204
209
  };
210
+ indexed?: boolean;
205
211
  merged?: boolean;
206
212
  panel?: 'rows' | 'columns';
207
213
  };
@@ -22,6 +22,7 @@ export const AggregationTypes = {
22
22
  /** Standard deviation aggregation type */
23
23
  StandardDeviation: 'stdev',
24
24
  };
25
+ const aggTypes = Object.values(AggregationTypes);
25
26
  /**
26
27
  * Different sort types.
27
28
  */