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"