Salta al contenuto
Note per Studenti Web semantico - RDF, linked open data e SPARQL

Web semantico - RDF, linked open data e SPARQL

In questa pagina 8

Il web dei documenti (HTML5 - struttura del documento, elementi semantici e formUn documento HTML5 ha la forma doctype (), html con lang, head (meta charset UTF-8, viewport, title, link, script) e body. Il parser HTML5 non dà mai errore: corregge (aggiunge il tbody, chiude i p). Gli elementi semantici (header, nav, main, section, article, aside, footer, figure, time) descrivono il ruolo del contenuto e danno ai lettori di schermo i punti di riferimento; div e span sono neutri. Link: a href (assoluto, relativo, #frammento, mailto), rel e target; immagini con alt obbligatorio. Tabelle: table, caption, thead, tbody, th con scope, td, colspan e rowspan, solo per dati. Form: action, method (GET mette i campi nella query string, per letture idempotenti e ricercabili; POST li mette nel corpo, per modificare lo stato), enctype (application/x-www-form-urlencoded predefinito, multipart/form-data per i file); si invia solo ciò che ha name; label for collega etichetta e campo; tipi di input (text, email, number, date, time, radio, checkbox, hidden...); validazione nativa con required, minlength, maxlength, min, max, step, pattern e type, aggirabile e quindi da ripetere sul server; novalidate la spegne. Accessibilità: lang, alt, label, gerarchia dei titoli, navigazione da tastiera, ARIA solo quando l'HTML nativo non basta.HTML5 - struttura del documento, elementi semantici e form →) è scritto per le persone: una pagina dice "Aula A1, 80 posti", ma per un programma è solo testo. Anche un'API JSON (Servizi REST in Java - progettazione delle risorse e implementazione con servletUn servizio REST espone risorse, non pagine: il server risponde con dati (JSON) e codici di stato, e la presentazione passa al client. Per progettare l'API si individuano i sostantivi del dominio (aula, prenotazione), si dà a collezioni ed elementi un URI (/api/aule, /api/aule/3, /api/prenotazioni, /api/prenotazioni/7: nomi al plurale, mai verbi), si usano i metodi HTTP con la semantica di RFC 9110 (GET sicuro e idempotente, POST crea e non è idempotente, PUT e DELETE idempotenti) e si filtrano le collezioni con la query (?aula=1&giorno=2026-10-19). I codici fanno parte del contratto: 200 lettura, 201 creazione con Location, 204 cancellazione, 400 richiesta malformata, 404 risorsa inesistente, 405 metodo non consentito con Allow, 409 conflitto (orario già occupato), 415 tipo di contenuto, 422 richiesta ben formata ma non elaborabile (aula inesistente), 500 guasto. Il JSON usa i nomi delle colonne (aula_id, ora_inizio, ora_fine) e gli errori hanno un corpo {"errore": "..."}. In Java una servlet mappata su /api/prenotazioni/* fa il dispatch sul path: getPathInfo() nullo o "/" indica la collezione, "/12" l'elemento; doGet, doPost e doDelete implementano i metodi, Gson converte oggetti e JSON, i metodi non previsti rispondono 405 con l'header Allow. Il servizio è senza stato: niente sessione, ogni richiesta porta tutto ciò che serve.Servizi REST in Java - progettazione delle risorse e implementazione con servlet →) ha un limite: i nomi dei campi (aula_id, posti) hanno un significato che conosce solo chi ha letto la documentazione di quel servizio. Il web semantico (Semantic Web, il "Web 3.0" del programma, Architettura delle applicazioni web - browser, server web e livelliUn'applicazione web è distribuita su più livelli (tier): presentazione (browser: HTML, CSS, JavaScript), logica applicativa (server web e application server) e dati (DBMS). Il client invia richieste HTTP, il server risponde; HTTP è senza stato, quindi lo stato dell'utente viaggia in cookie e sessioni. Il web server serve file statici, l'application server (container, per esempio Tomcat per le servlet Java) esegue codice che genera risposte dinamiche; spesso il web server fa da reverse proxy davanti all'application server. Il browser trasforma i byte della risposta in una pagina: decodifica, tokenizzazione e costruzione del DOM, CSS che diventa CSSOM, render tree, layout (reflow), paint e composizione; un script senza defer o async blocca il parsing, il CSS blocca il rendering. Origine = schema + host + porta; la same-origin policy limita ciò che uno script può leggere da altre origini. Web 1.0: pagine generate dal server, ogni azione ricarica la pagina; Web 2.0: AJAX, contenuti degli utenti, servizi REST; Web 3.0: dati con significato esplicito (RDF, linked data).Architettura delle applicazioni web - browser, server web e livelli →) vuole dati con significato esplicito e collegabili: ogni cosa ha un identificatore globale, i fatti si scrivono in forma standard e programmi diversi possono combinarli. Gli standard sono del W3C: RDF 1.1, Turtle, RDFS, OWL, SPARQL 1.1. Ritorna lo stesso esempio delle aule.

RDF: il modello a triple

RDF (Resource Description Framework) rappresenta ogni fatto come una tripla (triple) soggetto, predicato, oggetto:

  • il soggetto è la cosa di cui si parla (una risorsa);
  • il predicato è la proprietà, il tipo di relazione;
  • l'oggetto è il valore: un'altra risorsa oppure un letterale.

"L'aula A1 ha 80 posti" è la tripla (aulaA1, posti, 80); "la prenotazione 1 è nell'aula A1" è (pren1, inAula, aulaA1). Un insieme di triple è un grafo orientato etichettato: i nodi sono soggetti e oggetti, gli archi sono i predicati. Poiché l'oggetto di una tripla può essere soggetto di un'altra, i fatti si agganciano (è la differenza con una riga di tabella, che è chiusa):

pren1 ──inAula──> aulaA1 ──posti──> 80
  │                  └─────label──> "Aula A1"@it
  ├──studente──> "Rossi"
  └──giorno────> "2026-10-19"^^xsd:date

IRI e letterali

Le risorse si identificano con IRI (Internationalized Resource Identifier: un URI, Caching, autenticazione, tipi MIME e URI in HTTPLa cache HTTP riusa le risposte senza contattare il server finche' sono fresche (Cache-Control: max-age, Expires; in mancanza una durata euristica pari a circa il 10% del tempo trascorso da Last-Modified) e, quando sono scadute, le rivalida con una richiesta condizionale (If-None-Match con ETag, If-Modified-Since con Last-Modified) alla quale il server risponde 304 senza corpo; no-store vieta di memorizzare, no-cache obbliga a rivalidare, private esclude le cache condivise. L'autenticazione usa 401 con WWW-Authenticate e la risposta Authorization: Basic (base64 di utente:password, solo sopra TLS) o Digest (hash con nonce); 407 vale per i proxy. Content-Type porta il tipo MIME (tipo/sottotipo; parametri come charset e boundary), Accept e gli altri header Accept-* guidano la negoziazione. Un URI (RFC 3986) e' scheme://userinfo@host:porta/percorso?query#frammento, con percent-encoding %HH per i byte non ammessi e regole per risolvere i riferimenti relativi.Caching, autenticazione, tipi MIME e URI in HTTP →, in cui si ammettono anche caratteri Unicode), scritti tra parentesi angolari: <http://example.org/aule/aulaA1>. Un IRI è un nome globale: la stessa stringa in due dataset diversi indica la stessa cosa, ed è ciò che permette di collegare dati di origini diverse. Un IRI non deve per forza essere un indirizzo raggiungibile, ma è utile (vedi sotto).

I letterali (literals) sono valori: una stringa, con eventuale lingua ("Aula A1"@it) oppure un tipo di dato XML Schema ("80"^^xsd:integer, "2026-10-19"^^xsd:date). Senza tipo un letterale è una stringa. I letterali possono essere solo oggetti, mai soggetti. Esistono anche i nodi anonimi (blank nodes), risorse senza IRI, utili per strutture interne.

Dal relazionale a RDF

Una riga di tabella diventa un gruppo di triple con lo stesso soggetto:

Relazionale RDF
riga di aula (id 1) un soggetto, per esempio ex:aulaA1, con rdf:type ex:Aula
tabella aula una classe ex:Aula
colonne nome, posti proprietà rdfs:label, ex:posti
chiave esterna aula_id proprietà ex:inAula il cui oggetto è l'IRI dell'aula (un collegamento, non un numero)
valore nullo semplicemente nessuna tripla

Turtle

Turtle (Terse RDF Triple Language) è la sintassi testuale più leggibile. Regole:

  • @prefix ex: <http://example.org/aule/> . definisce un prefisso: ex:aulaA1 è l'abbreviazione di <http://example.org/aule/aulaA1>; ogni dichiarazione termina con un punto;
  • una tripla è soggetto predicato oggetto .;
  • a è l'abbreviazione di rdf:type ("è un");
  • il punto e virgola ripete il soggetto (altro predicato, altro oggetto), la virgola ripete soggetto e predicato (altro oggetto);
  • "testo"@it, "80"^^xsd:integer; i numeri e i booleani si possono scrivere senza virgolette (80, true);
  • # inizia un commento; le parentesi quadre scritte vuote o con proprietà dentro indicano un nodo anonimo.

Il dataset delle aule con le quattro prenotazioni dell'esempio:

turtle
@prefix ex:   <http://example.org/aule/> .
@prefix rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd:  <http://www.w3.org/2001/XMLSchema#> .

# --- vocabolario (RDFS): classi e proprietà ---
ex:Aula          a rdfs:Class ; rdfs:label "Aula"@it .
ex:Prenotazione  a rdfs:Class ; rdfs:label "Prenotazione"@it .
ex:posti      a rdf:Property ; rdfs:domain ex:Aula         ; rdfs:range xsd:integer .
ex:inAula     a rdf:Property ; rdfs:domain ex:Prenotazione ; rdfs:range ex:Aula .
ex:giorno     a rdf:Property ; rdfs:domain ex:Prenotazione ; rdfs:range xsd:date .
ex:oraInizio  a rdf:Property ; rdfs:domain ex:Prenotazione ; rdfs:range xsd:time .
ex:oraFine    a rdf:Property ; rdfs:domain ex:Prenotazione ; rdfs:range xsd:time .
ex:studente   a rdf:Property ; rdfs:domain ex:Prenotazione ; rdfs:range xsd:string .

# --- dati: le tre aule e le quattro prenotazioni dell'esempio ---
ex:aulaA1  a ex:Aula ; rdfs:label "Aula A1"@it ; ex:posti 80 .
ex:aulaB2  a ex:Aula ; rdfs:label "Aula B2"@it ; ex:posti 45 .
ex:labInfo a ex:Aula ; rdfs:label "Lab Info"@it ; ex:posti 30 .

ex:pren1  a ex:Prenotazione ; ex:inAula ex:aulaA1 ; ex:giorno "2026-10-19"^^xsd:date ;
          ex:oraInizio "09:00:00"^^xsd:time ; ex:oraFine "11:00:00"^^xsd:time ; ex:studente "Rossi" .
ex:pren2  a ex:Prenotazione ; ex:inAula ex:aulaA1 ; ex:giorno "2026-10-19"^^xsd:date ;
          ex:oraInizio "14:00:00"^^xsd:time ; ex:oraFine "16:00:00"^^xsd:time ; ex:studente "Bianchi" .
ex:pren3  a ex:Prenotazione ; ex:inAula ex:aulaB2 ; ex:giorno "2026-10-20"^^xsd:date ;
          ex:oraInizio "10:00:00"^^xsd:time ; ex:oraFine "12:00:00"^^xsd:time ; ex:studente "Verdi" .
ex:pren4  a ex:Prenotazione ; ex:inAula ex:labInfo ; ex:giorno "2026-10-21"^^xsd:date ;
          ex:oraInizio "09:00:00"^^xsd:time ; ex:oraFine "13:00:00"^^xsd:time ; ex:studente "Rossi" .

Parte per parte:

  • Le prime righe sono il vocabolario: ex:Aula e ex:Prenotazione sono classi; per ogni proprietà si dichiara a quale classe si applica (rdfs:domain) e che tipo di valori ha (rdfs:range).
  • ex:aulaA1 a ex:Aula ; rdfs:label "Aula A1"@it ; ex:posti 80 . sono tre triple con lo stesso soggetto (tipo, etichetta, posti); il 80 senza virgolette è un xsd:integer.
  • Per le prenotazioni, ex:inAula ex:aulaA1 collega il nodo della prenotazione a quello dell'aula: il grafo contiene il collegamento, e non c'è nulla da "joinare" con un valore comune.
  • Ore e giorni sono letterali tipizzati. Il totale è di 55 triple.
  • Il file è stato controllato con un piccolo analizzatore scritto per la prova (prefissi, punti e virgole, letterali, conteggio delle triple).

Altre serializzazioni: N-Triples (una tripla per riga, IRI completi, comoda da elaborare con programmi), RDF/XML (la prima, oggi poco usata), JSON-LD (JSON con un @context che associa i nomi ai IRI; permette di rendere "semantica" un'API JSON). Sono rappresentazioni dello stesso grafo.

Vocabolari e ontologie

Un vocabolario (o ontologia) è l'insieme di classi e proprietà con il loro significato, scritto anch'esso in RDF. Si riutilizzano vocabolari esistenti invece di inventare i propri (FOAF per le persone, Dublin Core per i metadati, schema.org per cose comuni): il riuso rende i dati compatibili.

RDFS (RDF Schema) è il vocabolario di base:

Termine Significato
rdfs:Class una classe di risorse
rdfs:subClassOf ogni istanza della prima classe è anche della seconda
rdfs:domain, rdfs:range la classe del soggetto e quella dell'oggetto di una proprietà
rdfs:label, rdfs:comment nome e descrizione leggibili

RDFS permette l'inferenza (reasoning): da ex:inAula rdfs:domain ex:Prenotazione e dalla tripla ex:pren5 ex:inAula ex:aulaA1, un ragionatore deduce ex:pren5 a ex:Prenotazione senza che sia scritto; da ex:Laboratorio rdfs:subClassOf ex:Aula deduce che ogni laboratorio è anche un'aula. Domain e range non sono vincoli (come i CHECK) ma fonte di deduzioni.

OWL (Web Ontology Language) aggiunge più espressività, in una riga: equivalenza di classi e proprietà (owl:equivalentClass), classi disgiunte, cardinalità, proprietà simmetriche o inverse, e soprattutto owl:sameAs, che dichiara che due IRI indicano la stessa cosa, con cui si collegano dataset diversi. Vale l'ipotesi del mondo aperto (open world assumption): ciò che non è dichiarato è sconosciuto, non falso, al contrario di un database relazionale dove una riga assente è un fatto negato.

Linked open data

I dati collegati (linked data) sono dati RDF pubblicati sul web in modo che si possano collegare; i dati aperti (open data) hanno una licenza che ne permette il riuso; insieme linked open data (LOD). Le quattro regole di Tim Berners-Lee:

  1. usare gli URI come nomi delle cose;
  2. usare URI HTTP, così che le persone (e i programmi) possano consultarli;
  3. quando si consulta un URI, restituire informazioni utili, usando gli standard (RDF, SPARQL);
  4. includere collegamenti ad altri URI, per scoprire altre cose.

Il passo 2-3 si chiama dereferenziazione: la richiesta HTTP all'IRI di una risorsa restituisce la sua descrizione, in HTML per il browser o in Turtle o JSON-LD per un programma, scelta con l'header Accept (negoziazione del contenuto, Caching, autenticazione, tipi MIME e URI in HTTPLa cache HTTP riusa le risposte senza contattare il server finche' sono fresche (Cache-Control: max-age, Expires; in mancanza una durata euristica pari a circa il 10% del tempo trascorso da Last-Modified) e, quando sono scadute, le rivalida con una richiesta condizionale (If-None-Match con ETag, If-Modified-Since con Last-Modified) alla quale il server risponde 304 senza corpo; no-store vieta di memorizzare, no-cache obbliga a rivalidare, private esclude le cache condivise. L'autenticazione usa 401 con WWW-Authenticate e la risposta Authorization: Basic (base64 di utente:password, solo sopra TLS) o Digest (hash con nonce); 407 vale per i proxy. Content-Type porta il tipo MIME (tipo/sottotipo; parametri come charset e boundary), Accept e gli altri header Accept-* guidano la negoziazione. Un URI (RFC 3986) e' scheme://userinfo@host:porta/percorso?query#frammento, con percent-encoding %HH per i byte non ammessi e regole per risolvere i riferimenti relativi.Caching, autenticazione, tipi MIME e URI in HTTP →):

http
GET /aule/aulaA1 HTTP/1.1
Host: example.org
Accept: text/turtle

La quarta regola è quella che crea la rete. Se il nostro dataset dicesse chi gestisce le aule, collegherebbe il proprio IRI a quello di un dataset pubblico come DBpedia (che estrae RDF da Wikipedia) o Wikidata:

turtle
@prefix owl: <http://www.w3.org/2002/07/owl#> .
ex:ateneo  owl:sameAs  <http://dbpedia.org/resource/University_of_Padua> .

Uno schema in cinque stelle misura la qualità dei dati aperti: 1 stella, dati disponibili sul web con licenza aperta (anche un PDF); 2 stelle, dati strutturati leggibili da una macchina (Excel); 3 stelle, formato non proprietario (CSV); 4 stelle, uso di IRI e standard RDF per identificare le cose; 5 stelle, collegamenti ad altri dati (la quarta regola).

SPARQL

SPARQL (SPARQL Protocol and RDF Query Language, versione 1.1) è il linguaggio di interrogazione dei grafi RDF, il corrispondente dell'SQL (SQL - interrogazioni, join e aggregazioniUn'interrogazione SQL ha almeno SELECT e FROM; le altre clausole (WHERE, GROUP BY, HAVING, ORDER BY) sono opzionali e HAVING richiede GROUP BY. Ordine logico di valutazione: FROM, WHERE, GROUP BY, HAVING, SELECT, DISTINCT, ORDER BY. SELECT corrisponde a π (ma senza DISTINCT non elimina i duplicati), WHERE a σ, gli alias ad AS/ρ, i join a ⋈ (INNER, LEFT, RIGHT, FULL). WHERE seleziona le righe prima del raggruppamento, HAVING seleziona i gruppi dopo. Le funzioni COUNT, SUM, AVG, MIN, MAX ignorano i NULL (COUNT(*) conta le righe, DISTINCT toglie i duplicati dentro la funzione); con un aggregato o con GROUP BY nella SELECT possono comparire solo colonne di raggruppamento o aggregati. LIKE usa % (qualsiasi sequenza) e _ (un carattere). I confronti con NULL non sono veri: serve IS NULL. Un filtro sulla tabella di destra di un LEFT JOIN va nella clausola ON, non in WHERE, altrimenti il join diventa interno. Con interi COUNT(a)/COUNT(b) è una divisione intera: serve CAST.SQL - interrogazioni, join e aggregazioni →). L'idea: si scrive uno schema di triple con variabili (che iniziano con ?) e il motore trova tutti i modi di sostituire le variabili in modo che le triple siano nel grafo.

sparql
PREFIX ex:   <http://example.org/aule/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>

SELECT ?nome ?posti
WHERE {
  ?aula  a            ex:Aula ;
         rdfs:label   ?nome ;
         ex:posti     ?posti .
  FILTER (?posti >= 45)
}
ORDER BY DESC(?posti)
  • PREFIX come in Turtle; SELECT indica le variabili da restituire; WHERE contiene il pattern.
  • ?aula a ex:Aula ; rdfs:label ?nome ; ex:posti ?posti è un insieme di tre triple con lo stesso soggetto (stessa sintassi ; di Turtle): è un pattern di grafo di base (basic graph pattern). Le variabili con lo stesso nome devono avere lo stesso valore: è l'equivalente di un join.
  • FILTER seleziona con un'espressione booleana (confronti, &&, ||, REGEX, LANG...).
  • ORDER BY, LIMIT, OFFSET, DISTINCT come in SQL.

Risultato sui dati delle aule (valutato sul grafo; vedi l'esercizio):

nome posti
Aula A1 80
Aula B2 45

(Lab Info ha 30 posti e il filtro lo esclude.) Il percorso lungo gli archi si fa semplicemente scrivendo più triple: le prenotazioni dell'Aula A1 il 19 ottobre, con chi le ha fatte.

sparql
PREFIX ex:  <http://example.org/aule/>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>

SELECT ?inizio ?fine ?studente
WHERE {
  ?p  ex:inAula     ex:aulaA1 ;
      ex:giorno     "2026-10-19"^^xsd:date ;
      ex:oraInizio  ?inizio ;
      ex:oraFine    ?fine ;
      ex:studente   ?studente .
}
ORDER BY STR(?inizio)
inizio fine studente
"09:00:00"^^xsd:time "11:00:00"^^xsd:time "Rossi"
"14:00:00"^^xsd:time "16:00:00"^^xsd:time "Bianchi"

ORDER BY STR(?inizio) ordina per la forma testuale dell'ora: in ISO 8601 l'ordine alfabetico è quello cronologico, e SPARQL non garantisce un ordine per i valori xsd:time.

Altri costrutti:

Costrutto Uso
OPTIONAL { ... } include i risultati anche se il pattern interno non trova nulla (come un outer join): le variabili restano non legate
UNION unione dei risultati di due pattern
FILTER NOT EXISTS { ... } esclude le soluzioni per cui il pattern interno ha risposta (negazione)
GROUP BY, COUNT, SUM, AVG, HAVING aggregazioni, come in SQL
ASK { ... } risponde true/false
CONSTRUCT { ... } WHERE { ... } produce un nuovo grafo invece di una tabella
DESCRIBE <iri> restituisce le triple che descrivono la risorsa

Tre esempi sul grafo delle aule. Le aule senza prenotazioni il 19 ottobre (risultato: Aula B2 e Lab Info):

sparql
SELECT ?nome
WHERE {
  ?aula a ex:Aula ; rdfs:label ?nome .
  FILTER NOT EXISTS { ?p ex:inAula ?aula ; ex:giorno "2026-10-19"^^xsd:date }
}

Gli studenti con più di una prenotazione (risultato: Rossi, 2):

sparql
SELECT ?studente (COUNT(?p) AS ?n)
WHERE { ?p a ex:Prenotazione ; ex:studente ?studente }
GROUP BY ?studente
HAVING (COUNT(?p) > 1)

La sovrapposizione di orario (la stessa regola inizio_esistente < nuova_fine e fine_esistente > nuovo_inizio della servlet), come interrogazione ASK: si può prenotare l'Aula A1 il 19 dalle 10:00 alle 12:00?

sparql
ASK {
  ?p ex:inAula ex:aulaA1 ; ex:giorno "2026-10-19"^^xsd:date ;
     ex:oraInizio ?i ; ex:oraFine ?f .
  FILTER (STR(?i) < "12:00:00" && STR(?f) > "10:00:00")
}

Risposta true (Rossi, 09:00-11:00 si sovrappone); con 11:00-14:00 la risposta è false. Il confronto è fatto sulle stringhe ISO per le stesse ragioni di ORDER BY STR.

Dove si eseguono

Le query girano su un triplestore (Apache Jena Fuseki, Blazegraph, GraphDB, ...) o su un endpoint SPARQL pubblico (DBpedia, Wikidata). Il protocollo SPARQL usa HTTP: la query va nel parametro query di una GET (o nel corpo di una POST) e il risultato torna in un formato scelto con Accept, come application/sparql-results+json:

http
GET /sparql?query=SELECT%20%3Fnome%20WHERE%20%7B%20...%20%7D HTTP/1.1
Accept: application/sparql-results+json

Quando si interroga un endpoint remoto da una pagina vale quanto detto su same-origin e CORS (AJAX e fetch - applicazioni Web 2.0 e MVC rivistoAJAX (Asynchronous JavaScript and XML, oggi quasi sempre con JSON) permette a JavaScript di chiedere dati al server in background e aggiornare solo una parte del DOM, senza ricaricare la pagina. L'oggetto storico è XMLHttpRequest (open, send, eventi onload e onerror); l'API moderna è fetch, che restituisce una Promise di Response (ok, status, json()). Una Promise rappresenta un risultato futuro (in attesa, mantenuta, rifiutata) e si concatena con then, catch e finally; async/await è la sintassi che la fa leggere come codice sequenziale, e dopo ogni await il resto della funzione riparte quando la Promise si risolve (il codice sincrono finisce prima, poi vengono le promise, poi i timer). Attenzione: fetch rifiuta la Promise solo per errori di rete; un 404 o un 500 sono risposte e si controllano con response.ok. Il client delle aule ha api.js (funzioni che chiamano l'API REST e lanciano ErroreApi se il codice non è 2xx) e pagina.js (legge il form, chiama api.js, aggiorna la tabella con textContent, un solo gestore di click per i bottoni di cancellazione). Il paradigma MVC viene rivisto: il server resta con model e controller REST ma restituisce solo dati, la view è nel browser (DOM costruito da JavaScript) e il controller della pagina sono i gestori di eventi; lo stato dell'interfaccia sta nel client e il server è senza stato. La same-origin policy (origine = schema, host, porta) impedisce a uno script di leggere risposte di altre origini; CORS permette al server di autorizzarle con Access-Control-Allow-Origin, e le richieste non semplici (JSON, DELETE) sono precedute da una richiesta OPTIONS di preflight.AJAX e fetch - applicazioni Web 2.0 e MVC rivisto →).

RDF e modello relazionale

Relazionale RDF
unità riga di una tabella tripla
identificatori chiavi locali alla tabella (id) IRI globali
schema fisso e dichiarato prima (CREATE TABLE) flessibile, il vocabolario si può estendere; non obbligatorio
collegamenti chiave esterna e join sul valore archi del grafo, anche tra dataset diversi
assenza di dato NULL o riga mancante nessuna tripla; vale il mondo aperto
interrogazione SQL SPARQL
vincoli PRIMARY KEY, CHECK, ... RDFS e OWL deducono, non vincolano (a controllare esistono linguaggi come SHACL)

Il relazionale è la scelta per i dati transazionali con regole di integrità (Modello relazionale - relazioni, chiavi e vincoliIl modello relazionale (Codd, IBM, 1970) rappresenta i dati con relazioni, cioè tabelle, ed è basato su valori: i legami tra relazioni sono confronti di valori, non puntatori, il che garantisce indipendenza dalle strutture fisiche, portabilità e bidirezionalità. Una relazione matematica è un sottoinsieme del prodotto cartesiano di n domini (insieme di n-uple ordinate, senza duplicati né ordine tra le righe); una relazione del modello è un insieme di tuple, funzioni dagli attributi ai valori del dominio, quindi non posizionale. Si distinguono schema di relazione e istanza, schema di base di dati (relazioni più vincoli) e istanza. NULL è un valore speciale fuori da ogni dominio. I vincoli di integrità sono intra-relazionali (di dominio, di tupla, di chiave) e inter-relazionali (integrità referenziale, con la chiave esterna). Superchiave: insieme di attributi che non ripete mai i valori; chiave: superchiave minimale; ogni relazione ne ha almeno una (tutti gli attributi); la chiave primaria è una chiave scelta, senza NULL.Modello relazionale - relazioni, chiavi e vincoli →); anche il modello concettuale (Modello entità-relazione - entità, relazioni e attributiIl modello entità-relazione (ER, Chen 1976) descrive il mini-mondo con entità (classi di oggetti autonomi), relazioni (associazioni tra due o più entità, di grado 2 o più) e attributi (proprietà locali di entità o relazioni su un dominio; NULL per l'informazione mancante, con tre significati; attributi composti). Ogni costrutto ha una semantica estensionale: un'entità è un insieme di oggetti (senza ripetizioni né ordine), una relazione è un sottoinsieme del prodotto cartesiano delle entità coinvolte (quindi la stessa coppia non può comparire due volte: per gli eventi ripetuti si introduce una nuova entità, come Sessione o Fattura), un attributo è una funzione totale dalle istanze a un dominio. Si sceglie un'entità invece di un attributo quando il concetto ha istanze con proprietà proprie o collegamenti; mai trasformare un'entità in relazione. Lo schema ER è documentato dal dizionario dei dati (entità, relazioni, attributi, vincoli esterni).Modello entità-relazione - entità, relazioni e attributi →) è un grafo di entità e relazioni, ma poi si traduce in tabelle. RDF serve per integrare dati di origini diverse con significato condiviso.

Errori tipici

  • Credere che un IRI debba corrispondere a una pagina esistente, o confondere IRI e letterale (ex:Rossi è una risorsa, "Rossi" è una stringa).
  • Dimenticare il punto finale o scambiare ; e , in Turtle; usare un prefisso senza dichiararlo.
  • Mettere un letterale come soggetto.
  • Trattare rdfs:domain e rdfs:range come vincoli di validità: sono premesse per inferenze.
  • Applicare l'ipotesi del mondo chiuso a RDF (l'assenza di una tripla non vuol dire che il fatto è falso).
  • In SPARQL: dimenticare di legare le variabili con lo stesso nome, o credere che ORDER BY sui xsd:time ordini sempre in modo cronologico.
  • Chiamare "linked data" un file RDF che non ha collegamenti ad altri dataset.

Domande d'esame

1. Che cos'è una tripla RDF? Scrivere in Turtle i fatti "l'aula Lab Info ha 30 posti" e "la prenotazione 4 è nel Lab Info il 21 ottobre 2026 ed è di Rossi". Traccia. Soggetto, predicato, oggetto, con soggetto e predicato IRI e oggetto IRI o letterale. ex:labInfo a ex:Aula ; rdfs:label "Lab Info"@it ; ex:posti 30 . e ex:pren4 a ex:Prenotazione ; ex:inAula ex:labInfo ; ex:giorno "2026-10-21"^^xsd:date ; ex:studente "Rossi" .

2. Enunciare le quattro regole dei linked data e dire come si collega un dataset a un altro. Che cosa afferma owl:sameAs? Traccia. URI come nomi, URI HTTP consultabili, informazioni utili in RDF/SPARQL quando si consulta, link ad altri URI. Con triple i cui oggetti sono IRI di altri dataset, o con owl:sameAs che dichiara che due IRI indicano la stessa cosa.

3. Scrivere la query SPARQL che restituisce nome e numero di prenotazioni di ciascuna aula, includendo le aule senza prenotazioni. Traccia. SELECT ?nome (COUNT(?p) AS ?n) WHERE { ?aula a ex:Aula ; rdfs:label ?nome . OPTIONAL { ?p ex:inAula ?aula } } GROUP BY ?nome: l'OPTIONAL mantiene le aule senza prenotazioni, dove ?p non è legata e COUNT(?p) dà 0. Con i dati dell'esempio: Aula A1 2, Aula B2 1, Lab Info 1.

Versione ripasso

Web semantico (Web 3.0). Dati con significato esplicito, collegabili tra origini diverse. Standard W3C: RDF 1.1, Turtle, RDFS, OWL, SPARQL 1.1. Limite dell'API JSON: il significato dei campi sta nella documentazione di quel servizio.

RDF. Tripla soggetto, predicato, oggetto; l'insieme è un grafo orientato etichettato. Soggetto e predicato sono risorse con IRI (URI con Unicode, nome globale); l'oggetto è IRI o letterale ("Aula A1"@it, "80"^^xsd:integer, "2026-10-19"^^xsd:date; senza tipo è una stringa); i letterali non sono mai soggetti. Nodi anonimi per risorse senza IRI.

Relazionale RDF
riga di aula soggetto (ex:aulaA1, a ex:Aula)
tabella classe
colonne proprietà (rdfs:label, ex:posti)
chiave esterna aula_id proprietà ex:inAula il cui oggetto è l'IRI dell'aula
NULL nessuna tripla

Turtle. @prefix ex: <http://example.org/aule/> . (ogni dichiarazione finisce con .); a = rdf:type; ; ripete il soggetto, , ripete soggetto e predicato; "testo"@it, ^^xsd:date, numeri senza virgolette; # commento. Altre sintassi per lo stesso grafo: N-Triples, RDF/XML, JSON-LD.

turtle
@prefix ex:   <http://example.org/aule/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd:  <http://www.w3.org/2001/XMLSchema#> .

ex:aulaA1  a ex:Aula ; rdfs:label "Aula A1"@it ; ex:posti 80 .
ex:aulaB2  a ex:Aula ; rdfs:label "Aula B2"@it ; ex:posti 45 .
ex:labInfo a ex:Aula ; rdfs:label "Lab Info"@it ; ex:posti 30 .

ex:pren1  a ex:Prenotazione ; ex:inAula ex:aulaA1 ; ex:giorno "2026-10-19"^^xsd:date ;
          ex:oraInizio "09:00:00"^^xsd:time ; ex:oraFine "11:00:00"^^xsd:time ; ex:studente "Rossi" .

Il dataset completo ha 55 triple (vocabolario, 3 aule, 4 prenotazioni). ex:inAula ex:aulaA1 è un collegamento nel grafo, non un join sul valore.

Vocabolari. RDFS: rdfs:Class, rdfs:subClassOf, rdfs:domain, rdfs:range, rdfs:label, rdfs:comment. Permette inferenze: da ex:inAula rdfs:domain ex:Prenotazione e ex:pren5 ex:inAula ex:aulaA1 si deduce ex:pren5 a ex:Prenotazione; domain e range non sono vincoli. OWL in una riga: equivalenze di classi, disgiunzioni, cardinalità, proprietà inverse, owl:sameAs (due IRI, stessa cosa). Mondo aperto: ciò che non è dichiarato è sconosciuto, non falso. Si riusano vocabolari esistenti (FOAF, Dublin Core, schema.org).

Linked open data. Le quattro regole di Berners-Lee: 1 URI come nomi delle cose; 2 URI HTTP consultabili; 3 informazioni utili in standard (RDF, SPARQL) quando si consulta (dereferenziazione, formato scelto con Accept); 4 link ad altri URI. Open: licenza che permette il riuso. Cinque stelle: 1 sul web con licenza aperta, 2 strutturato, 3 formato non proprietario, 4 IRI e RDF, 5 collegamenti ad altri dati.

turtle
ex:ateneo  owl:sameAs  <http://dbpedia.org/resource/University_of_Padua> .

SPARQL 1.1. Pattern di triple con variabili (?x); le variabili con lo stesso nome fanno da join. SELECT ... WHERE { ... }, FILTER, ORDER BY, LIMIT, DISTINCT, OPTIONAL (outer join), UNION, FILTER NOT EXISTS, GROUP BY con COUNT/SUM/AVG e HAVING, ASK (true/false), CONSTRUCT (nuovo grafo), DESCRIBE.

sparql
SELECT ?nome ?posti
WHERE {
  ?aula  a ex:Aula ; rdfs:label ?nome ; ex:posti ?posti .
  FILTER (?posti >= 45)
}
ORDER BY DESC(?posti)

Risultato: Aula A1 80, Aula B2 45.

sparql
SELECT ?inizio ?fine ?studente
WHERE {
  ?p  ex:inAula ex:aulaA1 ; ex:giorno "2026-10-19"^^xsd:date ;
      ex:oraInizio ?inizio ; ex:oraFine ?fine ; ex:studente ?studente .
}
ORDER BY STR(?inizio)

Risultato: 09:00:00-11:00:00 Rossi, 14:00:00-16:00:00 Bianchi. ORDER BY STR(...) perché SPARQL non garantisce l'ordine cronologico dei xsd:time.

  • Aule libere il 19: FILTER NOT EXISTS { ?p ex:inAula ?aula ; ex:giorno "2026-10-19"^^xsd:date } →\to Aula B2, Lab Info.
  • Studenti con più prenotazioni: GROUP BY ?studente HAVING (COUNT(?p) > 1) →\to Rossi 2.
  • Sovrapposizione come ASK: FILTER (STR(?i) < "12:00:00" && STR(?f) > "10:00:00") (inizio esistente prima della nuova fine e fine esistente dopo il nuovo inizio): 10:00-12:00 →\to true, 11:00-14:00 →\to false.
  • Conteggio anche a zero: OPTIONAL { ?p ex:inAula ?aula } con COUNT(?p) →\to A1 2, B2 1, Lab Info 1.
  • Esecuzione su un triplestore o un endpoint SPARQL (DBpedia, Wikidata); protocollo HTTP con query e risultati scelti con Accept (application/sparql-results+json).

RDF contro relazionale. Riga contro tripla; chiavi locali contro IRI globali; schema fisso contro flessibile; join sul valore contro archi, anche tra dataset; NULL contro nessuna tripla; mondo chiuso contro aperto; SQL contro SPARQL; vincoli reali (CHECK) contro RDFS/OWL che deducono (la validazione si fa con SHACL).

Errori tipici

  • IRI e letterale confusi (ex:Rossi contro "Rossi"); IRI creduto una pagina esistente.
  • Punto finale dimenticato, ; e , scambiati, prefisso non dichiarato; letterale come soggetto.
  • Domain e range letti come vincoli; mondo chiuso applicato a RDF.
  • Variabili SPARQL non legate fra loro; ORDER BY sui xsd:time senza STR.

Domande d'esame (tracce)

  • Tripla = soggetto, predicato, oggetto; ex:labInfo a ex:Aula ; rdfs:label "Lab Info"@it ; ex:posti 30 .
  • Quattro regole dei linked data; collegamenti con IRI di altri dataset o owl:sameAs.
  • Query con OPTIONAL e COUNT(?p) per includere le aule a zero prenotazioni.

Collegamenti: Architettura delle applicazioni web - browser, server web e livelliUn'applicazione web è distribuita su più livelli (tier): presentazione (browser: HTML, CSS, JavaScript), logica applicativa (server web e application server) e dati (DBMS). Il client invia richieste HTTP, il server risponde; HTTP è senza stato, quindi lo stato dell'utente viaggia in cookie e sessioni. Il web server serve file statici, l'application server (container, per esempio Tomcat per le servlet Java) esegue codice che genera risposte dinamiche; spesso il web server fa da reverse proxy davanti all'application server. Il browser trasforma i byte della risposta in una pagina: decodifica, tokenizzazione e costruzione del DOM, CSS che diventa CSSOM, render tree, layout (reflow), paint e composizione; un script senza defer o async blocca il parsing, il CSS blocca il rendering. Origine = schema + host + porta; la same-origin policy limita ciò che uno script può leggere da altre origini. Web 1.0: pagine generate dal server, ogni azione ricarica la pagina; Web 2.0: AJAX, contenuti degli utenti, servizi REST; Web 3.0: dati con significato esplicito (RDF, linked data).Architettura delle applicazioni web - browser, server web e livelli →, Servizi REST in Java - progettazione delle risorse e implementazione con servletUn servizio REST espone risorse, non pagine: il server risponde con dati (JSON) e codici di stato, e la presentazione passa al client. Per progettare l'API si individuano i sostantivi del dominio (aula, prenotazione), si dà a collezioni ed elementi un URI (/api/aule, /api/aule/3, /api/prenotazioni, /api/prenotazioni/7: nomi al plurale, mai verbi), si usano i metodi HTTP con la semantica di RFC 9110 (GET sicuro e idempotente, POST crea e non è idempotente, PUT e DELETE idempotenti) e si filtrano le collezioni con la query (?aula=1&giorno=2026-10-19). I codici fanno parte del contratto: 200 lettura, 201 creazione con Location, 204 cancellazione, 400 richiesta malformata, 404 risorsa inesistente, 405 metodo non consentito con Allow, 409 conflitto (orario già occupato), 415 tipo di contenuto, 422 richiesta ben formata ma non elaborabile (aula inesistente), 500 guasto. Il JSON usa i nomi delle colonne (aula_id, ora_inizio, ora_fine) e gli errori hanno un corpo {"errore": "..."}. In Java una servlet mappata su /api/prenotazioni/* fa il dispatch sul path: getPathInfo() nullo o "/" indica la collezione, "/12" l'elemento; doGet, doPost e doDelete implementano i metodi, Gson converte oggetti e JSON, i metodi non previsti rispondono 405 con l'header Allow. Il servizio è senza stato: niente sessione, ogni richiesta porta tutto ciò che serve.Servizi REST in Java - progettazione delle risorse e implementazione con servlet →, Esercizio - Triple RDF in Turtle e query SPARQL sulle aule (applicazione web).

Esercizi su questo argomento