Skip to content

Mapping-set selection can bridge document shapes via @nest, and the spec should say so #135

Open

Description

@simontaurus

Selection can bridge document shapes, not only vocabularies, because a promoted fragment may carry @nest. The selection rule already permits it: a processor writes { "@id": <synonym IRI>, ...fragment without x-oold-sssom }, and @nest is a member of a term definition. Nothing in the spec says so, and the surrounding prose reads as though a mapping set only renames terms.

This extends the normalization-by-chaining idea in #11 (expand, flatten) with the two pieces that turn it into a shape conversion: @nest in a promoted fragment, and framing before selection. It is a mapping-selection requirement in the sense of #108, and it depends on schema-derived frames as in #91.

Everything below is verified with pyld; both schemas pass the validator at v1.0.0-rc.2 (15/15 checks).

The pair

The flat shape, where the number sits on the quantity:

{
  "$schema": "https://oo-ld.org/latest/meta/oold-meta-schema.json",
  "$id": "QuantityValue.schema.json",
  "title": "QuantityValue",
  "description": "A number with a unit. The flat shape: the number sits on the quantity itself.",
  "@context": {
    "qudt": "http://qudt.org/schema/qudt/",
    "qunit": "http://qudt.org/vocab/unit/",
    "type": {"@id": "@type", "@container": "@set"},
    "value": "qudt:value",
    "unit": {"@id": "qudt:hasUnit", "@type": "@vocab"},
    "QuantityValue": "qudt:QuantityValue"
  },
  "type": "object",
  "properties": {
    "type": {"type": "array", "items": {"type": "string"}, "default": ["QuantityValue"]},
    "value": {"type": "number"},
    "unit": {"type": "string", "format": "iri-reference"}
  },
  "required": ["value"],
  "examples": [{"type": ["QuantityValue"], "value": 12.7, "unit": "qunit:SEC"}]
}

The same quantity in the shape EMMO uses, where the number is a node. Its QUDT reading is declared here, in the schema that wants the import, so the flat schema is untouched:

{
  "$schema": "https://oo-ld.org/latest/meta/oold-meta-schema.json",
  "$id": "NestedQuantityValue.schema.json",
  "title": "NestedQuantityValue",
  "description": "The same quantity in the shape EMMO uses: the number is a node reached from the quantity.",
  "@context": {
    "emmo": "https://w3id.org/emmo#",
    "qudt": "http://qudt.org/schema/qudt/",
    "type": {
      "@id": "@type",
      "@container": "@set"
    },
    "value": "emmo:hasQuantityValuePart",
    "numerical": "emmo:hasNumericalValue",
    "unit": {
      "@id": "emmo:hasMeasurementUnit",
      "@type": "@vocab"
    },
    "NestedQuantityValue": "emmo:QuantityValue",
    "qunit": "http://qudt.org/vocab/unit/"
  },
  "x-oold-context": {
    "value": {
      "@nest": {
        "x-oold-sssom": {
          "predicate_id": "skos:relatedMatch",
          "mapping_set_id": "https://w3id.org/oo-ld/schemas/mappings/qudt.sssom.tsv",
          "comment": "grouping only: under the QUDT reading the nesting carries no meaning"
        }
      }
    },
    "numerical": {
      "qudt:value": {
        "@nest": "value",
        "x-oold-sssom": {
          "predicate_id": "skos:exactMatch",
          "mapping_set_id": "https://w3id.org/oo-ld/schemas/mappings/qudt.sssom.tsv",
          "comment": "QUDT puts the number on the quantity; the nesting is dropped on export"
        }
      }
    }
  },
  "type": "object",
  "properties": {
    "type": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "default": [
        "NestedQuantityValue"
      ]
    },
    "value": {
      "type": "object",
      "properties": {
        "numerical": {
          "type": "number"
        }
      },
      "required": [
        "numerical"
      ]
    },
    "unit": {
      "type": "string",
      "format": "iri-reference"
    }
  },
  "required": [
    "value"
  ],
  "examples": [
    {
      "type": [
        "NestedQuantityValue"
      ],
      "value": {
        "numerical": 12.7
      },
      "unit": "qunit:SEC"
    }
  ]
}

Note the two synonyms: value maps to @nest, so under the QUDT reading the JSON nesting carries no meaning at all, and numerical maps to qudt:value while staying nested under value in the document.

What each reading produces

A. the nested instance under its own context
   <https://example.org/m1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <https://w3id.org/emmo#QuantityValue> .
   <https://example.org/m1> <https://w3id.org/emmo#hasMeasurementUnit> <http://qudt.org/vocab/unit/SEC> .
   <https://example.org/m1> <https://w3id.org/emmo#hasQuantityValuePart> _:b0 .
   _:b0 <https://w3id.org/emmo#hasNumericalValue> "1.27E1"^^<http://www.w3.org/2001/XMLSchema#double> .

B. the same instance with the qudt set selected
   <https://example.org/m1> <http://qudt.org/schema/qudt/value> "1.27E1"^^<http://www.w3.org/2001/XMLSchema#double> .
   <https://example.org/m1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <https://w3id.org/emmo#QuantityValue> .
   <https://example.org/m1> <https://w3id.org/emmo#hasMeasurementUnit> <http://qudt.org/vocab/unit/SEC> .

C. flat data imported: compacted with the promoted context
   {"@id": "https://example.org/m1", "type": ["qudt:QuantityValue"], "qudt:hasUnit": {"@id": "qunit:SEC"}, "value": {"numerical": 12.7}}

D. arriving nested RDF: compaction alone does not restore the shape
   {"@graph": [{"@id": "_:b0", "numerical": 12.7}, {"@id": "https://example.org/m1", "type": ["NestedQuantityValue"], "unit": "qunit:SEC", "value": {"@id": "_:b0"}}]}

E. framing with the schema restores it, and the set then reads it flat
   framed: {"@id": "https://example.org/m1", "type": ["NestedQuantityValue"], "unit": "qunit:SEC", "value": {"numerical": 12.7}}
   <https://example.org/m1> <http://qudt.org/schema/qudt/value> "1.27E1"^^<http://www.w3.org/2001/XMLSchema#double> .
   <https://example.org/m1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <https://w3id.org/emmo#QuantityValue> .
   <https://example.org/m1> <https://w3id.org/emmo#hasMeasurementUnit> <http://qudt.org/vocab/unit/SEC> .

F. the reverse as a context operation is rejected
    JsonLdError ('JSON-LD compact error; nested property must have an @nest value resolving to @nest.',)
Type: jsonld.SyntaxError
Code:

What this says

  1. A mapping set can decide the graph shape a document produces. A and B are the same file: an EMMO graph with a node, or a flat QUDT graph, chosen by selection. The type and unit keep their EMMO terms in B, since only value and numerical carry QUDT synonyms, which is the ordinary fallback behaviour.
  2. The importer declares the import. The @nest fragments live in the nested schema. C shows flat data compacting into the nested document that schema expects, without the flat schema knowing anything about it.
  3. A graph cannot be reshaped by a context. F is rejected with invalid @nest value: @nest may only point at a term that is itself @nest. That is correct, because dropping an intermediate node and reattaching its literal asserts something the graph does not contain.
  4. So arriving RDF is framed first. D shows compaction alone returning a flat list of node objects; E shows framing restoring the document, after which selection reads it either way. The frame follows from the schema, so this is a build step rather than a judgement, which is where Bundle the validation scripts as a reusable package (and/or fold into oold-python) #91 comes in.

Suggested spec additions

  1. In Selection, state that a promoted fragment may carry @nest, with a two-line example. The rule already allows it; only the prose is missing.
  2. State the boundary as a property rather than a limitation: a mapping set decides which graph a document produces; it cannot reshape a graph. Cite the invalid @nest value rule as the reason.
  3. Note that a conforming pipeline frames arriving RDF with the schema's frame before selection, so shape conversion is framing plus selection rather than a rule engine.

Filed from the reference-schema work, where the flat and nested quantity value is the standing example of two communities disagreeing about structure rather than about names.

Activity

  1. added
    scienceDomain-driven: science / materials science and engineering profile requests
    profileApplication-profile layer: raised by or affecting OO-LD profiles
    on Aug 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    profileApplication-profile layer: raised by or affecting OO-LD profilesscienceDomain-driven: science / materials science and engineering profile requests

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions