diff --git a/app/browse/datatable.tsx b/app/browse/datatable.tsx
index 83458edfc..f0aadb926 100644
--- a/app/browse/datatable.tsx
+++ b/app/browse/datatable.tsx
@@ -267,6 +267,7 @@ export const DataSetTable = ({
cubeFilters: chartConfig.cubes.map((cube) => ({
iri: cube.iri,
componentIris,
+ joinBy: cube.joinBy,
})),
},
});
diff --git a/app/browser/dataset-preview.tsx b/app/browser/dataset-preview.tsx
index d65fb6cd5..f162d6374 100644
--- a/app/browser/dataset-preview.tsx
+++ b/app/browser/dataset-preview.tsx
@@ -92,7 +92,7 @@ export const DataSetPreview = ({
}) => {
const footnotesClasses = useFootnotesStyles({ useMarginTop: false });
const locale = useLocale();
- const filters = [{ iri: dataSetIri }];
+ const cubeFilters = [{ iri: dataSetIri }];
const [
{ data: previewData, fetching: fetchingPreview, error: previewError },
] = useDataCubePreviewQuery({
@@ -114,7 +114,7 @@ export const DataSetPreview = ({
sourceType: dataSource.type,
sourceUrl: dataSource.url,
locale,
- cubeFilters: filters,
+ cubeFilters,
},
});
const classes = useStyles({
@@ -197,7 +197,7 @@ export const DataSetPreview = ({
{dataCubeByIri.observations.sparqlEditorUrl && (
({
iri: cube.iri,
componentIris,
+ joinBy: cube.joinBy,
})),
},
});
diff --git a/app/charts/column/chart-column.tsx b/app/charts/column/chart-column.tsx
index 73c6f9b1b..069c99bee 100644
--- a/app/charts/column/chart-column.tsx
+++ b/app/charts/column/chart-column.tsx
@@ -69,6 +69,7 @@ export const ChartColumnsVisualization = ({
cubeFilters: chartConfig.cubes.map((cube) => ({
iri: cube.iri,
componentIris,
+ joinBy: cube.joinBy,
})),
},
});
diff --git a/app/charts/column/columns-state.tsx b/app/charts/column/columns-state.tsx
index 4666ba313..424142ba8 100644
--- a/app/charts/column/columns-state.tsx
+++ b/app/charts/column/columns-state.tsx
@@ -235,6 +235,8 @@ const useColumnsState = (
return `${getYError(d)}${yErrorMeasure?.unit ?? ""}`;
};
+ const y = getY(d);
+
return {
xAnchor,
yAnchor,
@@ -246,7 +248,7 @@ const useColumnsState = (
xValue: xTimeUnit ? timeFormatUnit(xLabel, xTimeUnit) : xLabel,
datum: {
label: undefined,
- value: `${yValueFormatter(getY(d))}`,
+ value: y !== null && isNaN(y) ? "-" : `${yValueFormatter(getY(d))}`,
error: getError(d),
color: "",
},
diff --git a/app/charts/column/columns.tsx b/app/charts/column/columns.tsx
index 11661ffda..b133f8730 100644
--- a/app/charts/column/columns.tsx
+++ b/app/charts/column/columns.tsx
@@ -96,7 +96,8 @@ export const Columns = () => {
return chartData.map((d) => {
const key = getRenderingKey(d);
const xScaled = xScale(getX(d)) as number;
- const y = getY(d) ?? NaN;
+ const yRaw = getY(d);
+ const y = yRaw === null || isNaN(yRaw) ? 0 : yRaw;
const yScaled = yScale(y);
const yRender = yScale(Math.max(y, 0));
const height = Math.abs(yScaled - y0);
diff --git a/app/charts/combo/chart-combo-line-column.tsx b/app/charts/combo/chart-combo-line-column.tsx
index 52b91eac1..a6dc4f178 100644
--- a/app/charts/combo/chart-combo-line-column.tsx
+++ b/app/charts/combo/chart-combo-line-column.tsx
@@ -51,6 +51,7 @@ export const ChartComboLineColumnVisualization = (
cubeFilters: chartConfig.cubes.map((cube) => ({
iri: cube.iri,
componentIris,
+ joinBy: cube.joinBy,
})),
},
});
diff --git a/app/charts/combo/chart-combo-line-dual.tsx b/app/charts/combo/chart-combo-line-dual.tsx
index 852cd2901..39e34822e 100644
--- a/app/charts/combo/chart-combo-line-dual.tsx
+++ b/app/charts/combo/chart-combo-line-dual.tsx
@@ -51,6 +51,7 @@ export const ChartComboLineDualVisualization = (
cubeFilters: chartConfig.cubes.map((cube) => ({
iri: cube.iri,
componentIris,
+ joinBy: cube.joinBy,
})),
},
});
diff --git a/app/charts/combo/chart-combo-line-single.tsx b/app/charts/combo/chart-combo-line-single.tsx
index 7c0a7e50c..a652930a7 100644
--- a/app/charts/combo/chart-combo-line-single.tsx
+++ b/app/charts/combo/chart-combo-line-single.tsx
@@ -56,6 +56,7 @@ export const ChartComboLineSingleVisualization = (
cubeFilters: chartConfig.cubes.map((cube) => ({
iri: cube.iri,
componentIris,
+ joinBy: cube.joinBy,
})),
},
});
diff --git a/app/charts/index.ts b/app/charts/index.ts
index a1513b532..e5297d355 100644
--- a/app/charts/index.ts
+++ b/app/charts/index.ts
@@ -137,7 +137,7 @@ export const chartTypesOrder: { [k in ChartType]: number } = {
* @param dimensions
* @param preferredType
*/
-export const findPreferredDimension = (
+const findPreferredDimension = (
dimensions: Component[],
preferredType?: DimensionType
) => {
@@ -1885,9 +1885,11 @@ const adjustSegmentSorting = ({
export const getPossibleChartTypes = ({
dimensions,
measures,
+ allowedChartTypes,
}: {
dimensions: Dimension[];
measures: Measure[];
+ allowedChartTypes?: ChartType[];
}): ChartType[] => {
const numericalMeasures = measures.filter(isNumericalMeasure);
const ordinalMeasures = measures.filter(isOrdinalMeasure);
@@ -1945,7 +1947,11 @@ export const getPossibleChartTypes = ({
}
return chartTypes
- .filter((d) => possibles.includes(d))
+ .filter(
+ (d) =>
+ possibles.includes(d) &&
+ (!allowedChartTypes || allowedChartTypes.includes(d))
+ )
.sort((a, b) => chartTypesOrder[a] - chartTypesOrder[b]);
};
diff --git a/app/charts/line/chart-lines.tsx b/app/charts/line/chart-lines.tsx
index aafaa070e..b98306fd3 100644
--- a/app/charts/line/chart-lines.tsx
+++ b/app/charts/line/chart-lines.tsx
@@ -56,6 +56,7 @@ export const ChartLinesVisualization = ({
cubeFilters: chartConfig.cubes.map((cube) => ({
iri: cube.iri,
componentIris,
+ joinBy: cube.joinBy,
})),
},
});
diff --git a/app/charts/map/chart-map.tsx b/app/charts/map/chart-map.tsx
index ec701ed65..f6b3cdcac 100644
--- a/app/charts/map/chart-map.tsx
+++ b/app/charts/map/chart-map.tsx
@@ -59,6 +59,7 @@ export const ChartMapVisualization = ({
cubeFilters: chartConfig.cubes.map((cube) => ({
iri: cube.iri,
componentIris,
+ joinBy: cube.joinBy,
})),
},
});
diff --git a/app/charts/pie/chart-pie.tsx b/app/charts/pie/chart-pie.tsx
index 7796a3bfa..30234a0a3 100644
--- a/app/charts/pie/chart-pie.tsx
+++ b/app/charts/pie/chart-pie.tsx
@@ -52,6 +52,7 @@ export const ChartPieVisualization = ({
cubeFilters: chartConfig.cubes.map((cube) => ({
iri: cube.iri,
componentIris,
+ joinBy: cube.joinBy,
})),
},
});
diff --git a/app/charts/scatterplot/chart-scatterplot.tsx b/app/charts/scatterplot/chart-scatterplot.tsx
index 693fcc6bc..b18d56c81 100644
--- a/app/charts/scatterplot/chart-scatterplot.tsx
+++ b/app/charts/scatterplot/chart-scatterplot.tsx
@@ -64,6 +64,7 @@ export const ChartScatterplotVisualization = ({
cubeFilters: chartConfig.cubes.map((cube) => ({
iri: cube.iri,
componentIris,
+ joinBy: cube.joinBy,
})),
},
});
diff --git a/app/charts/shared/chart-data-filters.tsx b/app/charts/shared/chart-data-filters.tsx
index 6013b24f3..18f7514e2 100644
--- a/app/charts/shared/chart-data-filters.tsx
+++ b/app/charts/shared/chart-data-filters.tsx
@@ -18,7 +18,6 @@ import {
DataSource,
Filters,
getFiltersByMappingStatus,
- QueryFilters,
useChartConfigFilters,
useConfiguratorState,
} from "@/configurator";
@@ -97,10 +96,9 @@ export const ChartDataFilters = (props: ChartDataFiltersProps) => {
const cubeQueryFilters = queryFilters.find(
(d) => d.iri === cube.iri
) as DataCubeObservationFilter;
- const filtersByMappingStatus = getFiltersByMappingStatus(
- chartConfig,
- cube.iri
- );
+ const filtersByMappingStatus = getFiltersByMappingStatus(chartConfig, {
+ cubeIri: cube.iri,
+ });
const { unmappedFilters, mappedFilters } = filtersByMappingStatus;
const unmappedKeys = Object.keys(unmappedFilters);
const unmappedQueryFiltersArray = Object.entries(
@@ -214,7 +212,7 @@ type DataFilterProps = {
dataSource: DataSource;
chartConfig: ChartConfig;
dataFilters: DataFilters;
- interactiveFilters: QueryFilters;
+ interactiveFilters: Filters;
disabled: boolean;
};
diff --git a/app/charts/shared/chart-helpers.tsx b/app/charts/shared/chart-helpers.tsx
index 7b67ca8d5..34a9ed9cd 100644
--- a/app/charts/shared/chart-helpers.tsx
+++ b/app/charts/shared/chart-helpers.tsx
@@ -32,6 +32,7 @@ import { parseDate } from "@/configurator/components/ui-helpers";
import { FIELD_VALUE_NONE } from "@/configurator/constants";
import { Component, Dimension, Measure, Observation } from "@/domain/data";
import { truthy } from "@/domain/types";
+import { JOIN_BY_DIMENSION_IRI } from "@/graphql/hook-utils";
import { DataCubeObservationFilter } from "@/graphql/resolver-types";
import {
InteractiveFiltersState,
@@ -93,12 +94,10 @@ export const useQueryFilters = ({
return {
iri: cube.iri,
- componentIris:
- dimensions.length > 0 && measures.length > 0
- ? [...dimensions, ...measures]
- .filter((d) => d.cubeIri === cube.iri)
- .map((d) => d.iri)
- : undefined,
+ // componentIris: getComponentIris(cube.iri, {
+ // dimensions,
+ // measures,
+ // }),
filters: prepareQueryFilters(
chartConfig.chartType,
filters,
@@ -120,10 +119,48 @@ export const useQueryFilters = ({
]);
};
+// Handle correctly when improving performance of data fetching!
+// const getComponentIris = (
+// cubeIri: string,
+// options: {
+// dimensions: Dimension[];
+// measures: Measure[];
+// }
+// ) => {
+// const { dimensions, measures } = options;
+
+// if (dimensions.length === 0 && measures.length === 0) {
+// return;
+// }
+
+// const filteredDimensionIris: string[] = [];
+
+// for (const dimension of dimensions) {
+// if (dimension.isJoinByDimension) {
+// if (dimension.originalIris.some((d) => d.cubeIri === cubeIri)) {
+// filteredDimensionIris.push(
+// ...dimension.originalIris.map((d) => d.dimensionIri)
+// );
+// }
+// } else {
+// if (dimension.cubeIri === cubeIri) {
+// filteredDimensionIris.push(dimension.iri);
+// }
+// }
+// }
+
+// return [
+// ...filteredDimensionIris,
+// ...measures.filter((d) => d.cubeIri === cubeIri).map((d) => d.iri),
+// ];
+// };
+
type IFKey = keyof NonNullable;
export const getChartConfigFilterComponentIris = ({ cubes }: ChartConfig) => {
- return Object.keys(getChartConfigFilters(cubes));
+ return Object.keys(getChartConfigFilters(cubes)).filter(
+ (d) => d !== JOIN_BY_DIMENSION_IRI
+ );
};
const getMapChartConfigAdditionalFields = ({ fields }: MapConfig) => {
@@ -212,12 +249,16 @@ export const extractChartConfigComponentIris = (chartConfig: ChartConfig) => {
});
}
- return uniq(
- [...fieldIris, ...additionalFieldIris, ...filterIris, ...IFIris].filter(
- Boolean
+ return (
+ uniq(
+ [...fieldIris, ...additionalFieldIris, ...filterIris, ...IFIris].filter(
+ Boolean
+ )
)
- // Important so the order is consistent when querying.
- ).sort();
+ .filter((d) => d !== JOIN_BY_DIMENSION_IRI)
+ // Important so the order is consistent when querying.
+ .sort()
+ );
};
/** Use to remove missing values from chart data. */
diff --git a/app/charts/table/chart-table.tsx b/app/charts/table/chart-table.tsx
index 5d02c4561..14b6966eb 100644
--- a/app/charts/table/chart-table.tsx
+++ b/app/charts/table/chart-table.tsx
@@ -40,6 +40,7 @@ export const ChartTableVisualization = ({
cubeFilters: chartConfig.cubes.map((cube) => ({
iri: cube.iri,
componentIris,
+ joinBy: cube.joinBy,
})),
},
});
diff --git a/app/components/chart-filters-list.tsx b/app/components/chart-filters-list.tsx
index 44c726f10..8c19bfedb 100644
--- a/app/components/chart-filters-list.tsx
+++ b/app/components/chart-filters-list.tsx
@@ -43,13 +43,13 @@ export const ChartFiltersList = (props: ChartFiltersListProps) => {
sourceType: dataSource.type,
sourceUrl: dataSource.url,
locale,
- cubeFilters: filters
- ? filters.map((filter) => ({
- iri: filter.iri,
- componentIris: filter.componentIris,
- filters: filter.filters,
- }))
- : [],
+ cubeFilters:
+ filters?.map((filter) => ({
+ iri: filter.iri,
+ componentIris: filter.componentIris,
+ filters: filter.filters,
+ joinBy: filter.joinBy,
+ })) ?? [],
},
pause: !filters,
});
diff --git a/app/components/chart-preview.tsx b/app/components/chart-preview.tsx
index 1886c5954..c86035cbf 100644
--- a/app/components/chart-preview.tsx
+++ b/app/components/chart-preview.tsx
@@ -82,6 +82,8 @@ export const ChartPreviewInner = (props: ChartPreviewProps) => {
cubeFilters: chartConfig.cubes.map((cube) => ({
iri: cube.iri,
componentIris,
+ filters: cube.filters,
+ joinBy: cube.joinBy,
})),
},
});
@@ -93,8 +95,8 @@ export const ChartPreviewInner = (props: ChartPreviewProps) => {
} = useChartTablePreview();
const handleToggleTableView = useEvent(() => setIsTablePreview((c) => !c));
- const dimensions = components?.dataCubesComponents.dimensions ?? [];
- const measures = components?.dataCubesComponents.measures ?? [];
+ const dimensions = components?.dataCubesComponents.dimensions;
+ const measures = components?.dataCubesComponents.measures;
const allComponents = useMemo(() => {
if (!components?.dataCubesComponents) {
return [];
diff --git a/app/components/chart-published.tsx b/app/components/chart-published.tsx
index e8d68811d..c44159e51 100644
--- a/app/components/chart-published.tsx
+++ b/app/components/chart-published.tsx
@@ -152,6 +152,7 @@ const ChartPublishedInner = (props: ChartPublishInnerProps) => {
cubeFilters: chartConfig.cubes.map((cube) => ({
iri: cube.iri,
componentIris,
+ joinBy: cube.joinBy,
})),
},
});
diff --git a/app/components/chart-selection-tabs.tsx b/app/components/chart-selection-tabs.tsx
index ec73c5ff5..15b304381 100644
--- a/app/components/chart-selection-tabs.tsx
+++ b/app/components/chart-selection-tabs.tsx
@@ -263,6 +263,8 @@ const PublishChartButton = () => {
cubeFilters: chartConfig.cubes.map((cube) => ({
iri: cube.iri,
componentIris,
+ filters: cube.filters,
+ joinBy: cube.joinBy,
})),
},
});
diff --git a/app/components/chart-with-filters.tsx b/app/components/chart-with-filters.tsx
index 3ec888233..1d3ab49b2 100644
--- a/app/components/chart-with-filters.tsx
+++ b/app/components/chart-with-filters.tsx
@@ -84,8 +84,8 @@ const GenericChart = (props: GenericChartProps) => {
props;
const queryFilters = useQueryFilters({
chartConfig,
- dimensions: dimensions ?? [],
- measures: measures ?? [],
+ dimensions,
+ measures,
});
const commonProps = {
dataSource,
diff --git a/app/config-types.ts b/app/config-types.ts
index 00050c305..59dddef6d 100644
--- a/app/config-types.ts
+++ b/app/config-types.ts
@@ -111,7 +111,6 @@ export type FilterValueMultiValues = FilterValueMulti["values"];
const Filters = t.record(t.string, FilterValue, "Filters");
export type Filters = t.TypeOf;
-export type QueryFilters = Filters | FilterValueSingle;
// Meta
const Title = t.type({
diff --git a/app/configurator/components/chart-configurator.tsx b/app/configurator/components/chart-configurator.tsx
index de30da874..41670699e 100644
--- a/app/configurator/components/chart-configurator.tsx
+++ b/app/configurator/components/chart-configurator.tsx
@@ -19,7 +19,7 @@ import { makeStyles } from "@mui/styles";
import isEmpty from "lodash/isEmpty";
import isEqual from "lodash/isEqual";
import sortBy from "lodash/sortBy";
-import { useEffect, useMemo, useRef, useState } from "react";
+import React, { useEffect, useMemo, useRef, useState } from "react";
import {
DragDropContext,
Draggable,
@@ -74,6 +74,7 @@ import {
isTemporalDimension,
Measure,
} from "@/domain/data";
+import { truthy } from "@/domain/types";
import {
useDataCubesComponentsQuery,
useDataCubesObservationsQuery,
@@ -171,13 +172,16 @@ const useEnsurePossibleFilters = ({
const [error, setError] = useState();
const lastFilters = useRef>({});
const client = useClient();
+ const joinByIris = React.useMemo(() => {
+ return chartConfig.cubes.flatMap((cube) => cube.joinBy).filter(truthy);
+ }, [chartConfig.cubes]);
useEffect(() => {
const run = async () => {
chartConfig.cubes.forEach(async (cube) => {
const { mappedFilters, unmappedFilters } = getFiltersByMappingStatus(
chartConfig,
- cube.iri
+ { cubeIri: cube.iri, joinByIris }
);
if (
@@ -247,6 +251,7 @@ const useEnsurePossibleFilters = ({
chartConfig.cubes,
state.dataSource.type,
state.dataSource.url,
+ joinByIris,
]);
return { error, fetching };
@@ -261,25 +266,30 @@ const useFilterReorder = ({
const chartConfig = getChartConfig(state);
const locale = useLocale();
const filters = getChartConfigFilters(chartConfig.cubes);
+ const joinByIris = React.useMemo(() => {
+ return chartConfig.cubes.flatMap((cube) => cube.joinBy).filter(truthy);
+ }, [chartConfig.cubes]);
const { mappedFiltersIris } = useMemo(() => {
- return getFiltersByMappingStatus(chartConfig);
- }, [chartConfig]);
+ return getFiltersByMappingStatus(chartConfig, { joinByIris });
+ }, [chartConfig, joinByIris]);
const variables = useMemo(() => {
const cubeFilters = chartConfig.cubes.map((cube) => {
- const { unmappedFilters } = getFiltersByMappingStatus(
- chartConfig,
- cube.iri
- );
+ const { unmappedFilters } = getFiltersByMappingStatus(chartConfig, {
+ cubeIri: cube.iri,
+ joinByIris,
+ });
return Object.keys(unmappedFilters).length > 0
? {
iri: cube.iri,
filters: unmappedFilters,
+ joinBy: cube.joinBy,
}
: {
iri: cube.iri,
filters: undefined,
+ joinBy: cube.joinBy,
};
});
@@ -295,7 +305,7 @@ const useFilterReorder = ({
cubeFilters,
requeryKey: requeryKey ? requeryKey : undefined,
};
- }, [chartConfig]);
+ }, [chartConfig, joinByIris]);
const [
{ data: componentsData, fetching: componentsFetching },
@@ -769,8 +779,8 @@ type ChartFieldsProps = {
};
const ChartFields = (props: ChartFieldsProps) => {
- const { dataSource, chartConfig, dimensions = [], measures = [] } = props;
- const components = [...dimensions, ...measures];
+ const { dataSource, chartConfig, dimensions, measures } = props;
+ const components = [...(dimensions ?? []), ...(measures ?? [])];
const queryFilters = useQueryFilters({
chartConfig,
dimensions,
diff --git a/app/configurator/components/chart-options-selector.tsx b/app/configurator/components/chart-options-selector.tsx
index 132deaa91..87cbc1222 100644
--- a/app/configurator/components/chart-options-selector.tsx
+++ b/app/configurator/components/chart-options-selector.tsx
@@ -103,15 +103,18 @@ export const ChartOptionsSelector = ({
sourceType: dataSource.type,
sourceUrl: dataSource.url,
locale,
- cubeFilters: chartConfig.cubes.map((cube) => ({ iri: cube.iri })),
+ cubeFilters: chartConfig.cubes.map((cube) => ({
+ iri: cube.iri,
+ joinBy: cube.joinBy,
+ })),
},
});
const dimensions = componentsData?.dataCubesComponents.dimensions;
const measures = componentsData?.dataCubesComponents.measures;
const queryFilters = useQueryFilters({
chartConfig,
- dimensions: dimensions ?? [],
- measures: measures ?? [],
+ dimensions,
+ measures,
});
const [{ data: observationsData }] = useDataCubesObservationsQuery({
variables: {
diff --git a/app/configurator/components/chart-type-selector.tsx b/app/configurator/components/chart-type-selector.tsx
index 24d786d2b..0ae613437 100644
--- a/app/configurator/components/chart-type-selector.tsx
+++ b/app/configurator/components/chart-type-selector.tsx
@@ -13,6 +13,7 @@ import clsx from "clsx";
import React, { SyntheticEvent } from "react";
import {
+ chartTypes,
comboChartTypes,
getPossibleChartTypes,
regularChartTypes,
@@ -129,7 +130,10 @@ export const ChartTypeSelector = ({
sourceType: state.dataSource.type,
sourceUrl: state.dataSource.url,
locale,
- cubeFilters: chartConfig.cubes.map((cube) => ({ iri: cube.iri })),
+ cubeFilters: chartConfig.cubes.map((cube) => ({
+ iri: cube.iri,
+ joinBy: cube.joinBy,
+ })),
},
});
const dimensions = data?.dataCubesComponents?.dimensions ?? [];
@@ -159,7 +163,12 @@ export const ChartTypeSelector = ({
return ;
}
- const possibleChartTypes = getPossibleChartTypes({ dimensions, measures });
+ const possibleChartTypes = getPossibleChartTypes({
+ dimensions,
+ measures,
+ allowedChartTypes:
+ chartConfig.cubes.length > 1 ? comboChartTypes : chartTypes,
+ });
return (
diff --git a/app/configurator/components/filters.tsx b/app/configurator/components/filters.tsx
index f78d17a1c..acfcac072 100644
--- a/app/configurator/components/filters.tsx
+++ b/app/configurator/components/filters.tsx
@@ -903,14 +903,13 @@ export const TimeFilter = (props: TimeFilterProps) => {
dispatch({
type: "CHART_CONFIG_FILTER_SET_RANGE",
value: {
- cubeIri: dimension.cubeIri,
- dimensionIri: dimension.iri,
+ dimension,
from,
to,
},
});
},
- [dispatch, dimension.cubeIri, dimension.iri]
+ [dispatch, dimension]
);
const temporalDimension =
diff --git a/app/configurator/configurator-state.spec.tsx b/app/configurator/configurator-state.spec.tsx
index 06bf9df66..9fde5d0b2 100644
--- a/app/configurator/configurator-state.spec.tsx
+++ b/app/configurator/configurator-state.spec.tsx
@@ -6,6 +6,7 @@ import {
ChartConfig,
ChartType,
ColumnConfig,
+ ComboLineDualConfig,
ConfiguratorStateConfiguringChart,
DataSource,
Filters,
@@ -13,6 +14,7 @@ import {
getChartConfig,
} from "@/config-types";
import {
+ ConfiguratorStateAction,
applyNonTableDimensionToFilters,
applyTableDimensionToFilters,
deriveFiltersFromFields,
@@ -24,6 +26,7 @@ import {
initChartStateFromCube,
initChartStateFromLocalStorage,
moveFilterField,
+ setRangeFilter,
updateColorMapping,
} from "@/configurator/configurator-state";
import { Component, Dimension, Measure, NominalDimension } from "@/domain/data";
@@ -90,6 +93,7 @@ jest.mock("@/graphql/client", () => {
],
},
},
+ operation: {},
};
},
},
@@ -809,12 +813,48 @@ describe("getFiltersByMappingStatus", () => {
},
} as any as MapConfig;
- const { mappedFiltersIris } = getFiltersByMappingStatus(config, "foo");
+ const { mappedFiltersIris } = getFiltersByMappingStatus(config, {
+ cubeIri: "foo",
+ });
expect([...mappedFiltersIris]).toEqual(
expect.arrayContaining(["areaColorIri", "symbolColorIri"])
);
});
+
+ it("should correctly retrieve filters when using joinBy dimension", () => {
+ const config = {
+ chartType: "line-dual",
+ cubes: [
+ {
+ iri: "fo1",
+ filters: {},
+ joinBy: "X1",
+ },
+ {
+ iri: "foo2",
+ filters: {},
+ joinBy: "X2",
+ },
+ ],
+ fields: {
+ x: {
+ componentIri: "joinBy",
+ },
+ },
+ } as any as ComboLineDualConfig;
+
+ const { mappedFiltersIris } = getFiltersByMappingStatus(config, {
+ cubeIri: "foo",
+ joinByIris: ["X1", "X2"],
+ });
+
+ // If the joinBy dimensions are treated as being mapped, we won't apply
+ // single filters to them when deriving filters from fields.
+ expect([...mappedFiltersIris]).toEqual(
+ expect.arrayContaining(["X1", "X2"])
+ );
+ });
});
describe("colorMapping", () => {
@@ -1079,3 +1119,80 @@ describe("handleChartOptionChanged", () => {
);
});
});
+
+describe("filtering", () => {
+ it("should add range filter", () => {
+ const draft = {
+ chartConfigs: [{ key: "ABC", cubes: [{ iri: "foo", filters: {} }] }],
+ activeChartKey: "ABC",
+ } as any as ConfiguratorStateConfiguringChart;
+ const action: Extract<
+ ConfiguratorStateAction,
+ { type: "CHART_CONFIG_FILTER_SET_RANGE" }
+ > = {
+ type: "CHART_CONFIG_FILTER_SET_RANGE",
+ value: {
+ dimension: { cubeIri: "foo", iri: "time" } as any as Dimension,
+ from: "2010",
+ to: "2014",
+ },
+ };
+
+ setRangeFilter(draft, action);
+
+ expect(draft.chartConfigs[0].cubes[0].filters).toEqual({
+ time: { type: "range", from: "2010", to: "2014" },
+ });
+ });
+
+ it("should add range filters to every cube if using joinBy dimension", () => {
+ const draft = {
+ chartConfigs: [
+ {
+ key: "ABC",
+ cubes: [
+ { iri: "foo1", filters: {} },
+ { iri: "foo2", filters: {} },
+ { iri: "foo3", filters: {} },
+ ],
+ },
+ ],
+ activeChartKey: "ABC",
+ } as any as ConfiguratorStateConfiguringChart;
+ const action: Extract<
+ ConfiguratorStateAction,
+ { type: "CHART_CONFIG_FILTER_SET_RANGE" }
+ > = {
+ type: "CHART_CONFIG_FILTER_SET_RANGE",
+ value: {
+ dimension: {
+ isJoinByDimension: true,
+ originalIris: [
+ {
+ cubeIri: "foo1",
+ dimensionIri: "time1",
+ },
+ {
+ cubeIri: "foo2",
+ dimensionIri: "time2",
+ },
+ {
+ cubeIri: "foo3",
+ dimensionIri: "time3",
+ },
+ ],
+ } as any as Dimension,
+ from: "2010",
+ to: "2014",
+ },
+ };
+
+ setRangeFilter(draft, action);
+
+ expect(draft.chartConfigs[0].cubes.map((cube) => cube.filters)).toEqual([
+ { time1: { type: "range", from: "2010", to: "2014" } },
+ { time2: { type: "range", from: "2010", to: "2014" } },
+ { time3: { type: "range", from: "2010", to: "2014" } },
+ ]);
+ });
+});
diff --git a/app/configurator/configurator-state.tsx b/app/configurator/configurator-state.tsx
index 59af72fa9..dba4abf62 100644
--- a/app/configurator/configurator-state.tsx
+++ b/app/configurator/configurator-state.tsx
@@ -62,8 +62,10 @@ import {
} from "@/domain/data";
import { DEFAULT_DATA_SOURCE } from "@/domain/datasource";
import { client } from "@/graphql/client";
+import { joinDimensions } from "@/graphql/hook-utils";
import { executeDataCubesComponentsQuery } from "@/graphql/hooks";
import {
+ DataCubeComponentFilter,
DataCubeComponentsDocument,
DataCubeComponentsQuery,
DataCubeComponentsQueryVariables,
@@ -240,8 +242,7 @@ export type ConfiguratorStateAction =
| {
type: "CHART_CONFIG_FILTER_SET_RANGE";
value: {
- cubeIri: string;
- dimensionIri: string;
+ dimension: Dimension;
from: string;
to: string;
};
@@ -335,32 +336,25 @@ const EMPTY_STATE: ConfiguratorStateSelectingDataSet = {
const getCachedComponents = (
draft: ConfiguratorStateConfiguringChart,
- cubeIris: string[],
+ cubeFilters: DataCubeComponentFilter[],
locale: Locale
): DataCubeComponents | undefined => {
- return cubeIris.reduce(
- (acc, cubeIri) => {
- const componentsQuery = client.readQuery<
- DataCubeComponentsQuery,
- DataCubeComponentsQueryVariables
- >(DataCubeComponentsDocument, {
- sourceType: draft.dataSource.type,
- sourceUrl: draft.dataSource.url,
- locale,
- cubeFilter: { iri: cubeIri },
- });
-
- if (componentsQuery?.data?.dataCubeComponents) {
- acc.dimensions.push(
- ...componentsQuery.data.dataCubeComponents.dimensions
- );
- acc.measures.push(...componentsQuery.data.dataCubeComponents.measures);
- }
+ const queries = cubeFilters.map((cubeFilter) => {
+ return client.readQuery<
+ DataCubeComponentsQuery,
+ DataCubeComponentsQueryVariables
+ >(DataCubeComponentsDocument, {
+ sourceType: draft.dataSource.type,
+ sourceUrl: draft.dataSource.url,
+ locale,
+ cubeFilter: { iri: cubeFilter.iri, joinBy: cubeFilter.joinBy },
+ })!;
+ });
- return acc;
- },
- { dimensions: [], measures: [] }
- );
+ return {
+ dimensions: joinDimensions(queries),
+ measures: queries.flatMap((q) => q.data?.dataCubeComponents.measures!),
+ };
};
export const getFilterValue = (
@@ -592,7 +586,11 @@ export const applyNonTableDimensionToFilters = ({
const _exhaustiveCheck: never = currentFilter;
return _exhaustiveCheck;
}
- } else if (!isField && dimension.isKeyDimension) {
+ } else if (
+ !isField &&
+ dimension.isKeyDimension &&
+ !dimension.isJoinByDimension
+ ) {
// If this scenario appears, it means that current filter is undefined -
// which means it must be converted to a single-filter (if it's a keyDimension,
// otherwise a 'No filter' option should be selected by default).
@@ -736,13 +734,26 @@ export const getNonGenericFieldValues = (
*/
export const getFiltersByMappingStatus = (
chartConfig: ChartConfig,
- cubeIri?: string
+ options: {
+ /** Treat original iris of joinBy dimension as fields (currently joinBy dimension
+ * can only be mapped to a field).
+ *
+ * This ensures that we won't apply single filters to original joinBy dimensions.
+ * */
+ joinByIris?: string[];
+ cubeIri?: string;
+ }
) => {
+ const { joinByIris, cubeIri } = options;
const genericFieldValues = Object.values(chartConfig.fields).map(
(d) => d.componentIri
);
const nonGenericFieldValues = getNonGenericFieldValues(chartConfig);
- const iris = new Set([...genericFieldValues, ...nonGenericFieldValues]);
+ const iris = new Set([
+ ...genericFieldValues,
+ ...nonGenericFieldValues,
+ ...(joinByIris ?? []),
+ ]);
const filters = getChartConfigFilters(chartConfig.cubes, cubeIri);
const mappedFilters = pickBy(filters, (_, iri) => iris.has(iri));
const unmappedFilters = pickBy(filters, (_, iri) => !iris.has(iri));
@@ -783,7 +794,10 @@ export const handleChartFieldChanged = (
const f = get(chartConfig.fields, field);
const dataCubesComponents = getCachedComponents(
draft,
- chartConfig.cubes.map((cube) => cube.iri),
+ chartConfig.cubes.map((cube) => ({
+ iri: cube.iri,
+ joinBy: cube.joinBy,
+ })),
locale
);
const dimensions = dataCubesComponents?.dimensions ?? [];
@@ -837,7 +851,10 @@ export const handleChartOptionChanged = (
const updatePath = field === null ? path : `fields["${field}"].${path}`;
const dataCubesComponents = getCachedComponents(
draft,
- chartConfig.cubes.map((cube) => cube.iri),
+ chartConfig.cubes.map((cube) => ({
+ iri: cube.iri,
+ joinBy: cube.joinBy,
+ })),
locale
);
const dimensions = dataCubesComponents?.dimensions ?? [];
@@ -929,6 +946,48 @@ const handleInteractiveFilterChanged = (
return draft;
};
+export const setRangeFilter = (
+ draft: ConfiguratorState,
+ action: Extract<
+ ConfiguratorStateAction,
+ { type: "CHART_CONFIG_FILTER_SET_RANGE" }
+ >
+) => {
+ const { dimension, from, to } = action.value;
+ const chartConfig = getChartConfig(draft);
+ const adjustFilter = (cubeIri: string, dimensionIri: string) => {
+ const cube = chartConfig.cubes.find((cube) => cube.iri === cubeIri);
+
+ if (cube) {
+ cube.filters[dimensionIri] = {
+ type: "range",
+ from,
+ to,
+ };
+ }
+ };
+
+ if (dimension.isJoinByDimension) {
+ for (const { cubeIri, dimensionIri } of dimension.originalIris) {
+ adjustFilter(cubeIri, dimensionIri);
+ }
+ } else {
+ adjustFilter(dimension.cubeIri, dimension.iri);
+ }
+
+ if (chartConfig.interactiveFiltersConfig) {
+ chartConfig.interactiveFiltersConfig.timeRange = {
+ componentIri: dimension.iri,
+ active: chartConfig.interactiveFiltersConfig.timeRange.active,
+ presets: {
+ type: "range",
+ from,
+ to,
+ },
+ };
+ }
+};
+
const reducer: Reducer = (
draft,
action
@@ -950,7 +1009,10 @@ const reducer: Reducer = (
const chartConfig = getChartConfig(draft, chartKey);
const dataCubesComponents = getCachedComponents(
draft,
- chartConfig.cubes.map((cube) => cube.iri),
+ chartConfig.cubes.map((cube) => ({
+ iri: cube.iri,
+ joinBy: cube.joinBy,
+ })),
locale
);
const dimensions = dataCubesComponents?.dimensions;
@@ -993,7 +1055,10 @@ const reducer: Reducer = (
delete (chartConfig.fields as GenericFields)[action.value.field];
const dataCubesComponents = getCachedComponents(
draft,
- chartConfig.cubes.map((cube) => cube.iri),
+ chartConfig.cubes.map((cube) => ({
+ iri: cube.iri,
+ joinBy: cube.joinBy,
+ })),
action.value.locale
);
const dimensions = dataCubesComponents?.dimensions ?? [];
@@ -1173,29 +1238,7 @@ const reducer: Reducer = (
case "CHART_CONFIG_FILTER_SET_RANGE":
if (draft.state === "CONFIGURING_CHART") {
- const { cubeIri, dimensionIri, from, to } = action.value;
- const chartConfig = getChartConfig(draft);
- const cube = chartConfig.cubes.find((cube) => cube.iri === cubeIri);
-
- if (cube) {
- cube.filters[dimensionIri] = {
- type: "range",
- from,
- to,
- };
-
- if (chartConfig.interactiveFiltersConfig) {
- chartConfig.interactiveFiltersConfig.timeRange = {
- componentIri: dimensionIri,
- active: chartConfig.interactiveFiltersConfig.timeRange.active,
- presets: {
- type: "range",
- from,
- to,
- },
- };
- }
- }
+ setRangeFilter(draft, action);
}
return draft;
@@ -1247,7 +1290,10 @@ const reducer: Reducer = (
const chartConfig = getChartConfig(draft);
const dataCubesComponents = getCachedComponents(
draft,
- chartConfig.cubes.map((cube) => cube.iri),
+ chartConfig.cubes.map((cube) => ({
+ iri: cube.iri,
+ joinBy: cube.joinBy,
+ })),
action.value.locale
);
diff --git a/app/configurator/interactive-filters/interactive-filters-configurator.tsx b/app/configurator/interactive-filters/interactive-filters-configurator.tsx
index 61517680b..72b5f2599 100644
--- a/app/configurator/interactive-filters/interactive-filters-configurator.tsx
+++ b/app/configurator/interactive-filters/interactive-filters-configurator.tsx
@@ -40,7 +40,10 @@ export const InteractiveFiltersConfigurator = ({
sourceType: dataSource.type,
sourceUrl: dataSource.url,
locale,
- cubeFilters: chartConfig.cubes.map((cube) => ({ iri: cube.iri })),
+ cubeFilters: chartConfig.cubes.map((cube) => ({
+ iri: cube.iri,
+ joinBy: cube.joinBy,
+ })),
},
});
diff --git a/app/configurator/table/table-chart-configurator.hook.tsx b/app/configurator/table/table-chart-configurator.hook.tsx
index 9fc7e2e4e..e9edf32c0 100644
--- a/app/configurator/table/table-chart-configurator.hook.tsx
+++ b/app/configurator/table/table-chart-configurator.hook.tsx
@@ -25,7 +25,10 @@ export const useTableChartController = (
sourceType: state.dataSource.type,
sourceUrl: state.dataSource.url,
locale,
- cubeFilters: chartConfig.cubes.map((cube) => ({ iri: cube.iri })),
+ cubeFilters: chartConfig.cubes.map((cube) => ({
+ iri: cube.iri,
+ joinBy: cube.joinBy,
+ })),
},
});
diff --git a/app/domain/data.ts b/app/domain/data.ts
index 15ec72d00..a4d214200 100644
--- a/app/domain/data.ts
+++ b/app/domain/data.ts
@@ -84,7 +84,7 @@ export type DataCubeMetadata = {
export type Component = Dimension | Measure;
-type BasicComponent = {
+export type BaseComponent = {
cubeIri: string;
iri: string;
label: string;
@@ -99,9 +99,21 @@ type BasicComponent = {
related?: RelatedDimension[];
};
-type BasicDimension = BasicComponent & {
+export type BaseDimension = BaseComponent & {
hierarchy?: HierarchyValue[] | null;
-};
+} & (
+ | {
+ isJoinByDimension: true;
+ originalIris: {
+ cubeIri: string;
+ dimensionIri: string;
+ }[];
+ }
+ | {
+ isJoinByDimension?: never;
+ originalIris?: never;
+ }
+ );
export type Dimension =
| NominalDimension
@@ -112,50 +124,50 @@ export type Dimension =
| GeoShapesDimension
| StandardErrorDimension;
-export type NominalDimension = BasicDimension & {
+export type NominalDimension = BaseDimension & {
__typename: "NominalDimension";
};
-export type OrdinalDimension = BasicDimension & {
+export type OrdinalDimension = BaseDimension & {
__typename: "OrdinalDimension";
};
-export type TemporalDimension = BasicDimension & {
+export type TemporalDimension = BaseDimension & {
__typename: "TemporalDimension";
timeUnit: TimeUnit;
timeFormat: string;
};
-export type TemporalOrdinalDimension = BasicDimension & {
+export type TemporalOrdinalDimension = BaseDimension & {
__typename: "TemporalOrdinalDimension";
};
-export type GeoCoordinatesDimension = BasicDimension & {
+export type GeoCoordinatesDimension = BaseDimension & {
__typename: "GeoCoordinatesDimension";
};
-export type GeoShapesDimension = BasicDimension & {
+export type GeoShapesDimension = BaseDimension & {
__typename: "GeoShapesDimension";
};
-export type StandardErrorDimension = BasicDimension & {
+export type StandardErrorDimension = BaseDimension & {
__typename: "StandardErrorDimension";
};
export type Measure = NumericalMeasure | OrdinalMeasure;
-type BasicMeasure = BasicComponent & {
+type BaseMeasure = BaseComponent & {
isCurrency?: boolean;
isDecimal?: boolean;
currencyExponent?: number;
resolution?: number;
};
-export type NumericalMeasure = BasicMeasure & {
+export type NumericalMeasure = BaseMeasure & {
__typename: "NumericalMeasure";
};
-export type OrdinalMeasure = BasicMeasure & {
+export type OrdinalMeasure = BaseMeasure & {
__typename: "OrdinalMeasure";
};
diff --git a/app/graphql/hook-utils.spec.ts b/app/graphql/hook-utils.spec.ts
index 71c37146b..6e56cb3f9 100644
--- a/app/graphql/hook-utils.spec.ts
+++ b/app/graphql/hook-utils.spec.ts
@@ -73,10 +73,10 @@ describe("mergeObservations", () => {
const result = mergeObservations(queries);
expect(result).toEqual([
- { YEAR: 2000, AMOUNT: 2000 },
- { year: 2010, amount: 2010, YEAR: 2010, AMOUNT: 2010 },
- { year: 2011, amount: 2011 },
- { YEAR: 2020, AMOUNT: 2020 },
+ { joinBy: 2000, AMOUNT: 2000 },
+ { joinBy: 2010, amount: 2010, AMOUNT: 2010 },
+ { joinBy: 2011, amount: 2011 },
+ { joinBy: 2020, AMOUNT: 2020 },
]);
});
});
diff --git a/app/graphql/hook-utils.ts b/app/graphql/hook-utils.ts
index cea8ca4be..7fd5082ac 100644
--- a/app/graphql/hook-utils.ts
+++ b/app/graphql/hook-utils.ts
@@ -1,12 +1,79 @@
+import { ascending } from "d3";
import { OperationResult } from "urql";
-import { Observation, ObservationValue } from "@/domain/data";
+import { Dimension, Observation, ObservationValue } from "@/domain/data";
import {
+ DataCubeComponentsQuery,
+ DataCubeComponentsQueryVariables,
DataCubeObservationsQuery,
DataCubeObservationsQueryVariables,
Exact,
} from "@/graphql/query-hooks";
+export const JOIN_BY_DIMENSION_IRI = "joinBy";
+
+/** Use to exclude joinBy dimensions when fetching dimensions, and create
+ * a new joinBy dimension with values from all joinBy dimensions.
+ */
+export const joinDimensions = (
+ queries: OperationResult<
+ DataCubeComponentsQuery,
+ Exact
+ >[]
+) => {
+ const joinByDimensions: Dimension[] = [];
+ const dimensions: Dimension[] = [];
+
+ for (const q of queries) {
+ if (!q.data?.dataCubeComponents) {
+ continue;
+ }
+
+ const { dimensions: queryDimensions } = q.data.dataCubeComponents;
+
+ const joinBy = q.operation.variables?.cubeFilter.joinBy;
+ const joinByDimension = queryDimensions.find((d) => d.iri === joinBy);
+
+ if (!joinByDimension) {
+ dimensions.push(...queryDimensions);
+
+ continue;
+ }
+
+ joinByDimensions.push(joinByDimension);
+ dimensions.push(
+ ...queryDimensions.filter((d) => d.iri !== joinByDimension.iri)
+ );
+ }
+
+ if (joinByDimensions.length > 1) {
+ const joinByDimension: Dimension = {
+ ...joinByDimensions[0],
+ values: joinByDimensions
+ .flatMap((d) => d.values)
+ .sort((a, b) =>
+ ascending(
+ a.position ?? a.value ?? undefined,
+ b.position ?? b.value ?? undefined
+ )
+ ),
+ iri: JOIN_BY_DIMENSION_IRI,
+ // Non-relevant, as we rely on the originalIris property.
+ cubeIri: JOIN_BY_DIMENSION_IRI,
+ // FIXME: adapt to design
+ label: JOIN_BY_DIMENSION_IRI,
+ isJoinByDimension: true,
+ originalIris: joinByDimensions.map((d) => ({
+ cubeIri: d.cubeIri,
+ dimensionIri: d.iri,
+ })),
+ };
+ dimensions.unshift(joinByDimension);
+ }
+
+ return dimensions;
+};
+
type JoinByKey = NonNullable<
DataCubeObservationsQueryVariables["cubeFilter"]["joinBy"]
>;
@@ -39,9 +106,12 @@ export const mergeObservations = (
continue;
}
+ // Remove joinBy dimension from the observation, to use explicit joinBy as key
+ const { [joinBy]: x, ...om } = o;
+ om.joinBy = key;
const existing: Observation | undefined = acc[key];
// TODO: handle cases of same column names across merged observations
- acc[key] = Object.assign(existing ?? {}, o);
+ acc[key] = Object.assign(existing ?? {}, om);
}
return acc;
diff --git a/app/graphql/hooks.ts b/app/graphql/hooks.ts
index 637a5d7bb..63beb58e2 100644
--- a/app/graphql/hooks.ts
+++ b/app/graphql/hooks.ts
@@ -7,7 +7,7 @@ import {
} from "@/domain/data";
import { client } from "./client";
-import { mergeObservations } from "./hook-utils";
+import { joinDimensions, mergeObservations } from "./hook-utils";
import {
DataCubeComponentFilter,
DataCubeComponentsDocument,
@@ -146,6 +146,12 @@ export const executeDataCubesComponentsQuery = async (
) => {
const { locale, sourceType, sourceUrl, cubeFilters } = variables;
+ if (cubeFilters.length > 1 && !cubeFilters.every((f) => f.joinBy)) {
+ throw new Error(
+ "When fetching data from multiple cubes, all cube filters must have joinBy property set."
+ );
+ }
+
const queries = await Promise.all(
cubeFilters.map((cubeFilter) => {
const cubeVariables = { locale, sourceType, sourceUrl, cubeFilter };
@@ -172,20 +178,34 @@ export const executeDataCubesComponentsQuery = async (
const error = queries.find((q) => q.error)?.error;
const fetching = !error && queries.some((q) => !q.data);
+ if (error || fetching) {
+ return {
+ data: undefined,
+ error,
+ fetching,
+ };
+ }
+
+ if (queries.length === 1) {
+ return {
+ data: {
+ dataCubesComponents: {
+ dimensions: queries[0].data?.dataCubeComponents.dimensions!,
+ measures: queries[0].data?.dataCubeComponents.measures!,
+ },
+ },
+ error,
+ fetching,
+ };
+ }
+
return {
- data:
- error || fetching
- ? undefined
- : {
- dataCubesComponents: {
- dimensions: queries.flatMap(
- (q) => q.data?.dataCubeComponents.dimensions!
- ),
- measures: queries.flatMap(
- (q) => q.data?.dataCubeComponents.measures!
- ),
- },
- },
+ data: {
+ dataCubesComponents: {
+ dimensions: joinDimensions(queries),
+ measures: queries.flatMap((q) => q.data?.dataCubeComponents.measures!),
+ },
+ },
error,
fetching,
};
diff --git a/app/graphql/query-hooks.ts b/app/graphql/query-hooks.ts
index 294c88d9c..436d9a706 100644
--- a/app/graphql/query-hooks.ts
+++ b/app/graphql/query-hooks.ts
@@ -2,7 +2,7 @@ import { DataCubeComponents } from '../domain/data';
import { DataCubeMetadata } from '../domain/data';
import { DataCubeObservations } from '../domain/data';
import { DimensionValue } from '../domain/data';
-import { QueryFilters } from '../configurator';
+import { Filters } from '../configurator';
import { HierarchyValue } from '../domain/data';
import { Observation } from '../domain/data';
import { RawObservation } from '../domain/data';
@@ -26,7 +26,7 @@ export type Scalars = {
DataCubeObservations: DataCubeObservations;
DimensionValue: DimensionValue;
FilterValue: any;
- Filters: QueryFilters;
+ Filters: Filters;
GeoShapes: any;
HierarchyValue: HierarchyValue;
Observation: Observation;
@@ -98,6 +98,7 @@ export type DataCubeComponentFilter = {
latest?: Maybe;
filters?: Maybe;
componentIris?: Maybe>;
+ joinBy?: Maybe;
};
diff --git a/app/graphql/resolver-types.ts b/app/graphql/resolver-types.ts
index b97ed2ed8..1d11d17da 100644
--- a/app/graphql/resolver-types.ts
+++ b/app/graphql/resolver-types.ts
@@ -99,6 +99,7 @@ export type DataCubeComponentFilter = {
latest?: Maybe;
filters?: Maybe;
componentIris?: Maybe>;
+ joinBy?: Maybe;
};
diff --git a/app/graphql/resolvers/rdf.ts b/app/graphql/resolvers/rdf.ts
index 9e926b9bb..d59c72a5a 100644
--- a/app/graphql/resolvers/rdf.ts
+++ b/app/graphql/resolvers/rdf.ts
@@ -5,6 +5,8 @@ import { LRUCache } from "typescript-lru-cache";
import { Filters } from "@/configurator";
import {
+ BaseComponent,
+ BaseDimension,
Dimension,
DimensionValue,
Measure,
@@ -189,7 +191,7 @@ export const dataCubeComponents: NonNullable<
b.position ?? b.value ?? undefined
)
);
- const baseComponent = {
+ const baseComponent: BaseComponent = {
// We need to use original iri here, as the cube iri might have changed.
cubeIri: iri,
iri: data.iri,
@@ -230,7 +232,7 @@ export const dataCubeComponents: NonNullable<
filters ? undefined : values
)
: null;
- const baseDimension = {
+ const baseDimension: BaseDimension = {
...baseComponent,
hierarchy,
};
@@ -253,7 +255,7 @@ export const dataCubeComponents: NonNullable<
break;
}
default: {
- const dimension: Dimension = {
+ const dimension: Exclude = {
__typename: dimensionType,
...baseDimension,
};
diff --git a/app/graphql/resolvers/sql.ts b/app/graphql/resolvers/sql.ts
index 0a20f11b5..266b60ebc 100644
--- a/app/graphql/resolvers/sql.ts
+++ b/app/graphql/resolvers/sql.ts
@@ -1,7 +1,7 @@
import max from "lodash/max";
import min from "lodash/min";
-import { QueryFilters } from "@/config-types";
+import { Filters } from "@/configurator";
import { DimensionValue, Observation } from "@/domain/data";
import { SQL_ENDPOINT } from "@/domain/env";
import {
@@ -220,7 +220,7 @@ export const dimensionValues: NonNullable<
// FIXME: type of cube should be different for RDF and SQL.
const observations = filterObservations(
(cube as any).observations,
- slicedFilters as QueryFilters | undefined
+ slicedFilters as Filters | undefined
);
const values = Array.from(new Set(observations.map((d) => d[iri])));
@@ -248,7 +248,7 @@ export const dataCubeMeasures: NonNullable =
const filterObservations = (
allObservations: Observation[],
- filters: QueryFilters | undefined
+ filters: Filters | undefined
) => {
const observations: Observation[] = [];
@@ -302,7 +302,7 @@ export const observations: NonNullable =
: rawObservations;
const observations = filterObservations(
allObservations,
- filters as QueryFilters | undefined
+ filters as Filters | undefined
);
return {
diff --git a/app/graphql/schema.graphql b/app/graphql/schema.graphql
index 673b1dc11..e8948abb9 100644
--- a/app/graphql/schema.graphql
+++ b/app/graphql/schema.graphql
@@ -343,6 +343,7 @@ input DataCubeComponentFilter {
latest: Boolean
filters: Filters
componentIris: [String!]
+ joinBy: String
}
input DataCubeMetadataFilter {
diff --git a/codegen.yml b/codegen.yml
index f23f70ea3..186faf866 100644
--- a/codegen.yml
+++ b/codegen.yml
@@ -19,7 +19,7 @@ generates:
DimensionValue: "../domain/data#DimensionValue"
HierarchyValue: "../domain/data#HierarchyValue"
RawObservation: "../domain/data#RawObservation"
- Filters: "../configurator#QueryFilters"
+ Filters: "../configurator#Filters"
GeoShape: "../domain/data#GeoShape"
SearchCube: "../domain/data#SearchCube"
DataCubeComponents: "../domain/data#DataCubeComponents"