Summary
#832 (0.26.0) drops id from every Reference Type entry on the grounds that nothing read it. Linked Open Limburg reads it: a work’s IIIF Presentation manifest is an associatedMedia entry whose node IRI is the manifest URL, and the node states nothing else. With 0.26.0 iiifManifest comes out null on every work that has one.
Observed
limburg/lol#173 (Renovate, @lde/search 0.25.0 → 0.26.0) fails apps/search-indexer/test/iiif-manifest.test.ts:
× is read from the named manifest node among the associated media
AssertionError: expected undefined to be 'https://example.org/iiif/work/manifest'
The test exists for exactly this: it projects through @lde/search rather than hand-building the entry, so that “a projection change that starves the derive fails here rather than at deploy, where it shows as iiifManifest: null on every work”.
What the data looks like, Limburgs Museum dump, one of 518 manifests:
<…/38b5276a…> schema:isBasedOn <…/5a1b6571…/iiif.json> .
<…/5a1b6571…/iiif.json> schema:encodingFormat "application/ld+json;profile='http://iiif.io/api/presentation/3/context.json'" .
SCHEMA-AP-NDE § 4.2.6 has the same shape one hop up: the manifest is an associatedMedia entry with @id the manifest URI and encodingFormat the Presentation context. Neither shape carries contentUrl or any other property naming the URL. MediaObject and IiifService are Reference Types in LOL’s schema – a reproduction has no identity of its own – so after #832 the derive has nothing to read.
Why the #832 argument does not cover this
The PR shows that no engine concern reads a nested entry’s id: the collection does not declare it, welds use the flat companion, no query joins on it. All true. But a derive is a consumer too, and here the IRI is the value: the manifest URL is the payload, not an identity to join on. That is the one case where a named referent’s IRI is data, and it is what the profile prescribes.
Proposed
Keep the default from #832 – an entry of a Reference Type has no id – and let a Reference Type declare that it wants the referent’s IRI, so the projection carries it under a named field. Two shapes that would work from LOL’s side:
- a field whose
path is the node itself, e.g. { name: 'url', kind: 'reference', path: '@id' } (the framing already has @id; only the projection would need to map it), or
- a type-level flag,
keepId: true (or identity: 'iri'), that restores the id for that Reference Type only.
Either keeps the dedupe #832 wanted for role edges, where the IRI really is arbitrary, while a manifest entry stays addressable. I would take the first: it is a declaration on the field that wants the value, it reuses the @id framing already produces, and it adds no second notion of identity to a type that #832 just said has none. Until one exists LOL stays on @lde/search 0.25.x, which also blocks the @lde/search-indexer 0.18 line for it.
Summary
#832 (0.26.0) drops
idfrom every Reference Type entry on the grounds that nothing read it. Linked Open Limburg reads it: a work’s IIIF Presentation manifest is anassociatedMediaentry whose node IRI is the manifest URL, and the node states nothing else. With 0.26.0iiifManifestcomes outnullon every work that has one.Observed
limburg/lol#173 (Renovate,
@lde/search0.25.0 → 0.26.0) failsapps/search-indexer/test/iiif-manifest.test.ts:The test exists for exactly this: it projects through
@lde/searchrather than hand-building the entry, so that “a projection change that starves the derive fails here rather than at deploy, where it shows asiiifManifest: nullon every work”.What the data looks like, Limburgs Museum dump, one of 518 manifests:
SCHEMA-AP-NDE § 4.2.6 has the same shape one hop up: the manifest is an
associatedMediaentry with@idthe manifest URI andencodingFormatthe Presentation context. Neither shape carriescontentUrlor any other property naming the URL.MediaObjectandIiifServiceare Reference Types in LOL’s schema – a reproduction has no identity of its own – so after #832 the derive has nothing to read.Why the #832 argument does not cover this
The PR shows that no engine concern reads a nested entry’s id: the collection does not declare it, welds use the flat companion, no query joins on it. All true. But a
deriveis a consumer too, and here the IRI is the value: the manifest URL is the payload, not an identity to join on. That is the one case where a named referent’s IRI is data, and it is what the profile prescribes.Proposed
Keep the default from #832 – an entry of a Reference Type has no
id– and let a Reference Type declare that it wants the referent’s IRI, so the projection carries it under a named field. Two shapes that would work from LOL’s side:pathis the node itself, e.g.{ name: 'url', kind: 'reference', path: '@id' }(the framing already has@id; only the projection would need to map it), orkeepId: true(oridentity: 'iri'), that restores the id for that Reference Type only.Either keeps the dedupe #832 wanted for role edges, where the IRI really is arbitrary, while a manifest entry stays addressable. I would take the first: it is a declaration on the field that wants the value, it reuses the
@idframing already produces, and it adds no second notion of identity to a type that #832 just said has none. Until one exists LOL stays on@lde/search0.25.x, which also blocks the@lde/search-indexer0.18 line for it.