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:dateIRI 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 dirdf: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:
@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:Aulaeex:Prenotazionesono 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); il80senza virgolette è unxsd:integer.- Per le prenotazioni,
ex:inAula ex:aulaA1collega 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:
- usare gli URI come nomi delle cose;
- usare URI HTTP, così che le persone (e i programmi) possano consultarli;
- quando si consulta un URI, restituire informazioni utili, usando gli standard (RDF, SPARQL);
- 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 →):
GET /aule/aulaA1 HTTP/1.1
Host: example.org
Accept: text/turtleLa 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:
@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.
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)PREFIXcome in Turtle;SELECTindica le variabili da restituire;WHEREcontiene 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.FILTERseleziona con un'espressione booleana (confronti,&&,||,REGEX,LANG...).ORDER BY,LIMIT,OFFSET,DISTINCTcome 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.
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):
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):
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?
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:
GET /sparql?query=SELECT%20%3Fnome%20WHERE%20%7B%20...%20%7D HTTP/1.1
Accept: application/sparql-results+jsonRDF 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:domainerdfs:rangecome 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 BYsuixsd:timeordini 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.
@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.
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.
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.
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 }Aula B2, Lab Info. - Studenti con più prenotazioni:
GROUP BY ?studente HAVING (COUNT(?p) > 1)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:00true, 11:00-14:00false. - Conteggio anche a zero:
OPTIONAL { ?p ex:inAula ?aula }conCOUNT(?p)A1 2, B2 1, Lab Info 1. - Esecuzione su un triplestore o un endpoint SPARQL (DBpedia, Wikidata); protocollo HTTP con
querye risultati scelti conAccept(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:Rossicontro"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 BYsuixsd:timesenzaSTR.
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
OPTIONALeCOUNT(?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).