Skip to content

Close the derive hole: make path the whole statement of what the projection reads #607

Description

@ddeboer

Close the derive hole: make path the whole statement of what the projection reads.

Problem

derive receives the framed node and reads arbitrary predicates through irisOf / literalsOf / firstLiteralOf – exported, per project.ts, “so derivations can read arbitrary paths”. So SearchField.path is not a complete statement of what the projection reads.

In the Dataset Register’s Dataset type, 10 predicates are reachable via path and 13 only from inside a closure: class, format, conformsTo, additionalType, validUntil, iiifEntities, manifestsSampled, manifestsValidated, quadsValidated, schemaApNdeConformant, subjectUrisSampled, subjectUrisResolved, subjectNamespaceDurable. More than half.

Two consequences:

The drift is already real, not hypothetical: register-source.ts CONSTRUCTs dr:dateRead, and no field in the schema reads it. Nothing catches that.

Scope

Split the two concerns: path says what to read from the graph; derive says what to compute from what was read.

  • derive: (document) => unknown – drop the FramedNode argument. A derive reads fields declared before it, exactly as status_rank and terms already do.
  • Every predicate a derive needs becomes a real field with a path.
  • Honour the model’s existing promise for every kind. schema.ts says “a field exposes exactly the roles it declares”, so a field declaring no role (output / searchable / filterable / facetable / sortable) is internal: projected into the document so later derives can read it, pruned before the writer, absent from the collection definition. text already behaves this way (applyText and typesenseFields both no-op); keyword / reference / integer / number / date / boolean write and declare unconditionally. Make them consistent.
  • Unexport irisOf / literalsOf / firstLiteralOf.

Acceptance

  • The set of field.path over a type is exactly the set of predicates the projection reads.
  • A zero-role field of every kind reaches neither the document handed to a writer nor the collection definition.

Consequences

Breaking for @lde/search: consumers migrate derives to internal fields plus document reads.

Net code goes down. The Dataset Register additionally deletes parseNumber, parseBoolean and the qualityMeasurements WeakMap memo – an internal field is projected once and read by N derives, so the memo becomes structural rather than hand-rolled. An internal field costs nothing at the engine: not stored, not indexed, no RAM.

Relates to

Precondition for #548. Design context: #534.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Fields

    No fields configured for Feature.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions