From 8fe3e456f9ea4cbb9b583b4e0a4e3ab365d76c43 Mon Sep 17 00:00:00 2001 From: David de Boer Date: Fri, 21 Aug 2026 13:57:39 +0200 Subject: [PATCH] fix(dataset-registry-client): type the IRI-valued schema properties as IRIs - Declare '@type': ldkit.IRI on license, accessURL, mediaType, compressFormat and conformsTo, which hold IRIs in DCAT-AP. Without it LDkit falls back to xsd:string and encodes a search value as a plain literal, so a filter such as mediaType $in [...] compares an IRI to a string and can never match. - The mismatch was invisible because LDkit emitted a search FILTER inside the OPTIONAL block of an optional property, where it only unbound the value instead of removing the row; ldkit 2.7.2 hoists the FILTER out of the block and makes it bite. - Cover a distribution that declares no media type through an $id query: media-type criteria now narrow a dataset's distributions to the matching ones, so a filtered query no longer reaches that path. --- .../dataset-registry-client/src/schema.ts | 11 ++++++++-- .../test/client.test.ts | 20 +++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/packages/dataset-registry-client/src/schema.ts b/packages/dataset-registry-client/src/schema.ts index 2c95a6d5..1a153916 100644 --- a/packages/dataset-registry-client/src/schema.ts +++ b/packages/dataset-registry-client/src/schema.ts @@ -1,4 +1,4 @@ -import { dcterms, foaf, xsd } from 'ldkit/namespaces'; +import { dcterms, foaf, ldkit, xsd } from 'ldkit/namespaces'; import { dcat } from './dcat.js'; export const DatasetSchema = { @@ -19,6 +19,7 @@ export const DatasetSchema = { }, license: { '@id': dcterms.license, + '@type': ldkit.IRI, '@optional': true, }, creator: { @@ -47,9 +48,13 @@ export const DatasetSchema = { '@array': true, '@schema': { '@type': dcat.Distribution, - accessURL: dcat.accessURL, + accessURL: { + '@id': dcat.accessURL, + '@type': ldkit.IRI, + }, mediaType: { '@id': dcat.mediaType, + '@type': ldkit.IRI, '@optional': true, }, byteSize: { @@ -59,10 +64,12 @@ export const DatasetSchema = { }, compressFormat: { '@id': dcat.compressFormat, + '@type': ldkit.IRI, '@optional': true, }, conformsTo: { '@id': dcterms.conformsTo, + '@type': ldkit.IRI, '@optional': true, }, modified: { diff --git a/packages/dataset-registry-client/test/client.test.ts b/packages/dataset-registry-client/test/client.test.ts index 5bdc3d83..29e34e4e 100644 --- a/packages/dataset-registry-client/test/client.test.ts +++ b/packages/dataset-registry-client/test/client.test.ts @@ -100,6 +100,26 @@ describe('Client', () => { expect(count).toEqual(1); }); + it('reads back a distribution that declares no media type', async () => { + // Only reachable without media-type criteria: those narrow a dataset’s + // distributions to the matching ones, so a filtered query never returns + // this distribution. + const results = await client.query({ + $id: 'http://bar.org/id/dataset/bar', + }); + + const mediaTypes = []; + for await (const dataset of results) { + mediaTypes.push( + ...dataset.distributions.map( + (distribution) => distribution.mediaType, + ), + ); + } + + expect(mediaTypes).toContain(undefined); + }); + it('throws an error for non-CONSTRUCT queries', async () => { const query = ` PREFIX dcat: