Skip to content

Commit

Permalink
UI-9786 - Explicitly preserve the default measure when migrating from…
Browse files Browse the repository at this point in the history
… 4.3 to 5.0 (#130)

* 9786 - Explicitly preserve the default measure when migrating from 4.3 to 5.0

* Update the function so that it only adds the defualt measure when there exists a hierachy on columns and no measures are explicitly expressed.

* Fixup

* Update the tests for the new function

* Update snapshots

* Update the _migrateQuery test

* Adds a test ensuring that the default measure is added when the columns axis contains a sorted hierachy.

* just change order

---------

Co-authored-by: Nestor Zepeda <[email protected]>
  • Loading branch information
antoinetissier and NZepeda authored Nov 4, 2024
1 parent 5c4f836 commit 1760329
Show file tree
Hide file tree
Showing 4 changed files with 231 additions and 4 deletions.
134 changes: 134 additions & 0 deletions src/4.3_to_5.0/_addDefaultMeasureIfNoneIsExplicitlyExpressed.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { MdxSelect, parse, stringify } from "@activeviam/mdx-5.0";
import { _addDefaultMeasureIfNoneIsExplicitlyExpressed } from "./_addDefaultMeasureIfNoneIsExplicitlyExpressed";
import { dataModelsForTests } from "@activeviam/data-model-5.0";

const cube = dataModelsForTests.sandbox.catalogs[0].cubes[0];

describe("_addDefaultMeasureIfNoneIsExplicitlyExpressed", () => {
it("adds the default measure on columns when no measure is expressed on axes, but at least one hierarchy is expressed on the columns axis", () => {
const mdx: MdxSelect = parse(`
SELECT
[Currency].[Currency].Members ON COLUMNS
FROM [EquityDerivativesCube]`);
expect(
stringify(_addDefaultMeasureIfNoneIsExplicitlyExpressed(mdx, { cube }), {
indent: true,
}),
).toMatchInlineSnapshot(`
"SELECT
Crossjoin(
[Currency].[Currency].Members,
{
[Measures].[contributors.COUNT]
}
) ON COLUMNS
FROM [EquityDerivativesCube]"
`);
});

it("adds the default measure when there are no measures expressed and the columns axis contains a sorted hierarchy", () => {
const mdx: MdxSelect = parse(`
WITH
Member [Measures].[[Currency]].[Currency]].[Currency]]_for_order] AS Rank(
[Currency].[Currency].CurrentMember,
[Currency].[Currency].Members
)
SELECT
NON EMPTY Order(
Hierarchize(
Descendants(
{
[Currency].[Currency].[ALL].[AllMember]
},
1,
SELF_AND_BEFORE
)
),
[Measures].[[Currency]].[Currency]].[Currency]]_for_order],
DESC
) ON COLUMNS
FROM [EquityDerivativesCube]`);

expect(
stringify(_addDefaultMeasureIfNoneIsExplicitlyExpressed(mdx, { cube }), {
indent: true,
}),
).toMatchInlineSnapshot(`
"WITH
Member [Measures].[[Currency]].[Currency]].[Currency]]_for_order] AS Rank(
[Currency].[Currency].CurrentMember,
[Currency].[Currency].Members
)
SELECT
NON EMPTY Order(
Crossjoin(
Hierarchize(
Descendants(
{
[Currency].[Currency].[ALL].[AllMember]
},
1,
SELF_AND_BEFORE
)
),
{
[Measures].[contributors.COUNT]
}
),
[Measures].[[Currency]].[Currency]].[Currency]]_for_order],
DESC
) ON COLUMNS
FROM [EquityDerivativesCube]"
`);
});

it("does not add the default measure on columns when there are no measures expressed and the columns axis is empty", () => {
const mdx: MdxSelect = parse(`
SELECT
[Currency].[Currency].Members ON ROWS
FROM [EquityDerivativesCube]`);

expect(
stringify(_addDefaultMeasureIfNoneIsExplicitlyExpressed(mdx, { cube }), {
indent: true,
}),
).toMatchInlineSnapshot(`
"SELECT
[Currency].[Currency].Members ON ROWS
FROM [EquityDerivativesCube]"
`);
});

it("does not add if there are no axes expressed", () => {
const mdx: MdxSelect = parse(`
SELECT
FROM [EquityDerivativesCube]`);

expect(
stringify(_addDefaultMeasureIfNoneIsExplicitlyExpressed(mdx, { cube }), {
indent: true,
}),
).toMatchInlineSnapshot(`
"SELECT
FROM [EquityDerivativesCube]"
`);
});

it("returns the given MDX if a measure is already expressed", () => {
const mdx: MdxSelect = parse(`
SELECT
[Currency].[Currency].Members ON ROWS,
[Measures].[pnl.SUM] ON COLUMNS
FROM [EquityDerivativesCube]`);
expect(
stringify(_addDefaultMeasureIfNoneIsExplicitlyExpressed(mdx, { cube }), {
indent: true,
}),
).toMatchInlineSnapshot(`
"SELECT
[Currency].[Currency].Members ON ROWS,
[Measures].[pnl.SUM] ON COLUMNS
FROM [EquityDerivativesCube]"
`);
});
});
59 changes: 59 additions & 0 deletions src/4.3_to_5.0/_addDefaultMeasureIfNoneIsExplicitlyExpressed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { Cube } from "@activeviam/data-model-5.0";
import {
addMeasure,
getMeasures,
isMdxDrillthrough,
MdxDrillthrough,
MdxSelect,
} from "@activeviam/mdx-5.0";

export function _addDefaultMeasureIfNoneIsExplicitlyExpressed(
mdx: MdxDrillthrough,
{ cube }: { cube: Cube },
): MdxDrillthrough;
export function _addDefaultMeasureIfNoneIsExplicitlyExpressed(
mdx: MdxSelect,
{ cube }: { cube: Cube },
): MdxSelect;
export function _addDefaultMeasureIfNoneIsExplicitlyExpressed(
mdx: MdxSelect | MdxDrillthrough,
{ cube }: { cube: Cube },
): MdxSelect | MdxDrillthrough;

/**
* When there is any hierarchy expressed on the columns axis, but no measure explicitly expressed, the default measure is:
* - displayed in Atoti UI 4.3
* - not displayed in Atoti 5.0
* This function explicitly adds it to the MDX so that it is isofunctional for the user.
*/
export function _addDefaultMeasureIfNoneIsExplicitlyExpressed(
mdx: MdxSelect | MdxDrillthrough,
{ cube }: { cube: Cube },
): MdxSelect | MdxDrillthrough {
if (isMdxDrillthrough(mdx)) {
return mdx;
}

if (mdx.axes.length === 0) {
return mdx;
}

const columnsAxis = mdx.axes.find(
({ name }) => name === "COLUMNS" || name === "0",
);

if (!columnsAxis) {
return mdx;
}

const doesExplicitlyIncludeAMeasure = getMeasures(mdx).length > 0;
if (doesExplicitlyIncludeAMeasure) {
return mdx;
}

// A default measure is necessarily defined in each cube.
const defaultMeasureName = cube.defaultMembers.find(
({ dimension }) => dimension === "Measures",
)!.path[0];
return addMeasure(mdx, { cube, measureName: defaultMeasureName });
}
28 changes: 28 additions & 0 deletions src/4.3_to_5.0/_migrateQuery.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,32 @@ describe("_migrateQuery", () => {
CELL PROPERTIES VALUE, FORMATTED_VALUE, BACK_COLOR, FORE_COLOR, FONT_FLAGS"
`);
});

it("adds the default measure on columns when no measure is expressed on axes, but at least one hierarchy is expressed on the columns axis", () => {
const legacyQuery: LegacyQuery = {
mdx: `
SELECT
[Currency].[Currency].Members ON COLUMNS
FROM [EquityDerivativesCube]`,
};
const [
{
query: { mdx },
},
] = _migrateQuery({
legacyQuery,
cube,
shouldUpdateFiltersMdx: true,
});
expect(stringify(mdx!, { indent: true })).toMatchInlineSnapshot(`
"SELECT
Crossjoin(
[Currency].[Currency].Members,
{
[Measures].[contributors.COUNT]
}
) ON COLUMNS
FROM [EquityDerivativesCube]"
`);
});
});
14 changes: 10 additions & 4 deletions src/4.3_to_5.0/_migrateQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from "./_migrateContextValues";
import { _cleanupDescendants } from "./_cleanupDescendants";
import { _cleanupDrilldownLevel } from "./_cleanupDrilldownLevel";
import { _addDefaultMeasureIfNoneIsExplicitlyExpressed } from "./_addDefaultMeasureIfNoneIsExplicitlyExpressed";

export interface LegacyQuery {
mdx?: string;
Expand Down Expand Up @@ -78,7 +79,7 @@ export const _migrateQuery = <T extends MdxSelect | MdxDrillthrough>({
];
}

const parsedMdx = parse<T>(mdx);
const parsedMdx = parse<MdxSelect | MdxDrillthrough>(mdx);

let improvedMdx = parsedMdx;
try {
Expand All @@ -90,15 +91,20 @@ export const _migrateQuery = <T extends MdxSelect | MdxDrillthrough>({
// So attempt to improve it, but do not block the migration if the process fails.
}

const filters = getFilters(improvedMdx, { cube }).map(
const mdxWithMeasures = _addDefaultMeasureIfNoneIsExplicitlyExpressed(
improvedMdx,
{ cube },
);

const filters = getFilters(mdxWithMeasures, { cube }).map(
({ mdx: filterMdx }) => filterMdx,
);

const finalMdx = shouldUpdateFiltersMdx
? // The filters part of the MDX is removed from the query and stored in state.filters.
setFilters(improvedMdx, { filters: [], cube })
setFilters(mdxWithMeasures, { filters: [], cube })
: // The filters part of the MDX remains stored as is, in state.query.
improvedMdx;
mdxWithMeasures;

// TODO UI-5036 Migrate query ranges.
return [
Expand Down

0 comments on commit 1760329

Please sign in to comment.