From 4dccb8780e4ee69ff24badc188bdd5c8d3773c7c Mon Sep 17 00:00:00 2001 From: Patrick Browne Date: Tue, 8 Feb 2022 16:31:30 +0100 Subject: [PATCH] fix: Ability to fetch dimension values when datatypes has undefined values When the dataset has undefined values, the shacl constraint for dataType is expressed as an "or" containing two constraints (undefined, and the real datatype). The parseDataType function handles this case and provides us with the correct datatype. Shared dimensions have no dataType and the filter for values will be done with a named node ?dimension1 = , whereas for literal dimensions, we use the str function to match on the string value of the value (discarding the type information) --- app/rdf/parse.ts | 2 +- app/rdf/query-dimension-values.ts | 46 ++++++++++++------------------- 2 files changed, 19 insertions(+), 29 deletions(-) diff --git a/app/rdf/parse.ts b/app/rdf/parse.ts index 88f258a4e..9a19428be 100644 --- a/app/rdf/parse.ts +++ b/app/rdf/parse.ts @@ -111,7 +111,7 @@ export const getScaleType = ( : undefined; }; -const parseDimensionDatatype = (dim: CubeDimension) => { +export const parseDimensionDatatype = (dim: CubeDimension) => { let dataType = dim.datatype; let hasUndefinedValues = false; diff --git a/app/rdf/query-dimension-values.ts b/app/rdf/query-dimension-values.ts index 3b65ebad8..e45687cd7 100644 --- a/app/rdf/query-dimension-values.ts +++ b/app/rdf/query-dimension-values.ts @@ -6,35 +6,25 @@ import { sparqlClient } from "./sparql-client"; import { Cube, CubeDimension } from "rdf-cube-view-query"; import { dimensionIsVersioned } from "./queries"; import * as ns from "./namespace"; +import { parseDimensionDatatype } from "./parse"; interface DimensionValue { value: Literal | NamedNode; } /** - * Formats a filter value into the right format given - * the datatype of the dimension - * - * Seems a bit fragile, we should find a way to directly add the ^^xsd - * given the datatype instead of handling everycase + * Formats a filter value into the right format, string literal + * for dimensions with a datatype, and named node for shared + * dimensions. */ const formatFilterValue = ( value: string | number, - dimension: CubeDimension + dataType?: NamedNode ) => { - if (!dimension.datatype) { + if (!dataType) { return `<${value}>`; } else { - // Seems fragile - if (dimension.datatype.value === ns.xsd.gYear.value) { - return `"${value}"^^xsd:gYear`; - } else if (dimension.datatype.value === ns.xsd.date.value) { - return `"${value}"^^xsd:date`; - } else if (dimension.datatype.value === ns.xsd.dateTime.value) { - return `"${value}"^^xsd:dateTime`; - } else { - return `"${value}"`; - } + return `"${value}"`; } }; @@ -46,20 +36,20 @@ const formatFilterIntoSparqlFilter = ( ) => { const suffix = versioned ? "_unversioned" : ""; const dimensionVar = `?dimension${suffix}${index}`; - // We do not keep the language information inside the filter so for now - // we only filter on the value - const leftSide = - dimension?.datatype?.value === ns.rdf.langString.value - ? `str(${dimensionVar})` - : dimensionVar; + const { dataType } = parseDimensionDatatype(dimension); + + // Shared dimensions have no dataType and the filter for values will be + // done with a named node ?dimension1 = , whereas for literal dimensions, + // we use the str function to match on the string value of the value + // (discarding the type information), since the type information is + // not stored in the chart config filters + const leftSide = dataType ? `str(${dimensionVar})` : dimensionVar; if (filter.type === "single") { - return `FILTER ( (${leftSide} = ${formatFilterValue( - filter.value, - dimension - )}) )`; + const rightSide = formatFilterValue(filter.value, dataType); + return `FILTER ( (${leftSide} = ${rightSide}) )`; } else if (filter.type === "multi") { return `FILTER ( (${leftSide} in (${Object.keys(filter.values) - .map((x) => formatFilterValue(x, dimension)) + .map((x) => formatFilterValue(x, dataType)) .join(",")}) ) )`; } else { return "";