← Back to demo

Catena-X Round-Trip Query

A SPARQL SELECT over urn:graph:catx-samm reconstructs the original SAMM JSON payload via CONCAT aggregation. The result is asserted against the reference file material_data_test_pa6gf30.json to confirm the CatX → RDF conversion preserves all values without loss.

1. What the query verifies

Node A loads two files into urn:graph:catx-samm: the YARRRML-converted instance data (catx-samm.ttl) and the SAMM aspect model (catx-samm-model.ttl). The round-trip query reads both to reconstruct every section of the original CatX SAMM JSON — including the 547-entry PvT table — and verifies that the reconstructed JSON is semantically identical to the source document.

Reference file: material_data_test_pa6gf30.json (PA6GF30 material data, 5 top-level sections: materialInformation, mechanicalProperty, physicalProperty, thermophysicalProperty, pvt).

2. Query structure

Four correlated sub-SELECTs each aggregate a section into a JSON fragment, then the outer SELECT concatenates them. The SAMM aspect model's RDF property list (n3:properties/rdf:rest*/rdf:first) drives the general sections; thermophysical scalars, linearThermalExpansionCoefficient, and pvt are handled by dedicated sub-SELECTs because they require path traversal.

PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX n1:  <urn:samm:io.catenax.material_data:1.0.0#>
PREFIX n3:  <urn:samm:org.eclipse.esmf.samm:meta-model:2.2.0#>

SELECT (CONCAT(
  '{',
    ?sectionsJson, ',',
    '"thermophysicalProperty":{', ?thermoScalarsJson,
      ',"linearThermalExpansionCoefficient":{', ?ltecJson, '}}', ',',
    '"pvt":', ?pvtJson,
  '}'
) AS ?json)

FROM <urn:graph:catx-samm>
WHERE {

  # pvt — 547 entries, each a {temperature, specificVolume, pressure} object
  {
    SELECT ?mat
      (CONCAT('[', GROUP_CONCAT(?pvtEntry ; SEPARATOR=','), ']') AS ?pvtJson)
    WHERE {
      {
        SELECT ?mat ?pvt
          (CONCAT('{', GROUP_CONCAT(
            CONCAT('"', STRAFTER(STR(?p), '#'), '":',
                   IF(REGEX(STR(?v), '^-?[0-9]+(\\.[0-9]+)?([eE][+-]?[0-9]+)?$'),
                      STR(?v), CONCAT('"', STR(?v), '"')))
            ; SEPARATOR=',')
          , '}') AS ?pvtEntry)
        WHERE {
          ?mat a n1:MaterialData .
          ?mat n1:thermophysicalProperty/n1:pvt ?pvt .
          ?pvt ?p ?v .
          FILTER(isLiteral(?v) && ?p != rdf:type)
        }
        GROUP BY ?mat ?pvt
      }
    }
    GROUP BY ?mat
  }

  # linearThermalExpansionCoefficient scalars
  {
    SELECT ?mat
      (GROUP_CONCAT(
        CONCAT('"', STRAFTER(STR(?p), '#'), '":',
               IF(REGEX(STR(?v), '^-?[0-9]+(\\.[0-9]+)?([eE][+-]?[0-9]+)?$'),
                  STR(?v), CONCAT('"', STR(?v), '"')))
        ; SEPARATOR=',') AS ?ltecJson)
    WHERE {
      ?mat a n1:MaterialData .
      ?mat n1:thermophysicalProperty/n1:linearThermalExpansionCoefficient ?ltec .
      ?ltec ?p ?v .
      FILTER(isLiteral(?v) && ?p != rdf:type)
    }
    GROUP BY ?mat
  }

  # thermophysicalProperty scalars (humidity, meltingTemperature, glassTransitionTemperature, waterAbsorption)
  {
    SELECT ?mat
      (GROUP_CONCAT(
        CONCAT('"', STRAFTER(STR(?p), '#'), '":',
               IF(REGEX(STR(?v), '^-?[0-9]+(\\.[0-9]+)?([eE][+-]?[0-9]+)?$'),
                  STR(?v), CONCAT('"', STR(?v), '"')))
        ; SEPARATOR=',') AS ?thermoScalarsJson)
    WHERE {
      ?mat a n1:MaterialData .
      ?mat n1:thermophysicalProperty ?thermo .
      ?thermo ?p ?v .
      FILTER(isLiteral(?v) && ?p != rdf:type)
    }
    GROUP BY ?mat
  }

  # General sections via SAMM aspect model properties list
  # (materialInformation, mechanicalProperty, physicalProperty)
  {
    SELECT ?mat (GROUP_CONCAT(?sectionJson ; SEPARATOR=',') AS ?sectionsJson)
    WHERE {
      {
        SELECT ?mat ?sectionName
          (CONCAT('"', ?sectionName, '"":{"',
            GROUP_CONCAT(
              CONCAT('"', STRAFTER(STR(?p), '#'), '":',
                     IF(REGEX(STR(?v), '^-?[0-9]+(\\.[0-9]+)?([eE][+-]?[0-9]+)?$'),
                        STR(?v), CONCAT('"', STR(?v), '"')))
              ; SEPARATOR=','),
          '}') AS ?sectionJson)
        WHERE {
          ?mat a n1:MaterialData .
          n1:MaterialData n3:properties/rdf:rest*/rdf:first ?sectionProp .
          BIND(STRAFTER(STR(?sectionProp), '#') AS ?sectionName)
          FILTER(?sectionName != 'thermophysicalProperty')
          ?mat ?sectionProp ?sectionInst .
          ?sectionInst ?p ?v .
          FILTER(isLiteral(?v) && ?p != rdf:type)
        }
        GROUP BY ?mat ?sectionName
      }
    }
    GROUP BY ?mat
  }

}

3. Expected result

A single row with binding ?json containing a JSON object whose top-level keys match the original payload. Object key order depends on GROUP_CONCAT evaluation order (non-deterministic in SPARQL); the assertion uses canonical comparison (keys sorted recursively, array-of-objects sorted by serialisation) so order differences do not cause false failures.

SectionKeyValue
materialInformationmaterialNamePA6GF30
materialInformationmaterialIdentifierZ1234
mechanicalPropertyyoungsModulus9800
mechanicalPropertyimpactStrength74
mechanicalPropertystressAtBreak140
mechanicalPropertyflexuralStrength210
mechanicalPropertyflexuralModulus8600
mechanicalPropertystrainAtBreak3
physicalPropertydensity1530
thermophysicalPropertyhumidity2.1
thermophysicalPropertymeltingTemperature223
thermophysicalPropertyglassTransitionTemperature20
thermophysicalPropertywaterAbsorption6.5
thermophysicalProperty → linearThermalExpansionCoefficientParallel0.0025
thermophysicalProperty → linearThermalExpansionCoefficientTransverse0.0115
pvt(547 entries)temperature, specificVolume, pressure