Problem
A type that serves as a labelSource must declare its display field with the literal name label: labelFieldOf() does fieldNamed(searchType, 'label') (packages/search/src/schema.ts:691), and assertResolvableLabelSources() rejects the schema otherwise (schema.ts:702-734).
That word is a choice LDE makes, but the profile a schema declares to may make a different one. In SCHEMA-AP-NDE, CreativeWork, Person, Organization, Place and DefinedTerm all carry schema:name, and consumers expect the API to serve it as name. Today that is only possible for a type nothing resolves labels from – in Linked Open Limburg, CreativeWork serves name while Person, Organization, Place and Term are forced to serve label, purely because they are label sources. The data behind every one of them is schema:name. See https://codeberg.org/limburg/lol/issues/82.
So the surface word is decided by an internal role rather than by the profile the schema declares to, and the asymmetry it produces is not explicable to a consumer.
Proposal
Let a type name its own label field, defaulting to label:
export const term = defineSearchType({
name: 'Term',
class: `${SCHEMA}DefinedTerm`,
labelField: 'name',
fields: [{ name: 'name', kind: 'text', /* … */ }],
});
SearchTypeBase (schema.ts:286-302) gains readonly labelField?: string.
labelFieldOf() reads searchType.labelField ?? 'label'; the validation message at schema.ts:728-731 quotes the configured name.
search-typesense needs no change – it already resolves the field through labelFieldOf() (search.ts:172).
- In
search-api-graphql, the reference object type currently emits the key label as a literal (build-schema.ts:284); it would key off the label source’s configured name, so TermReference becomes { id, name }. ValueBucket.label stays as it is – it is per-facet-field, and a per-type name would make the bucket type non-uniform.
- The engine-port property
label (packages/search/src/engine.ts:185,197) can stay: it is a wire name, not a served one.
Non-breaking – every existing schema keeps the default.
Alternative considered
A field-level output alias consumed only by build-schema.ts, leaving labelFieldOf() untouched. Smaller, but it splits the name the schema declares from the name the API serves, and the declaration is the thing we read.
This does not undo #718 – the point of that change was that the label source, the reference resolving against it and the facet bucket all use one word. That still holds; this only lets a schema choose which word.
Problem
A type that serves as a
labelSourcemust declare its display field with the literal namelabel:labelFieldOf()doesfieldNamed(searchType, 'label')(packages/search/src/schema.ts:691), andassertResolvableLabelSources()rejects the schema otherwise (schema.ts:702-734).That word is a choice LDE makes, but the profile a schema declares to may make a different one. In SCHEMA-AP-NDE,
CreativeWork,Person,Organization,PlaceandDefinedTermall carryschema:name, and consumers expect the API to serve it asname. Today that is only possible for a type nothing resolves labels from – in Linked Open Limburg,CreativeWorkservesnamewhilePerson,Organization,PlaceandTermare forced to servelabel, purely because they are label sources. The data behind every one of them isschema:name. See https://codeberg.org/limburg/lol/issues/82.So the surface word is decided by an internal role rather than by the profile the schema declares to, and the asymmetry it produces is not explicable to a consumer.
Proposal
Let a type name its own label field, defaulting to
label:SearchTypeBase(schema.ts:286-302) gainsreadonly labelField?: string.labelFieldOf()readssearchType.labelField ?? 'label'; the validation message atschema.ts:728-731quotes the configured name.search-typesenseneeds no change – it already resolves the field throughlabelFieldOf()(search.ts:172).search-api-graphql, the reference object type currently emits the keylabelas a literal (build-schema.ts:284); it would key off the label source’s configured name, soTermReferencebecomes{ id, name }.ValueBucket.labelstays as it is – it is per-facet-field, and a per-type name would make the bucket type non-uniform.label(packages/search/src/engine.ts:185,197) can stay: it is a wire name, not a served one.Non-breaking – every existing schema keeps the default.
Alternative considered
A field-level output alias consumed only by
build-schema.ts, leavinglabelFieldOf()untouched. Smaller, but it splits the name the schema declares from the name the API serves, and the declaration is the thing we read.This does not undo #718 – the point of that change was that the label source, the reference resolving against it and the facet bucket all use one word. That still holds; this only lets a schema choose which word.