Semantic Web and Web 3.0 · class 11 · demo

One query, two graphs

Neither half answers alone: the manufacturer's name is only in the domain graph, the block only on the chain. ?certificate appears in both GRAPH blocks of examples/06-indexer/cross.rq, so it joins them.

Manufacturer?certificate?block?transaction?anchoredStatus
Acme Ldacertificate:2026-001 84129910x5ebcd5…937c1d trueanchored
Barros & Filhos, S.A.certificate:2026-002 84129920x607da5…57203e truerevoked

The first five columns are the result of cross.rq. Cerdeira is gone. Without OPTIONAL, a manufacturer with nothing on chain does not match at all: the query can no longer say «not anchored», which was the interesting answer. The Status column is not in cross.rq: the demo also reads onto:revoked of the same record ?r. Writing that column in SPARQL, with BIND and IF, is TODO 8 of the challenge.

How it ran

The query, as in the example (data/cross.rq):

PREFIX onto:   <https://data.csoares.me/onto#>
PREFIX schema: <https://schema.org/>
PREFIX prov:   <http://www.w3.org/ns/prov#>

SELECT ?manufacturer ?certificate ?block ?transaction ?anchored WHERE {
  # GRAPH restricts a pattern to one named graph of the dataset.
  GRAPH <https://data.csoares.me/graph/domain> {
    ?f a onto:Manufacturer ;
       schema:name ?manufacturer ;
       onto:hasCertificate ?certificate .
  }
  # OPTIONAL keeps the manufacturer even when the chain has nothing for it:
  # ?block and ?transaction are then simply unbound.
  OPTIONAL {
    GRAPH <https://data.csoares.me/graph/chain> {
      ?r a onto:OnChainRecord ;
         onto:document ?certificate ;
         onto:block ?block ;
         onto:transaction ?transaction .
    }
  }
  # BOUND(?x) is true when the OPTIONAL part matched.
  BIND (BOUND(?block) AS ?anchored)
}
ORDER BY ?manufacturer

There is no SPARQL engine in this process (rdflib, in cross.py, is Python). The same graph patterns are matched here in JavaScript (lib/rdf.mjs: bgp, optional), live, on every request:

GRAPH <…/graph/domain>   21 triples
    ?f a onto:Manufacturer .
    ?f schema:name ?manufacturer .
    ?f onto:hasCertificate ?certificate .
  → 3 rows

(plain join) GRAPH <…/graph/chain>   19 triples
    ?r a onto:OnChainRecord .
    ?r onto:document ?certificate .
    ?r onto:block ?block .
    ?r onto:transaction ?transaction .
  → 2 rows, 2 with ?block bound

BIND (BOUND(?block) AS ?anchored)
ORDER BY ?manufacturer

The domain graph

A copy of examples/06-indexer/domain.ttl, in <https://data.csoares.me/graph/domain>: three manufacturers, three certificates. Nothing in it says anything about the chain.

@prefix onto: <https://data.csoares.me/onto#> .
@prefix schema: <https://schema.org/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix certificate: <https://data.csoares.me/certificate/> .
@prefix manufacturer: <https://data.csoares.me/manufacturer/> .

manufacturer:501234567 a onto:Manufacturer ;
    schema:name "Acme Lda"@en ;
    onto:taxId "501234567" ;
    onto:hasCertificate certificate:2026-001 .

manufacturer:502345678 a onto:Manufacturer ;
    schema:name "Barros & Filhos, S.A."@en ;
    onto:taxId "502345678" ;
    onto:hasCertificate certificate:2026-002 .

manufacturer:503456789 a onto:Manufacturer ;
    schema:name "Cerdeira Industrial"@en ;
    onto:taxId "503456789" ;
    onto:hasCertificate certificate:2026-003 .

certificate:2026-001 a onto:Certificate ;
    schema:name "Supply certificate 2026-001"@en ;
    onto:validUntil "2027-12-31"^^xsd:date .

certificate:2026-002 a onto:Certificate ;
    schema:name "Supply certificate 2026-002"@en ;
    onto:validUntil "2027-06-30"^^xsd:date .

certificate:2026-003 a onto:Certificate ;
    schema:name "Supply certificate 2026-003"@en ;
    onto:validUntil "2027-03-31"^^xsd:date .

An in-memory chain (the Ethereum virtual machine, @ethereumjs/evm), in this one process: no test network, no wallet, no keys, no money. Nothing a visitor does is kept.